]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_tree.c
60e1c09782756a415ec2751939168ba83bbd689c
[apple/xnu.git] / bsd / miscfs / devfs / devfs_tree.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 /*
24 * Copyright 1997,1998 Julian Elischer. All rights reserved.
25 * julian@freebsd.org
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are
29 * met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
37 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
40 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
42 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
43 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 *
48 * devfs_tree.c
49 */
50
51 /*
52 * HISTORY
53 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
54 * - removed mounting of "hidden" mountpoint
55 * - fixed problem in which devnode->dn_vn pointer was not
56 * updated with the vnode returned from checkalias()
57 * - replaced devfs_vntodn() with a macro VTODN()
58 * - rewrote dev_finddir() to not use recursion
59 * - added locking to avoid data structure corruption (DEVFS_(UN)LOCK())
60 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
61 * - fixed problem with devfs_dntovn() checking the v_id against the
62 * value cached in the device node; a union mount on top of us causes
63 * the v_id to get incremented thus, we would end up returning a new
64 * vnode instead of the existing one that has the mounted_here
65 * field filled in; the net effect was that the filesystem mounted
66 * on top of us would never show up
67 * - added devfs_stats to store how many data structures are actually
68 * allocated
69 */
70
71 /* SPLIT_DEVS means each devfs uses a different devnode for the same device */
72 /* Otherwise the same device always ends up at the same vnode even if */
73 /* reached througgh a different devfs instance. The practical difference */
74 /* is that with the same vnode, chmods and chowns show up on all instances of */
75 /* a device. (etc) */
76
77 #define SPLIT_DEVS 1 /* maybe make this an option */
78 /*#define SPLIT_DEVS 1*/
79
80 #include <sys/param.h>
81 #include <sys/systm.h>
82 #include <sys/kernel.h>
83 #include <sys/conf.h>
84 #include <sys/malloc.h>
85 #include <sys/mount.h>
86 #include <sys/proc.h>
87 #include <sys/vnode.h>
88 #include <stdarg.h>
89
90 #include "devfs.h"
91 #include "devfsdefs.h"
92
93 struct lock__bsd__ devfs_lock; /* the "big switch" */
94 devdirent_t * dev_root = NULL; /* root of backing tree */
95 struct devfs_stats devfs_stats; /* hold stats */
96
97 #ifdef HIDDEN_MOUNTPOINT
98 static struct mount *devfs_hidden_mount;
99 #endif HIDDEN_MOINTPOINT
100
101 static int devfs_ready = 0;
102
103 #define NOCREATE FALSE
104 #define CREATE TRUE
105
106 /*
107 * Set up the root directory node in the backing plane
108 * This is happenning before the vfs system has been
109 * set up yet, so be careful about what we reference..
110 * Notice that the ops are by indirection.. as they haven't
111 * been set up yet!
112 * DEVFS has a hidden mountpoint that is used as the anchor point
113 * for the internal 'blueprint' version of the dev filesystem tree.
114 */
115 /*proto*/
116 int
117 devfs_sinit(void)
118 {
119 lockinit(&devfs_lock, PINOD, "devfs", 0, 0);
120 if (dev_add_entry("root", NULL, DEV_DIR, NULL, NULL, NULL,
121 &dev_root)) {
122 printf("devfs_sinit: dev_add_entry failed ");
123 return (EOPNOTSUPP);
124 }
125 #ifdef HIDDEN_MOUNTPOINT
126 MALLOC(devfs_hidden_mount, struct mount *, sizeof(struct mount),
127 M_MOUNT, M_WAITOK);
128 bzero(devfs_hidden_mount,sizeof(struct mount));
129
130 /* Initialize the default IO constraints */
131 mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
132 mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
133
134 devfs_mount(devfs_hidden_mount,"dummy",NULL,NULL,NULL);
135 dev_root->de_dnp->dn_dvm
136 = (struct devfsmount *)devfs_hidden_mount->mnt_data;
137 #endif HIDDEN_MOUNTPOINT
138 devfs_ready = 1;
139 return (0);
140 }
141
142 /***********************************************************************\
143 *************************************************************************
144 * Routines used to find our way to a point in the tree *
145 *************************************************************************
146 \***********************************************************************/
147
148
149 /***************************************************************\
150 * Search down the linked list off a dir to find "name" *
151 * return the devnode_t * for that node.
152 \***************************************************************/
153 /*proto*/
154 devdirent_t *
155 dev_findname(devnode_t * dir,char *name)
156 {
157 devdirent_t * newfp;
158 if (dir->dn_type != DEV_DIR) return 0;/*XXX*/ /* printf?*/
159
160 if (name[0] == '.')
161 {
162 if(name[1] == 0)
163 {
164 return dir->dn_typeinfo.Dir.myname;
165 }
166 if((name[1] == '.') && (name[2] == 0))
167 {
168 /* for root, .. == . */
169 return dir->dn_typeinfo.Dir.parent->dn_typeinfo.Dir.myname;
170 }
171 }
172 newfp = dir->dn_typeinfo.Dir.dirlist;
173 while(newfp)
174 {
175 if(!(strcmp(name,newfp->de_name)))
176 return newfp;
177 newfp = newfp->de_next;
178 }
179 return NULL;
180 }
181
182 #if 0
183 /***********************************************************************\
184 * Given a starting node (0 for root) and a pathname, return the node *
185 * for the end item on the path. It MUST BE A DIRECTORY. If the 'CREATE' *
186 * option is true, then create any missing nodes in the path and create *
187 * and return the final node as well. *
188 * This is used to set up a directory, before making nodes in it.. *
189 * *
190 * Warning: This function is RECURSIVE. *
191 \***********************************************************************/
192 int
193 dev_finddir(char * orig_path, /* find this dir (err if not dir) */
194 devnode_t * dirnode, /* starting point */
195 int create, /* create path? */
196 devnode_t * * dn_pp) /* returned */
197 {
198 devdirent_t * dirent_p;
199 devnode_t * dnp = NULL;
200 char pathbuf[DEVMAXPATHSIZE];
201 char *path;
202 char *name;
203 register char *cp;
204 int retval;
205
206
207 /***************************************\
208 * If no parent directory is given *
209 * then start at the root of the tree *
210 \***************************************/
211 if(!dirnode) dirnode = dev_root->de_dnp;
212
213 /***************************************\
214 * Sanity Checks *
215 \***************************************/
216 if (dirnode->dn_type != DEV_DIR) return ENOTDIR;
217 if(strlen(orig_path) > (DEVMAXPATHSIZE - 1)) return ENAMETOOLONG;
218
219
220 path = pathbuf;
221 strcpy(path,orig_path);
222
223 /***************************************\
224 * always absolute, skip leading / *
225 * get rid of / or // or /// etc. *
226 \***************************************/
227 while(*path == '/') path++;
228
229 /***************************************\
230 * If nothing left, then parent was it.. *
231 \***************************************/
232 if ( *path == '\0' ) {
233 *dn_pp = dirnode;
234 return 0;
235 }
236
237 /***************************************\
238 * find the next segment of the name *
239 \***************************************/
240 cp = name = path;
241 while((*cp != '/') && (*cp != 0)) {
242 cp++;
243 }
244
245 /***********************************************\
246 * Check to see if it's the last component *
247 \***********************************************/
248 if(*cp) {
249 path = cp + 1; /* path refers to the rest */
250 *cp = 0; /* name is now a separate string */
251 if(!(*path)) {
252 path = (char *)0; /* was trailing slash */
253 }
254 } else {
255 path = NULL; /* no more to do */
256 }
257
258 /***************************************\
259 * Start scanning along the linked list *
260 \***************************************/
261 dirent_p = dev_findname(dirnode,name);
262 if(dirent_p) { /* check it's a directory */
263 dnp = dirent_p->de_dnp;
264 if(dnp->dn_type != DEV_DIR) return ENOTDIR;
265 } else {
266 /***************************************\
267 * The required element does not exist *
268 * So we will add it if asked to. *
269 \***************************************/
270 if(!create) return ENOENT;
271
272 if((retval = dev_add_entry(name, dirnode,
273 DEV_DIR, NULL, NULL, NULL,
274 &dirent_p)) != 0) {
275 return retval;
276 }
277 dnp = dirent_p->de_dnp;
278 devfs_propogate(dirnode->dn_typeinfo.Dir.myname,dirent_p);
279 }
280 if(path != NULL) { /* decide whether to recurse more or return */
281 return (dev_finddir(path,dnp,create,dn_pp));
282 } else {
283 *dn_pp = dnp;
284 return 0;
285 }
286 }
287 #endif 0
288 /***********************************************************************\
289 * Given a starting node (0 for root) and a pathname, return the node *
290 * for the end item on the path. It MUST BE A DIRECTORY. If the 'CREATE' *
291 * option is true, then create any missing nodes in the path and create *
292 * and return the final node as well. *
293 * This is used to set up a directory, before making nodes in it.. *
294 \***********************************************************************/
295 /* proto */
296 int
297 dev_finddir(char * path,
298 devnode_t * dirnode,
299 int create,
300 devnode_t * * dn_pp)
301 {
302 devnode_t * dnp = NULL;
303 int error = 0;
304 char * scan;
305
306
307 if (!dirnode) /* dirnode == NULL means start at root */
308 dirnode = dev_root->de_dnp;
309
310 if (dirnode->dn_type != DEV_DIR)
311 return ENOTDIR;
312
313 if (strlen(path) > (DEVMAXPATHSIZE - 1))
314 return ENAMETOOLONG;
315
316 scan = path;
317
318 while (*scan == '/')
319 scan++;
320
321 *dn_pp = NULL;
322
323 while (1) {
324 char component[DEVMAXPATHSIZE];
325 devdirent_t * dirent_p;
326 char * start;
327
328 if (*scan == 0) {
329 /* we hit the end of the string, we're done */
330 *dn_pp = dirnode;
331 break;
332 }
333 start = scan;
334 while (*scan != '/' && *scan)
335 scan++;
336
337 strncpy(component, start, scan - start);
338 if (*scan == '/')
339 scan++;
340
341 dirent_p = dev_findname(dirnode, component);
342 if (dirent_p) {
343 dnp = dirent_p->de_dnp;
344 if (dnp->dn_type != DEV_DIR) {
345 error = ENOTDIR;
346 break;
347 }
348 }
349 else {
350 if (!create) {
351 error = ENOENT;
352 break;
353 }
354 error = dev_add_entry(component, dirnode,
355 DEV_DIR, NULL, NULL, NULL, &dirent_p);
356 if (error)
357 break;
358 dnp = dirent_p->de_dnp;
359 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, dirent_p);
360 }
361 dirnode = dnp; /* continue relative to this directory */
362 }
363 return (error);
364 }
365
366
367 /***********************************************************************\
368 * Add a new NAME element to the devfs *
369 * If we're creating a root node, then dirname is NULL *
370 * Basically this creates a new namespace entry for the device node *
371 * *
372 * Creates a name node, and links it to the supplied node *
373 \***********************************************************************/
374 /*proto*/
375 int
376 dev_add_name(char * name, devnode_t * dirnode, devdirent_t * back,
377 devnode_t * dnp, devdirent_t * *dirent_pp)
378 {
379 devdirent_t * dirent_p = NULL;
380
381 if(dirnode != NULL ) {
382 if(dirnode->dn_type != DEV_DIR) return(ENOTDIR);
383
384 if( dev_findname(dirnode,name))
385 return(EEXIST);
386 }
387 /*
388 * make sure the name is legal
389 * slightly misleading in the case of NULL
390 */
391 if (!name || (strlen(name) > (DEVMAXNAMESIZE - 1)))
392 return (ENAMETOOLONG);
393
394 /*
395 * Allocate and fill out a new directory entry
396 */
397 MALLOC(dirent_p, devdirent_t *, sizeof(devdirent_t),
398 M_DEVFSNAME, M_WAITOK);
399 if (!dirent_p) {
400 return ENOMEM;
401 }
402 bzero(dirent_p,sizeof(devdirent_t));
403
404 /* inherrit our parent's mount info */ /*XXX*/
405 /* a kludge but.... */
406 if(dirnode && ( dnp->dn_dvm == NULL)) {
407 dnp->dn_dvm = dirnode->dn_dvm;
408 /* if(!dnp->dn_dvm) printf("parent had null dvm "); */
409 }
410
411 /*
412 * Link the two together
413 * include the implicit link in the count of links to the devnode..
414 * this stops it from being accidentally freed later.
415 */
416 dirent_p->de_dnp = dnp;
417 dnp->dn_links++ ; /* implicit from our own name-node */
418
419 /*
420 * Make sure that we can find all the links that reference a node
421 * so that we can get them all if we need to zap the node.
422 */
423 if(dnp->dn_linklist) {
424 dirent_p->de_nextlink = dnp->dn_linklist;
425 dirent_p->de_prevlinkp = dirent_p->de_nextlink->de_prevlinkp;
426 dirent_p->de_nextlink->de_prevlinkp = &(dirent_p->de_nextlink);
427 *dirent_p->de_prevlinkp = dirent_p;
428 } else {
429 dirent_p->de_nextlink = dirent_p;
430 dirent_p->de_prevlinkp = &(dirent_p->de_nextlink);
431 }
432 dnp->dn_linklist = dirent_p;
433
434 /*
435 * If the node is a directory, then we need to handle the
436 * creation of the .. link.
437 * A NULL dirnode indicates a root node, so point to ourself.
438 */
439 if(dnp->dn_type == DEV_DIR) {
440 dnp->dn_typeinfo.Dir.myname = dirent_p;
441 /*
442 * If we are unlinking from an old dir, decrement its links
443 * as we point our '..' elsewhere
444 * Note: it's up to the calling code to remove the
445 * us from the original directory's list
446 */
447 if(dnp->dn_typeinfo.Dir.parent) {
448 dnp->dn_typeinfo.Dir.parent->dn_links--;
449 }
450 if(dirnode) {
451 dnp->dn_typeinfo.Dir.parent = dirnode;
452 } else {
453 dnp->dn_typeinfo.Dir.parent = dnp;
454 }
455 dnp->dn_typeinfo.Dir.parent->dn_links++; /* account for the new '..' */
456 }
457
458 /*
459 * put the name into the directory entry.
460 */
461 strcpy(dirent_p->de_name, name);
462
463
464 /*
465 * Check if we are not making a root node..
466 * (i.e. have parent)
467 */
468 if(dirnode) {
469 /*
470 * Put it on the END of the linked list of directory entries
471 */
472 int len;
473
474 dirent_p->de_parent = dirnode; /* null for root */
475 dirent_p->de_prevp = dirnode->dn_typeinfo.Dir.dirlast;
476 dirent_p->de_next = *(dirent_p->de_prevp); /* should be NULL */
477 /*right?*/
478 *(dirent_p->de_prevp) = dirent_p;
479 dirnode->dn_typeinfo.Dir.dirlast = &(dirent_p->de_next);
480 dirnode->dn_typeinfo.Dir.entrycount++;
481 dirnode->dn_len += strlen(name) + 8;/*ok, ok?*/
482 }
483
484 *dirent_pp = dirent_p;
485 DEVFS_INCR_ENTRIES();
486 return 0 ;
487 }
488
489
490 /***********************************************************************\
491 * Add a new element to the devfs plane. *
492 * *
493 * Creates a new dev_node to go with it if the prototype should not be *
494 * reused. (Is a DIR, or we select SPLIT_DEVS at compile time) *
495 * typeinfo gives us info to make our node if we don't have a prototype. *
496 * If typeinfo is null and proto exists, then the typeinfo field of *
497 * the proto is used intead in the CREATE case. *
498 * note the 'links' count is 0 (except if a dir) *
499 * but it is only cleared on a transition *
500 * so this is ok till we link it to something *
501 * Even in SPLIT_DEVS mode, *
502 * if the node already exists on the wanted plane, just return it *
503 \***********************************************************************/
504 /*proto*/
505 int
506 dev_add_node(int entrytype, devnode_type_t * typeinfo, devnode_t * proto,
507 devnode_t * *dn_pp, struct devfsmount *dvm)
508 {
509 devnode_t * dnp = NULL;
510
511 #if defined SPLIT_DEVS
512 /*
513 * If we have a prototype, then check if there is already a sibling
514 * on the mount plane we are looking at, if so, just return it.
515 */
516 if (proto) {
517 dnp = proto->dn_nextsibling;
518 while( dnp != proto) {
519 if (dnp->dn_dvm == dvm) {
520 *dn_pp = dnp;
521 return (0);
522 }
523 dnp = dnp->dn_nextsibling;
524 }
525 if (typeinfo == NULL)
526 typeinfo = &(proto->dn_typeinfo);
527 }
528 #else /* SPLIT_DEVS */
529 if ( proto ) {
530 switch (proto->type) {
531 case DEV_BDEV:
532 case DEV_CDEV:
533 *dn_pp = proto;
534 return 0;
535 }
536 }
537 #endif /* SPLIT_DEVS */
538 MALLOC(dnp, devnode_t *, sizeof(devnode_t), M_DEVFSNODE, M_WAITOK);
539 if (!dnp) {
540 return ENOMEM;
541 }
542
543 /*
544 * If we have a proto, that means that we are duplicating some
545 * other device, which can only happen if we are not at the back plane
546 */
547 if(proto) {
548 bcopy(proto, dnp, sizeof(devnode_t));
549 dnp->dn_links = 0;
550 dnp->dn_linklist = NULL;
551 dnp->dn_vn = NULL;
552 dnp->dn_len = 0;
553 /* add to END of siblings list */
554 dnp->dn_prevsiblingp = proto->dn_prevsiblingp;
555 *(dnp->dn_prevsiblingp) = dnp;
556 dnp->dn_nextsibling = proto;
557 proto->dn_prevsiblingp = &(dnp->dn_nextsibling);
558 } else {
559 struct timeval tv;
560
561 /*
562 * We have no prototype, so start off with a clean slate
563 */
564 tv = time;
565 bzero(dnp,sizeof(devnode_t));
566 dnp->dn_type = entrytype;
567 dnp->dn_nextsibling = dnp;
568 dnp->dn_prevsiblingp = &(dnp->dn_nextsibling);
569 dnp->dn_atime.tv_sec = tv.tv_sec;
570 dnp->dn_mtime.tv_sec = tv.tv_sec;
571 dnp->dn_ctime.tv_sec = tv.tv_sec;
572 }
573 dnp->dn_dvm = dvm;
574
575 /*
576 * fill out the dev node according to type
577 */
578 switch(entrytype) {
579 case DEV_DIR:
580 /*
581 * As it's a directory, make sure
582 * it has a null entries list
583 */
584 dnp->dn_typeinfo.Dir.dirlast = &(dnp->dn_typeinfo.Dir.dirlist);
585 dnp->dn_typeinfo.Dir.dirlist = (devdirent_t *)0;
586 dnp->dn_typeinfo.Dir.entrycount = 0;
587 /* until we know better, it has a null parent pointer*/
588 dnp->dn_typeinfo.Dir.parent = NULL;
589 dnp->dn_links++; /* for .*/
590 dnp->dn_typeinfo.Dir.myname = NULL;
591 /*
592 * make sure that the ops associated with it are the ops
593 * that we use (by default) for directories
594 */
595 dnp->dn_ops = &devfs_vnodeop_p;
596 dnp->dn_mode |= 0555; /* default perms */
597 break;
598 case DEV_SLNK:
599 /*
600 * As it's a symlink allocate and store the link info
601 * Symlinks should only ever be created by the user,
602 * so they are not on the back plane and should not be
603 * propogated forward.. a bit like directories in that way..
604 * A symlink only exists on one plane and has its own
605 * node.. therefore we might be on any random plane.
606 */
607 MALLOC(dnp->dn_typeinfo.Slnk.name, char *,
608 typeinfo->Slnk.namelen+1,
609 M_DEVFSNODE, M_WAITOK);
610 if (!dnp->dn_typeinfo.Slnk.name) {
611 FREE(dnp,M_DEVFSNODE);
612 return ENOMEM;
613 }
614 strncpy(dnp->dn_typeinfo.Slnk.name, typeinfo->Slnk.name,
615 typeinfo->Slnk.namelen);
616 dnp->dn_typeinfo.Slnk.name[typeinfo->Slnk.namelen] = '\0';
617 dnp->dn_typeinfo.Slnk.namelen = typeinfo->Slnk.namelen;
618 DEVFS_INCR_STRINGSPACE(dnp->dn_typeinfo.Slnk.namelen + 1);
619 dnp->dn_ops = &devfs_vnodeop_p;
620 dnp->dn_mode |= 0555; /* default perms */
621 break;
622 case DEV_CDEV:
623 case DEV_BDEV:
624 /*
625 * Make sure it has DEVICE type ops
626 * and device specific fields are correct
627 */
628 dnp->dn_ops = &devfs_spec_vnodeop_p;
629 dnp->dn_typeinfo.dev = typeinfo->dev;
630 break;
631 default:
632 return EINVAL;
633 }
634
635 *dn_pp = dnp;
636 DEVFS_INCR_NODES();
637 return 0 ;
638 }
639
640
641 /*proto*/
642 void
643 devnode_free(devnode_t * dnp)
644 {
645 if (dnp->dn_type == DEV_SLNK) {
646 DEVFS_DECR_STRINGSPACE(dnp->dn_typeinfo.Slnk.namelen + 1);
647 FREE(dnp->dn_typeinfo.Slnk.name,M_DEVFSNODE);
648 }
649 FREE(dnp, M_DEVFSNODE);
650 DEVFS_DECR_NODES();
651 return;
652 }
653
654 /*proto*/
655 void
656 devfs_dn_free(devnode_t * dnp)
657 {
658 if(--dnp->dn_links <= 0 ) /* can be -1 for initial free, on error */
659 {
660 /*probably need to do other cleanups XXX */
661 if (dnp->dn_nextsibling != dnp) {
662 devnode_t * * prevp = dnp->dn_prevsiblingp;
663 *prevp = dnp->dn_nextsibling;
664 dnp->dn_nextsibling->dn_prevsiblingp = prevp;
665
666 }
667 if (dnp->dn_vn == NULL) {
668 #if 0
669 printf("devfs_dn_free: free'ing %x\n", (unsigned int)dnp);
670 #endif 0
671 devnode_free(dnp); /* no accesses/references */
672 }
673 else {
674 #if 0
675 printf("devfs_dn_free: marking %x for deletion\n",
676 (unsigned int)dnp);
677 #endif 0
678 dnp->dn_delete = TRUE;
679 }
680 }
681 }
682
683 /***********************************************************************\
684 * Front Node Operations *
685 * Add or delete a chain of front nodes *
686 \***********************************************************************/
687
688 /***********************************************************************\
689 * Given a directory backing node, and a child backing node, add the *
690 * appropriate front nodes to the front nodes of the directory to *
691 * represent the child node to the user *
692 * *
693 * on failure, front nodes will either be correct or not exist for each *
694 * front dir, however dirs completed will not be stripped of completed *
695 * frontnodes on failure of a later frontnode *
696 * *
697 * This allows a new node to be propogated through all mounted planes *
698 * *
699 \***********************************************************************/
700 /*proto*/
701 int
702 devfs_propogate(devdirent_t * parent,devdirent_t * child)
703 {
704 int error;
705 devdirent_t * newnmp;
706 devnode_t * dnp = child->de_dnp;
707 devnode_t * pdnp = parent->de_dnp;
708 devnode_t * adnp = parent->de_dnp;
709 int type = child->de_dnp->dn_type;
710
711 /***********************************************\
712 * Find the other instances of the parent node *
713 \***********************************************/
714 for (adnp = pdnp->dn_nextsibling;
715 adnp != pdnp;
716 adnp = adnp->dn_nextsibling)
717 {
718 /*
719 * Make the node, using the original as a prototype)
720 * if the node already exists on that plane it won't be
721 * re-made..
722 */
723 if ((error = dev_add_entry(child->de_name, adnp, type,
724 NULL, dnp, adnp->dn_dvm,
725 &newnmp)) != 0) {
726 printf("duplicating %s failed\n",child->de_name);
727 }
728 }
729 return 0; /* for now always succeed */
730 }
731
732 /***********************************************************************
733 * remove all instances of this devicename [for backing nodes..]
734 * note.. if there is another link to the node (non dir nodes only)
735 * then the devfs_node will still exist as the ref count will be non-0
736 * removing a directory node will remove all sup-nodes on all planes (ZAP)
737 *
738 * Used by device drivers to remove nodes that are no longer relevant
739 * The argument is the 'cookie' they were given when they created the node
740 * this function is exported.. see devfs.h
741 ***********************************************************************/
742 void
743 devfs_remove(void *dirent_p)
744 {
745 devnode_t * dnp = ((devdirent_t *)dirent_p)->de_dnp;
746 devnode_t * dnp2;
747 boolean_t funnel_state;
748
749 funnel_state = thread_funnel_set(kernel_flock, TRUE);
750
751 if (!devfs_ready) {
752 printf("devfs_remove: not ready for devices!\n");
753 goto out;
754 }
755
756 DEVFS_LOCK(0);
757
758 /* keep removing the next sibling till only we exist. */
759 while((dnp2 = dnp->dn_nextsibling) != dnp) {
760
761 /*
762 * Keep removing the next front node till no more exist
763 */
764 dnp->dn_nextsibling = dnp2->dn_nextsibling;
765 dnp->dn_nextsibling->dn_prevsiblingp = &(dnp->dn_nextsibling);
766 dnp2->dn_nextsibling = dnp2;
767 dnp2->dn_prevsiblingp = &(dnp2->dn_nextsibling);
768 while(dnp2->dn_linklist) {
769 dev_free_name(dnp2->dn_linklist);
770 }
771 }
772
773 /*
774 * then free the main node
775 * If we are not running in SPLIT_DEVS mode, then
776 * THIS is what gets rid of the propogated nodes.
777 */
778 while(dnp->dn_linklist) {
779 dev_free_name(dnp->dn_linklist);
780 }
781 DEVFS_UNLOCK(0);
782 out:
783 (void) thread_funnel_set(kernel_flock, funnel_state);
784 return ;
785 }
786
787
788 /***************************************************************
789 * duplicate the backing tree into a tree of nodes hung off the
790 * mount point given as the argument. Do this by
791 * calling dev_dup_entry which recurses all the way
792 * up the tree..
793 **************************************************************/
794 /*proto*/
795 int
796 dev_dup_plane(struct devfsmount *devfs_mp_p)
797 {
798 devdirent_t * new;
799 int error = 0;
800
801 if ((error = dev_dup_entry(NULL, dev_root, &new, devfs_mp_p)))
802 return error;
803 devfs_mp_p->plane_root = new;
804 return error;
805 }
806
807
808
809 /***************************************************************\
810 * Free a whole plane
811 \***************************************************************/
812 /*proto*/
813 void
814 devfs_free_plane(struct devfsmount *devfs_mp_p)
815 {
816 devdirent_t * dirent_p;
817
818 dirent_p = devfs_mp_p->plane_root;
819 if(dirent_p) {
820 dev_free_hier(dirent_p);
821 dev_free_name(dirent_p);
822 }
823 devfs_mp_p->plane_root = NULL;
824 }
825
826 /***************************************************************\
827 * Create and link in a new front element.. *
828 * Parent can be 0 for a root node *
829 * Not presently usable to make a symlink XXX *
830 * (Ok, symlinks don't propogate)
831 * recursively will create subnodes corresponding to equivalent *
832 * child nodes in the base level *
833 \***************************************************************/
834 /*proto*/
835 int
836 dev_dup_entry(devnode_t * parent, devdirent_t * back, devdirent_t * *dnm_pp,
837 struct devfsmount *dvm)
838 {
839 devdirent_t * entry_p;
840 devdirent_t * newback;
841 devdirent_t * newfront;
842 int error;
843 devnode_t * dnp = back->de_dnp;
844 int type = dnp->dn_type;
845
846 /*
847 * go get the node made (if we need to)
848 * use the back one as a prototype
849 */
850 if ((error = dev_add_entry(back->de_name, parent, type,
851 NULL, dnp,
852 parent?parent->dn_dvm:dvm, &entry_p)) != 0) {
853 printf("duplicating %s failed\n",back->de_name);
854 }
855
856 /*
857 * If we have just made the root, then insert the pointer to the
858 * mount information
859 */
860 if(dvm) {
861 entry_p->de_dnp->dn_dvm = dvm;
862 }
863
864 /*
865 * If it is a directory, then recurse down all the other
866 * subnodes in it....
867 * note that this time we don't pass on the mount info..
868 */
869 if (type == DEV_DIR)
870 {
871 for(newback = back->de_dnp->dn_typeinfo.Dir.dirlist;
872 newback; newback = newback->de_next)
873 {
874 if((error = dev_dup_entry(entry_p->de_dnp,
875 newback, &newfront, NULL)) != 0)
876 {
877 break; /* back out with an error */
878 }
879 }
880 }
881 *dnm_pp = entry_p;
882 return error;
883 }
884
885 /***************************************************************\
886 * Free a name node *
887 * remember that if there are other names pointing to the *
888 * dev_node then it may not get freed yet *
889 * can handle if there is no dnp *
890 \***************************************************************/
891 /*proto*/
892 int
893 dev_free_name(devdirent_t * dirent_p)
894 {
895 devnode_t * parent = dirent_p->de_parent;
896 devnode_t * dnp = dirent_p->de_dnp;
897
898 if(dnp) {
899 if(dnp->dn_type == DEV_DIR)
900 {
901 devnode_t * p;
902
903 if(dnp->dn_typeinfo.Dir.dirlist)
904 return (ENOTEMPTY);
905 p = dnp->dn_typeinfo.Dir.parent;
906 devfs_dn_free(dnp); /* account for '.' */
907 devfs_dn_free(p); /* '..' */
908 }
909 /*
910 * unlink us from the list of links for this node
911 * If we are the only link, it's easy!
912 * if we are a DIR of course there should not be any
913 * other links.
914 */
915 if(dirent_p->de_nextlink == dirent_p) {
916 dnp->dn_linklist = NULL;
917 } else {
918 if(dnp->dn_linklist == dirent_p) {
919 dnp->dn_linklist = dirent_p->de_nextlink;
920 }
921 dirent_p->de_nextlink->de_prevlinkp
922 = dirent_p->de_prevlinkp;
923 *dirent_p->de_prevlinkp = dirent_p->de_nextlink;
924 }
925 devfs_dn_free(dnp);
926 }
927
928 /*
929 * unlink ourselves from the directory on this plane
930 */
931 if(parent) /* if not fs root */
932 {
933 if( (*dirent_p->de_prevp = dirent_p->de_next) )/* yes, assign */
934 {
935 dirent_p->de_next->de_prevp = dirent_p->de_prevp;
936 }
937 else
938 {
939 parent->dn_typeinfo.Dir.dirlast
940 = dirent_p->de_prevp;
941 }
942 parent->dn_typeinfo.Dir.entrycount--;
943 parent->dn_len -= strlen(dirent_p->de_name) + 8;
944 }
945
946 DEVFS_DECR_ENTRIES();
947 FREE(dirent_p,M_DEVFSNAME);
948 return 0;
949 }
950
951 /***************************************************************\
952 * Free a hierarchy starting at a directory node name *
953 * remember that if there are other names pointing to the *
954 * dev_node then it may not get freed yet *
955 * can handle if there is no dnp *
956 * leave the node itself allocated. *
957 \***************************************************************/
958 /*proto*/
959 void
960 dev_free_hier(devdirent_t * dirent_p)
961 {
962 devnode_t * dnp = dirent_p->de_dnp;
963
964 if(dnp) {
965 if(dnp->dn_type == DEV_DIR)
966 {
967 while(dnp->dn_typeinfo.Dir.dirlist)
968 {
969 dev_free_hier(dnp->dn_typeinfo.Dir.dirlist);
970 dev_free_name(dnp->dn_typeinfo.Dir.dirlist);
971 }
972 }
973 }
974 }
975
976 /***************************************************************\
977 * given a dev_node, find the appropriate vnode if one is already
978 * associated, or get a new one and associate it with the dev_node
979 \***************************************************************/
980 /*proto*/
981 int
982 devfs_dntovn(devnode_t * dnp, struct vnode **vn_pp, struct proc * p)
983 {
984 struct vnode *vn_p, *nvp;
985 int error = 0;
986
987 *vn_pp = NULL;
988 vn_p = dnp->dn_vn;
989 if (vn_p) { /* already has a vnode */
990 *vn_pp = vn_p;
991 return(vget(vn_p, LK_EXCLUSIVE, p));
992 }
993 if (!(error = getnewvnode(VT_DEVFS, dnp->dn_dvm->mount,
994 *(dnp->dn_ops), &vn_p))) {
995 switch(dnp->dn_type) {
996 case DEV_SLNK:
997 vn_p->v_type = VLNK;
998 break;
999 case DEV_DIR:
1000 if (dnp->dn_typeinfo.Dir.parent == dnp) {
1001 vn_p->v_flag |= VROOT;
1002 }
1003 vn_p->v_type = VDIR;
1004 break;
1005 case DEV_BDEV:
1006 case DEV_CDEV:
1007 vn_p->v_type
1008 = (dnp->dn_type == DEV_BDEV) ? VBLK : VCHR;
1009 if ((nvp = checkalias(vn_p, dnp->dn_typeinfo.dev,
1010 dnp->dn_dvm->mount)) != NULL) {
1011 vput(vn_p);
1012 vn_p = nvp;
1013 }
1014 break;
1015 }
1016 vn_p->v_mount = dnp->dn_dvm->mount;/* XXX Duplicated */
1017 *vn_pp = vn_p;
1018 vn_p->v_data = (void *)dnp;
1019 dnp->dn_vn = vn_p;
1020 error = vn_lock(vn_p, LK_EXCLUSIVE | LK_RETRY, p);
1021 }
1022 return error;
1023 }
1024
1025 /***********************************************************************\
1026 * add a whole device, with no prototype.. make name element and node *
1027 * Used for adding the original device entries *
1028 \***********************************************************************/
1029 /*proto*/
1030 int
1031 dev_add_entry(char *name, devnode_t * parent, int type, devnode_type_t * typeinfo,
1032 devnode_t * proto, struct devfsmount *dvm, devdirent_t * *nm_pp)
1033 {
1034 devnode_t * dnp;
1035 int error = 0;
1036
1037 if ((error = dev_add_node(type, typeinfo, proto, &dnp,
1038 (parent?parent->dn_dvm:dvm))) != 0)
1039 {
1040 printf("devfs: %s: base node allocation failed (Errno=%d)\n",
1041 name,error);
1042 return error;
1043 }
1044 if ((error = dev_add_name(name ,parent ,NULL, dnp, nm_pp)) != 0)
1045 {
1046 devfs_dn_free(dnp); /* 1->0 for dir, 0->(-1) for other */
1047 printf("devfs: %s: name slot allocation failed (Errno=%d)\n",
1048 name,error);
1049
1050 }
1051 return error;
1052 }
1053
1054 #include <sys/subr_prf.h>
1055
1056 /*
1057 * Function: devfs_make_node
1058 *
1059 * Purpose
1060 * Create a device node with the given pathname in the devfs namespace.
1061 *
1062 * Parameters:
1063 * dev - the dev_t value to associate
1064 * chrblk - block or character device (DEVFS_CHAR or DEVFS_BLOCK)
1065 * uid, gid - ownership
1066 * perms - permissions
1067 * fmt, ... - path format string with printf args to format the path name
1068 * Returns:
1069 * A handle to a device node if successful, NULL otherwise.
1070 */
1071 void *
1072 devfs_make_node(dev_t dev, int chrblk, uid_t uid,
1073 gid_t gid, int perms, char *fmt, ...)
1074 {
1075 devdirent_t * new_dev = NULL;
1076 devnode_t * dnp; /* devnode for parent directory */
1077 devnode_type_t typeinfo;
1078
1079 char *name, *path, buf[256]; /* XXX */
1080 char * b_ptr = buf;
1081 boolean_t funnel_state;
1082 int i;
1083 va_list ap;
1084
1085 funnel_state = thread_funnel_set(kernel_flock, TRUE);
1086
1087 if (!devfs_ready) {
1088 printf("devfs_make_node: not ready for devices!\n");
1089 goto out;
1090 }
1091
1092 if (chrblk != DEVFS_CHAR && chrblk != DEVFS_BLOCK)
1093 goto out;
1094
1095 va_start(ap, fmt);
1096 prf(fmt, ap, TOSTR, (struct tty *)&b_ptr);
1097 va_end(ap);
1098 *b_ptr = 0;
1099
1100 name = NULL;
1101
1102 for(i=strlen(buf); i>0; i--)
1103 if(buf[i] == '/') {
1104 name=&buf[i];
1105 buf[i]=0;
1106 break;
1107 }
1108
1109 if (name) {
1110 *name++ = '\0';
1111 path = buf;
1112 } else {
1113 name = buf;
1114 path = "/";
1115 }
1116
1117 DEVFS_LOCK(0);
1118 /* find/create directory path ie. mkdir -p */
1119 if (dev_finddir(path, NULL, CREATE, &dnp) == 0) {
1120 typeinfo.dev = dev;
1121 if (dev_add_entry(name, dnp,
1122 (chrblk == DEVFS_CHAR) ? DEV_CDEV : DEV_BDEV,
1123 &typeinfo, NULL, NULL, &new_dev) == 0) {
1124 new_dev->de_dnp->dn_gid = gid;
1125 new_dev->de_dnp->dn_uid = uid;
1126 new_dev->de_dnp->dn_mode |= perms;
1127 devfs_propogate(dnp->dn_typeinfo.Dir.myname, new_dev);
1128 }
1129 }
1130 DEVFS_UNLOCK(0);
1131
1132 out:
1133 (void) thread_funnel_set(kernel_flock, funnel_state);
1134 return new_dev;
1135 }
1136
1137 /*
1138 * Function: devfs_make_link
1139 *
1140 * Purpose:
1141 * Create a link to a previously created device node.
1142 *
1143 * Returns:
1144 * 0 if successful, -1 if failed
1145 */
1146 int
1147 devfs_make_link(void *original, char *fmt, ...)
1148 {
1149 devdirent_t * new_dev = NULL;
1150 devdirent_t * orig = (devdirent_t *) original;
1151 devnode_t * dirnode; /* devnode for parent directory */
1152
1153 va_list ap;
1154 char *p, buf[256]; /* XXX */
1155 char * b_ptr = buf;
1156 int i;
1157 boolean_t funnel_state;
1158
1159 funnel_state = thread_funnel_set(kernel_flock, TRUE);
1160
1161 if (!devfs_ready) {
1162 printf("devfs_make_link: not ready for devices!\n");
1163 goto out;
1164 }
1165
1166 va_start(ap, fmt);
1167 prf(fmt, ap, TOSTR, (struct tty *)&b_ptr);
1168 va_end(ap);
1169 *b_ptr = 0;
1170
1171 p = NULL;
1172
1173 for(i=strlen(buf); i>0; i--)
1174 if(buf[i] == '/') {
1175 p=&buf[i];
1176 buf[i]=0;
1177 break;
1178 }
1179 DEVFS_LOCK(0);
1180 if (p) {
1181 *p++ = '\0';
1182 if (dev_finddir(buf, NULL, CREATE, &dirnode)
1183 || dev_add_name(p, dirnode, NULL, orig->de_dnp, &new_dev))
1184 goto fail;
1185 } else {
1186 if (dev_finddir("", NULL, CREATE, &dirnode)
1187 || dev_add_name(buf, dirnode, NULL, orig->de_dnp, &new_dev))
1188 goto fail;
1189 }
1190 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, new_dev);
1191 fail:
1192 DEVFS_UNLOCK(0);
1193 out:
1194 (void) thread_funnel_set(kernel_flock, funnel_state);
1195 return ((new_dev != NULL) ? 0 : -1);
1196 }
1197