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