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