]>
git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_tree.c
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
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
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
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.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
32 * Copyright 1997,1998 Julian Elischer. All rights reserved.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions are
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.
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
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
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 */
85 #define SPLIT_DEVS 1 /* maybe make this an option */
86 /*#define SPLIT_DEVS 1*/
88 #include <sys/param.h>
89 #include <sys/systm.h>
90 #include <sys/kernel.h>
92 #include <sys/malloc.h>
93 #include <sys/mount_internal.h>
95 #include <sys/vnode.h>
99 #include "devfsdefs.h"
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
*);
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
;
113 devdirent_t
* dev_root
= NULL
; /* root of backing tree */
114 struct devfs_stats devfs_stats
; /* hold stats */
116 #ifdef HIDDEN_MOUNTPOINT
117 static struct mount
*devfs_hidden_mount
;
118 #endif /* HIDDEN_MOINTPOINT */
120 static int devfs_ready
= 0;
122 #define NOCREATE FALSE
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
131 * DEVFS has a hidden mountpoint that is used as the anchor point
132 * for the internal 'blueprint' version of the dev filesystem tree.
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
);
144 devfs_lck_attr
= lck_attr_alloc_init();
145 //lck_attr_setdebug(devfs_lck_attr);
147 lck_mtx_init(&devfs_mutex
, devfs_lck_grp
, devfs_lck_attr
);
150 error
= dev_add_entry("root", NULL
, DEV_DIR
, NULL
, NULL
, NULL
, &dev_root
);
154 printf("devfs_sinit: dev_add_entry failed ");
157 #ifdef HIDDEN_MOUNTPOINT
158 MALLOC(devfs_hidden_mount
, struct mount
*, sizeof(struct mount
),
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
);
166 /* Initialize the default IO constraints */
167 mp
->mnt_maxreadcnt
= mp
->mnt_maxwritecnt
= MAXPHYS
;
168 mp
->mnt_segreadcnt
= mp
->mnt_segwritecnt
= 32;
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 */
178 /***********************************************************************\
179 *************************************************************************
180 * Routines used to find our way to a point in the tree *
181 *************************************************************************
182 \***********************************************************************/
186 /***************************************************************
187 * Search down the linked list off a dir to find "name"
188 * return the devnode_t * for that node.
190 * called with DEVFS_LOCK held
191 ***************************************************************/
193 dev_findname(devnode_t
* dir
, char *name
)
196 if (dir
->dn_type
!= DEV_DIR
) return 0;/*XXX*/ /* printf?*/
202 return dir
->dn_typeinfo
.Dir
.myname
;
204 if((name
[1] == '.') && (name
[2] == 0))
206 /* for root, .. == . */
207 return dir
->dn_typeinfo
.Dir
.parent
->dn_typeinfo
.Dir
.myname
;
210 newfp
= dir
->dn_typeinfo
.Dir
.dirlist
;
214 if(!(strcmp(name
,newfp
->de_name
)))
216 newfp
= newfp
->de_next
;
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..
228 * called with DEVFS_LOCK held
229 ***********************************************************************/
231 dev_finddir(char * path
,
236 devnode_t
* dnp
= NULL
;
241 if (!dirnode
) /* dirnode == NULL means start at root */
242 dirnode
= dev_root
->de_dnp
;
244 if (dirnode
->dn_type
!= DEV_DIR
)
247 if (strlen(path
) > (DEVMAXPATHSIZE
- 1))
258 char component
[DEVMAXPATHSIZE
];
259 devdirent_t
* dirent_p
;
263 /* we hit the end of the string, we're done */
268 while (*scan
!= '/' && *scan
)
271 strncpy(component
, start
, scan
- start
);
272 component
[ scan
- start
] = '\0';
276 dirent_p
= dev_findname(dirnode
, component
);
278 dnp
= dirent_p
->de_dnp
;
279 if (dnp
->dn_type
!= DEV_DIR
) {
289 error
= dev_add_entry(component
, dirnode
,
290 DEV_DIR
, NULL
, NULL
, NULL
, &dirent_p
);
293 dnp
= dirent_p
->de_dnp
;
294 devfs_propogate(dirnode
->dn_typeinfo
.Dir
.myname
, dirent_p
);
296 dirnode
= dnp
; /* continue relative to this directory */
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
307 * Creates a name node, and links it to the supplied node
309 * called with DEVFS_LOCK held
310 ***********************************************************************/
312 dev_add_name(char * name
, devnode_t
* dirnode
, __unused devdirent_t
* back
,
313 devnode_t
* dnp
, devdirent_t
* *dirent_pp
)
315 devdirent_t
* dirent_p
= NULL
;
317 if(dirnode
!= NULL
) {
318 if(dirnode
->dn_type
!= DEV_DIR
) return(ENOTDIR
);
320 if( dev_findname(dirnode
,name
))
324 * make sure the name is legal
325 * slightly misleading in the case of NULL
327 if (!name
|| (strlen(name
) > (DEVMAXNAMESIZE
- 1)))
328 return (ENAMETOOLONG
);
331 * Allocate and fill out a new directory entry
333 MALLOC(dirent_p
, devdirent_t
*, sizeof(devdirent_t
),
334 M_DEVFSNAME
, M_WAITOK
);
338 bzero(dirent_p
,sizeof(devdirent_t
));
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 "); */
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.
352 dirent_p
->de_dnp
= dnp
;
353 dnp
->dn_links
++ ; /* implicit from our own name-node */
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.
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
;
365 dirent_p
->de_nextlink
= dirent_p
;
366 dirent_p
->de_prevlinkp
= &(dirent_p
->de_nextlink
);
368 dnp
->dn_linklist
= dirent_p
;
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.
375 if(dnp
->dn_type
== DEV_DIR
) {
376 dnp
->dn_typeinfo
.Dir
.myname
= dirent_p
;
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
383 if(dnp
->dn_typeinfo
.Dir
.parent
) {
384 dnp
->dn_typeinfo
.Dir
.parent
->dn_links
--;
387 dnp
->dn_typeinfo
.Dir
.parent
= dirnode
;
389 dnp
->dn_typeinfo
.Dir
.parent
= dnp
;
391 dnp
->dn_typeinfo
.Dir
.parent
->dn_links
++; /* account for the new '..' */
395 * put the name into the directory entry.
397 strcpy(dirent_p
->de_name
, name
);
401 * Check if we are not making a root node..
406 * Put it on the END of the linked list of directory entries
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 */
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?*/
418 *dirent_pp
= dirent_p
;
419 DEVFS_INCR_ENTRIES();
424 /***********************************************************************
425 * Add a new element to the devfs plane.
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
438 * called with DEVFS_LOCK held
439 ***********************************************************************/
441 dev_add_node(int entrytype
, devnode_type_t
* typeinfo
, devnode_t
* proto
,
442 devnode_t
* *dn_pp
, struct devfsmount
*dvm
)
444 devnode_t
* dnp
= NULL
;
446 #if defined SPLIT_DEVS
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.
452 dnp
= proto
->dn_nextsibling
;
453 while( dnp
!= proto
) {
454 if (dnp
->dn_dvm
== dvm
) {
458 dnp
= dnp
->dn_nextsibling
;
460 if (typeinfo
== NULL
)
461 typeinfo
= &(proto
->dn_typeinfo
);
463 #else /* SPLIT_DEVS */
465 switch (proto
->type
) {
472 #endif /* SPLIT_DEVS */
473 MALLOC(dnp
, devnode_t
*, sizeof(devnode_t
), M_DEVFSNODE
, M_WAITOK
);
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
483 bcopy(proto
, dnp
, sizeof(devnode_t
));
485 dnp
->dn_linklist
= NULL
;
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
);
497 * We have no prototype, so start off with a clean slate
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
;
511 * fill out the dev node according to type
516 * As it's a directory, make sure
517 * it has a null entries list
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
;
527 * make sure that the ops associated with it are the ops
528 * that we use (by default) for directories
530 dnp
->dn_ops
= &devfs_vnodeop_p
;
531 dnp
->dn_mode
|= 0555; /* default perms */
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.
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
);
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 */
560 * Make sure it has DEVICE type ops
561 * and device specific fields are correct
563 dnp
->dn_ops
= &devfs_spec_vnodeop_p
;
564 dnp
->dn_typeinfo
.dev
= typeinfo
->dev
;
576 /***********************************************************************
577 * called with DEVFS_LOCK held
578 **********************************************************************/
580 devnode_free(devnode_t
* dnp
)
582 if (dnp
->dn_lflags
& DN_BUSY
) {
583 dnp
->dn_lflags
|= DN_DELETE
;
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
);
591 FREE(dnp
, M_DEVFSNODE
);
595 /***********************************************************************
596 * called with DEVFS_LOCK held
597 **********************************************************************/
599 devfs_dn_free(devnode_t
* dnp
)
601 if(--dnp
->dn_links
<= 0 ) /* can be -1 for initial free, on error */
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
;
610 if (dnp
->dn_vn
== NULL
) {
611 devnode_free(dnp
); /* no accesses/references */
614 dnp
->dn_delete
= TRUE
;
619 /***********************************************************************\
620 * Front Node Operations *
621 * Add or delete a chain of front nodes *
622 \***********************************************************************/
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
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
634 * This allows a new node to be propogated through all mounted planes
636 * called with DEVFS_LOCK held
637 ***********************************************************************/
639 devfs_propogate(devdirent_t
* parent
,devdirent_t
* child
)
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
;
648 /***********************************************
649 * Find the other instances of the parent node
650 ***********************************************/
651 for (adnp
= pdnp
->dn_nextsibling
;
653 adnp
= adnp
->dn_nextsibling
)
656 * Make the node, using the original as a prototype)
657 * if the node already exists on that plane it won't be
660 if ((error
= dev_add_entry(child
->de_name
, adnp
, type
,
661 NULL
, dnp
, adnp
->dn_dvm
,
663 printf("duplicating %s failed\n",child
->de_name
);
666 return 0; /* for now always succeed */
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)
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 ***********************************************************************/
681 devfs_remove(void *dirent_p
)
683 devnode_t
* dnp
= ((devdirent_t
*)dirent_p
)->de_dnp
;
690 printf("devfs_remove: not ready for devices!\n");
694 /* keep removing the next sibling till only we exist. */
695 while ((dnp2
= dnp
->dn_nextsibling
) != dnp
) {
698 * Keep removing the next front node till no more exist
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
) {
706 lastlink
= (1 == dnp2
->dn_links
);
707 dev_free_name(dnp2
->dn_linklist
);
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.
717 if (dnp
->dn_linklist
) {
719 lastlink
= (1 == dnp
->dn_links
);
720 dev_free_name(dnp
->dn_linklist
);
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
737 * called with DEVFS_LOCK held
738 **************************************************************/
740 dev_dup_plane(struct devfsmount
*devfs_mp_p
)
745 if ((error
= dev_dup_entry(NULL
, dev_root
, &new, devfs_mp_p
)))
747 devfs_mp_p
->plane_root
= new;
753 /***************************************************************
756 * called with DEVFS_LOCK held
757 ***************************************************************/
759 devfs_free_plane(struct devfsmount
*devfs_mp_p
)
761 devdirent_t
* dirent_p
;
763 dirent_p
= devfs_mp_p
->plane_root
;
765 dev_free_hier(dirent_p
);
766 dev_free_name(dirent_p
);
768 devfs_mp_p
->plane_root
= NULL
;
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
780 * called with DEVFS_LOCK held
781 ***************************************************************/
783 dev_dup_entry(devnode_t
* parent
, devdirent_t
* back
, devdirent_t
* *dnm_pp
,
784 struct devfsmount
*dvm
)
786 devdirent_t
* entry_p
;
787 devdirent_t
* newback
;
788 devdirent_t
* newfront
;
790 devnode_t
* dnp
= back
->de_dnp
;
791 int type
= dnp
->dn_type
;
794 * go get the node made (if we need to)
795 * use the back one as a prototype
797 if ((error
= dev_add_entry(back
->de_name
, parent
, type
,
799 parent
?parent
->dn_dvm
:dvm
, &entry_p
)) != 0) {
800 printf("duplicating %s failed\n",back
->de_name
);
804 * If we have just made the root, then insert the pointer to the
808 entry_p
->de_dnp
->dn_dvm
= dvm
;
812 * If it is a directory, then recurse down all the other
814 * note that this time we don't pass on the mount info..
818 for(newback
= back
->de_dnp
->dn_typeinfo
.Dir
.dirlist
;
819 newback
; newback
= newback
->de_next
)
821 if((error
= dev_dup_entry(entry_p
->de_dnp
,
822 newback
, &newfront
, NULL
)) != 0)
824 break; /* back out with an error */
833 /***************************************************************
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
839 * called with DEVFS_LOCK held
840 ***************************************************************/
843 dev_free_name(devdirent_t
* dirent_p
)
845 devnode_t
* parent
= dirent_p
->de_parent
;
846 devnode_t
* dnp
= dirent_p
->de_dnp
;
849 if(dnp
->dn_type
== DEV_DIR
)
853 if(dnp
->dn_typeinfo
.Dir
.dirlist
)
855 p
= dnp
->dn_typeinfo
.Dir
.parent
;
856 devfs_dn_free(dnp
); /* account for '.' */
857 devfs_dn_free(p
); /* '..' */
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
865 if(dirent_p
->de_nextlink
== dirent_p
) {
866 dnp
->dn_linklist
= NULL
;
868 if(dnp
->dn_linklist
== dirent_p
) {
869 dnp
->dn_linklist
= dirent_p
->de_nextlink
;
871 dirent_p
->de_nextlink
->de_prevlinkp
872 = dirent_p
->de_prevlinkp
;
873 *dirent_p
->de_prevlinkp
= dirent_p
->de_nextlink
;
879 * unlink ourselves from the directory on this plane
881 if(parent
) /* if not fs root */
883 if( (*dirent_p
->de_prevp
= dirent_p
->de_next
) )/* yes, assign */
885 dirent_p
->de_next
->de_prevp
= dirent_p
->de_prevp
;
889 parent
->dn_typeinfo
.Dir
.dirlast
890 = dirent_p
->de_prevp
;
892 parent
->dn_typeinfo
.Dir
.entrycount
--;
893 parent
->dn_len
-= strlen(dirent_p
->de_name
) + 8;
896 DEVFS_DECR_ENTRIES();
897 FREE(dirent_p
, M_DEVFSNAME
);
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.
909 * called with DEVFS_LOCK held
910 ***************************************************************/
913 dev_free_hier(devdirent_t
* dirent_p
)
915 devnode_t
* dnp
= dirent_p
->de_dnp
;
918 if(dnp
->dn_type
== DEV_DIR
)
920 while(dnp
->dn_typeinfo
.Dir
.dirlist
)
922 dev_free_hier(dnp
->dn_typeinfo
.Dir
.dirlist
);
923 dev_free_name(dnp
->dn_typeinfo
.Dir
.dirlist
);
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
934 * called with DEVFS_LOCK held
935 ***************************************************************/
937 devfs_dntovn(devnode_t
* dnp
, struct vnode
**vn_pp
, __unused
struct proc
* p
)
941 struct vnode_fsparam vfsp
;
942 enum vtype vtype
= 0;
949 dnp
->dn_lflags
|= DN_BUSY
;
951 if (vn_p
) { /* already has a vnode */
954 vid
= vnode_vid(vn_p
);
958 error
= vnode_getwithvid(vn_p
, vid
);
962 if (dnp
->dn_lflags
& DN_DELETE
) {
964 * our BUSY node got marked for
965 * deletion while the DEVFS lock
970 * vnode_getwithvid returned a valid ref
971 * which we need to drop
976 * set the error to EAGAIN
977 * which will cause devfs_lookup
985 devfs_release_busy(dnp
);
990 if (dnp
->dn_lflags
& DN_CREATE
) {
991 dnp
->dn_lflags
|= DN_CREATEWAIT
;
992 msleep(&dnp
->dn_lflags
, &devfs_mutex
, PRIBIO
, 0 , 0);
996 dnp
->dn_lflags
|= DN_CREATE
;
998 switch (dnp
->dn_type
) {
1003 if (dnp
->dn_typeinfo
.Dir
.parent
== dnp
) {
1010 vtype
= (dnp
->dn_type
== DEV_BDEV
) ? VBLK
: VCHR
;
1013 vfsp
.vnfs_mp
= dnp
->dn_dvm
->mount
;
1014 vfsp
.vnfs_vtype
= vtype
;
1015 vfsp
.vnfs_str
= "devfs";
1017 vfsp
.vnfs_fsnode
= dnp
;
1019 vfsp
.vnfs_vops
= *(dnp
->dn_ops
);
1021 if (vtype
== VBLK
|| vtype
== VCHR
)
1022 vfsp
.vnfs_rdev
= dnp
->dn_typeinfo
.dev
;
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
;
1033 error
= vnode_create(VNCREATE_FLAVOR
, VCREATESIZE
, &vfsp
, &vn_p
);
1039 panic("devnode already has a vnode?");
1043 vnode_settag(vn_p
, VT_DEVFS
);
1047 dnp
->dn_lflags
&= ~DN_CREATE
;
1049 if (dnp
->dn_lflags
& DN_CREATEWAIT
) {
1050 dnp
->dn_lflags
&= ~DN_CREATEWAIT
;
1051 wakeup(&dnp
->dn_lflags
);
1054 devfs_release_busy(dnp
);
1060 /***********************************************************************
1061 * called with DEVFS_LOCK held
1062 ***********************************************************************/
1064 devfs_release_busy(devnode_t
*dnp
) {
1066 dnp
->dn_lflags
&= ~DN_BUSY
;
1068 if (dnp
->dn_lflags
& DN_DELETE
)
1072 /***********************************************************************
1073 * add a whole device, with no prototype.. make name element and node
1074 * Used for adding the original device entries
1076 * called with DEVFS_LOCK held
1077 ***********************************************************************/
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
)
1085 if ((error
= dev_add_node(type
, typeinfo
, proto
, &dnp
,
1086 (parent
?parent
->dn_dvm
:dvm
))) != 0)
1088 printf("devfs: %s: base node allocation failed (Errno=%d)\n",
1092 if ((error
= dev_add_name(name
,parent
,NULL
, dnp
, nm_pp
)) != 0)
1094 devfs_dn_free(dnp
); /* 1->0 for dir, 0->(-1) for other */
1095 printf("devfs: %s: name slot allocation failed (Errno=%d)\n",
1104 * Function: devfs_make_node
1107 * Create a device node with the given pathname in the devfs namespace.
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
1116 * A handle to a device node if successful, NULL otherwise.
1119 devfs_make_node(dev_t dev
, int chrblk
, uid_t uid
,
1120 gid_t gid
, int perms
, const char *fmt
, ...)
1122 devdirent_t
* new_dev
= NULL
;
1123 devnode_t
* dnp
; /* devnode for parent directory */
1124 devnode_type_t typeinfo
;
1126 char *name
, *path
, buf
[256]; /* XXX */
1134 printf("devfs_make_node: not ready for devices!\n");
1137 if (chrblk
!= DEVFS_CHAR
&& chrblk
!= DEVFS_BLOCK
)
1143 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1148 for(i
=strlen(buf
); i
>0; i
--)
1164 /* find/create directory path ie. mkdir -p */
1165 if (dev_finddir(path
, NULL
, CREATE
, &dnp
) == 0) {
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
);
1183 * Function: devfs_make_link
1186 * Create a link to a previously created device node.
1189 * 0 if successful, -1 if failed
1192 devfs_make_link(void *original
, char *fmt
, ...)
1194 devdirent_t
* new_dev
= NULL
;
1195 devdirent_t
* orig
= (devdirent_t
*) original
;
1196 devnode_t
* dirnode
; /* devnode for parent directory */
1199 char *p
, buf
[256]; /* XXX */
1205 printf("devfs_make_link: not ready for devices!\n");
1211 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1216 for(i
=strlen(buf
); i
>0; i
--) {
1228 if (dev_finddir(buf
, NULL
, CREATE
, &dirnode
)
1229 || dev_add_name(p
, dirnode
, NULL
, orig
->de_dnp
, &new_dev
))
1232 if (dev_finddir("", NULL
, CREATE
, &dirnode
)
1233 || dev_add_name(buf
, dirnode
, NULL
, orig
->de_dnp
, &new_dev
))
1236 devfs_propogate(dirnode
->dn_typeinfo
.Dir
.myname
, new_dev
);
1241 return ((new_dev
!= NULL
) ? 0 : -1);