]>
git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_tree.c
b380cb3aa3f89a8bed330e2d086b68570362e56c
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 * Copyright 1997,1998 Julian Elischer. All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions are
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright notice,
36 * this list of conditions and the following disclaimer in the documentation
37 * and/or other materials provided with the distribution.
39 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
40 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
41 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
43 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
45 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
46 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
57 * - removed mounting of "hidden" mountpoint
58 * - fixed problem in which devnode->dn_vn pointer was not
59 * updated with the vnode returned from checkalias()
60 * - replaced devfs_vntodn() with a macro VTODN()
61 * - rewrote dev_finddir() to not use recursion
62 * - added locking to avoid data structure corruption (DEVFS_(UN)LOCK())
63 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
64 * - fixed problem with devfs_dntovn() checking the v_id against the
65 * value cached in the device node; a union mount on top of us causes
66 * the v_id to get incremented thus, we would end up returning a new
67 * vnode instead of the existing one that has the mounted_here
68 * field filled in; the net effect was that the filesystem mounted
69 * on top of us would never show up
70 * - added devfs_stats to store how many data structures are actually
74 /* SPLIT_DEVS means each devfs uses a different devnode for the same device */
75 /* Otherwise the same device always ends up at the same vnode even if */
76 /* reached througgh a different devfs instance. The practical difference */
77 /* is that with the same vnode, chmods and chowns show up on all instances of */
80 #define SPLIT_DEVS 1 /* maybe make this an option */
81 /*#define SPLIT_DEVS 1*/
83 #include <sys/param.h>
84 #include <sys/systm.h>
85 #include <sys/kernel.h>
87 #include <sys/malloc.h>
88 #include <sys/mount.h>
90 #include <sys/vnode.h>
94 #include "devfsdefs.h"
96 struct lock__bsd__ devfs_lock
; /* the "big switch" */
97 devdirent_t
* dev_root
= NULL
; /* root of backing tree */
98 struct devfs_stats devfs_stats
; /* hold stats */
100 #ifdef HIDDEN_MOUNTPOINT
101 static struct mount
*devfs_hidden_mount
;
102 #endif HIDDEN_MOINTPOINT
104 static int devfs_ready
= 0;
106 #define NOCREATE FALSE
110 * Set up the root directory node in the backing plane
111 * This is happenning before the vfs system has been
112 * set up yet, so be careful about what we reference..
113 * Notice that the ops are by indirection.. as they haven't
115 * DEVFS has a hidden mountpoint that is used as the anchor point
116 * for the internal 'blueprint' version of the dev filesystem tree.
122 lockinit(&devfs_lock
, PINOD
, "devfs", 0, 0);
123 if (dev_add_entry("root", NULL
, DEV_DIR
, NULL
, NULL
, NULL
,
125 printf("devfs_sinit: dev_add_entry failed ");
128 #ifdef HIDDEN_MOUNTPOINT
129 MALLOC(devfs_hidden_mount
, struct mount
*, sizeof(struct mount
),
131 bzero(devfs_hidden_mount
,sizeof(struct mount
));
133 /* Initialize the default IO constraints */
134 mp
->mnt_maxreadcnt
= mp
->mnt_maxwritecnt
= MAXPHYS
;
135 mp
->mnt_segreadcnt
= mp
->mnt_segwritecnt
= 32;
137 devfs_mount(devfs_hidden_mount
,"dummy",NULL
,NULL
,NULL
);
138 dev_root
->de_dnp
->dn_dvm
139 = (struct devfsmount
*)devfs_hidden_mount
->mnt_data
;
140 #endif HIDDEN_MOUNTPOINT
145 /***********************************************************************\
146 *************************************************************************
147 * Routines used to find our way to a point in the tree *
148 *************************************************************************
149 \***********************************************************************/
152 /***************************************************************\
153 * Search down the linked list off a dir to find "name" *
154 * return the devnode_t * for that node.
155 \***************************************************************/
158 dev_findname(devnode_t
* dir
,char *name
)
161 if (dir
->dn_type
!= DEV_DIR
) return 0;/*XXX*/ /* printf?*/
167 return dir
->dn_typeinfo
.Dir
.myname
;
169 if((name
[1] == '.') && (name
[2] == 0))
171 /* for root, .. == . */
172 return dir
->dn_typeinfo
.Dir
.parent
->dn_typeinfo
.Dir
.myname
;
175 newfp
= dir
->dn_typeinfo
.Dir
.dirlist
;
178 if(!(strcmp(name
,newfp
->de_name
)))
180 newfp
= newfp
->de_next
;
186 /***********************************************************************\
187 * Given a starting node (0 for root) and a pathname, return the node *
188 * for the end item on the path. It MUST BE A DIRECTORY. If the 'CREATE' *
189 * option is true, then create any missing nodes in the path and create *
190 * and return the final node as well. *
191 * This is used to set up a directory, before making nodes in it.. *
193 * Warning: This function is RECURSIVE. *
194 \***********************************************************************/
196 dev_finddir(char * orig_path
, /* find this dir (err if not dir) */
197 devnode_t
* dirnode
, /* starting point */
198 int create
, /* create path? */
199 devnode_t
* * dn_pp
) /* returned */
201 devdirent_t
* dirent_p
;
202 devnode_t
* dnp
= NULL
;
203 char pathbuf
[DEVMAXPATHSIZE
];
210 /***************************************\
211 * If no parent directory is given *
212 * then start at the root of the tree *
213 \***************************************/
214 if(!dirnode
) dirnode
= dev_root
->de_dnp
;
216 /***************************************\
218 \***************************************/
219 if (dirnode
->dn_type
!= DEV_DIR
) return ENOTDIR
;
220 if(strlen(orig_path
) > (DEVMAXPATHSIZE
- 1)) return ENAMETOOLONG
;
224 strcpy(path
,orig_path
);
226 /***************************************\
227 * always absolute, skip leading / *
228 * get rid of / or // or /// etc. *
229 \***************************************/
230 while(*path
== '/') path
++;
232 /***************************************\
233 * If nothing left, then parent was it.. *
234 \***************************************/
235 if ( *path
== '\0' ) {
240 /***************************************\
241 * find the next segment of the name *
242 \***************************************/
244 while((*cp
!= '/') && (*cp
!= 0)) {
248 /***********************************************\
249 * Check to see if it's the last component *
250 \***********************************************/
252 path
= cp
+ 1; /* path refers to the rest */
253 *cp
= 0; /* name is now a separate string */
255 path
= (char *)0; /* was trailing slash */
258 path
= NULL
; /* no more to do */
261 /***************************************\
262 * Start scanning along the linked list *
263 \***************************************/
264 dirent_p
= dev_findname(dirnode
,name
);
265 if(dirent_p
) { /* check it's a directory */
266 dnp
= dirent_p
->de_dnp
;
267 if(dnp
->dn_type
!= DEV_DIR
) return ENOTDIR
;
269 /***************************************\
270 * The required element does not exist *
271 * So we will add it if asked to. *
272 \***************************************/
273 if(!create
) return ENOENT
;
275 if((retval
= dev_add_entry(name
, dirnode
,
276 DEV_DIR
, NULL
, NULL
, NULL
,
280 dnp
= dirent_p
->de_dnp
;
281 devfs_propogate(dirnode
->dn_typeinfo
.Dir
.myname
,dirent_p
);
283 if(path
!= NULL
) { /* decide whether to recurse more or return */
284 return (dev_finddir(path
,dnp
,create
,dn_pp
));
291 /***********************************************************************\
292 * Given a starting node (0 for root) and a pathname, return the node *
293 * for the end item on the path. It MUST BE A DIRECTORY. If the 'CREATE' *
294 * option is true, then create any missing nodes in the path and create *
295 * and return the final node as well. *
296 * This is used to set up a directory, before making nodes in it.. *
297 \***********************************************************************/
300 dev_finddir(char * path
,
305 devnode_t
* dnp
= NULL
;
310 if (!dirnode
) /* dirnode == NULL means start at root */
311 dirnode
= dev_root
->de_dnp
;
313 if (dirnode
->dn_type
!= DEV_DIR
)
316 if (strlen(path
) > (DEVMAXPATHSIZE
- 1))
327 char component
[DEVMAXPATHSIZE
];
328 devdirent_t
* dirent_p
;
332 /* we hit the end of the string, we're done */
337 while (*scan
!= '/' && *scan
)
340 strncpy(component
, start
, scan
- start
);
344 dirent_p
= dev_findname(dirnode
, component
);
346 dnp
= dirent_p
->de_dnp
;
347 if (dnp
->dn_type
!= DEV_DIR
) {
357 error
= dev_add_entry(component
, dirnode
,
358 DEV_DIR
, NULL
, NULL
, NULL
, &dirent_p
);
361 dnp
= dirent_p
->de_dnp
;
362 devfs_propogate(dirnode
->dn_typeinfo
.Dir
.myname
, dirent_p
);
364 dirnode
= dnp
; /* continue relative to this directory */
370 /***********************************************************************\
371 * Add a new NAME element to the devfs *
372 * If we're creating a root node, then dirname is NULL *
373 * Basically this creates a new namespace entry for the device node *
375 * Creates a name node, and links it to the supplied node *
376 \***********************************************************************/
379 dev_add_name(char * name
, devnode_t
* dirnode
, devdirent_t
* back
,
380 devnode_t
* dnp
, devdirent_t
* *dirent_pp
)
382 devdirent_t
* dirent_p
= NULL
;
384 if(dirnode
!= NULL
) {
385 if(dirnode
->dn_type
!= DEV_DIR
) return(ENOTDIR
);
387 if( dev_findname(dirnode
,name
))
391 * make sure the name is legal
392 * slightly misleading in the case of NULL
394 if (!name
|| (strlen(name
) > (DEVMAXNAMESIZE
- 1)))
395 return (ENAMETOOLONG
);
398 * Allocate and fill out a new directory entry
400 MALLOC(dirent_p
, devdirent_t
*, sizeof(devdirent_t
),
401 M_DEVFSNAME
, M_WAITOK
);
405 bzero(dirent_p
,sizeof(devdirent_t
));
407 /* inherrit our parent's mount info */ /*XXX*/
408 /* a kludge but.... */
409 if(dirnode
&& ( dnp
->dn_dvm
== NULL
)) {
410 dnp
->dn_dvm
= dirnode
->dn_dvm
;
411 /* if(!dnp->dn_dvm) printf("parent had null dvm "); */
415 * Link the two together
416 * include the implicit link in the count of links to the devnode..
417 * this stops it from being accidentally freed later.
419 dirent_p
->de_dnp
= dnp
;
420 dnp
->dn_links
++ ; /* implicit from our own name-node */
423 * Make sure that we can find all the links that reference a node
424 * so that we can get them all if we need to zap the node.
426 if(dnp
->dn_linklist
) {
427 dirent_p
->de_nextlink
= dnp
->dn_linklist
;
428 dirent_p
->de_prevlinkp
= dirent_p
->de_nextlink
->de_prevlinkp
;
429 dirent_p
->de_nextlink
->de_prevlinkp
= &(dirent_p
->de_nextlink
);
430 *dirent_p
->de_prevlinkp
= dirent_p
;
432 dirent_p
->de_nextlink
= dirent_p
;
433 dirent_p
->de_prevlinkp
= &(dirent_p
->de_nextlink
);
435 dnp
->dn_linklist
= dirent_p
;
438 * If the node is a directory, then we need to handle the
439 * creation of the .. link.
440 * A NULL dirnode indicates a root node, so point to ourself.
442 if(dnp
->dn_type
== DEV_DIR
) {
443 dnp
->dn_typeinfo
.Dir
.myname
= dirent_p
;
445 * If we are unlinking from an old dir, decrement its links
446 * as we point our '..' elsewhere
447 * Note: it's up to the calling code to remove the
448 * us from the original directory's list
450 if(dnp
->dn_typeinfo
.Dir
.parent
) {
451 dnp
->dn_typeinfo
.Dir
.parent
->dn_links
--;
454 dnp
->dn_typeinfo
.Dir
.parent
= dirnode
;
456 dnp
->dn_typeinfo
.Dir
.parent
= dnp
;
458 dnp
->dn_typeinfo
.Dir
.parent
->dn_links
++; /* account for the new '..' */
462 * put the name into the directory entry.
464 strcpy(dirent_p
->de_name
, name
);
468 * Check if we are not making a root node..
473 * Put it on the END of the linked list of directory entries
477 dirent_p
->de_parent
= dirnode
; /* null for root */
478 dirent_p
->de_prevp
= dirnode
->dn_typeinfo
.Dir
.dirlast
;
479 dirent_p
->de_next
= *(dirent_p
->de_prevp
); /* should be NULL */
481 *(dirent_p
->de_prevp
) = dirent_p
;
482 dirnode
->dn_typeinfo
.Dir
.dirlast
= &(dirent_p
->de_next
);
483 dirnode
->dn_typeinfo
.Dir
.entrycount
++;
484 dirnode
->dn_len
+= strlen(name
) + 8;/*ok, ok?*/
487 *dirent_pp
= dirent_p
;
488 DEVFS_INCR_ENTRIES();
493 /***********************************************************************\
494 * Add a new element to the devfs plane. *
496 * Creates a new dev_node to go with it if the prototype should not be *
497 * reused. (Is a DIR, or we select SPLIT_DEVS at compile time) *
498 * typeinfo gives us info to make our node if we don't have a prototype. *
499 * If typeinfo is null and proto exists, then the typeinfo field of *
500 * the proto is used intead in the CREATE case. *
501 * note the 'links' count is 0 (except if a dir) *
502 * but it is only cleared on a transition *
503 * so this is ok till we link it to something *
504 * Even in SPLIT_DEVS mode, *
505 * if the node already exists on the wanted plane, just return it *
506 \***********************************************************************/
509 dev_add_node(int entrytype
, devnode_type_t
* typeinfo
, devnode_t
* proto
,
510 devnode_t
* *dn_pp
, struct devfsmount
*dvm
)
512 devnode_t
* dnp
= NULL
;
514 #if defined SPLIT_DEVS
516 * If we have a prototype, then check if there is already a sibling
517 * on the mount plane we are looking at, if so, just return it.
520 dnp
= proto
->dn_nextsibling
;
521 while( dnp
!= proto
) {
522 if (dnp
->dn_dvm
== dvm
) {
526 dnp
= dnp
->dn_nextsibling
;
528 if (typeinfo
== NULL
)
529 typeinfo
= &(proto
->dn_typeinfo
);
531 #else /* SPLIT_DEVS */
533 switch (proto
->type
) {
540 #endif /* SPLIT_DEVS */
541 MALLOC(dnp
, devnode_t
*, sizeof(devnode_t
), M_DEVFSNODE
, M_WAITOK
);
547 * If we have a proto, that means that we are duplicating some
548 * other device, which can only happen if we are not at the back plane
551 bcopy(proto
, dnp
, sizeof(devnode_t
));
553 dnp
->dn_linklist
= NULL
;
556 /* add to END of siblings list */
557 dnp
->dn_prevsiblingp
= proto
->dn_prevsiblingp
;
558 *(dnp
->dn_prevsiblingp
) = dnp
;
559 dnp
->dn_nextsibling
= proto
;
560 proto
->dn_prevsiblingp
= &(dnp
->dn_nextsibling
);
565 * We have no prototype, so start off with a clean slate
568 bzero(dnp
,sizeof(devnode_t
));
569 dnp
->dn_type
= entrytype
;
570 dnp
->dn_nextsibling
= dnp
;
571 dnp
->dn_prevsiblingp
= &(dnp
->dn_nextsibling
);
572 dnp
->dn_atime
.tv_sec
= tv
.tv_sec
;
573 dnp
->dn_mtime
.tv_sec
= tv
.tv_sec
;
574 dnp
->dn_ctime
.tv_sec
= tv
.tv_sec
;
579 * fill out the dev node according to type
584 * As it's a directory, make sure
585 * it has a null entries list
587 dnp
->dn_typeinfo
.Dir
.dirlast
= &(dnp
->dn_typeinfo
.Dir
.dirlist
);
588 dnp
->dn_typeinfo
.Dir
.dirlist
= (devdirent_t
*)0;
589 dnp
->dn_typeinfo
.Dir
.entrycount
= 0;
590 /* until we know better, it has a null parent pointer*/
591 dnp
->dn_typeinfo
.Dir
.parent
= NULL
;
592 dnp
->dn_links
++; /* for .*/
593 dnp
->dn_typeinfo
.Dir
.myname
= NULL
;
595 * make sure that the ops associated with it are the ops
596 * that we use (by default) for directories
598 dnp
->dn_ops
= &devfs_vnodeop_p
;
599 dnp
->dn_mode
|= 0555; /* default perms */
603 * As it's a symlink allocate and store the link info
604 * Symlinks should only ever be created by the user,
605 * so they are not on the back plane and should not be
606 * propogated forward.. a bit like directories in that way..
607 * A symlink only exists on one plane and has its own
608 * node.. therefore we might be on any random plane.
610 MALLOC(dnp
->dn_typeinfo
.Slnk
.name
, char *,
611 typeinfo
->Slnk
.namelen
+1,
612 M_DEVFSNODE
, M_WAITOK
);
613 if (!dnp
->dn_typeinfo
.Slnk
.name
) {
614 FREE(dnp
,M_DEVFSNODE
);
617 strncpy(dnp
->dn_typeinfo
.Slnk
.name
, typeinfo
->Slnk
.name
,
618 typeinfo
->Slnk
.namelen
);
619 dnp
->dn_typeinfo
.Slnk
.name
[typeinfo
->Slnk
.namelen
] = '\0';
620 dnp
->dn_typeinfo
.Slnk
.namelen
= typeinfo
->Slnk
.namelen
;
621 DEVFS_INCR_STRINGSPACE(dnp
->dn_typeinfo
.Slnk
.namelen
+ 1);
622 dnp
->dn_ops
= &devfs_vnodeop_p
;
623 dnp
->dn_mode
|= 0555; /* default perms */
628 * Make sure it has DEVICE type ops
629 * and device specific fields are correct
631 dnp
->dn_ops
= &devfs_spec_vnodeop_p
;
632 dnp
->dn_typeinfo
.dev
= typeinfo
->dev
;
646 devnode_free(devnode_t
* dnp
)
648 if (dnp
->dn_type
== DEV_SLNK
) {
649 DEVFS_DECR_STRINGSPACE(dnp
->dn_typeinfo
.Slnk
.namelen
+ 1);
650 FREE(dnp
->dn_typeinfo
.Slnk
.name
,M_DEVFSNODE
);
652 FREE(dnp
, M_DEVFSNODE
);
659 devfs_dn_free(devnode_t
* dnp
)
661 if(--dnp
->dn_links
<= 0 ) /* can be -1 for initial free, on error */
663 /*probably need to do other cleanups XXX */
664 if (dnp
->dn_nextsibling
!= dnp
) {
665 devnode_t
* * prevp
= dnp
->dn_prevsiblingp
;
666 *prevp
= dnp
->dn_nextsibling
;
667 dnp
->dn_nextsibling
->dn_prevsiblingp
= prevp
;
670 if (dnp
->dn_vn
== NULL
) {
672 printf("devfs_dn_free: free'ing %x\n", (unsigned int)dnp
);
674 devnode_free(dnp
); /* no accesses/references */
678 printf("devfs_dn_free: marking %x for deletion\n",
681 dnp
->dn_delete
= TRUE
;
686 /***********************************************************************\
687 * Front Node Operations *
688 * Add or delete a chain of front nodes *
689 \***********************************************************************/
691 /***********************************************************************\
692 * Given a directory backing node, and a child backing node, add the *
693 * appropriate front nodes to the front nodes of the directory to *
694 * represent the child node to the user *
696 * on failure, front nodes will either be correct or not exist for each *
697 * front dir, however dirs completed will not be stripped of completed *
698 * frontnodes on failure of a later frontnode *
700 * This allows a new node to be propogated through all mounted planes *
702 \***********************************************************************/
705 devfs_propogate(devdirent_t
* parent
,devdirent_t
* child
)
708 devdirent_t
* newnmp
;
709 devnode_t
* dnp
= child
->de_dnp
;
710 devnode_t
* pdnp
= parent
->de_dnp
;
711 devnode_t
* adnp
= parent
->de_dnp
;
712 int type
= child
->de_dnp
->dn_type
;
714 /***********************************************\
715 * Find the other instances of the parent node *
716 \***********************************************/
717 for (adnp
= pdnp
->dn_nextsibling
;
719 adnp
= adnp
->dn_nextsibling
)
722 * Make the node, using the original as a prototype)
723 * if the node already exists on that plane it won't be
726 if ((error
= dev_add_entry(child
->de_name
, adnp
, type
,
727 NULL
, dnp
, adnp
->dn_dvm
,
729 printf("duplicating %s failed\n",child
->de_name
);
732 return 0; /* for now always succeed */
735 /***********************************************************************
736 * remove all instances of this devicename [for backing nodes..]
737 * note.. if there is another link to the node (non dir nodes only)
738 * then the devfs_node will still exist as the ref count will be non-0
739 * removing a directory node will remove all sup-nodes on all planes (ZAP)
741 * Used by device drivers to remove nodes that are no longer relevant
742 * The argument is the 'cookie' they were given when they created the node
743 * this function is exported.. see devfs.h
744 ***********************************************************************/
746 devfs_remove(void *dirent_p
)
748 devnode_t
* dnp
= ((devdirent_t
*)dirent_p
)->de_dnp
;
750 boolean_t funnel_state
;
753 funnel_state
= thread_funnel_set(kernel_flock
, TRUE
);
756 printf("devfs_remove: not ready for devices!\n");
762 /* keep removing the next sibling till only we exist. */
763 while((dnp2
= dnp
->dn_nextsibling
) != dnp
) {
766 * Keep removing the next front node till no more exist
768 dnp
->dn_nextsibling
= dnp2
->dn_nextsibling
;
769 dnp
->dn_nextsibling
->dn_prevsiblingp
= &(dnp
->dn_nextsibling
);
770 dnp2
->dn_nextsibling
= dnp2
;
771 dnp2
->dn_prevsiblingp
= &(dnp2
->dn_nextsibling
);
772 if(dnp2
->dn_linklist
) {
774 lastlink
= (1 == dnp2
->dn_links
);
775 dev_free_name(dnp2
->dn_linklist
);
781 * then free the main node
782 * If we are not running in SPLIT_DEVS mode, then
783 * THIS is what gets rid of the propogated nodes.
785 if(dnp
->dn_linklist
) {
787 lastlink
= (1 == dnp
->dn_links
);
788 dev_free_name(dnp
->dn_linklist
);
793 (void) thread_funnel_set(kernel_flock
, funnel_state
);
798 /***************************************************************
799 * duplicate the backing tree into a tree of nodes hung off the
800 * mount point given as the argument. Do this by
801 * calling dev_dup_entry which recurses all the way
803 **************************************************************/
806 dev_dup_plane(struct devfsmount
*devfs_mp_p
)
811 if ((error
= dev_dup_entry(NULL
, dev_root
, &new, devfs_mp_p
)))
813 devfs_mp_p
->plane_root
= new;
819 /***************************************************************\
821 \***************************************************************/
824 devfs_free_plane(struct devfsmount
*devfs_mp_p
)
826 devdirent_t
* dirent_p
;
828 dirent_p
= devfs_mp_p
->plane_root
;
830 dev_free_hier(dirent_p
);
831 dev_free_name(dirent_p
);
833 devfs_mp_p
->plane_root
= NULL
;
836 /***************************************************************\
837 * Create and link in a new front element.. *
838 * Parent can be 0 for a root node *
839 * Not presently usable to make a symlink XXX *
840 * (Ok, symlinks don't propogate)
841 * recursively will create subnodes corresponding to equivalent *
842 * child nodes in the base level *
843 \***************************************************************/
846 dev_dup_entry(devnode_t
* parent
, devdirent_t
* back
, devdirent_t
* *dnm_pp
,
847 struct devfsmount
*dvm
)
849 devdirent_t
* entry_p
;
850 devdirent_t
* newback
;
851 devdirent_t
* newfront
;
853 devnode_t
* dnp
= back
->de_dnp
;
854 int type
= dnp
->dn_type
;
857 * go get the node made (if we need to)
858 * use the back one as a prototype
860 if ((error
= dev_add_entry(back
->de_name
, parent
, type
,
862 parent
?parent
->dn_dvm
:dvm
, &entry_p
)) != 0) {
863 printf("duplicating %s failed\n",back
->de_name
);
867 * If we have just made the root, then insert the pointer to the
871 entry_p
->de_dnp
->dn_dvm
= dvm
;
875 * If it is a directory, then recurse down all the other
877 * note that this time we don't pass on the mount info..
881 for(newback
= back
->de_dnp
->dn_typeinfo
.Dir
.dirlist
;
882 newback
; newback
= newback
->de_next
)
884 if((error
= dev_dup_entry(entry_p
->de_dnp
,
885 newback
, &newfront
, NULL
)) != 0)
887 break; /* back out with an error */
895 /***************************************************************\
897 * remember that if there are other names pointing to the *
898 * dev_node then it may not get freed yet *
899 * can handle if there is no dnp *
900 \***************************************************************/
903 dev_free_name(devdirent_t
* dirent_p
)
905 devnode_t
* parent
= dirent_p
->de_parent
;
906 devnode_t
* dnp
= dirent_p
->de_dnp
;
909 if(dnp
->dn_type
== DEV_DIR
)
913 if(dnp
->dn_typeinfo
.Dir
.dirlist
)
915 p
= dnp
->dn_typeinfo
.Dir
.parent
;
916 devfs_dn_free(dnp
); /* account for '.' */
917 devfs_dn_free(p
); /* '..' */
920 * unlink us from the list of links for this node
921 * If we are the only link, it's easy!
922 * if we are a DIR of course there should not be any
925 if(dirent_p
->de_nextlink
== dirent_p
) {
926 dnp
->dn_linklist
= NULL
;
928 if(dnp
->dn_linklist
== dirent_p
) {
929 dnp
->dn_linklist
= dirent_p
->de_nextlink
;
931 dirent_p
->de_nextlink
->de_prevlinkp
932 = dirent_p
->de_prevlinkp
;
933 *dirent_p
->de_prevlinkp
= dirent_p
->de_nextlink
;
939 * unlink ourselves from the directory on this plane
941 if(parent
) /* if not fs root */
943 if( (*dirent_p
->de_prevp
= dirent_p
->de_next
) )/* yes, assign */
945 dirent_p
->de_next
->de_prevp
= dirent_p
->de_prevp
;
949 parent
->dn_typeinfo
.Dir
.dirlast
950 = dirent_p
->de_prevp
;
952 parent
->dn_typeinfo
.Dir
.entrycount
--;
953 parent
->dn_len
-= strlen(dirent_p
->de_name
) + 8;
956 DEVFS_DECR_ENTRIES();
957 FREE(dirent_p
,M_DEVFSNAME
);
961 /***************************************************************\
962 * Free a hierarchy starting at a directory node name *
963 * remember that if there are other names pointing to the *
964 * dev_node then it may not get freed yet *
965 * can handle if there is no dnp *
966 * leave the node itself allocated. *
967 \***************************************************************/
970 dev_free_hier(devdirent_t
* dirent_p
)
972 devnode_t
* dnp
= dirent_p
->de_dnp
;
975 if(dnp
->dn_type
== DEV_DIR
)
977 while(dnp
->dn_typeinfo
.Dir
.dirlist
)
979 dev_free_hier(dnp
->dn_typeinfo
.Dir
.dirlist
);
980 dev_free_name(dnp
->dn_typeinfo
.Dir
.dirlist
);
986 /***************************************************************\
987 * given a dev_node, find the appropriate vnode if one is already
988 * associated, or get a new one and associate it with the dev_node
989 \***************************************************************/
992 devfs_dntovn(devnode_t
* dnp
, struct vnode
**vn_pp
, struct proc
* p
)
994 struct vnode
*vn_p
, *nvp
;
999 if (vn_p
) { /* already has a vnode */
1001 return(vget(vn_p
, LK_EXCLUSIVE
, p
));
1003 if (!(error
= getnewvnode(VT_DEVFS
, dnp
->dn_dvm
->mount
,
1004 *(dnp
->dn_ops
), &vn_p
))) {
1005 switch(dnp
->dn_type
) {
1007 vn_p
->v_type
= VLNK
;
1010 if (dnp
->dn_typeinfo
.Dir
.parent
== dnp
) {
1011 vn_p
->v_flag
|= VROOT
;
1013 vn_p
->v_type
= VDIR
;
1018 = (dnp
->dn_type
== DEV_BDEV
) ? VBLK
: VCHR
;
1019 if ((nvp
= checkalias(vn_p
, dnp
->dn_typeinfo
.dev
,
1020 dnp
->dn_dvm
->mount
)) != NULL
) {
1026 vn_p
->v_mount
= dnp
->dn_dvm
->mount
;/* XXX Duplicated */
1028 vn_p
->v_data
= (void *)dnp
;
1030 error
= vn_lock(vn_p
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1035 /***********************************************************************\
1036 * add a whole device, with no prototype.. make name element and node *
1037 * Used for adding the original device entries *
1038 \***********************************************************************/
1041 dev_add_entry(char *name
, devnode_t
* parent
, int type
, devnode_type_t
* typeinfo
,
1042 devnode_t
* proto
, struct devfsmount
*dvm
, devdirent_t
* *nm_pp
)
1047 if ((error
= dev_add_node(type
, typeinfo
, proto
, &dnp
,
1048 (parent
?parent
->dn_dvm
:dvm
))) != 0)
1050 printf("devfs: %s: base node allocation failed (Errno=%d)\n",
1054 if ((error
= dev_add_name(name
,parent
,NULL
, dnp
, nm_pp
)) != 0)
1056 devfs_dn_free(dnp
); /* 1->0 for dir, 0->(-1) for other */
1057 printf("devfs: %s: name slot allocation failed (Errno=%d)\n",
1065 * Function: devfs_make_node
1068 * Create a device node with the given pathname in the devfs namespace.
1071 * dev - the dev_t value to associate
1072 * chrblk - block or character device (DEVFS_CHAR or DEVFS_BLOCK)
1073 * uid, gid - ownership
1074 * perms - permissions
1075 * fmt, ... - path format string with printf args to format the path name
1077 * A handle to a device node if successful, NULL otherwise.
1080 devfs_make_node(dev_t dev
, int chrblk
, uid_t uid
,
1081 gid_t gid
, int perms
, char *fmt
, ...)
1083 devdirent_t
* new_dev
= NULL
;
1084 devnode_t
* dnp
; /* devnode for parent directory */
1085 devnode_type_t typeinfo
;
1087 char *name
, *path
, buf
[256]; /* XXX */
1088 boolean_t funnel_state
;
1092 funnel_state
= thread_funnel_set(kernel_flock
, TRUE
);
1095 printf("devfs_make_node: not ready for devices!\n");
1099 if (chrblk
!= DEVFS_CHAR
&& chrblk
!= DEVFS_BLOCK
)
1103 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1108 for(i
=strlen(buf
); i
>0; i
--)
1124 /* find/create directory path ie. mkdir -p */
1125 if (dev_finddir(path
, NULL
, CREATE
, &dnp
) == 0) {
1127 if (dev_add_entry(name
, dnp
,
1128 (chrblk
== DEVFS_CHAR
) ? DEV_CDEV
: DEV_BDEV
,
1129 &typeinfo
, NULL
, NULL
, &new_dev
) == 0) {
1130 new_dev
->de_dnp
->dn_gid
= gid
;
1131 new_dev
->de_dnp
->dn_uid
= uid
;
1132 new_dev
->de_dnp
->dn_mode
|= perms
;
1133 devfs_propogate(dnp
->dn_typeinfo
.Dir
.myname
, new_dev
);
1139 (void) thread_funnel_set(kernel_flock
, funnel_state
);
1144 * Function: devfs_make_link
1147 * Create a link to a previously created device node.
1150 * 0 if successful, -1 if failed
1153 devfs_make_link(void *original
, char *fmt
, ...)
1155 devdirent_t
* new_dev
= NULL
;
1156 devdirent_t
* orig
= (devdirent_t
*) original
;
1157 devnode_t
* dirnode
; /* devnode for parent directory */
1160 char *p
, buf
[256]; /* XXX */
1162 boolean_t funnel_state
;
1164 funnel_state
= thread_funnel_set(kernel_flock
, TRUE
);
1167 printf("devfs_make_link: not ready for devices!\n");
1172 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1177 for(i
=strlen(buf
); i
>0; i
--)
1186 if (dev_finddir(buf
, NULL
, CREATE
, &dirnode
)
1187 || dev_add_name(p
, dirnode
, NULL
, orig
->de_dnp
, &new_dev
))
1190 if (dev_finddir("", NULL
, CREATE
, &dirnode
)
1191 || dev_add_name(buf
, dirnode
, NULL
, orig
->de_dnp
, &new_dev
))
1194 devfs_propogate(dirnode
->dn_typeinfo
.Dir
.myname
, new_dev
);
1198 (void) thread_funnel_set(kernel_flock
, funnel_state
);
1199 return ((new_dev
!= NULL
) ? 0 : -1);