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