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