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