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