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