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