]>
git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_tree.c
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * Copyright 1997,1998 Julian Elischer. All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions are
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.
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
57 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
58 * support for mandatory and extensible security protections. This notice
59 * is included in support of clause 2.2 (b) of the Apple Public License,
65 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
66 * - removed mounting of "hidden" mountpoint
67 * - fixed problem in which devnode->dn_vn pointer was not
68 * updated with the vnode returned from checkalias()
69 * - replaced devfs_vntodn() with a macro VTODN()
70 * - rewrote dev_finddir() to not use recursion
71 * - added locking to avoid data structure corruption (DEVFS_(UN)LOCK())
72 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
73 * - fixed problem with devfs_dntovn() checking the v_id against the
74 * value cached in the device node; a union mount on top of us causes
75 * the v_id to get incremented thus, we would end up returning a new
76 * vnode instead of the existing one that has the mounted_here
77 * field filled in; the net effect was that the filesystem mounted
78 * on top of us would never show up
79 * - added devfs_stats to store how many data structures are actually
83 /* SPLIT_DEVS means each devfs uses a different devnode for the same device */
84 /* Otherwise the same device always ends up at the same vnode even if */
85 /* reached througgh a different devfs instance. The practical difference */
86 /* is that with the same vnode, chmods and chowns show up on all instances of */
89 #define SPLIT_DEVS 1 /* maybe make this an option */
90 /*#define SPLIT_DEVS 1*/
92 #include <sys/param.h>
93 #include <sys/systm.h>
94 #include <sys/kernel.h>
96 #include <sys/malloc.h>
97 #include <sys/mount_internal.h>
99 #include <sys/vnode_internal.h>
101 #include <libkern/OSAtomic.h>
102 #define BSD_KERNEL_PRIVATE 1 /* devfs_make_link() prototype */
104 #include "devfsdefs.h"
107 #include <security/mac_framework.h>
110 static void devfs_release_busy(devnode_t
*);
111 static void dev_free_hier(devdirent_t
*);
112 static int devfs_propogate(devdirent_t
*, devdirent_t
*);
113 static int dev_finddir(const char *, devnode_t
*, int, devnode_t
**);
114 static int dev_dup_entry(devnode_t
*, devdirent_t
*, devdirent_t
**, struct devfsmount
*);
117 lck_grp_t
* devfs_lck_grp
;
118 lck_grp_attr_t
* devfs_lck_grp_attr
;
119 lck_attr_t
* devfs_lck_attr
;
120 lck_mtx_t devfs_mutex
;
122 devdirent_t
* dev_root
= NULL
; /* root of backing tree */
123 struct devfs_stats devfs_stats
; /* hold stats */
125 #ifdef HIDDEN_MOUNTPOINT
126 static struct mount
*devfs_hidden_mount
;
127 #endif /* HIDDEN_MOINTPOINT */
129 static int devfs_ready
= 0;
131 #define DEVFS_NOCREATE FALSE
132 #define DEVFS_CREATE TRUE
135 * Set up the root directory node in the backing plane
136 * This is happenning before the vfs system has been
137 * set up yet, so be careful about what we reference..
138 * Notice that the ops are by indirection.. as they haven't
140 * DEVFS has a hidden mountpoint that is used as the anchor point
141 * for the internal 'blueprint' version of the dev filesystem tree.
149 devfs_lck_grp_attr
= lck_grp_attr_alloc_init();
150 devfs_lck_grp
= lck_grp_alloc_init("devfs_lock", devfs_lck_grp_attr
);
152 devfs_lck_attr
= lck_attr_alloc_init();
154 lck_mtx_init(&devfs_mutex
, devfs_lck_grp
, devfs_lck_attr
);
157 error
= dev_add_entry("root", NULL
, DEV_DIR
, NULL
, NULL
, NULL
, &dev_root
);
161 printf("devfs_sinit: dev_add_entry failed ");
164 #ifdef HIDDEN_MOUNTPOINT
165 MALLOC(devfs_hidden_mount
, struct mount
*, sizeof(struct mount
),
167 bzero(devfs_hidden_mount
,sizeof(struct mount
));
168 mount_lock_init(devfs_hidden_mount
);
169 TAILQ_INIT(&devfs_hidden_mount
->mnt_vnodelist
);
170 TAILQ_INIT(&devfs_hidden_mount
->mnt_workerqueue
);
171 TAILQ_INIT(&devfs_hidden_mount
->mnt_newvnodes
);
173 mac_mount_label_init(devfs_hidden_mount
);
174 mac_mount_label_associate(vfs_context_kernel(), devfs_hidden_mount
);
177 /* Initialize the default IO constraints */
178 mp
->mnt_maxreadcnt
= mp
->mnt_maxwritecnt
= MAXPHYS
;
179 mp
->mnt_segreadcnt
= mp
->mnt_segwritecnt
= 32;
181 mp
->mnt_realrootvp
= NULLVP
;
182 mp
->mnt_authcache_ttl
= CACHED_LOOKUP_RIGHT_TTL
;
184 devfs_mount(devfs_hidden_mount
,"dummy",NULL
,NULL
,NULL
);
185 dev_root
->de_dnp
->dn_dvm
186 = (struct devfsmount
*)devfs_hidden_mount
->mnt_data
;
187 #endif /* HIDDEN_MOUNTPOINT */
189 mac_devfs_label_associate_directory("/", strlen("/"),
190 dev_root
->de_dnp
, "/");
196 /***********************************************************************\
197 *************************************************************************
198 * Routines used to find our way to a point in the tree *
199 *************************************************************************
200 \***********************************************************************/
204 /***************************************************************
205 * Search down the linked list off a dir to find "name"
206 * return the devnode_t * for that node.
208 * called with DEVFS_LOCK held
209 ***************************************************************/
211 dev_findname(devnode_t
* dir
, const char *name
)
214 if (dir
->dn_type
!= DEV_DIR
) return 0;/*XXX*/ /* printf?*/
220 return dir
->dn_typeinfo
.Dir
.myname
;
222 if((name
[1] == '.') && (name
[2] == 0))
224 /* for root, .. == . */
225 return dir
->dn_typeinfo
.Dir
.parent
->dn_typeinfo
.Dir
.myname
;
228 newfp
= dir
->dn_typeinfo
.Dir
.dirlist
;
232 if(!(strncmp(name
, newfp
->de_name
, sizeof(newfp
->de_name
))))
234 newfp
= newfp
->de_next
;
239 /***********************************************************************
240 * Given a starting node (0 for root) and a pathname, return the node
241 * for the end item on the path. It MUST BE A DIRECTORY. If the 'DEVFS_CREATE'
242 * option is true, then create any missing nodes in the path and create
243 * and return the final node as well.
244 * This is used to set up a directory, before making nodes in it..
246 * called with DEVFS_LOCK held
247 ***********************************************************************/
249 dev_finddir(const char * path
,
254 devnode_t
* dnp
= NULL
;
258 char fullpath
[DEVMAXPATHSIZE
];
262 if (!dirnode
) /* dirnode == NULL means start at root */
263 dirnode
= dev_root
->de_dnp
;
265 if (dirnode
->dn_type
!= DEV_DIR
)
268 if (strlen(path
) > (DEVMAXPATHSIZE
- 1))
272 strlcpy (fullpath
, path
, DEVMAXPATHSIZE
);
282 char component
[DEVMAXPATHSIZE
];
283 devdirent_t
* dirent_p
;
287 /* we hit the end of the string, we're done */
292 while (*scan
!= '/' && *scan
)
295 strlcpy(component
, start
, scan
- start
);
299 dirent_p
= dev_findname(dirnode
, component
);
301 dnp
= dirent_p
->de_dnp
;
302 if (dnp
->dn_type
!= DEV_DIR
) {
312 error
= dev_add_entry(component
, dirnode
,
313 DEV_DIR
, NULL
, NULL
, NULL
, &dirent_p
);
316 dnp
= dirent_p
->de_dnp
;
318 mac_devfs_label_associate_directory(
319 dirnode
->dn_typeinfo
.Dir
.myname
->de_name
,
320 strlen(dirnode
->dn_typeinfo
.Dir
.myname
->de_name
),
323 devfs_propogate(dirnode
->dn_typeinfo
.Dir
.myname
, dirent_p
);
325 dirnode
= dnp
; /* continue relative to this directory */
331 /***********************************************************************
332 * Add a new NAME element to the devfs
333 * If we're creating a root node, then dirname is NULL
334 * Basically this creates a new namespace entry for the device node
336 * Creates a name node, and links it to the supplied node
338 * called with DEVFS_LOCK held
339 ***********************************************************************/
341 dev_add_name(const char * name
, devnode_t
* dirnode
, __unused devdirent_t
* back
,
342 devnode_t
* dnp
, devdirent_t
* *dirent_pp
)
344 devdirent_t
* dirent_p
= NULL
;
346 if(dirnode
!= NULL
) {
347 if(dirnode
->dn_type
!= DEV_DIR
) return(ENOTDIR
);
349 if( dev_findname(dirnode
,name
))
353 * make sure the name is legal
354 * slightly misleading in the case of NULL
356 if (!name
|| (strlen(name
) > (DEVMAXNAMESIZE
- 1)))
357 return (ENAMETOOLONG
);
360 * Allocate and fill out a new directory entry
362 MALLOC(dirent_p
, devdirent_t
*, sizeof(devdirent_t
),
363 M_DEVFSNAME
, M_WAITOK
);
367 bzero(dirent_p
,sizeof(devdirent_t
));
369 /* inherrit our parent's mount info */ /*XXX*/
370 /* a kludge but.... */
371 if(dirnode
&& ( dnp
->dn_dvm
== NULL
)) {
372 dnp
->dn_dvm
= dirnode
->dn_dvm
;
373 /* if(!dnp->dn_dvm) printf("parent had null dvm "); */
377 * Link the two together
378 * include the implicit link in the count of links to the devnode..
379 * this stops it from being accidentally freed later.
381 dirent_p
->de_dnp
= dnp
;
382 dnp
->dn_links
++ ; /* implicit from our own name-node */
385 * Make sure that we can find all the links that reference a node
386 * so that we can get them all if we need to zap the node.
388 if(dnp
->dn_linklist
) {
389 dirent_p
->de_nextlink
= dnp
->dn_linklist
;
390 dirent_p
->de_prevlinkp
= dirent_p
->de_nextlink
->de_prevlinkp
;
391 dirent_p
->de_nextlink
->de_prevlinkp
= &(dirent_p
->de_nextlink
);
392 *dirent_p
->de_prevlinkp
= dirent_p
;
394 dirent_p
->de_nextlink
= dirent_p
;
395 dirent_p
->de_prevlinkp
= &(dirent_p
->de_nextlink
);
397 dnp
->dn_linklist
= dirent_p
;
400 * If the node is a directory, then we need to handle the
401 * creation of the .. link.
402 * A NULL dirnode indicates a root node, so point to ourself.
404 if(dnp
->dn_type
== DEV_DIR
) {
405 dnp
->dn_typeinfo
.Dir
.myname
= dirent_p
;
407 * If we are unlinking from an old dir, decrement its links
408 * as we point our '..' elsewhere
409 * Note: it's up to the calling code to remove the
410 * us from the original directory's list
412 if(dnp
->dn_typeinfo
.Dir
.parent
) {
413 dnp
->dn_typeinfo
.Dir
.parent
->dn_links
--;
416 dnp
->dn_typeinfo
.Dir
.parent
= dirnode
;
418 dnp
->dn_typeinfo
.Dir
.parent
= dnp
;
420 dnp
->dn_typeinfo
.Dir
.parent
->dn_links
++; /* account for the new '..' */
424 * put the name into the directory entry.
426 strlcpy(dirent_p
->de_name
, name
, DEVMAXNAMESIZE
);
430 * Check if we are not making a root node..
435 * Put it on the END of the linked list of directory entries
437 dirent_p
->de_parent
= dirnode
; /* null for root */
438 dirent_p
->de_prevp
= dirnode
->dn_typeinfo
.Dir
.dirlast
;
439 dirent_p
->de_next
= *(dirent_p
->de_prevp
); /* should be NULL */
441 *(dirent_p
->de_prevp
) = dirent_p
;
442 dirnode
->dn_typeinfo
.Dir
.dirlast
= &(dirent_p
->de_next
);
443 dirnode
->dn_typeinfo
.Dir
.entrycount
++;
444 dirnode
->dn_len
+= strlen(name
) + 8;/*ok, ok?*/
447 *dirent_pp
= dirent_p
;
448 DEVFS_INCR_ENTRIES();
453 /***********************************************************************
454 * Add a new element to the devfs plane.
456 * Creates a new dev_node to go with it if the prototype should not be
457 * reused. (Is a DIR, or we select SPLIT_DEVS at compile time)
458 * typeinfo gives us info to make our node if we don't have a prototype.
459 * If typeinfo is null and proto exists, then the typeinfo field of
460 * the proto is used intead in the DEVFS_CREATE case.
461 * note the 'links' count is 0 (except if a dir)
462 * but it is only cleared on a transition
463 * so this is ok till we link it to something
464 * Even in SPLIT_DEVS mode,
465 * if the node already exists on the wanted plane, just return it
467 * called with DEVFS_LOCK held
468 ***********************************************************************/
470 dev_add_node(int entrytype
, devnode_type_t
* typeinfo
, devnode_t
* proto
,
471 devnode_t
* *dn_pp
, struct devfsmount
*dvm
)
473 devnode_t
* dnp
= NULL
;
475 #if defined SPLIT_DEVS
477 * If we have a prototype, then check if there is already a sibling
478 * on the mount plane we are looking at, if so, just return it.
481 dnp
= proto
->dn_nextsibling
;
482 while( dnp
!= proto
) {
483 if (dnp
->dn_dvm
== dvm
) {
487 dnp
= dnp
->dn_nextsibling
;
489 if (typeinfo
== NULL
)
490 typeinfo
= &(proto
->dn_typeinfo
);
492 #else /* SPLIT_DEVS */
494 switch (proto
->type
) {
501 #endif /* SPLIT_DEVS */
502 MALLOC(dnp
, devnode_t
*, sizeof(devnode_t
), M_DEVFSNODE
, M_WAITOK
);
508 * If we have a proto, that means that we are duplicating some
509 * other device, which can only happen if we are not at the back plane
512 bcopy(proto
, dnp
, sizeof(devnode_t
));
514 dnp
->dn_linklist
= NULL
;
517 /* add to END of siblings list */
518 dnp
->dn_prevsiblingp
= proto
->dn_prevsiblingp
;
519 *(dnp
->dn_prevsiblingp
) = dnp
;
520 dnp
->dn_nextsibling
= proto
;
521 proto
->dn_prevsiblingp
= &(dnp
->dn_nextsibling
);
523 mac_devfs_label_init(dnp
);
524 mac_devfs_label_copy(proto
->dn_label
, dnp
->dn_label
);
530 * We have no prototype, so start off with a clean slate
533 bzero(dnp
, sizeof(devnode_t
));
534 dnp
->dn_type
= entrytype
;
535 dnp
->dn_nextsibling
= dnp
;
536 dnp
->dn_prevsiblingp
= &(dnp
->dn_nextsibling
);
537 dnp
->dn_atime
.tv_sec
= tv
.tv_sec
;
538 dnp
->dn_mtime
.tv_sec
= tv
.tv_sec
;
539 dnp
->dn_ctime
.tv_sec
= tv
.tv_sec
;
541 mac_devfs_label_init(dnp
);
547 * fill out the dev node according to type
552 * As it's a directory, make sure
553 * it has a null entries list
555 dnp
->dn_typeinfo
.Dir
.dirlast
= &(dnp
->dn_typeinfo
.Dir
.dirlist
);
556 dnp
->dn_typeinfo
.Dir
.dirlist
= (devdirent_t
*)0;
557 dnp
->dn_typeinfo
.Dir
.entrycount
= 0;
558 /* until we know better, it has a null parent pointer*/
559 dnp
->dn_typeinfo
.Dir
.parent
= NULL
;
560 dnp
->dn_links
++; /* for .*/
561 dnp
->dn_typeinfo
.Dir
.myname
= NULL
;
563 * make sure that the ops associated with it are the ops
564 * that we use (by default) for directories
566 dnp
->dn_ops
= &devfs_vnodeop_p
;
567 dnp
->dn_mode
|= 0555; /* default perms */
571 * As it's a symlink allocate and store the link info
572 * Symlinks should only ever be created by the user,
573 * so they are not on the back plane and should not be
574 * propogated forward.. a bit like directories in that way..
575 * A symlink only exists on one plane and has its own
576 * node.. therefore we might be on any random plane.
578 MALLOC(dnp
->dn_typeinfo
.Slnk
.name
, char *,
579 typeinfo
->Slnk
.namelen
+1,
580 M_DEVFSNODE
, M_WAITOK
);
581 if (!dnp
->dn_typeinfo
.Slnk
.name
) {
582 FREE(dnp
,M_DEVFSNODE
);
585 strlcpy(dnp
->dn_typeinfo
.Slnk
.name
, typeinfo
->Slnk
.name
,
586 typeinfo
->Slnk
.namelen
+ 1);
587 dnp
->dn_typeinfo
.Slnk
.namelen
= typeinfo
->Slnk
.namelen
;
588 DEVFS_INCR_STRINGSPACE(dnp
->dn_typeinfo
.Slnk
.namelen
+ 1);
589 dnp
->dn_ops
= &devfs_vnodeop_p
;
590 dnp
->dn_mode
|= 0555; /* default perms */
595 * Make sure it has DEVICE type ops
596 * and device specific fields are correct
598 dnp
->dn_ops
= &devfs_spec_vnodeop_p
;
599 dnp
->dn_typeinfo
.dev
= typeinfo
->dev
;
611 /***********************************************************************
612 * called with DEVFS_LOCK held
613 **********************************************************************/
615 devnode_free(devnode_t
* dnp
)
617 if (dnp
->dn_lflags
& DN_BUSY
) {
618 dnp
->dn_lflags
|= DN_DELETE
;
622 mac_devfs_label_destroy(dnp
);
624 if (dnp
->dn_type
== DEV_SLNK
) {
625 DEVFS_DECR_STRINGSPACE(dnp
->dn_typeinfo
.Slnk
.namelen
+ 1);
626 FREE(dnp
->dn_typeinfo
.Slnk
.name
, M_DEVFSNODE
);
629 FREE(dnp
, M_DEVFSNODE
);
633 /***********************************************************************
634 * called with DEVFS_LOCK held
635 **********************************************************************/
637 devfs_dn_free(devnode_t
* dnp
)
639 if(--dnp
->dn_links
<= 0 ) /* can be -1 for initial free, on error */
641 /*probably need to do other cleanups XXX */
642 if (dnp
->dn_nextsibling
!= dnp
) {
643 devnode_t
* * prevp
= dnp
->dn_prevsiblingp
;
644 *prevp
= dnp
->dn_nextsibling
;
645 dnp
->dn_nextsibling
->dn_prevsiblingp
= prevp
;
648 if (dnp
->dn_vn
== NULL
) {
649 devnode_free(dnp
); /* no accesses/references */
652 dnp
->dn_delete
= TRUE
;
657 /***********************************************************************\
658 * Front Node Operations *
659 * Add or delete a chain of front nodes *
660 \***********************************************************************/
663 /***********************************************************************
664 * Given a directory backing node, and a child backing node, add the
665 * appropriate front nodes to the front nodes of the directory to
666 * represent the child node to the user
668 * on failure, front nodes will either be correct or not exist for each
669 * front dir, however dirs completed will not be stripped of completed
670 * frontnodes on failure of a later frontnode
672 * This allows a new node to be propogated through all mounted planes
674 * called with DEVFS_LOCK held
675 ***********************************************************************/
677 devfs_propogate(devdirent_t
* parent
,devdirent_t
* child
)
680 devdirent_t
* newnmp
;
681 devnode_t
* dnp
= child
->de_dnp
;
682 devnode_t
* pdnp
= parent
->de_dnp
;
683 devnode_t
* adnp
= parent
->de_dnp
;
684 int type
= child
->de_dnp
->dn_type
;
686 /***********************************************
687 * Find the other instances of the parent node
688 ***********************************************/
689 for (adnp
= pdnp
->dn_nextsibling
;
691 adnp
= adnp
->dn_nextsibling
)
694 * Make the node, using the original as a prototype)
695 * if the node already exists on that plane it won't be
698 if ((error
= dev_add_entry(child
->de_name
, adnp
, type
,
699 NULL
, dnp
, adnp
->dn_dvm
,
701 printf("duplicating %s failed\n",child
->de_name
);
704 return 0; /* for now always succeed */
708 /***********************************************************************
709 * remove all instances of this devicename [for backing nodes..]
710 * note.. if there is another link to the node (non dir nodes only)
711 * then the devfs_node will still exist as the ref count will be non-0
712 * removing a directory node will remove all sup-nodes on all planes (ZAP)
714 * Used by device drivers to remove nodes that are no longer relevant
715 * The argument is the 'cookie' they were given when they created the node
716 * this function is exported.. see devfs.h
717 ***********************************************************************/
719 devfs_remove(void *dirent_p
)
721 devnode_t
* dnp
= ((devdirent_t
*)dirent_p
)->de_dnp
;
728 printf("devfs_remove: not ready for devices!\n");
732 /* keep removing the next sibling till only we exist. */
733 while ((dnp2
= dnp
->dn_nextsibling
) != dnp
) {
736 * Keep removing the next front node till no more exist
738 dnp
->dn_nextsibling
= dnp2
->dn_nextsibling
;
739 dnp
->dn_nextsibling
->dn_prevsiblingp
= &(dnp
->dn_nextsibling
);
740 dnp2
->dn_nextsibling
= dnp2
;
741 dnp2
->dn_prevsiblingp
= &(dnp2
->dn_nextsibling
);
742 if (dnp2
->dn_linklist
) {
744 lastlink
= (1 == dnp2
->dn_links
);
745 dev_free_name(dnp2
->dn_linklist
);
751 * then free the main node
752 * If we are not running in SPLIT_DEVS mode, then
753 * THIS is what gets rid of the propogated nodes.
755 if (dnp
->dn_linklist
) {
757 lastlink
= (1 == dnp
->dn_links
);
758 dev_free_name(dnp
->dn_linklist
);
769 /***************************************************************
770 * duplicate the backing tree into a tree of nodes hung off the
771 * mount point given as the argument. Do this by
772 * calling dev_dup_entry which recurses all the way
775 * called with DEVFS_LOCK held
776 **************************************************************/
778 dev_dup_plane(struct devfsmount
*devfs_mp_p
)
783 if ((error
= dev_dup_entry(NULL
, dev_root
, &new, devfs_mp_p
)))
785 devfs_mp_p
->plane_root
= new;
791 /***************************************************************
794 * called with DEVFS_LOCK held
795 ***************************************************************/
797 devfs_free_plane(struct devfsmount
*devfs_mp_p
)
799 devdirent_t
* dirent_p
;
801 dirent_p
= devfs_mp_p
->plane_root
;
803 dev_free_hier(dirent_p
);
804 dev_free_name(dirent_p
);
806 devfs_mp_p
->plane_root
= NULL
;
810 /***************************************************************
811 * Create and link in a new front element..
812 * Parent can be 0 for a root node
813 * Not presently usable to make a symlink XXX
814 * (Ok, symlinks don't propogate)
815 * recursively will create subnodes corresponding to equivalent
816 * child nodes in the base level
818 * called with DEVFS_LOCK held
819 ***************************************************************/
821 dev_dup_entry(devnode_t
* parent
, devdirent_t
* back
, devdirent_t
* *dnm_pp
,
822 struct devfsmount
*dvm
)
824 devdirent_t
* entry_p
;
825 devdirent_t
* newback
;
826 devdirent_t
* newfront
;
828 devnode_t
* dnp
= back
->de_dnp
;
829 int type
= dnp
->dn_type
;
832 * go get the node made (if we need to)
833 * use the back one as a prototype
835 if ((error
= dev_add_entry(back
->de_name
, parent
, type
,
837 parent
?parent
->dn_dvm
:dvm
, &entry_p
)) != 0) {
838 printf("duplicating %s failed\n",back
->de_name
);
842 * If we have just made the root, then insert the pointer to the
846 entry_p
->de_dnp
->dn_dvm
= dvm
;
850 * If it is a directory, then recurse down all the other
852 * note that this time we don't pass on the mount info..
856 for(newback
= back
->de_dnp
->dn_typeinfo
.Dir
.dirlist
;
857 newback
; newback
= newback
->de_next
)
859 if((error
= dev_dup_entry(entry_p
->de_dnp
,
860 newback
, &newfront
, NULL
)) != 0)
862 break; /* back out with an error */
871 /***************************************************************
873 * remember that if there are other names pointing to the
874 * dev_node then it may not get freed yet
875 * can handle if there is no dnp
877 * called with DEVFS_LOCK held
878 ***************************************************************/
881 dev_free_name(devdirent_t
* dirent_p
)
883 devnode_t
* parent
= dirent_p
->de_parent
;
884 devnode_t
* dnp
= dirent_p
->de_dnp
;
887 if(dnp
->dn_type
== DEV_DIR
)
891 if(dnp
->dn_typeinfo
.Dir
.dirlist
)
893 p
= dnp
->dn_typeinfo
.Dir
.parent
;
894 devfs_dn_free(dnp
); /* account for '.' */
895 devfs_dn_free(p
); /* '..' */
898 * unlink us from the list of links for this node
899 * If we are the only link, it's easy!
900 * if we are a DIR of course there should not be any
903 if(dirent_p
->de_nextlink
== dirent_p
) {
904 dnp
->dn_linklist
= NULL
;
906 if(dnp
->dn_linklist
== dirent_p
) {
907 dnp
->dn_linklist
= dirent_p
->de_nextlink
;
909 dirent_p
->de_nextlink
->de_prevlinkp
910 = dirent_p
->de_prevlinkp
;
911 *dirent_p
->de_prevlinkp
= dirent_p
->de_nextlink
;
917 * unlink ourselves from the directory on this plane
919 if(parent
) /* if not fs root */
921 if( (*dirent_p
->de_prevp
= dirent_p
->de_next
) )/* yes, assign */
923 dirent_p
->de_next
->de_prevp
= dirent_p
->de_prevp
;
927 parent
->dn_typeinfo
.Dir
.dirlast
928 = dirent_p
->de_prevp
;
930 parent
->dn_typeinfo
.Dir
.entrycount
--;
931 parent
->dn_len
-= strlen(dirent_p
->de_name
) + 8;
934 DEVFS_DECR_ENTRIES();
935 FREE(dirent_p
, M_DEVFSNAME
);
940 /***************************************************************
941 * Free a hierarchy starting at a directory node name
942 * remember that if there are other names pointing to the
943 * dev_node then it may not get freed yet
944 * can handle if there is no dnp
945 * leave the node itself allocated.
947 * called with DEVFS_LOCK held
948 ***************************************************************/
951 dev_free_hier(devdirent_t
* dirent_p
)
953 devnode_t
* dnp
= dirent_p
->de_dnp
;
956 if(dnp
->dn_type
== DEV_DIR
)
958 while(dnp
->dn_typeinfo
.Dir
.dirlist
)
960 dev_free_hier(dnp
->dn_typeinfo
.Dir
.dirlist
);
961 dev_free_name(dnp
->dn_typeinfo
.Dir
.dirlist
);
968 /***************************************************************
969 * given a dev_node, find the appropriate vnode if one is already
970 * associated, or get a new one and associate it with the dev_node
972 * called with DEVFS_LOCK held
973 ***************************************************************/
975 devfs_dntovn(devnode_t
* dnp
, struct vnode
**vn_pp
, __unused
struct proc
* p
)
978 struct vnode
** vnptr
;
980 struct vnode_fsparam vfsp
;
981 enum vtype vtype
= 0;
983 int n_minor
= DEVFS_CLONE_ALLOC
; /* new minor number for clone device */
989 dnp
->dn_lflags
|= DN_BUSY
;
991 if (vn_p
) { /* already has a vnode */
994 vid
= vnode_vid(vn_p
);
998 error
= vnode_getwithvid(vn_p
, vid
);
1002 if (dnp
->dn_lflags
& DN_DELETE
) {
1004 * our BUSY node got marked for
1005 * deletion while the DEVFS lock
1010 * vnode_getwithvid returned a valid ref
1011 * which we need to drop
1016 * set the error to EAGAIN
1017 * which will cause devfs_lookup
1018 * to retry this node
1025 devfs_release_busy(dnp
);
1030 if (dnp
->dn_lflags
& DN_CREATE
) {
1031 dnp
->dn_lflags
|= DN_CREATEWAIT
;
1032 msleep(&dnp
->dn_lflags
, &devfs_mutex
, PRIBIO
, 0 , 0);
1036 dnp
->dn_lflags
|= DN_CREATE
;
1038 switch (dnp
->dn_type
) {
1043 if (dnp
->dn_typeinfo
.Dir
.parent
== dnp
) {
1050 vtype
= (dnp
->dn_type
== DEV_BDEV
) ? VBLK
: VCHR
;
1053 vfsp
.vnfs_mp
= dnp
->dn_dvm
->mount
;
1054 vfsp
.vnfs_vtype
= vtype
;
1055 vfsp
.vnfs_str
= "devfs";
1057 vfsp
.vnfs_fsnode
= dnp
;
1059 vfsp
.vnfs_vops
= *(dnp
->dn_ops
);
1061 if (vtype
== VBLK
|| vtype
== VCHR
) {
1063 * Ask the clone minor number function for a new minor number
1064 * to use for the next device instance. If an administative
1065 * limit has been reached, this function will return -1.
1067 if (dnp
->dn_clone
!= NULL
) {
1068 int n_major
= major(dnp
->dn_typeinfo
.dev
);
1070 n_minor
= (*dnp
->dn_clone
)(dnp
->dn_typeinfo
.dev
, DEVFS_CLONE_ALLOC
);
1071 if (n_minor
== -1) {
1072 devfs_release_busy(dnp
);
1076 vfsp
.vnfs_rdev
= makedev(n_major
, n_minor
);;
1078 vfsp
.vnfs_rdev
= dnp
->dn_typeinfo
.dev
;
1083 vfsp
.vnfs_filesize
= 0;
1084 vfsp
.vnfs_flags
= VNFS_NOCACHE
| VNFS_CANTCACHE
;
1085 /* Tag system files */
1086 vfsp
.vnfs_marksystem
= 0;
1087 vfsp
.vnfs_markroot
= markroot
;
1091 if (dnp
->dn_clone
== NULL
)
1092 vnptr
= &dnp
->dn_vn
;
1095 error
= vnode_create(VNCREATE_FLAVOR
, VCREATESIZE
, &vfsp
, vnptr
);
1100 if ((dnp
->dn_clone
!= NULL
) && (dnp
->dn_vn
!= NULLVP
) )
1101 panic("devnode already has a vnode?");
1103 * Don't cache the vnode for the next open, if the
1104 * device is a cloning device (each open gets it's
1105 * own per-device instance vnode).
1107 if (dnp
->dn_clone
== NULL
) {
1108 *vn_pp
= dnp
->dn_vn
;
1113 } else if (n_minor
!= DEVFS_CLONE_ALLOC
) {
1115 * If we failed the create, we need to release the cloned minor
1116 * back to the free list. In general, this is only useful if
1117 * the clone function results in a state change in the cloned
1118 * device for which the minor number was obtained. If we get
1119 * past this point withouth falling into this case, it's
1120 * assumed that any state to be released will be released when
1121 * the vnode is dropped, instead.
1123 (void)(*dnp
->dn_clone
)(dnp
->dn_typeinfo
.dev
, DEVFS_CLONE_FREE
);
1126 dnp
->dn_lflags
&= ~DN_CREATE
;
1127 if (dnp
->dn_lflags
& DN_CREATEWAIT
) {
1128 dnp
->dn_lflags
&= ~DN_CREATEWAIT
;
1129 wakeup(&dnp
->dn_lflags
);
1132 devfs_release_busy(dnp
);
1138 /***********************************************************************
1139 * called with DEVFS_LOCK held
1140 ***********************************************************************/
1142 devfs_release_busy(devnode_t
*dnp
) {
1144 dnp
->dn_lflags
&= ~DN_BUSY
;
1146 if (dnp
->dn_lflags
& DN_DELETE
)
1150 /***********************************************************************
1151 * add a whole device, with no prototype.. make name element and node
1152 * Used for adding the original device entries
1154 * called with DEVFS_LOCK held
1155 ***********************************************************************/
1157 dev_add_entry(const char *name
, devnode_t
* parent
, int type
, devnode_type_t
* typeinfo
,
1158 devnode_t
* proto
, struct devfsmount
*dvm
, devdirent_t
* *nm_pp
)
1163 if ((error
= dev_add_node(type
, typeinfo
, proto
, &dnp
,
1164 (parent
?parent
->dn_dvm
:dvm
))) != 0)
1166 printf("devfs: %s: base node allocation failed (Errno=%d)\n",
1170 if ((error
= dev_add_name(name
,parent
,NULL
, dnp
, nm_pp
)) != 0)
1172 devfs_dn_free(dnp
); /* 1->0 for dir, 0->(-1) for other */
1173 printf("devfs: %s: name slot allocation failed (Errno=%d)\n",
1182 * Function: devfs_make_node
1185 * Create a device node with the given pathname in the devfs namespace.
1188 * dev - the dev_t value to associate
1189 * chrblk - block or character device (DEVFS_CHAR or DEVFS_BLOCK)
1190 * uid, gid - ownership
1191 * perms - permissions
1192 * clone - minor number cloning function
1193 * fmt, ... - path format string with printf args to format the path name
1195 * A handle to a device node if successful, NULL otherwise.
1198 devfs_make_node_clone(dev_t dev
, int chrblk
, uid_t uid
,
1199 gid_t gid
, int perms
, int (*clone
)(dev_t dev
, int action
),
1200 const char *fmt
, ...)
1202 devdirent_t
* new_dev
= NULL
;
1203 devnode_t
* dnp
; /* devnode for parent directory */
1204 devnode_type_t typeinfo
;
1206 char *name
, buf
[256]; /* XXX */
1215 printf("devfs_make_node: not ready for devices!\n");
1218 if (chrblk
!= DEVFS_CHAR
&& chrblk
!= DEVFS_BLOCK
)
1224 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1229 for(i
=strlen(buf
); i
>0; i
--)
1245 /* find/create directory path ie. mkdir -p */
1246 if (dev_finddir(path
, NULL
, DEVFS_CREATE
, &dnp
) == 0) {
1248 if (dev_add_entry(name
, dnp
,
1249 (chrblk
== DEVFS_CHAR
) ? DEV_CDEV
: DEV_BDEV
,
1250 &typeinfo
, NULL
, NULL
, &new_dev
) == 0) {
1251 new_dev
->de_dnp
->dn_gid
= gid
;
1252 new_dev
->de_dnp
->dn_uid
= uid
;
1253 new_dev
->de_dnp
->dn_mode
|= perms
;
1254 new_dev
->de_dnp
->dn_clone
= clone
;
1255 devfs_propogate(dnp
->dn_typeinfo
.Dir
.myname
, new_dev
);
1266 * Function: devfs_make_node
1269 * Create a device node with the given pathname in the devfs namespace.
1272 * dev - the dev_t value to associate
1273 * chrblk - block or character device (DEVFS_CHAR or DEVFS_BLOCK)
1274 * uid, gid - ownership
1275 * perms - permissions
1276 * fmt, ... - path format string with printf args to format the path name
1278 * A handle to a device node if successful, NULL otherwise.
1281 devfs_make_node(dev_t dev
, int chrblk
, uid_t uid
,
1282 gid_t gid
, int perms
, const char *fmt
, ...)
1284 devdirent_t
* new_dev
= NULL
;
1285 devnode_t
* dnp
; /* devnode for parent directory */
1286 devnode_type_t typeinfo
;
1288 char *name
, buf
[256]; /* XXX */
1291 char buff
[sizeof(buf
)];
1300 printf("devfs_make_node: not ready for devices!\n");
1303 if (chrblk
!= DEVFS_CHAR
&& chrblk
!= DEVFS_BLOCK
)
1309 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1313 bcopy(buf
, buff
, sizeof(buff
));
1314 buff
[sizeof(buff
)-1] = 0;
1318 for(i
=strlen(buf
); i
>0; i
--)
1334 /* find/create directory path ie. mkdir -p */
1335 if (dev_finddir(path
, NULL
, DEVFS_CREATE
, &dnp
) == 0) {
1337 if (dev_add_entry(name
, dnp
,
1338 (chrblk
== DEVFS_CHAR
) ? DEV_CDEV
: DEV_BDEV
,
1339 &typeinfo
, NULL
, NULL
, &new_dev
) == 0) {
1340 new_dev
->de_dnp
->dn_gid
= gid
;
1341 new_dev
->de_dnp
->dn_uid
= uid
;
1342 new_dev
->de_dnp
->dn_mode
|= perms
;
1343 new_dev
->de_dnp
->dn_clone
= NULL
;
1346 mac_devfs_label_associate_device(dev
, new_dev
->de_dnp
, buff
);
1348 devfs_propogate(dnp
->dn_typeinfo
.Dir
.myname
, new_dev
);
1358 * Function: devfs_make_link
1361 * Create a link to a previously created device node.
1364 * 0 if successful, -1 if failed
1367 devfs_make_link(void *original
, char *fmt
, ...)
1369 devdirent_t
* new_dev
= NULL
;
1370 devdirent_t
* orig
= (devdirent_t
*) original
;
1371 devnode_t
* dirnode
; /* devnode for parent directory */
1374 char *p
, buf
[256]; /* XXX */
1380 printf("devfs_make_link: not ready for devices!\n");
1386 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1391 for(i
=strlen(buf
); i
>0; i
--) {
1403 if (dev_finddir(buf
, NULL
, DEVFS_CREATE
, &dirnode
)
1404 || dev_add_name(p
, dirnode
, NULL
, orig
->de_dnp
, &new_dev
))
1407 if (dev_finddir("", NULL
, DEVFS_CREATE
, &dirnode
)
1408 || dev_add_name(buf
, dirnode
, NULL
, orig
->de_dnp
, &new_dev
))
1411 devfs_propogate(dirnode
->dn_typeinfo
.Dir
.myname
, new_dev
);
1416 return ((new_dev
!= NULL
) ? 0 : -1);