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
110static void	devfs_release_busy(devnode_t *);
111static void	dev_free_hier(devdirent_t *);
112static int	devfs_propogate(devdirent_t *, devdirent_t *);
113static int	dev_finddir(const char *, devnode_t *, int, devnode_t **);
114static int	dev_dup_entry(devnode_t *, devdirent_t *, devdirent_t **, struct devfsmount *);
115
116
117lck_grp_t	* devfs_lck_grp;
118lck_grp_attr_t	* devfs_lck_grp_attr;
119lck_attr_t	* devfs_lck_attr;
120lck_mtx_t  	  devfs_mutex;
121
122devdirent_t *		dev_root = NULL; 	/* root of backing tree */
123struct devfs_stats	devfs_stats;		/* hold stats */
124
125#ifdef HIDDEN_MOUNTPOINT
126static struct mount *devfs_hidden_mount;
127#endif /* HIDDEN_MOINTPOINT */
128
129static 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*/
144int
145devfs_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 ***************************************************************/
210devdirent_t *
211dev_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 ***********************************************************************/
248static int
249dev_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 ***********************************************************************/
340int
341dev_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***********************************************************************/
469int
470dev_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 **********************************************************************/
614void
615devnode_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 **********************************************************************/
636static void
637devfs_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 ***********************************************************************/
676static int
677devfs_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 ***********************************************************************/
718void
719devfs_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	}
761out:
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 **************************************************************/
777int
778dev_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 ***************************************************************/
796void
797devfs_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 ***************************************************************/
820static int
821dev_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
880int
881dev_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
950static void
951dev_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 ***************************************************************/
974int
975devfs_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
985retry:
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 ***********************************************************************/
1141static void
1142devfs_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 ***********************************************************************/
1156int
1157dev_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 */
1197void *
1198devfs_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	}
1258out:
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 */
1280void *
1281devfs_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	}
1351out:
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 */
1366int
1367devfs_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);
1412fail:
1413out:
1414	DEVFS_UNLOCK();
1415
1416	return ((new_dev != NULL) ? 0 : -1);
1417}
1418