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