]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_tree.c
xnu-1228.12.14.tar.gz
[apple/xnu.git] / bsd / miscfs / devfs / devfs_tree.c
1 /*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * Copyright 1997,1998 Julian Elischer. All rights reserved.
31 * julian@freebsd.org
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions are
35 * met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright notice,
39 * this list of conditions and the following disclaimer in the documentation
40 * and/or other materials provided with the distribution.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
43 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
44 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
45 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
46 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 *
54 * devfs_tree.c
55 */
56 /*
57 * 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,
60 * Version 2.0.
61 */
62
63 /*
64 * HISTORY
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
80 * allocated
81 */
82
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 */
87 /* a device. (etc) */
88
89 #define SPLIT_DEVS 1 /* maybe make this an option */
90 /*#define SPLIT_DEVS 1*/
91
92 #include <sys/param.h>
93 #include <sys/systm.h>
94 #include <sys/kernel.h>
95 #include <sys/conf.h>
96 #include <sys/malloc.h>
97 #include <sys/mount_internal.h>
98 #include <sys/proc.h>
99 #include <sys/vnode_internal.h>
100 #include <stdarg.h>
101 #include <libkern/OSAtomic.h>
102 #define BSD_KERNEL_PRIVATE 1 /* devfs_make_link() prototype */
103 #include "devfs.h"
104 #include "devfsdefs.h"
105
106 #if CONFIG_MACF
107 #include <security/mac_framework.h>
108 #endif
109
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 *);
115
116
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;
121
122 devdirent_t * dev_root = NULL; /* root of backing tree */
123 struct devfs_stats devfs_stats; /* hold stats */
124
125 #ifdef HIDDEN_MOUNTPOINT
126 static struct mount *devfs_hidden_mount;
127 #endif /* HIDDEN_MOINTPOINT */
128
129 static int devfs_ready = 0;
130
131 #define DEVFS_NOCREATE FALSE
132 #define DEVFS_CREATE TRUE
133
134 /*
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
139 * been set up yet!
140 * DEVFS has a hidden mountpoint that is used as the anchor point
141 * for the internal 'blueprint' version of the dev filesystem tree.
142 */
143 /*proto*/
144 int
145 devfs_sinit(void)
146 {
147 int error;
148
149 devfs_lck_grp_attr = lck_grp_attr_alloc_init();
150 devfs_lck_grp = lck_grp_alloc_init("devfs_lock", devfs_lck_grp_attr);
151
152 devfs_lck_attr = lck_attr_alloc_init();
153
154 lck_mtx_init(&devfs_mutex, devfs_lck_grp, devfs_lck_attr);
155
156 DEVFS_LOCK();
157 error = dev_add_entry("root", NULL, DEV_DIR, NULL, NULL, NULL, &dev_root);
158 DEVFS_UNLOCK();
159
160 if (error) {
161 printf("devfs_sinit: dev_add_entry failed ");
162 return (ENOTSUP);
163 }
164 #ifdef HIDDEN_MOUNTPOINT
165 MALLOC(devfs_hidden_mount, struct mount *, sizeof(struct mount),
166 M_MOUNT, M_WAITOK);
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);
172 #if CONFIG_MACF
173 mac_mount_label_init(devfs_hidden_mount);
174 mac_mount_label_associate(vfs_context_kernel(), devfs_hidden_mount);
175 #endif
176
177 /* Initialize the default IO constraints */
178 mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
179 mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
180 mp->mnt_ioflags = 0;
181 mp->mnt_realrootvp = NULLVP;
182 mp->mnt_authcache_ttl = CACHED_LOOKUP_RIGHT_TTL;
183
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 */
188 #if CONFIG_MACF
189 mac_devfs_label_associate_directory("/", strlen("/"),
190 dev_root->de_dnp, "/");
191 #endif
192 devfs_ready = 1;
193 return (0);
194 }
195
196 /***********************************************************************\
197 *************************************************************************
198 * Routines used to find our way to a point in the tree *
199 *************************************************************************
200 \***********************************************************************/
201
202
203
204 /***************************************************************
205 * Search down the linked list off a dir to find "name"
206 * return the devnode_t * for that node.
207 *
208 * called with DEVFS_LOCK held
209 ***************************************************************/
210 devdirent_t *
211 dev_findname(devnode_t * dir, const char *name)
212 {
213 devdirent_t * newfp;
214 if (dir->dn_type != DEV_DIR) return 0;/*XXX*/ /* printf?*/
215
216 if (name[0] == '.')
217 {
218 if(name[1] == 0)
219 {
220 return dir->dn_typeinfo.Dir.myname;
221 }
222 if((name[1] == '.') && (name[2] == 0))
223 {
224 /* for root, .. == . */
225 return dir->dn_typeinfo.Dir.parent->dn_typeinfo.Dir.myname;
226 }
227 }
228 newfp = dir->dn_typeinfo.Dir.dirlist;
229
230 while(newfp)
231 {
232 if(!(strncmp(name, newfp->de_name, sizeof(newfp->de_name))))
233 return newfp;
234 newfp = newfp->de_next;
235 }
236 return NULL;
237 }
238
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..
245 *
246 * called with DEVFS_LOCK held
247 ***********************************************************************/
248 static int
249 dev_finddir(const char * path,
250 devnode_t * dirnode,
251 int create,
252 devnode_t * * dn_pp)
253 {
254 devnode_t * dnp = NULL;
255 int error = 0;
256 const char * scan;
257 #if CONFIG_MACF
258 char fullpath[DEVMAXPATHSIZE];
259 #endif
260
261
262 if (!dirnode) /* dirnode == NULL means start at root */
263 dirnode = dev_root->de_dnp;
264
265 if (dirnode->dn_type != DEV_DIR)
266 return ENOTDIR;
267
268 if (strlen(path) > (DEVMAXPATHSIZE - 1))
269 return ENAMETOOLONG;
270
271 #if CONFIG_MACF
272 strlcpy (fullpath, path, DEVMAXPATHSIZE);
273 #endif
274 scan = path;
275
276 while (*scan == '/')
277 scan++;
278
279 *dn_pp = NULL;
280
281 while (1) {
282 char component[DEVMAXPATHSIZE];
283 devdirent_t * dirent_p;
284 const char * start;
285
286 if (*scan == 0) {
287 /* we hit the end of the string, we're done */
288 *dn_pp = dirnode;
289 break;
290 }
291 start = scan;
292 while (*scan != '/' && *scan)
293 scan++;
294
295 strlcpy(component, start, scan - start);
296 if (*scan == '/')
297 scan++;
298
299 dirent_p = dev_findname(dirnode, component);
300 if (dirent_p) {
301 dnp = dirent_p->de_dnp;
302 if (dnp->dn_type != DEV_DIR) {
303 error = ENOTDIR;
304 break;
305 }
306 }
307 else {
308 if (!create) {
309 error = ENOENT;
310 break;
311 }
312 error = dev_add_entry(component, dirnode,
313 DEV_DIR, NULL, NULL, NULL, &dirent_p);
314 if (error)
315 break;
316 dnp = dirent_p->de_dnp;
317 #if CONFIG_MACF
318 mac_devfs_label_associate_directory(
319 dirnode->dn_typeinfo.Dir.myname->de_name,
320 strlen(dirnode->dn_typeinfo.Dir.myname->de_name),
321 dnp, fullpath);
322 #endif
323 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, dirent_p);
324 }
325 dirnode = dnp; /* continue relative to this directory */
326 }
327 return (error);
328 }
329
330
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
335 *
336 * Creates a name node, and links it to the supplied node
337 *
338 * called with DEVFS_LOCK held
339 ***********************************************************************/
340 int
341 dev_add_name(const char * name, devnode_t * dirnode, __unused devdirent_t * back,
342 devnode_t * dnp, devdirent_t * *dirent_pp)
343 {
344 devdirent_t * dirent_p = NULL;
345
346 if(dirnode != NULL ) {
347 if(dirnode->dn_type != DEV_DIR) return(ENOTDIR);
348
349 if( dev_findname(dirnode,name))
350 return(EEXIST);
351 }
352 /*
353 * make sure the name is legal
354 * slightly misleading in the case of NULL
355 */
356 if (!name || (strlen(name) > (DEVMAXNAMESIZE - 1)))
357 return (ENAMETOOLONG);
358
359 /*
360 * Allocate and fill out a new directory entry
361 */
362 MALLOC(dirent_p, devdirent_t *, sizeof(devdirent_t),
363 M_DEVFSNAME, M_WAITOK);
364 if (!dirent_p) {
365 return ENOMEM;
366 }
367 bzero(dirent_p,sizeof(devdirent_t));
368
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 "); */
374 }
375
376 /*
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.
380 */
381 dirent_p->de_dnp = dnp;
382 dnp->dn_links++ ; /* implicit from our own name-node */
383
384 /*
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.
387 */
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;
393 } else {
394 dirent_p->de_nextlink = dirent_p;
395 dirent_p->de_prevlinkp = &(dirent_p->de_nextlink);
396 }
397 dnp->dn_linklist = dirent_p;
398
399 /*
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.
403 */
404 if(dnp->dn_type == DEV_DIR) {
405 dnp->dn_typeinfo.Dir.myname = dirent_p;
406 /*
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
411 */
412 if(dnp->dn_typeinfo.Dir.parent) {
413 dnp->dn_typeinfo.Dir.parent->dn_links--;
414 }
415 if(dirnode) {
416 dnp->dn_typeinfo.Dir.parent = dirnode;
417 } else {
418 dnp->dn_typeinfo.Dir.parent = dnp;
419 }
420 dnp->dn_typeinfo.Dir.parent->dn_links++; /* account for the new '..' */
421 }
422
423 /*
424 * put the name into the directory entry.
425 */
426 strlcpy(dirent_p->de_name, name, DEVMAXNAMESIZE);
427
428
429 /*
430 * Check if we are not making a root node..
431 * (i.e. have parent)
432 */
433 if(dirnode) {
434 /*
435 * Put it on the END of the linked list of directory entries
436 */
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 */
440 /*right?*/
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?*/
445 }
446
447 *dirent_pp = dirent_p;
448 DEVFS_INCR_ENTRIES();
449 return 0 ;
450 }
451
452
453 /***********************************************************************
454 * Add a new element to the devfs plane.
455 *
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
466 *
467 * called with DEVFS_LOCK held
468 ***********************************************************************/
469 int
470 dev_add_node(int entrytype, devnode_type_t * typeinfo, devnode_t * proto,
471 devnode_t * *dn_pp, struct devfsmount *dvm)
472 {
473 devnode_t * dnp = NULL;
474
475 #if defined SPLIT_DEVS
476 /*
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.
479 */
480 if (proto) {
481 dnp = proto->dn_nextsibling;
482 while( dnp != proto) {
483 if (dnp->dn_dvm == dvm) {
484 *dn_pp = dnp;
485 return (0);
486 }
487 dnp = dnp->dn_nextsibling;
488 }
489 if (typeinfo == NULL)
490 typeinfo = &(proto->dn_typeinfo);
491 }
492 #else /* SPLIT_DEVS */
493 if ( proto ) {
494 switch (proto->type) {
495 case DEV_BDEV:
496 case DEV_CDEV:
497 *dn_pp = proto;
498 return 0;
499 }
500 }
501 #endif /* SPLIT_DEVS */
502 MALLOC(dnp, devnode_t *, sizeof(devnode_t), M_DEVFSNODE, M_WAITOK);
503 if (!dnp) {
504 return ENOMEM;
505 }
506
507 /*
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
510 */
511 if (proto) {
512 bcopy(proto, dnp, sizeof(devnode_t));
513 dnp->dn_links = 0;
514 dnp->dn_linklist = NULL;
515 dnp->dn_vn = NULL;
516 dnp->dn_len = 0;
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);
522 #if CONFIG_MACF
523 mac_devfs_label_init(dnp);
524 mac_devfs_label_copy(proto->dn_label, dnp->dn_label);
525 #endif
526 } else {
527 struct timeval tv;
528
529 /*
530 * We have no prototype, so start off with a clean slate
531 */
532 microtime(&tv);
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;
540 #if CONFIG_MACF
541 mac_devfs_label_init(dnp);
542 #endif
543 }
544 dnp->dn_dvm = dvm;
545
546 /*
547 * fill out the dev node according to type
548 */
549 switch(entrytype) {
550 case DEV_DIR:
551 /*
552 * As it's a directory, make sure
553 * it has a null entries list
554 */
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;
562 /*
563 * make sure that the ops associated with it are the ops
564 * that we use (by default) for directories
565 */
566 dnp->dn_ops = &devfs_vnodeop_p;
567 dnp->dn_mode |= 0555; /* default perms */
568 break;
569 case DEV_SLNK:
570 /*
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.
577 */
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);
583 return ENOMEM;
584 }
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 */
591 break;
592 case DEV_CDEV:
593 case DEV_BDEV:
594 /*
595 * Make sure it has DEVICE type ops
596 * and device specific fields are correct
597 */
598 dnp->dn_ops = &devfs_spec_vnodeop_p;
599 dnp->dn_typeinfo.dev = typeinfo->dev;
600 break;
601 default:
602 return EINVAL;
603 }
604
605 *dn_pp = dnp;
606 DEVFS_INCR_NODES();
607 return 0 ;
608 }
609
610
611 /***********************************************************************
612 * called with DEVFS_LOCK held
613 **********************************************************************/
614 void
615 devnode_free(devnode_t * dnp)
616 {
617 if (dnp->dn_lflags & DN_BUSY) {
618 dnp->dn_lflags |= DN_DELETE;
619 return;
620 }
621 #if CONFIG_MACF
622 mac_devfs_label_destroy(dnp);
623 #endif
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);
627 }
628 DEVFS_DECR_NODES();
629 FREE(dnp, M_DEVFSNODE);
630 }
631
632
633 /***********************************************************************
634 * called with DEVFS_LOCK held
635 **********************************************************************/
636 static void
637 devfs_dn_free(devnode_t * dnp)
638 {
639 if(--dnp->dn_links <= 0 ) /* can be -1 for initial free, on error */
640 {
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;
646
647 }
648 if (dnp->dn_vn == NULL) {
649 devnode_free(dnp); /* no accesses/references */
650 }
651 else {
652 dnp->dn_delete = TRUE;
653 }
654 }
655 }
656
657 /***********************************************************************\
658 * Front Node Operations *
659 * Add or delete a chain of front nodes *
660 \***********************************************************************/
661
662
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
667 *
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
671 *
672 * This allows a new node to be propogated through all mounted planes
673 *
674 * called with DEVFS_LOCK held
675 ***********************************************************************/
676 static int
677 devfs_propogate(devdirent_t * parent,devdirent_t * child)
678 {
679 int error;
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;
685
686 /***********************************************
687 * Find the other instances of the parent node
688 ***********************************************/
689 for (adnp = pdnp->dn_nextsibling;
690 adnp != pdnp;
691 adnp = adnp->dn_nextsibling)
692 {
693 /*
694 * Make the node, using the original as a prototype)
695 * if the node already exists on that plane it won't be
696 * re-made..
697 */
698 if ((error = dev_add_entry(child->de_name, adnp, type,
699 NULL, dnp, adnp->dn_dvm,
700 &newnmp)) != 0) {
701 printf("duplicating %s failed\n",child->de_name);
702 }
703 }
704 return 0; /* for now always succeed */
705 }
706
707
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)
713 *
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 ***********************************************************************/
718 void
719 devfs_remove(void *dirent_p)
720 {
721 devnode_t * dnp = ((devdirent_t *)dirent_p)->de_dnp;
722 devnode_t * dnp2;
723 boolean_t lastlink;
724
725 DEVFS_LOCK();
726
727 if (!devfs_ready) {
728 printf("devfs_remove: not ready for devices!\n");
729 goto out;
730 }
731
732 /* keep removing the next sibling till only we exist. */
733 while ((dnp2 = dnp->dn_nextsibling) != dnp) {
734
735 /*
736 * Keep removing the next front node till no more exist
737 */
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) {
743 do {
744 lastlink = (1 == dnp2->dn_links);
745 dev_free_name(dnp2->dn_linklist);
746 } while (!lastlink);
747 }
748 }
749
750 /*
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.
754 */
755 if (dnp->dn_linklist) {
756 do {
757 lastlink = (1 == dnp->dn_links);
758 dev_free_name(dnp->dn_linklist);
759 } while (!lastlink);
760 }
761 out:
762 DEVFS_UNLOCK();
763
764 return ;
765 }
766
767
768
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
773 * up the tree..
774 *
775 * called with DEVFS_LOCK held
776 **************************************************************/
777 int
778 dev_dup_plane(struct devfsmount *devfs_mp_p)
779 {
780 devdirent_t * new;
781 int error = 0;
782
783 if ((error = dev_dup_entry(NULL, dev_root, &new, devfs_mp_p)))
784 return error;
785 devfs_mp_p->plane_root = new;
786 return error;
787 }
788
789
790
791 /***************************************************************
792 * Free a whole plane
793 *
794 * called with DEVFS_LOCK held
795 ***************************************************************/
796 void
797 devfs_free_plane(struct devfsmount *devfs_mp_p)
798 {
799 devdirent_t * dirent_p;
800
801 dirent_p = devfs_mp_p->plane_root;
802 if (dirent_p) {
803 dev_free_hier(dirent_p);
804 dev_free_name(dirent_p);
805 }
806 devfs_mp_p->plane_root = NULL;
807 }
808
809
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
817 *
818 * called with DEVFS_LOCK held
819 ***************************************************************/
820 static int
821 dev_dup_entry(devnode_t * parent, devdirent_t * back, devdirent_t * *dnm_pp,
822 struct devfsmount *dvm)
823 {
824 devdirent_t * entry_p;
825 devdirent_t * newback;
826 devdirent_t * newfront;
827 int error;
828 devnode_t * dnp = back->de_dnp;
829 int type = dnp->dn_type;
830
831 /*
832 * go get the node made (if we need to)
833 * use the back one as a prototype
834 */
835 if ((error = dev_add_entry(back->de_name, parent, type,
836 NULL, dnp,
837 parent?parent->dn_dvm:dvm, &entry_p)) != 0) {
838 printf("duplicating %s failed\n",back->de_name);
839 }
840
841 /*
842 * If we have just made the root, then insert the pointer to the
843 * mount information
844 */
845 if(dvm) {
846 entry_p->de_dnp->dn_dvm = dvm;
847 }
848
849 /*
850 * If it is a directory, then recurse down all the other
851 * subnodes in it....
852 * note that this time we don't pass on the mount info..
853 */
854 if (type == DEV_DIR)
855 {
856 for(newback = back->de_dnp->dn_typeinfo.Dir.dirlist;
857 newback; newback = newback->de_next)
858 {
859 if((error = dev_dup_entry(entry_p->de_dnp,
860 newback, &newfront, NULL)) != 0)
861 {
862 break; /* back out with an error */
863 }
864 }
865 }
866 *dnm_pp = entry_p;
867 return error;
868 }
869
870
871 /***************************************************************
872 * Free a name node
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
876 *
877 * called with DEVFS_LOCK held
878 ***************************************************************/
879
880 int
881 dev_free_name(devdirent_t * dirent_p)
882 {
883 devnode_t * parent = dirent_p->de_parent;
884 devnode_t * dnp = dirent_p->de_dnp;
885
886 if(dnp) {
887 if(dnp->dn_type == DEV_DIR)
888 {
889 devnode_t * p;
890
891 if(dnp->dn_typeinfo.Dir.dirlist)
892 return (ENOTEMPTY);
893 p = dnp->dn_typeinfo.Dir.parent;
894 devfs_dn_free(dnp); /* account for '.' */
895 devfs_dn_free(p); /* '..' */
896 }
897 /*
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
901 * other links.
902 */
903 if(dirent_p->de_nextlink == dirent_p) {
904 dnp->dn_linklist = NULL;
905 } else {
906 if(dnp->dn_linklist == dirent_p) {
907 dnp->dn_linklist = dirent_p->de_nextlink;
908 }
909 dirent_p->de_nextlink->de_prevlinkp
910 = dirent_p->de_prevlinkp;
911 *dirent_p->de_prevlinkp = dirent_p->de_nextlink;
912 }
913 devfs_dn_free(dnp);
914 }
915
916 /*
917 * unlink ourselves from the directory on this plane
918 */
919 if(parent) /* if not fs root */
920 {
921 if( (*dirent_p->de_prevp = dirent_p->de_next) )/* yes, assign */
922 {
923 dirent_p->de_next->de_prevp = dirent_p->de_prevp;
924 }
925 else
926 {
927 parent->dn_typeinfo.Dir.dirlast
928 = dirent_p->de_prevp;
929 }
930 parent->dn_typeinfo.Dir.entrycount--;
931 parent->dn_len -= strlen(dirent_p->de_name) + 8;
932 }
933
934 DEVFS_DECR_ENTRIES();
935 FREE(dirent_p, M_DEVFSNAME);
936 return 0;
937 }
938
939
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.
946 *
947 * called with DEVFS_LOCK held
948 ***************************************************************/
949
950 static void
951 dev_free_hier(devdirent_t * dirent_p)
952 {
953 devnode_t * dnp = dirent_p->de_dnp;
954
955 if(dnp) {
956 if(dnp->dn_type == DEV_DIR)
957 {
958 while(dnp->dn_typeinfo.Dir.dirlist)
959 {
960 dev_free_hier(dnp->dn_typeinfo.Dir.dirlist);
961 dev_free_name(dnp->dn_typeinfo.Dir.dirlist);
962 }
963 }
964 }
965 }
966
967
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
971 *
972 * called with DEVFS_LOCK held
973 ***************************************************************/
974 int
975 devfs_dntovn(devnode_t * dnp, struct vnode **vn_pp, __unused struct proc * p)
976 {
977 struct vnode *vn_p;
978 struct vnode ** vnptr;
979 int error = 0;
980 struct vnode_fsparam vfsp;
981 enum vtype vtype = 0;
982 int markroot = 0;
983 int n_minor = DEVFS_CLONE_ALLOC; /* new minor number for clone device */
984
985 retry:
986 *vn_pp = NULL;
987 vn_p = dnp->dn_vn;
988
989 dnp->dn_lflags |= DN_BUSY;
990
991 if (vn_p) { /* already has a vnode */
992 uint32_t vid;
993
994 vid = vnode_vid(vn_p);
995
996 DEVFS_UNLOCK();
997
998 error = vnode_getwithvid(vn_p, vid);
999
1000 DEVFS_LOCK();
1001
1002 if (dnp->dn_lflags & DN_DELETE) {
1003 /*
1004 * our BUSY node got marked for
1005 * deletion while the DEVFS lock
1006 * was dropped...
1007 */
1008 if (error == 0) {
1009 /*
1010 * vnode_getwithvid returned a valid ref
1011 * which we need to drop
1012 */
1013 vnode_put(vn_p);
1014 }
1015 /*
1016 * set the error to EAGAIN
1017 * which will cause devfs_lookup
1018 * to retry this node
1019 */
1020 error = EAGAIN;
1021 }
1022 if ( !error)
1023 *vn_pp = vn_p;
1024
1025 devfs_release_busy(dnp);
1026
1027 return error;
1028 }
1029
1030 if (dnp->dn_lflags & DN_CREATE) {
1031 dnp->dn_lflags |= DN_CREATEWAIT;
1032 msleep(&dnp->dn_lflags, &devfs_mutex, PRIBIO, 0 , 0);
1033 goto retry;
1034 }
1035
1036 dnp->dn_lflags |= DN_CREATE;
1037
1038 switch (dnp->dn_type) {
1039 case DEV_SLNK:
1040 vtype = VLNK;
1041 break;
1042 case DEV_DIR:
1043 if (dnp->dn_typeinfo.Dir.parent == dnp) {
1044 markroot = 1;
1045 }
1046 vtype = VDIR;
1047 break;
1048 case DEV_BDEV:
1049 case DEV_CDEV:
1050 vtype = (dnp->dn_type == DEV_BDEV) ? VBLK : VCHR;
1051 break;
1052 }
1053 vfsp.vnfs_mp = dnp->dn_dvm->mount;
1054 vfsp.vnfs_vtype = vtype;
1055 vfsp.vnfs_str = "devfs";
1056 vfsp.vnfs_dvp = 0;
1057 vfsp.vnfs_fsnode = dnp;
1058 vfsp.vnfs_cnp = 0;
1059 vfsp.vnfs_vops = *(dnp->dn_ops);
1060
1061 if (vtype == VBLK || vtype == VCHR) {
1062 /*
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.
1066 */
1067 if (dnp->dn_clone != NULL) {
1068 int n_major = major(dnp->dn_typeinfo.dev);
1069
1070 n_minor = (*dnp->dn_clone)(dnp->dn_typeinfo.dev, DEVFS_CLONE_ALLOC);
1071 if (n_minor == -1) {
1072 devfs_release_busy(dnp);
1073 return ENOMEM;
1074 }
1075
1076 vfsp.vnfs_rdev = makedev(n_major, n_minor);;
1077 } else {
1078 vfsp.vnfs_rdev = dnp->dn_typeinfo.dev;
1079 }
1080 } else {
1081 vfsp.vnfs_rdev = 0;
1082 }
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;
1088
1089 DEVFS_UNLOCK();
1090
1091 if (dnp->dn_clone == NULL)
1092 vnptr = &dnp->dn_vn;
1093 else
1094 vnptr = &vn_p;
1095 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, vnptr);
1096
1097 DEVFS_LOCK();
1098
1099 if (error == 0) {
1100 if ((dnp->dn_clone != NULL) && (dnp->dn_vn != NULLVP) )
1101 panic("devnode already has a vnode?");
1102 /*
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).
1106 */
1107 if (dnp->dn_clone == NULL) {
1108 *vn_pp = dnp->dn_vn;
1109 } else {
1110 *vn_pp = vn_p;
1111 }
1112
1113 } else if (n_minor != DEVFS_CLONE_ALLOC) {
1114 /*
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.
1122 */
1123 (void)(*dnp->dn_clone)(dnp->dn_typeinfo.dev, DEVFS_CLONE_FREE);
1124 }
1125
1126 dnp->dn_lflags &= ~DN_CREATE;
1127 if (dnp->dn_lflags & DN_CREATEWAIT) {
1128 dnp->dn_lflags &= ~DN_CREATEWAIT;
1129 wakeup(&dnp->dn_lflags);
1130 }
1131
1132 devfs_release_busy(dnp);
1133
1134 return error;
1135 }
1136
1137
1138 /***********************************************************************
1139 * called with DEVFS_LOCK held
1140 ***********************************************************************/
1141 static void
1142 devfs_release_busy(devnode_t *dnp) {
1143
1144 dnp->dn_lflags &= ~DN_BUSY;
1145
1146 if (dnp->dn_lflags & DN_DELETE)
1147 devnode_free(dnp);
1148 }
1149
1150 /***********************************************************************
1151 * add a whole device, with no prototype.. make name element and node
1152 * Used for adding the original device entries
1153 *
1154 * called with DEVFS_LOCK held
1155 ***********************************************************************/
1156 int
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)
1159 {
1160 devnode_t * dnp;
1161 int error = 0;
1162
1163 if ((error = dev_add_node(type, typeinfo, proto, &dnp,
1164 (parent?parent->dn_dvm:dvm))) != 0)
1165 {
1166 printf("devfs: %s: base node allocation failed (Errno=%d)\n",
1167 name,error);
1168 return error;
1169 }
1170 if ((error = dev_add_name(name ,parent ,NULL, dnp, nm_pp)) != 0)
1171 {
1172 devfs_dn_free(dnp); /* 1->0 for dir, 0->(-1) for other */
1173 printf("devfs: %s: name slot allocation failed (Errno=%d)\n",
1174 name,error);
1175
1176 }
1177 return error;
1178 }
1179
1180
1181 /*
1182 * Function: devfs_make_node
1183 *
1184 * Purpose
1185 * Create a device node with the given pathname in the devfs namespace.
1186 *
1187 * Parameters:
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
1194 * Returns:
1195 * A handle to a device node if successful, NULL otherwise.
1196 */
1197 void *
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, ...)
1201 {
1202 devdirent_t * new_dev = NULL;
1203 devnode_t * dnp; /* devnode for parent directory */
1204 devnode_type_t typeinfo;
1205
1206 char *name, buf[256]; /* XXX */
1207 const char *path;
1208 int i;
1209 va_list ap;
1210
1211
1212 DEVFS_LOCK();
1213
1214 if (!devfs_ready) {
1215 printf("devfs_make_node: not ready for devices!\n");
1216 goto out;
1217 }
1218 if (chrblk != DEVFS_CHAR && chrblk != DEVFS_BLOCK)
1219 goto out;
1220
1221 DEVFS_UNLOCK();
1222
1223 va_start(ap, fmt);
1224 vsnprintf(buf, sizeof(buf), fmt, ap);
1225 va_end(ap);
1226
1227 name = NULL;
1228
1229 for(i=strlen(buf); i>0; i--)
1230 if(buf[i] == '/') {
1231 name=&buf[i];
1232 buf[i]=0;
1233 break;
1234 }
1235
1236 if (name) {
1237 *name++ = '\0';
1238 path = buf;
1239 } else {
1240 name = buf;
1241 path = "/";
1242 }
1243 DEVFS_LOCK();
1244
1245 /* find/create directory path ie. mkdir -p */
1246 if (dev_finddir(path, NULL, DEVFS_CREATE, &dnp) == 0) {
1247 typeinfo.dev = dev;
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);
1256 }
1257 }
1258 out:
1259 DEVFS_UNLOCK();
1260
1261 return new_dev;
1262 }
1263
1264
1265 /*
1266 * Function: devfs_make_node
1267 *
1268 * Purpose
1269 * Create a device node with the given pathname in the devfs namespace.
1270 *
1271 * Parameters:
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
1277 * Returns:
1278 * A handle to a device node if successful, NULL otherwise.
1279 */
1280 void *
1281 devfs_make_node(dev_t dev, int chrblk, uid_t uid,
1282 gid_t gid, int perms, const char *fmt, ...)
1283 {
1284 devdirent_t * new_dev = NULL;
1285 devnode_t * dnp; /* devnode for parent directory */
1286 devnode_type_t typeinfo;
1287
1288 char *name, buf[256]; /* XXX */
1289 const char *path;
1290 #if CONFIG_MACF
1291 char buff[sizeof(buf)];
1292 #endif
1293 int i;
1294 va_list ap;
1295
1296
1297 DEVFS_LOCK();
1298
1299 if (!devfs_ready) {
1300 printf("devfs_make_node: not ready for devices!\n");
1301 goto out;
1302 }
1303 if (chrblk != DEVFS_CHAR && chrblk != DEVFS_BLOCK)
1304 goto out;
1305
1306 DEVFS_UNLOCK();
1307
1308 va_start(ap, fmt);
1309 vsnprintf(buf, sizeof(buf), fmt, ap);
1310 va_end(ap);
1311
1312 #if CONFIG_MACF
1313 bcopy(buf, buff, sizeof(buff));
1314 buff[sizeof(buff)-1] = 0;
1315 #endif
1316 name = NULL;
1317
1318 for(i=strlen(buf); i>0; i--)
1319 if(buf[i] == '/') {
1320 name=&buf[i];
1321 buf[i]=0;
1322 break;
1323 }
1324
1325 if (name) {
1326 *name++ = '\0';
1327 path = buf;
1328 } else {
1329 name = buf;
1330 path = "/";
1331 }
1332 DEVFS_LOCK();
1333
1334 /* find/create directory path ie. mkdir -p */
1335 if (dev_finddir(path, NULL, DEVFS_CREATE, &dnp) == 0) {
1336 typeinfo.dev = dev;
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;
1344
1345 #if CONFIG_MACF
1346 mac_devfs_label_associate_device(dev, new_dev->de_dnp, buff);
1347 #endif
1348 devfs_propogate(dnp->dn_typeinfo.Dir.myname, new_dev);
1349 }
1350 }
1351 out:
1352 DEVFS_UNLOCK();
1353
1354 return new_dev;
1355 }
1356
1357 /*
1358 * Function: devfs_make_link
1359 *
1360 * Purpose:
1361 * Create a link to a previously created device node.
1362 *
1363 * Returns:
1364 * 0 if successful, -1 if failed
1365 */
1366 int
1367 devfs_make_link(void *original, char *fmt, ...)
1368 {
1369 devdirent_t * new_dev = NULL;
1370 devdirent_t * orig = (devdirent_t *) original;
1371 devnode_t * dirnode; /* devnode for parent directory */
1372
1373 va_list ap;
1374 char *p, buf[256]; /* XXX */
1375 int i;
1376
1377 DEVFS_LOCK();
1378
1379 if (!devfs_ready) {
1380 printf("devfs_make_link: not ready for devices!\n");
1381 goto out;
1382 }
1383 DEVFS_UNLOCK();
1384
1385 va_start(ap, fmt);
1386 vsnprintf(buf, sizeof(buf), fmt, ap);
1387 va_end(ap);
1388
1389 p = NULL;
1390
1391 for(i=strlen(buf); i>0; i--) {
1392 if(buf[i] == '/') {
1393 p=&buf[i];
1394 buf[i]=0;
1395 break;
1396 }
1397 }
1398 DEVFS_LOCK();
1399
1400 if (p) {
1401 *p++ = '\0';
1402
1403 if (dev_finddir(buf, NULL, DEVFS_CREATE, &dirnode)
1404 || dev_add_name(p, dirnode, NULL, orig->de_dnp, &new_dev))
1405 goto fail;
1406 } else {
1407 if (dev_finddir("", NULL, DEVFS_CREATE, &dirnode)
1408 || dev_add_name(buf, dirnode, NULL, orig->de_dnp, &new_dev))
1409 goto fail;
1410 }
1411 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, new_dev);
1412 fail:
1413 out:
1414 DEVFS_UNLOCK();
1415
1416 return ((new_dev != NULL) ? 0 : -1);
1417 }