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