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