]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/miscfs/devfs/devfs_tree.c
xnu-792.6.76.tar.gz
[apple/xnu.git] / bsd / miscfs / devfs / devfs_tree.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23/*
24 * Copyright 1997,1998 Julian Elischer. All rights reserved.
25 * julian@freebsd.org
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are
29 * met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
37 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
40 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
42 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
43 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 *
48 * devfs_tree.c
49 */
50
51/*
52 * HISTORY
53 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
54 * - removed mounting of "hidden" mountpoint
55 * - fixed problem in which devnode->dn_vn pointer was not
56 * updated with the vnode returned from checkalias()
57 * - replaced devfs_vntodn() with a macro VTODN()
58 * - rewrote dev_finddir() to not use recursion
59 * - added locking to avoid data structure corruption (DEVFS_(UN)LOCK())
60 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
61 * - fixed problem with devfs_dntovn() checking the v_id against the
62 * value cached in the device node; a union mount on top of us causes
63 * the v_id to get incremented thus, we would end up returning a new
64 * vnode instead of the existing one that has the mounted_here
65 * field filled in; the net effect was that the filesystem mounted
66 * on top of us would never show up
67 * - added devfs_stats to store how many data structures are actually
68 * allocated
69 */
70
71/* SPLIT_DEVS means each devfs uses a different devnode for the same device */
72/* Otherwise the same device always ends up at the same vnode even if */
73/* reached througgh a different devfs instance. The practical difference */
74/* is that with the same vnode, chmods and chowns show up on all instances of */
75/* a device. (etc) */
76
77#define SPLIT_DEVS 1 /* maybe make this an option */
78/*#define SPLIT_DEVS 1*/
79
80#include <sys/param.h>
81#include <sys/systm.h>
82#include <sys/kernel.h>
83#include <sys/conf.h>
84#include <sys/malloc.h>
85#include <sys/mount_internal.h>
86#include <sys/proc.h>
87#include <sys/vnode.h>
88#include <stdarg.h>
89
90#include "devfs.h"
91#include "devfsdefs.h"
92
93static void devfs_release_busy(devnode_t *);
94static void dev_free_hier(devdirent_t *);
95static int devfs_propogate(devdirent_t *, devdirent_t *);
96static int dev_finddir(char *, devnode_t *, int, devnode_t **);
97static int dev_dup_entry(devnode_t *, devdirent_t *, devdirent_t **, struct devfsmount *);
98
99
100lck_grp_t * devfs_lck_grp;
101lck_grp_attr_t * devfs_lck_grp_attr;
102lck_attr_t * devfs_lck_attr;
103lck_mtx_t devfs_mutex;
104
105devdirent_t * dev_root = NULL; /* root of backing tree */
106struct devfs_stats devfs_stats; /* hold stats */
107
108#ifdef HIDDEN_MOUNTPOINT
109static struct mount *devfs_hidden_mount;
110#endif /* HIDDEN_MOINTPOINT */
111
112static int devfs_ready = 0;
113
114#define NOCREATE FALSE
115#define CREATE TRUE
116
117/*
118 * Set up the root directory node in the backing plane
119 * This is happenning before the vfs system has been
120 * set up yet, so be careful about what we reference..
121 * Notice that the ops are by indirection.. as they haven't
122 * been set up yet!
123 * DEVFS has a hidden mountpoint that is used as the anchor point
124 * for the internal 'blueprint' version of the dev filesystem tree.
125 */
126/*proto*/
127int
128devfs_sinit(void)
129{
130 int error;
131
132 devfs_lck_grp_attr = lck_grp_attr_alloc_init();
133 lck_grp_attr_setstat(devfs_lck_grp_attr);
134 devfs_lck_grp = lck_grp_alloc_init("devfs_lock", devfs_lck_grp_attr);
135
136 devfs_lck_attr = lck_attr_alloc_init();
137 //lck_attr_setdebug(devfs_lck_attr);
138
139 lck_mtx_init(&devfs_mutex, devfs_lck_grp, devfs_lck_attr);
140
141 DEVFS_LOCK();
142 error = dev_add_entry("root", NULL, DEV_DIR, NULL, NULL, NULL, &dev_root);
143 DEVFS_UNLOCK();
144
145 if (error) {
146 printf("devfs_sinit: dev_add_entry failed ");
147 return (ENOTSUP);
148 }
149#ifdef HIDDEN_MOUNTPOINT
150 MALLOC(devfs_hidden_mount, struct mount *, sizeof(struct mount),
151 M_MOUNT, M_WAITOK);
152 bzero(devfs_hidden_mount,sizeof(struct mount));
153 mount_lock_init(devfs_hidden_mount);
154 TAILQ_INIT(&devfs_hidden_mount->mnt_vnodelist);
155 TAILQ_INIT(&devfs_hidden_mount->mnt_workerqueue);
156 TAILQ_INIT(&devfs_hidden_mount->mnt_newvnodes);
157
158 /* Initialize the default IO constraints */
159 mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
160 mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
161
162 devfs_mount(devfs_hidden_mount,"dummy",NULL,NULL,NULL);
163 dev_root->de_dnp->dn_dvm
164 = (struct devfsmount *)devfs_hidden_mount->mnt_data;
165#endif /* HIDDEN_MOUNTPOINT */
166 devfs_ready = 1;
167 return (0);
168}
169
170/***********************************************************************\
171*************************************************************************
172* Routines used to find our way to a point in the tree *
173*************************************************************************
174\***********************************************************************/
175
176
177
178/***************************************************************
179 * Search down the linked list off a dir to find "name"
180 * return the devnode_t * for that node.
181 *
182 * called with DEVFS_LOCK held
183 ***************************************************************/
184devdirent_t *
185dev_findname(devnode_t * dir, char *name)
186{
187 devdirent_t * newfp;
188 if (dir->dn_type != DEV_DIR) return 0;/*XXX*/ /* printf?*/
189
190 if (name[0] == '.')
191 {
192 if(name[1] == 0)
193 {
194 return dir->dn_typeinfo.Dir.myname;
195 }
196 if((name[1] == '.') && (name[2] == 0))
197 {
198 /* for root, .. == . */
199 return dir->dn_typeinfo.Dir.parent->dn_typeinfo.Dir.myname;
200 }
201 }
202 newfp = dir->dn_typeinfo.Dir.dirlist;
203
204 while(newfp)
205 {
206 if(!(strcmp(name,newfp->de_name)))
207 return newfp;
208 newfp = newfp->de_next;
209 }
210 return NULL;
211}
212
213/***********************************************************************
214 * Given a starting node (0 for root) and a pathname, return the node
215 * for the end item on the path. It MUST BE A DIRECTORY. If the 'CREATE'
216 * option is true, then create any missing nodes in the path and create
217 * and return the final node as well.
218 * This is used to set up a directory, before making nodes in it..
219 *
220 * called with DEVFS_LOCK held
221 ***********************************************************************/
222static int
223dev_finddir(char * path,
224 devnode_t * dirnode,
225 int create,
226 devnode_t * * dn_pp)
227{
228 devnode_t * dnp = NULL;
229 int error = 0;
230 char * scan;
231
232
233 if (!dirnode) /* dirnode == NULL means start at root */
234 dirnode = dev_root->de_dnp;
235
236 if (dirnode->dn_type != DEV_DIR)
237 return ENOTDIR;
238
239 if (strlen(path) > (DEVMAXPATHSIZE - 1))
240 return ENAMETOOLONG;
241
242 scan = path;
243
244 while (*scan == '/')
245 scan++;
246
247 *dn_pp = NULL;
248
249 while (1) {
250 char component[DEVMAXPATHSIZE];
251 devdirent_t * dirent_p;
252 char * start;
253
254 if (*scan == 0) {
255 /* we hit the end of the string, we're done */
256 *dn_pp = dirnode;
257 break;
258 }
259 start = scan;
260 while (*scan != '/' && *scan)
261 scan++;
262
263 strncpy(component, start, scan - start);
264 component[ scan - start ] = '\0';
265 if (*scan == '/')
266 scan++;
267
268 dirent_p = dev_findname(dirnode, component);
269 if (dirent_p) {
270 dnp = dirent_p->de_dnp;
271 if (dnp->dn_type != DEV_DIR) {
272 error = ENOTDIR;
273 break;
274 }
275 }
276 else {
277 if (!create) {
278 error = ENOENT;
279 break;
280 }
281 error = dev_add_entry(component, dirnode,
282 DEV_DIR, NULL, NULL, NULL, &dirent_p);
283 if (error)
284 break;
285 dnp = dirent_p->de_dnp;
286 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, dirent_p);
287 }
288 dirnode = dnp; /* continue relative to this directory */
289 }
290 return (error);
291}
292
293
294/***********************************************************************
295 * Add a new NAME element to the devfs
296 * If we're creating a root node, then dirname is NULL
297 * Basically this creates a new namespace entry for the device node
298 *
299 * Creates a name node, and links it to the supplied node
300 *
301 * called with DEVFS_LOCK held
302 ***********************************************************************/
303int
304dev_add_name(char * name, devnode_t * dirnode, __unused devdirent_t * back,
305 devnode_t * dnp, devdirent_t * *dirent_pp)
306{
307 devdirent_t * dirent_p = NULL;
308
309 if(dirnode != NULL ) {
310 if(dirnode->dn_type != DEV_DIR) return(ENOTDIR);
311
312 if( dev_findname(dirnode,name))
313 return(EEXIST);
314 }
315 /*
316 * make sure the name is legal
317 * slightly misleading in the case of NULL
318 */
319 if (!name || (strlen(name) > (DEVMAXNAMESIZE - 1)))
320 return (ENAMETOOLONG);
321
322 /*
323 * Allocate and fill out a new directory entry
324 */
325 MALLOC(dirent_p, devdirent_t *, sizeof(devdirent_t),
326 M_DEVFSNAME, M_WAITOK);
327 if (!dirent_p) {
328 return ENOMEM;
329 }
330 bzero(dirent_p,sizeof(devdirent_t));
331
332 /* inherrit our parent's mount info */ /*XXX*/
333 /* a kludge but.... */
334 if(dirnode && ( dnp->dn_dvm == NULL)) {
335 dnp->dn_dvm = dirnode->dn_dvm;
336 /* if(!dnp->dn_dvm) printf("parent had null dvm "); */
337 }
338
339 /*
340 * Link the two together
341 * include the implicit link in the count of links to the devnode..
342 * this stops it from being accidentally freed later.
343 */
344 dirent_p->de_dnp = dnp;
345 dnp->dn_links++ ; /* implicit from our own name-node */
346
347 /*
348 * Make sure that we can find all the links that reference a node
349 * so that we can get them all if we need to zap the node.
350 */
351 if(dnp->dn_linklist) {
352 dirent_p->de_nextlink = dnp->dn_linklist;
353 dirent_p->de_prevlinkp = dirent_p->de_nextlink->de_prevlinkp;
354 dirent_p->de_nextlink->de_prevlinkp = &(dirent_p->de_nextlink);
355 *dirent_p->de_prevlinkp = dirent_p;
356 } else {
357 dirent_p->de_nextlink = dirent_p;
358 dirent_p->de_prevlinkp = &(dirent_p->de_nextlink);
359 }
360 dnp->dn_linklist = dirent_p;
361
362 /*
363 * If the node is a directory, then we need to handle the
364 * creation of the .. link.
365 * A NULL dirnode indicates a root node, so point to ourself.
366 */
367 if(dnp->dn_type == DEV_DIR) {
368 dnp->dn_typeinfo.Dir.myname = dirent_p;
369 /*
370 * If we are unlinking from an old dir, decrement its links
371 * as we point our '..' elsewhere
372 * Note: it's up to the calling code to remove the
373 * us from the original directory's list
374 */
375 if(dnp->dn_typeinfo.Dir.parent) {
376 dnp->dn_typeinfo.Dir.parent->dn_links--;
377 }
378 if(dirnode) {
379 dnp->dn_typeinfo.Dir.parent = dirnode;
380 } else {
381 dnp->dn_typeinfo.Dir.parent = dnp;
382 }
383 dnp->dn_typeinfo.Dir.parent->dn_links++; /* account for the new '..' */
384 }
385
386 /*
387 * put the name into the directory entry.
388 */
389 strcpy(dirent_p->de_name, name);
390
391
392 /*
393 * Check if we are not making a root node..
394 * (i.e. have parent)
395 */
396 if(dirnode) {
397 /*
398 * Put it on the END of the linked list of directory entries
399 */
400 dirent_p->de_parent = dirnode; /* null for root */
401 dirent_p->de_prevp = dirnode->dn_typeinfo.Dir.dirlast;
402 dirent_p->de_next = *(dirent_p->de_prevp); /* should be NULL */
403 /*right?*/
404 *(dirent_p->de_prevp) = dirent_p;
405 dirnode->dn_typeinfo.Dir.dirlast = &(dirent_p->de_next);
406 dirnode->dn_typeinfo.Dir.entrycount++;
407 dirnode->dn_len += strlen(name) + 8;/*ok, ok?*/
408 }
409
410 *dirent_pp = dirent_p;
411 DEVFS_INCR_ENTRIES();
412 return 0 ;
413}
414
415
416/***********************************************************************
417 * Add a new element to the devfs plane.
418 *
419 * Creates a new dev_node to go with it if the prototype should not be
420 * reused. (Is a DIR, or we select SPLIT_DEVS at compile time)
421 * typeinfo gives us info to make our node if we don't have a prototype.
422 * If typeinfo is null and proto exists, then the typeinfo field of
423 * the proto is used intead in the CREATE case.
424 * note the 'links' count is 0 (except if a dir)
425 * but it is only cleared on a transition
426 * so this is ok till we link it to something
427 * Even in SPLIT_DEVS mode,
428 * if the node already exists on the wanted plane, just return it
429 *
430 * called with DEVFS_LOCK held
431***********************************************************************/
432int
433dev_add_node(int entrytype, devnode_type_t * typeinfo, devnode_t * proto,
434 devnode_t * *dn_pp, struct devfsmount *dvm)
435{
436 devnode_t * dnp = NULL;
437
438#if defined SPLIT_DEVS
439 /*
440 * If we have a prototype, then check if there is already a sibling
441 * on the mount plane we are looking at, if so, just return it.
442 */
443 if (proto) {
444 dnp = proto->dn_nextsibling;
445 while( dnp != proto) {
446 if (dnp->dn_dvm == dvm) {
447 *dn_pp = dnp;
448 return (0);
449 }
450 dnp = dnp->dn_nextsibling;
451 }
452 if (typeinfo == NULL)
453 typeinfo = &(proto->dn_typeinfo);
454 }
455#else /* SPLIT_DEVS */
456 if ( proto ) {
457 switch (proto->type) {
458 case DEV_BDEV:
459 case DEV_CDEV:
460 *dn_pp = proto;
461 return 0;
462 }
463 }
464#endif /* SPLIT_DEVS */
465 MALLOC(dnp, devnode_t *, sizeof(devnode_t), M_DEVFSNODE, M_WAITOK);
466 if (!dnp) {
467 return ENOMEM;
468 }
469
470 /*
471 * If we have a proto, that means that we are duplicating some
472 * other device, which can only happen if we are not at the back plane
473 */
474 if (proto) {
475 bcopy(proto, dnp, sizeof(devnode_t));
476 dnp->dn_links = 0;
477 dnp->dn_linklist = NULL;
478 dnp->dn_vn = NULL;
479 dnp->dn_len = 0;
480 /* add to END of siblings list */
481 dnp->dn_prevsiblingp = proto->dn_prevsiblingp;
482 *(dnp->dn_prevsiblingp) = dnp;
483 dnp->dn_nextsibling = proto;
484 proto->dn_prevsiblingp = &(dnp->dn_nextsibling);
485 } else {
486 struct timeval tv;
487
488 /*
489 * We have no prototype, so start off with a clean slate
490 */
491 microtime(&tv);
492 bzero(dnp, sizeof(devnode_t));
493 dnp->dn_type = entrytype;
494 dnp->dn_nextsibling = dnp;
495 dnp->dn_prevsiblingp = &(dnp->dn_nextsibling);
496 dnp->dn_atime.tv_sec = tv.tv_sec;
497 dnp->dn_mtime.tv_sec = tv.tv_sec;
498 dnp->dn_ctime.tv_sec = tv.tv_sec;
499 }
500 dnp->dn_dvm = dvm;
501
502 /*
503 * fill out the dev node according to type
504 */
505 switch(entrytype) {
506 case DEV_DIR:
507 /*
508 * As it's a directory, make sure
509 * it has a null entries list
510 */
511 dnp->dn_typeinfo.Dir.dirlast = &(dnp->dn_typeinfo.Dir.dirlist);
512 dnp->dn_typeinfo.Dir.dirlist = (devdirent_t *)0;
513 dnp->dn_typeinfo.Dir.entrycount = 0;
514 /* until we know better, it has a null parent pointer*/
515 dnp->dn_typeinfo.Dir.parent = NULL;
516 dnp->dn_links++; /* for .*/
517 dnp->dn_typeinfo.Dir.myname = NULL;
518 /*
519 * make sure that the ops associated with it are the ops
520 * that we use (by default) for directories
521 */
522 dnp->dn_ops = &devfs_vnodeop_p;
523 dnp->dn_mode |= 0555; /* default perms */
524 break;
525 case DEV_SLNK:
526 /*
527 * As it's a symlink allocate and store the link info
528 * Symlinks should only ever be created by the user,
529 * so they are not on the back plane and should not be
530 * propogated forward.. a bit like directories in that way..
531 * A symlink only exists on one plane and has its own
532 * node.. therefore we might be on any random plane.
533 */
534 MALLOC(dnp->dn_typeinfo.Slnk.name, char *,
535 typeinfo->Slnk.namelen+1,
536 M_DEVFSNODE, M_WAITOK);
537 if (!dnp->dn_typeinfo.Slnk.name) {
538 FREE(dnp,M_DEVFSNODE);
539 return ENOMEM;
540 }
541 strncpy(dnp->dn_typeinfo.Slnk.name, typeinfo->Slnk.name,
542 typeinfo->Slnk.namelen);
543 dnp->dn_typeinfo.Slnk.name[typeinfo->Slnk.namelen] = '\0';
544 dnp->dn_typeinfo.Slnk.namelen = typeinfo->Slnk.namelen;
545 DEVFS_INCR_STRINGSPACE(dnp->dn_typeinfo.Slnk.namelen + 1);
546 dnp->dn_ops = &devfs_vnodeop_p;
547 dnp->dn_mode |= 0555; /* default perms */
548 break;
549 case DEV_CDEV:
550 case DEV_BDEV:
551 /*
552 * Make sure it has DEVICE type ops
553 * and device specific fields are correct
554 */
555 dnp->dn_ops = &devfs_spec_vnodeop_p;
556 dnp->dn_typeinfo.dev = typeinfo->dev;
557 break;
558 default:
559 return EINVAL;
560 }
561
562 *dn_pp = dnp;
563 DEVFS_INCR_NODES();
564 return 0 ;
565}
566
567
568/***********************************************************************
569 * called with DEVFS_LOCK held
570 **********************************************************************/
571void
572devnode_free(devnode_t * dnp)
573{
574 if (dnp->dn_lflags & DN_BUSY) {
575 dnp->dn_lflags |= DN_DELETE;
576 return;
577 }
578 if (dnp->dn_type == DEV_SLNK) {
579 DEVFS_DECR_STRINGSPACE(dnp->dn_typeinfo.Slnk.namelen + 1);
580 FREE(dnp->dn_typeinfo.Slnk.name, M_DEVFSNODE);
581 }
582 DEVFS_DECR_NODES();
583 FREE(dnp, M_DEVFSNODE);
584}
585
586
587/***********************************************************************
588 * called with DEVFS_LOCK held
589 **********************************************************************/
590static void
591devfs_dn_free(devnode_t * dnp)
592{
593 if(--dnp->dn_links <= 0 ) /* can be -1 for initial free, on error */
594 {
595 /*probably need to do other cleanups XXX */
596 if (dnp->dn_nextsibling != dnp) {
597 devnode_t * * prevp = dnp->dn_prevsiblingp;
598 *prevp = dnp->dn_nextsibling;
599 dnp->dn_nextsibling->dn_prevsiblingp = prevp;
600
601 }
602 if (dnp->dn_vn == NULL) {
603 devnode_free(dnp); /* no accesses/references */
604 }
605 else {
606 dnp->dn_delete = TRUE;
607 }
608 }
609}
610
611/***********************************************************************\
612* Front Node Operations *
613* Add or delete a chain of front nodes *
614\***********************************************************************/
615
616
617/***********************************************************************
618 * Given a directory backing node, and a child backing node, add the
619 * appropriate front nodes to the front nodes of the directory to
620 * represent the child node to the user
621 *
622 * on failure, front nodes will either be correct or not exist for each
623 * front dir, however dirs completed will not be stripped of completed
624 * frontnodes on failure of a later frontnode
625 *
626 * This allows a new node to be propogated through all mounted planes
627 *
628 * called with DEVFS_LOCK held
629 ***********************************************************************/
630static int
631devfs_propogate(devdirent_t * parent,devdirent_t * child)
632{
633 int error;
634 devdirent_t * newnmp;
635 devnode_t * dnp = child->de_dnp;
636 devnode_t * pdnp = parent->de_dnp;
637 devnode_t * adnp = parent->de_dnp;
638 int type = child->de_dnp->dn_type;
639
640 /***********************************************
641 * Find the other instances of the parent node
642 ***********************************************/
643 for (adnp = pdnp->dn_nextsibling;
644 adnp != pdnp;
645 adnp = adnp->dn_nextsibling)
646 {
647 /*
648 * Make the node, using the original as a prototype)
649 * if the node already exists on that plane it won't be
650 * re-made..
651 */
652 if ((error = dev_add_entry(child->de_name, adnp, type,
653 NULL, dnp, adnp->dn_dvm,
654 &newnmp)) != 0) {
655 printf("duplicating %s failed\n",child->de_name);
656 }
657 }
658 return 0; /* for now always succeed */
659}
660
661
662/***********************************************************************
663 * remove all instances of this devicename [for backing nodes..]
664 * note.. if there is another link to the node (non dir nodes only)
665 * then the devfs_node will still exist as the ref count will be non-0
666 * removing a directory node will remove all sup-nodes on all planes (ZAP)
667 *
668 * Used by device drivers to remove nodes that are no longer relevant
669 * The argument is the 'cookie' they were given when they created the node
670 * this function is exported.. see devfs.h
671 ***********************************************************************/
672void
673devfs_remove(void *dirent_p)
674{
675 devnode_t * dnp = ((devdirent_t *)dirent_p)->de_dnp;
676 devnode_t * dnp2;
677 boolean_t lastlink;
678
679 DEVFS_LOCK();
680
681 if (!devfs_ready) {
682 printf("devfs_remove: not ready for devices!\n");
683 goto out;
684 }
685
686 /* keep removing the next sibling till only we exist. */
687 while ((dnp2 = dnp->dn_nextsibling) != dnp) {
688
689 /*
690 * Keep removing the next front node till no more exist
691 */
692 dnp->dn_nextsibling = dnp2->dn_nextsibling;
693 dnp->dn_nextsibling->dn_prevsiblingp = &(dnp->dn_nextsibling);
694 dnp2->dn_nextsibling = dnp2;
695 dnp2->dn_prevsiblingp = &(dnp2->dn_nextsibling);
696 if (dnp2->dn_linklist) {
697 do {
698 lastlink = (1 == dnp2->dn_links);
699 dev_free_name(dnp2->dn_linklist);
700 } while (!lastlink);
701 }
702 }
703
704 /*
705 * then free the main node
706 * If we are not running in SPLIT_DEVS mode, then
707 * THIS is what gets rid of the propogated nodes.
708 */
709 if (dnp->dn_linklist) {
710 do {
711 lastlink = (1 == dnp->dn_links);
712 dev_free_name(dnp->dn_linklist);
713 } while (!lastlink);
714 }
715out:
716 DEVFS_UNLOCK();
717
718 return ;
719}
720
721
722
723/***************************************************************
724 * duplicate the backing tree into a tree of nodes hung off the
725 * mount point given as the argument. Do this by
726 * calling dev_dup_entry which recurses all the way
727 * up the tree..
728 *
729 * called with DEVFS_LOCK held
730 **************************************************************/
731int
732dev_dup_plane(struct devfsmount *devfs_mp_p)
733{
734 devdirent_t * new;
735 int error = 0;
736
737 if ((error = dev_dup_entry(NULL, dev_root, &new, devfs_mp_p)))
738 return error;
739 devfs_mp_p->plane_root = new;
740 return error;
741}
742
743
744
745/***************************************************************
746 * Free a whole plane
747 *
748 * called with DEVFS_LOCK held
749 ***************************************************************/
750void
751devfs_free_plane(struct devfsmount *devfs_mp_p)
752{
753 devdirent_t * dirent_p;
754
755 dirent_p = devfs_mp_p->plane_root;
756 if (dirent_p) {
757 dev_free_hier(dirent_p);
758 dev_free_name(dirent_p);
759 }
760 devfs_mp_p->plane_root = NULL;
761}
762
763
764/***************************************************************
765 * Create and link in a new front element..
766 * Parent can be 0 for a root node
767 * Not presently usable to make a symlink XXX
768 * (Ok, symlinks don't propogate)
769 * recursively will create subnodes corresponding to equivalent
770 * child nodes in the base level
771 *
772 * called with DEVFS_LOCK held
773 ***************************************************************/
774static int
775dev_dup_entry(devnode_t * parent, devdirent_t * back, devdirent_t * *dnm_pp,
776 struct devfsmount *dvm)
777{
778 devdirent_t * entry_p;
779 devdirent_t * newback;
780 devdirent_t * newfront;
781 int error;
782 devnode_t * dnp = back->de_dnp;
783 int type = dnp->dn_type;
784
785 /*
786 * go get the node made (if we need to)
787 * use the back one as a prototype
788 */
789 if ((error = dev_add_entry(back->de_name, parent, type,
790 NULL, dnp,
791 parent?parent->dn_dvm:dvm, &entry_p)) != 0) {
792 printf("duplicating %s failed\n",back->de_name);
793 }
794
795 /*
796 * If we have just made the root, then insert the pointer to the
797 * mount information
798 */
799 if(dvm) {
800 entry_p->de_dnp->dn_dvm = dvm;
801 }
802
803 /*
804 * If it is a directory, then recurse down all the other
805 * subnodes in it....
806 * note that this time we don't pass on the mount info..
807 */
808 if (type == DEV_DIR)
809 {
810 for(newback = back->de_dnp->dn_typeinfo.Dir.dirlist;
811 newback; newback = newback->de_next)
812 {
813 if((error = dev_dup_entry(entry_p->de_dnp,
814 newback, &newfront, NULL)) != 0)
815 {
816 break; /* back out with an error */
817 }
818 }
819 }
820 *dnm_pp = entry_p;
821 return error;
822}
823
824
825/***************************************************************
826 * Free a name node
827 * remember that if there are other names pointing to the
828 * dev_node then it may not get freed yet
829 * can handle if there is no dnp
830 *
831 * called with DEVFS_LOCK held
832 ***************************************************************/
833
834int
835dev_free_name(devdirent_t * dirent_p)
836{
837 devnode_t * parent = dirent_p->de_parent;
838 devnode_t * dnp = dirent_p->de_dnp;
839
840 if(dnp) {
841 if(dnp->dn_type == DEV_DIR)
842 {
843 devnode_t * p;
844
845 if(dnp->dn_typeinfo.Dir.dirlist)
846 return (ENOTEMPTY);
847 p = dnp->dn_typeinfo.Dir.parent;
848 devfs_dn_free(dnp); /* account for '.' */
849 devfs_dn_free(p); /* '..' */
850 }
851 /*
852 * unlink us from the list of links for this node
853 * If we are the only link, it's easy!
854 * if we are a DIR of course there should not be any
855 * other links.
856 */
857 if(dirent_p->de_nextlink == dirent_p) {
858 dnp->dn_linklist = NULL;
859 } else {
860 if(dnp->dn_linklist == dirent_p) {
861 dnp->dn_linklist = dirent_p->de_nextlink;
862 }
863 dirent_p->de_nextlink->de_prevlinkp
864 = dirent_p->de_prevlinkp;
865 *dirent_p->de_prevlinkp = dirent_p->de_nextlink;
866 }
867 devfs_dn_free(dnp);
868 }
869
870 /*
871 * unlink ourselves from the directory on this plane
872 */
873 if(parent) /* if not fs root */
874 {
875 if( (*dirent_p->de_prevp = dirent_p->de_next) )/* yes, assign */
876 {
877 dirent_p->de_next->de_prevp = dirent_p->de_prevp;
878 }
879 else
880 {
881 parent->dn_typeinfo.Dir.dirlast
882 = dirent_p->de_prevp;
883 }
884 parent->dn_typeinfo.Dir.entrycount--;
885 parent->dn_len -= strlen(dirent_p->de_name) + 8;
886 }
887
888 DEVFS_DECR_ENTRIES();
889 FREE(dirent_p, M_DEVFSNAME);
890 return 0;
891}
892
893
894/***************************************************************
895 * Free a hierarchy starting at a directory node name
896 * remember that if there are other names pointing to the
897 * dev_node then it may not get freed yet
898 * can handle if there is no dnp
899 * leave the node itself allocated.
900 *
901 * called with DEVFS_LOCK held
902 ***************************************************************/
903
904static void
905dev_free_hier(devdirent_t * dirent_p)
906{
907 devnode_t * dnp = dirent_p->de_dnp;
908
909 if(dnp) {
910 if(dnp->dn_type == DEV_DIR)
911 {
912 while(dnp->dn_typeinfo.Dir.dirlist)
913 {
914 dev_free_hier(dnp->dn_typeinfo.Dir.dirlist);
915 dev_free_name(dnp->dn_typeinfo.Dir.dirlist);
916 }
917 }
918 }
919}
920
921
922/***************************************************************
923 * given a dev_node, find the appropriate vnode if one is already
924 * associated, or get a new one and associate it with the dev_node
925 *
926 * called with DEVFS_LOCK held
927 ***************************************************************/
928int
929devfs_dntovn(devnode_t * dnp, struct vnode **vn_pp, __unused struct proc * p)
930{
931 struct vnode *vn_p;
932 int error = 0;
933 struct vnode_fsparam vfsp;
934 enum vtype vtype = 0;
935 int markroot = 0;
936
937retry:
938 *vn_pp = NULL;
939 vn_p = dnp->dn_vn;
940
941 dnp->dn_lflags |= DN_BUSY;
942
943 if (vn_p) { /* already has a vnode */
944 uint32_t vid;
945
946 vid = vnode_vid(vn_p);
947
948 DEVFS_UNLOCK();
949
950 error = vnode_getwithvid(vn_p, vid);
951
952 DEVFS_LOCK();
953
954 if (dnp->dn_lflags & DN_DELETE) {
955 /*
956 * our BUSY node got marked for
957 * deletion while the DEVFS lock
958 * was dropped...
959 */
960 if (error == 0) {
961 /*
962 * vnode_getwithvid returned a valid ref
963 * which we need to drop
964 */
965 vnode_put(vn_p);
966 }
967 /*
968 * set the error to EAGAIN
969 * which will cause devfs_lookup
970 * to retry this node
971 */
972 error = EAGAIN;
973 }
974 if ( !error)
975 *vn_pp = vn_p;
976
977 devfs_release_busy(dnp);
978
979 return error;
980 }
981
982 if (dnp->dn_lflags & DN_CREATE) {
983 dnp->dn_lflags |= DN_CREATEWAIT;
984 msleep(&dnp->dn_lflags, &devfs_mutex, PRIBIO, 0 , 0);
985 goto retry;
986 }
987
988 dnp->dn_lflags |= DN_CREATE;
989
990 switch (dnp->dn_type) {
991 case DEV_SLNK:
992 vtype = VLNK;
993 break;
994 case DEV_DIR:
995 if (dnp->dn_typeinfo.Dir.parent == dnp) {
996 markroot = 1;
997 }
998 vtype = VDIR;
999 break;
1000 case DEV_BDEV:
1001 case DEV_CDEV:
1002 vtype = (dnp->dn_type == DEV_BDEV) ? VBLK : VCHR;
1003 break;
1004 }
1005 vfsp.vnfs_mp = dnp->dn_dvm->mount;
1006 vfsp.vnfs_vtype = vtype;
1007 vfsp.vnfs_str = "devfs";
1008 vfsp.vnfs_dvp = 0;
1009 vfsp.vnfs_fsnode = dnp;
1010 vfsp.vnfs_cnp = 0;
1011 vfsp.vnfs_vops = *(dnp->dn_ops);
1012
1013 if (vtype == VBLK || vtype == VCHR)
1014 vfsp.vnfs_rdev = dnp->dn_typeinfo.dev;
1015 else
1016 vfsp.vnfs_rdev = 0;
1017 vfsp.vnfs_filesize = 0;
1018 vfsp.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
1019 /* Tag system files */
1020 vfsp.vnfs_marksystem = 0;
1021 vfsp.vnfs_markroot = markroot;
1022
1023 DEVFS_UNLOCK();
1024
1025 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &vn_p);
1026
1027 DEVFS_LOCK();
1028
1029 if (error == 0) {
1030 if ((dnp->dn_vn)) {
1031 panic("devnode already has a vnode?");
1032 } else {
1033 dnp->dn_vn = vn_p;
1034 *vn_pp = vn_p;
1035 vnode_settag(vn_p, VT_DEVFS);
1036 }
1037 }
1038
1039 dnp->dn_lflags &= ~DN_CREATE;
1040
1041 if (dnp->dn_lflags & DN_CREATEWAIT) {
1042 dnp->dn_lflags &= ~DN_CREATEWAIT;
1043 wakeup(&dnp->dn_lflags);
1044 }
1045
1046 devfs_release_busy(dnp);
1047
1048 return error;
1049}
1050
1051
1052/***********************************************************************
1053 * called with DEVFS_LOCK held
1054 ***********************************************************************/
1055static void
1056devfs_release_busy(devnode_t *dnp) {
1057
1058 dnp->dn_lflags &= ~DN_BUSY;
1059
1060 if (dnp->dn_lflags & DN_DELETE)
1061 devnode_free(dnp);
1062}
1063
1064/***********************************************************************
1065 * add a whole device, with no prototype.. make name element and node
1066 * Used for adding the original device entries
1067 *
1068 * called with DEVFS_LOCK held
1069 ***********************************************************************/
1070int
1071dev_add_entry(char *name, devnode_t * parent, int type, devnode_type_t * typeinfo,
1072 devnode_t * proto, struct devfsmount *dvm, devdirent_t * *nm_pp)
1073{
1074 devnode_t * dnp;
1075 int error = 0;
1076
1077 if ((error = dev_add_node(type, typeinfo, proto, &dnp,
1078 (parent?parent->dn_dvm:dvm))) != 0)
1079 {
1080 printf("devfs: %s: base node allocation failed (Errno=%d)\n",
1081 name,error);
1082 return error;
1083 }
1084 if ((error = dev_add_name(name ,parent ,NULL, dnp, nm_pp)) != 0)
1085 {
1086 devfs_dn_free(dnp); /* 1->0 for dir, 0->(-1) for other */
1087 printf("devfs: %s: name slot allocation failed (Errno=%d)\n",
1088 name,error);
1089
1090 }
1091 return error;
1092}
1093
1094
1095/*
1096 * Function: devfs_make_node
1097 *
1098 * Purpose
1099 * Create a device node with the given pathname in the devfs namespace.
1100 *
1101 * Parameters:
1102 * dev - the dev_t value to associate
1103 * chrblk - block or character device (DEVFS_CHAR or DEVFS_BLOCK)
1104 * uid, gid - ownership
1105 * perms - permissions
1106 * fmt, ... - path format string with printf args to format the path name
1107 * Returns:
1108 * A handle to a device node if successful, NULL otherwise.
1109 */
1110void *
1111devfs_make_node(dev_t dev, int chrblk, uid_t uid,
1112 gid_t gid, int perms, const char *fmt, ...)
1113{
1114 devdirent_t * new_dev = NULL;
1115 devnode_t * dnp; /* devnode for parent directory */
1116 devnode_type_t typeinfo;
1117
1118 char *name, *path, buf[256]; /* XXX */
1119 int i;
1120 va_list ap;
1121
1122
1123 DEVFS_LOCK();
1124
1125 if (!devfs_ready) {
1126 printf("devfs_make_node: not ready for devices!\n");
1127 goto out;
1128 }
1129 if (chrblk != DEVFS_CHAR && chrblk != DEVFS_BLOCK)
1130 goto out;
1131
1132 DEVFS_UNLOCK();
1133
1134 va_start(ap, fmt);
1135 vsnprintf(buf, sizeof(buf), fmt, ap);
1136 va_end(ap);
1137
1138 name = NULL;
1139
1140 for(i=strlen(buf); i>0; i--)
1141 if(buf[i] == '/') {
1142 name=&buf[i];
1143 buf[i]=0;
1144 break;
1145 }
1146
1147 if (name) {
1148 *name++ = '\0';
1149 path = buf;
1150 } else {
1151 name = buf;
1152 path = "/";
1153 }
1154 DEVFS_LOCK();
1155
1156 /* find/create directory path ie. mkdir -p */
1157 if (dev_finddir(path, NULL, CREATE, &dnp) == 0) {
1158 typeinfo.dev = dev;
1159 if (dev_add_entry(name, dnp,
1160 (chrblk == DEVFS_CHAR) ? DEV_CDEV : DEV_BDEV,
1161 &typeinfo, NULL, NULL, &new_dev) == 0) {
1162 new_dev->de_dnp->dn_gid = gid;
1163 new_dev->de_dnp->dn_uid = uid;
1164 new_dev->de_dnp->dn_mode |= perms;
1165 devfs_propogate(dnp->dn_typeinfo.Dir.myname, new_dev);
1166 }
1167 }
1168out:
1169 DEVFS_UNLOCK();
1170
1171 return new_dev;
1172}
1173
1174/*
1175 * Function: devfs_make_link
1176 *
1177 * Purpose:
1178 * Create a link to a previously created device node.
1179 *
1180 * Returns:
1181 * 0 if successful, -1 if failed
1182 */
1183int
1184devfs_make_link(void *original, char *fmt, ...)
1185{
1186 devdirent_t * new_dev = NULL;
1187 devdirent_t * orig = (devdirent_t *) original;
1188 devnode_t * dirnode; /* devnode for parent directory */
1189
1190 va_list ap;
1191 char *p, buf[256]; /* XXX */
1192 int i;
1193
1194 DEVFS_LOCK();
1195
1196 if (!devfs_ready) {
1197 printf("devfs_make_link: not ready for devices!\n");
1198 goto out;
1199 }
1200 DEVFS_UNLOCK();
1201
1202 va_start(ap, fmt);
1203 vsnprintf(buf, sizeof(buf), fmt, ap);
1204 va_end(ap);
1205
1206 p = NULL;
1207
1208 for(i=strlen(buf); i>0; i--) {
1209 if(buf[i] == '/') {
1210 p=&buf[i];
1211 buf[i]=0;
1212 break;
1213 }
1214 }
1215 DEVFS_LOCK();
1216
1217 if (p) {
1218 *p++ = '\0';
1219
1220 if (dev_finddir(buf, NULL, CREATE, &dirnode)
1221 || dev_add_name(p, dirnode, NULL, orig->de_dnp, &new_dev))
1222 goto fail;
1223 } else {
1224 if (dev_finddir("", NULL, CREATE, &dirnode)
1225 || dev_add_name(buf, dirnode, NULL, orig->de_dnp, &new_dev))
1226 goto fail;
1227 }
1228 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, new_dev);
1229fail:
1230out:
1231 DEVFS_UNLOCK();
1232
1233 return ((new_dev != NULL) ? 0 : -1);
1234}
1235