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 * Copyright 1997,1998 Julian Elischer. All rights reserved. 30 * julian@freebsd.org 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions are 34 * met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright notice, 38 * this list of conditions and the following disclaimer in the documentation 39 * and/or other materials provided with the distribution. 40 * 41 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS 42 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 43 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR 45 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 47 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 48 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * devfs_vnops.c 54 */ 55 56/* 57 * HISTORY 58 * Clark Warner (warner_c@apple.com) Tue Feb 10 2000 59 * - Added err_copyfile to the vnode operations table 60 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999 61 * - instead of duplicating specfs here, created a vnode-ops table 62 * that redirects most operations to specfs (as is done with ufs); 63 * - removed routines that made no sense 64 * - cleaned up reclaim: replaced devfs_vntodn() with a macro VTODN() 65 * - cleaned up symlink, link locking 66 * - added the devfs_lock to protect devfs data structures against 67 * driver's calling devfs_add_devswf()/etc. 68 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999 69 * - free the devfs devnode in devfs_inactive(), not just in devfs_reclaim() 70 * to free up kernel memory as soon as it's available 71 * - got rid of devfsspec_{read, write} 72 * Dieter Siegmund (dieter@apple.com) Fri Sep 17 09:58:38 PDT 1999 73 * - update the mod/access times 74 */ 75/* 76 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce 77 * support for mandatory and extensible security protections. This notice 78 * is included in support of clause 2.2 (b) of the Apple Public License, 79 * Version 2.0. 80 */ 81 82#include <sys/param.h> 83#include <sys/systm.h> 84#include <sys/namei.h> 85#include <sys/kernel.h> 86#include <sys/fcntl.h> 87#include <sys/conf.h> 88#include <sys/disklabel.h> 89#include <sys/lock.h> 90#include <sys/stat.h> 91#include <sys/mount_internal.h> 92#include <sys/proc.h> 93#include <sys/kauth.h> 94#include <sys/time.h> 95#include <sys/vnode_internal.h> 96#include <miscfs/specfs/specdev.h> 97#include <sys/dirent.h> 98#include <sys/vmmeter.h> 99#include <sys/vm.h> 100#include <sys/uio_internal.h> 101 102#if CONFIG_MACF 103#include <security/mac_framework.h> 104#endif 105 106#include "devfsdefs.h" 107#include "devfs.h" 108 109#if FDESC 110#include "fdesc.h" 111#endif /* FDESC */ 112 113static int devfs_update(struct vnode *vp, struct timeval *access, 114 struct timeval *modify); 115void devfs_rele_node(devnode_t *); 116static void devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags); 117static boolean_t devfs_update_needed(long now_s, long last_s); 118void dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags); 119void dn_times_now(devnode_t *dnp, uint32_t just_changed_flags); 120void dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags); 121 122void 123dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags) 124{ 125 126 lck_mtx_assert(&devfs_attr_mutex, LCK_MTX_ASSERT_OWNED); 127 128 if (just_changed_flags & DEVFS_UPDATE_ACCESS) { 129 dnp->dn_atime.tv_sec = t1->tv_sec; 130 dnp->dn_atime.tv_nsec = t1->tv_usec * 1000; 131 dnp->dn_access = 0; 132 } else if (dnp->dn_access) { 133 dnp->dn_atime.tv_sec = MIN(t1->tv_sec, dnp->dn_atime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS); 134 dnp->dn_atime.tv_nsec = t1->tv_usec * 1000; 135 dnp->dn_access = 0; 136 } 137 138 if (just_changed_flags & DEVFS_UPDATE_MOD) { 139 dnp->dn_mtime.tv_sec = t2->tv_sec; 140 dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000; 141 dnp->dn_update = 0; 142 } else if (dnp->dn_update) { 143 dnp->dn_mtime.tv_sec = MIN(t2->tv_sec, dnp->dn_mtime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS); 144 dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000; 145 dnp->dn_update = 0; 146 } 147 148 if (just_changed_flags & DEVFS_UPDATE_CHANGE) { 149 dnp->dn_ctime.tv_sec = t3->tv_sec; 150 dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000; 151 dnp->dn_change = 0; 152 } else if (dnp->dn_change) { 153 dnp->dn_ctime.tv_sec = MIN(t3->tv_sec, dnp->dn_ctime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS); 154 dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000; 155 dnp->dn_change = 0; 156 } 157} 158 159void 160dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags) 161{ 162 if (just_changed_flags & DEVFS_UPDATE_CHANGE) { 163 dnp->dn_change = 1; 164 } 165 if (just_changed_flags & DEVFS_UPDATE_ACCESS) { 166 dnp->dn_access = 1; 167 } 168 if (just_changed_flags & DEVFS_UPDATE_MOD) { 169 dnp->dn_update = 1; 170 } 171} 172 173/* 174 * Update times based on pending updates and optionally a set of new changes. 175 */ 176void 177dn_times_now(devnode_t * dnp, uint32_t just_changed_flags) 178{ 179 struct timeval now; 180 181 DEVFS_ATTR_LOCK_SPIN(); 182 microtime(&now); 183 dn_times_locked(dnp, &now, &now, &now, just_changed_flags); 184 DEVFS_ATTR_UNLOCK(); 185} 186 187 188/* 189 * Convert a component of a pathname into a pointer to a locked node. 190 * This is a very central and rather complicated routine. 191 * If the file system is not maintained in a strict tree hierarchy, 192 * this can result in a deadlock situation (see comments in code below). 193 * 194 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on 195 * whether the name is to be looked up, created, renamed, or deleted. 196 * When CREATE, RENAME, or DELETE is specified, information usable in 197 * creating, renaming, or deleting a directory entry may be calculated. 198 * If flag has LOCKPARENT or'ed into it and the target of the pathname 199 * exists, lookup returns both the target and its parent directory locked. 200 * When creating or renaming and LOCKPARENT is specified, the target may 201 * not be ".". When deleting and LOCKPARENT is specified, the target may 202 * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK 203 * instead of two DNUNLOCKs. 204 * 205 * Overall outline of devfs_lookup: 206 * 207 * check accessibility of directory 208 * null terminate the component (lookup leaves the whole string alone) 209 * look for name in cache, if found, then if at end of path 210 * and deleting or creating, drop it, else return name 211 * search for name in directory, to found or notfound 212 * notfound: 213 * if creating, return locked directory, 214 * else return error 215 * found: 216 * if at end of path and deleting, return information to allow delete 217 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 218 * node and return info to allow rewrite 219 * if not at end, add name to cache; if at end and neither creating 220 * nor deleting, add name to cache 221 * On return to lookup, remove the null termination we put in at the start. 222 * 223 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked. 224 */ 225static int 226devfs_lookup(struct vnop_lookup_args *ap) 227 /*struct vnop_lookup_args { 228 struct vnode * a_dvp; directory vnode ptr 229 struct vnode ** a_vpp; where to put the result 230 struct componentname * a_cnp; the name we want 231 vfs_context_t a_context; 232 };*/ 233{ 234 struct componentname *cnp = ap->a_cnp; 235 vfs_context_t ctx = cnp->cn_context; 236 struct proc *p = vfs_context_proc(ctx); 237 struct vnode *dir_vnode = ap->a_dvp; 238 struct vnode **result_vnode = ap->a_vpp; 239 devnode_t * dir_node; /* the directory we are searching */ 240 devnode_t * node = NULL; /* the node we are searching for */ 241 devdirent_t * nodename; 242 int flags = cnp->cn_flags; 243 int op = cnp->cn_nameiop; /* LOOKUP, CREATE, RENAME, or DELETE */ 244 int wantparent = flags & (LOCKPARENT|WANTPARENT); 245 int error = 0; 246 char heldchar; /* the char at the end of the name componet */ 247 248retry: 249 250 *result_vnode = NULL; /* safe not sorry */ /*XXX*/ 251 252 /* okay to look at directory vnodes ourside devfs lock as they are not aliased */ 253 dir_node = VTODN(dir_vnode); 254 255 /* 256 * Make sure that our node is a directory as well. 257 */ 258 if (dir_node->dn_type != DEV_DIR) { 259 return (ENOTDIR); 260 } 261 262 DEVFS_LOCK(); 263 /* 264 * temporarily terminate string component 265 */ 266 heldchar = cnp->cn_nameptr[cnp->cn_namelen]; 267 cnp->cn_nameptr[cnp->cn_namelen] = '\0'; 268 269 nodename = dev_findname(dir_node, cnp->cn_nameptr); 270 /* 271 * restore saved character 272 */ 273 cnp->cn_nameptr[cnp->cn_namelen] = heldchar; 274 275 if (nodename) { 276 /* entry exists */ 277 node = nodename->de_dnp; 278 279 /* Do potential vnode allocation here inside the lock 280 * to make sure that our device node has a non-NULL dn_vn 281 * associated with it. The device node might otherwise 282 * get deleted out from under us (see devfs_dn_free()). 283 */ 284 error = devfs_dntovn(node, result_vnode, p); 285 } 286 DEVFS_UNLOCK(); 287 288 if (error) { 289 if (error == EAGAIN) 290 goto retry; 291 return error; 292 } 293 if (!nodename) { 294 /* 295 * we haven't called devfs_dntovn if we get here 296 * we have not taken a reference on the node.. no 297 * vnode_put is necessary on these error returns 298 * 299 * If it doesn't exist and we're not the last component, 300 * or we're at the last component, but we're not creating 301 * or renaming, return ENOENT. 302 */ 303 if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) { 304 return ENOENT; 305 } 306 /* 307 * We return with the directory locked, so that 308 * the parameters we set up above will still be 309 * valid if we actually decide to add a new entry. 310 * We return ni_vp == NULL to indicate that the entry 311 * does not currently exist; we leave a pointer to 312 * the (locked) directory vnode in namei_data->ni_dvp. 313 * 314 * NB - if the directory is unlocked, then this 315 * information cannot be used. 316 */ 317 return (EJUSTRETURN); 318 } 319 /* 320 * from this point forward, we need to vnode_put the reference 321 * picked up in devfs_dntovn if we decide to return an error 322 */ 323 324 /* 325 * If deleting, and at end of pathname, return 326 * parameters which can be used to remove file. 327 * If the wantparent flag isn't set, we return only 328 * the directory (in namei_data->ni_dvp), otherwise we go 329 * on and lock the node, being careful with ".". 330 */ 331 if (op == DELETE && (flags & ISLASTCN)) { 332 333 /* 334 * we are trying to delete '.'. What does this mean? XXX 335 */ 336 if (dir_node == node) { 337 if (*result_vnode) { 338 vnode_put(*result_vnode); 339 *result_vnode = NULL; 340 } 341 if ( ((error = vnode_get(dir_vnode)) == 0) ) { 342 *result_vnode = dir_vnode; 343 } 344 return (error); 345 } 346 return (0); 347 } 348 349 /* 350 * If rewriting (RENAME), return the vnode and the 351 * information required to rewrite the present directory 352 * Must get node of directory entry to verify it's a 353 * regular file, or empty directory. 354 */ 355 if (op == RENAME && wantparent && (flags & ISLASTCN)) { 356 357 /* 358 * Careful about locking second node. 359 * This can only occur if the target is ".". 360 */ 361 if (dir_node == node) { 362 error = EISDIR; 363 goto drop_ref; 364 } 365 return (0); 366 } 367 368 /* 369 * Step through the translation in the name. We do not unlock the 370 * directory because we may need it again if a symbolic link 371 * is relative to the current directory. Instead we save it 372 * unlocked as "saved_dir_node" XXX. We must get the target 373 * node before unlocking 374 * the directory to insure that the node will not be removed 375 * before we get it. We prevent deadlock by always fetching 376 * nodes from the root, moving down the directory tree. Thus 377 * when following backward pointers ".." we must unlock the 378 * parent directory before getting the requested directory. 379 * There is a potential race condition here if both the current 380 * and parent directories are removed before the lock for the 381 * node associated with ".." returns. We hope that this occurs 382 * infrequently since we cannot avoid this race condition without 383 * implementing a sophisticated deadlock detection algorithm. 384 * Note also that this simple deadlock detection scheme will not 385 * work if the file system has any hard links other than ".." 386 * that point backwards in the directory structure. 387 */ 388 if ((flags & ISDOTDOT) == 0 && dir_node == node) { 389 if (*result_vnode) { 390 vnode_put(*result_vnode); 391 *result_vnode = NULL; 392 } 393 if ( (error = vnode_get(dir_vnode)) ) { 394 return (error); 395 } 396 *result_vnode = dir_vnode; 397 } 398 return (0); 399 400drop_ref: 401 if (*result_vnode) { 402 vnode_put(*result_vnode); 403 *result_vnode = NULL; 404 } 405 return (error); 406} 407 408static int 409devfs_getattr(struct vnop_getattr_args *ap) 410 /*struct vnop_getattr_args { 411 struct vnode *a_vp; 412 struct vnode_attr *a_vap; 413 kauth_cred_t a_cred; 414 struct proc *a_p; 415 } */ 416{ 417 struct vnode *vp = ap->a_vp; 418 struct vnode_attr *vap = ap->a_vap; 419 devnode_t * file_node; 420 struct timeval now; 421 422 423 DEVFS_LOCK(); 424 file_node = VTODN(vp); 425 426 VATTR_RETURN(vap, va_mode, file_node->dn_mode); 427 428 /* 429 * Note: for DEV_CDEV and DEV_BDEV, we return the device from 430 * the vp, not the file_node; if we getting information on a 431 * cloning device, we want the cloned information, not the template. 432 */ 433 switch (file_node->dn_type) 434 { 435 case DEV_DIR: 436#if FDESC 437 case DEV_DEVFD: /* Like a directory */ 438#endif /* FDESC */ 439 VATTR_RETURN(vap, va_rdev, 0); 440 vap->va_mode |= (S_IFDIR); 441 break; 442 case DEV_CDEV: 443 VATTR_RETURN(vap, va_rdev, vp->v_rdev); 444 vap->va_mode |= (S_IFCHR); 445 break; 446 case DEV_BDEV: 447 VATTR_RETURN(vap, va_rdev, vp->v_rdev); 448 vap->va_mode |= (S_IFBLK); 449 break; 450 case DEV_SLNK: 451 VATTR_RETURN(vap, va_rdev, 0); 452 vap->va_mode |= (S_IFLNK); 453 break; 454 default: 455 VATTR_RETURN(vap, va_rdev, 0); /* default value only */ 456 } 457 VATTR_RETURN(vap, va_type, vp->v_type); 458 VATTR_RETURN(vap, va_nlink, file_node->dn_links); 459 VATTR_RETURN(vap, va_uid, file_node->dn_uid); 460 VATTR_RETURN(vap, va_gid, file_node->dn_gid); 461 VATTR_RETURN(vap, va_fsid, (uintptr_t)file_node->dn_dvm); 462 VATTR_RETURN(vap, va_fileid, (uintptr_t)file_node->dn_ino); 463 VATTR_RETURN(vap, va_data_size, file_node->dn_len); 464 465 /* return an override block size (advisory) */ 466 if (vp->v_type == VBLK) 467 VATTR_RETURN(vap, va_iosize, BLKDEV_IOSIZE); 468 else if (vp->v_type == VCHR) 469 VATTR_RETURN(vap, va_iosize, MAXPHYSIO); 470 else 471 VATTR_RETURN(vap, va_iosize, vp->v_mount->mnt_vfsstat.f_iosize); 472 473 474 DEVFS_ATTR_LOCK_SPIN(); 475 476 microtime(&now); 477 dn_times_locked(file_node, &now, &now, &now, 0); 478 479 /* if the time is bogus, set it to the boot time */ 480 if (file_node->dn_ctime.tv_sec == 0) { 481 file_node->dn_ctime.tv_sec = boottime_sec(); 482 file_node->dn_ctime.tv_nsec = 0; 483 } 484 if (file_node->dn_mtime.tv_sec == 0) 485 file_node->dn_mtime = file_node->dn_ctime; 486 if (file_node->dn_atime.tv_sec == 0) 487 file_node->dn_atime = file_node->dn_ctime; 488 VATTR_RETURN(vap, va_change_time, file_node->dn_ctime); 489 VATTR_RETURN(vap, va_modify_time, file_node->dn_mtime); 490 VATTR_RETURN(vap, va_access_time, file_node->dn_atime); 491 492 DEVFS_ATTR_UNLOCK(); 493 494 VATTR_RETURN(vap, va_gen, 0); 495 VATTR_RETURN(vap, va_filerev, 0); 496 VATTR_RETURN(vap, va_acl, NULL); 497 498 /* Hide the root so Finder doesn't display it */ 499 if (vnode_isvroot(vp)) { 500 VATTR_RETURN(vap, va_flags, UF_HIDDEN); 501 } else { 502 VATTR_RETURN(vap, va_flags, 0); 503 } 504 505 DEVFS_UNLOCK(); 506 507 return 0; 508} 509 510static int 511devfs_setattr(struct vnop_setattr_args *ap) 512 /*struct vnop_setattr_args { 513 struct vnode *a_vp; 514 struct vnode_attr *a_vap; 515 vfs_context_t a_context; 516 } */ 517{ 518 struct vnode *vp = ap->a_vp; 519 struct vnode_attr *vap = ap->a_vap; 520 int error = 0; 521 devnode_t * file_node; 522 struct timeval atimeval, mtimeval; 523 524 DEVFS_LOCK(); 525 526 file_node = VTODN(vp); 527 /* 528 * Go through the fields and update if set. 529 */ 530 if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) { 531 532 533 if (VATTR_IS_ACTIVE(vap, va_access_time)) 534 file_node->dn_access = 1; 535 if (VATTR_IS_ACTIVE(vap, va_modify_time)) { 536 file_node->dn_change = 1; 537 file_node->dn_update = 1; 538 } 539 atimeval.tv_sec = vap->va_access_time.tv_sec; 540 atimeval.tv_usec = vap->va_access_time.tv_nsec / 1000; 541 mtimeval.tv_sec = vap->va_modify_time.tv_sec; 542 mtimeval.tv_usec = vap->va_modify_time.tv_nsec / 1000; 543 544 if ( (error = devfs_update(vp, &atimeval, &mtimeval)) ) 545 goto exit; 546 } 547 VATTR_SET_SUPPORTED(vap, va_access_time); 548 VATTR_SET_SUPPORTED(vap, va_change_time); 549 550 /* 551 * Change the permissions. 552 */ 553 if (VATTR_IS_ACTIVE(vap, va_mode)) { 554 file_node->dn_mode &= ~07777; 555 file_node->dn_mode |= vap->va_mode & 07777; 556 } 557 VATTR_SET_SUPPORTED(vap, va_mode); 558 559 /* 560 * Change the owner. 561 */ 562 if (VATTR_IS_ACTIVE(vap, va_uid)) 563 file_node->dn_uid = vap->va_uid; 564 VATTR_SET_SUPPORTED(vap, va_uid); 565 566 /* 567 * Change the group. 568 */ 569 if (VATTR_IS_ACTIVE(vap, va_gid)) 570 file_node->dn_gid = vap->va_gid; 571 VATTR_SET_SUPPORTED(vap, va_gid); 572 exit: 573 DEVFS_UNLOCK(); 574 575 return error; 576} 577 578#if CONFIG_MACF 579static int 580devfs_setlabel(struct vnop_setlabel_args *ap) 581 /* struct vnop_setlabel_args { 582 struct vnodeop_desc *a_desc; 583 struct vnode *a_vp; 584 struct label *a_vl; 585 vfs_context_t a_context; 586 } */ 587{ 588 struct vnode *vp; 589 struct devnode *de; 590 591 vp = ap->a_vp; 592 de = VTODN(vp); 593 594 mac_vnode_label_update(ap->a_context, vp, ap->a_vl); 595 mac_devfs_label_update(vp->v_mount, de, vp); 596 597 return (0); 598} 599#endif 600 601static int 602devfs_read(struct vnop_read_args *ap) 603 /* struct vnop_read_args { 604 struct vnode *a_vp; 605 struct uio *a_uio; 606 int a_ioflag; 607 vfs_context_t a_context; 608 } */ 609{ 610 devnode_t * dn_p = VTODN(ap->a_vp); 611 612 switch (ap->a_vp->v_type) { 613 case VDIR: { 614 dn_p->dn_access = 1; 615 616 return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context); 617 } 618 default: { 619 printf("devfs_read(): bad file type %d", ap->a_vp->v_type); 620 return(EINVAL); 621 break; 622 } 623 } 624 return (0); /* not reached */ 625} 626 627static int 628devfs_close(struct vnop_close_args *ap) 629 /* struct vnop_close_args { 630 struct vnode *a_vp; 631 int a_fflag; 632 vfs_context_t a_context; 633 } */ 634{ 635 struct vnode * vp = ap->a_vp; 636 register devnode_t * dnp; 637 638 if (vnode_isinuse(vp, 1)) { 639 DEVFS_LOCK(); 640 dnp = VTODN(vp); 641 dn_times_now(dnp, 0); 642 DEVFS_UNLOCK(); 643 } 644 return (0); 645} 646 647static int 648devfsspec_close(struct vnop_close_args *ap) 649 /* struct vnop_close_args { 650 struct vnode *a_vp; 651 int a_fflag; 652 vfs_context_t a_context; 653 } */ 654{ 655 struct vnode * vp = ap->a_vp; 656 register devnode_t * dnp; 657 658 if (vnode_isinuse(vp, 0)) { 659 DEVFS_LOCK(); 660 dnp = VTODN(vp); 661 dn_times_now(dnp, 0); 662 DEVFS_UNLOCK(); 663 } 664 665 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap)); 666} 667 668static boolean_t 669devfs_update_needed(long now_s, long last_s) 670{ 671 if (now_s > last_s) { 672 if (now_s - last_s >= DEVFS_LAZY_UPDATE_SECONDS) { 673 return TRUE; 674 } 675 } 676 677 return FALSE; 678} 679 680/* 681 * Given a set of time updates required [to happen at some point], check 682 * either make those changes (and resolve other pending updates) or mark 683 * the devnode for a subsequent update. 684 */ 685static void 686devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags) 687{ 688 struct timeval now; 689 long now_s; 690 691 microtime(&now); 692 now_s = now.tv_sec; 693 694 if (dnp->dn_change || (just_changed_flags & DEVFS_UPDATE_CHANGE)) { 695 if (devfs_update_needed(now_s, dnp->dn_ctime.tv_sec)) { 696 dn_times_now(dnp, just_changed_flags); 697 return; 698 } 699 } 700 if (dnp->dn_access || (just_changed_flags & DEVFS_UPDATE_ACCESS)) { 701 if (devfs_update_needed(now_s, dnp->dn_atime.tv_sec)) { 702 dn_times_now(dnp, just_changed_flags); 703 return; 704 } 705 } 706 if (dnp->dn_update || (just_changed_flags & DEVFS_UPDATE_MOD)) { 707 if (devfs_update_needed(now_s, dnp->dn_mtime.tv_sec)) { 708 dn_times_now(dnp, just_changed_flags); 709 return; 710 } 711 } 712 713 /* Not going to do anything now--mark for later update */ 714 dn_mark_for_delayed_times_update(dnp, just_changed_flags); 715 716 return; 717} 718 719static int 720devfsspec_read(struct vnop_read_args *ap) 721 /* struct vnop_read_args { 722 struct vnode *a_vp; 723 struct uio *a_uio; 724 int a_ioflag; 725 kauth_cred_t a_cred; 726 } */ 727{ 728 register devnode_t * dnp = VTODN(ap->a_vp); 729 730 devfs_consider_time_update(dnp, DEVFS_UPDATE_ACCESS); 731 732 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap)); 733} 734 735static int 736devfsspec_write(struct vnop_write_args *ap) 737 /* struct vnop_write_args { 738 struct vnode *a_vp; 739 struct uio *a_uio; 740 int a_ioflag; 741 vfs_context_t a_context; 742 } */ 743{ 744 register devnode_t * dnp = VTODN(ap->a_vp); 745 746 devfs_consider_time_update(dnp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD); 747 748 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap)); 749} 750 751/* 752 * Write data to a file or directory. 753 */ 754static int 755devfs_write(struct vnop_write_args *ap) 756 /* struct vnop_write_args { 757 struct vnode *a_vp; 758 struct uio *a_uio; 759 int a_ioflag; 760 kauth_cred_t a_cred; 761 } */ 762{ 763 switch (ap->a_vp->v_type) { 764 case VDIR: 765 return(EISDIR); 766 default: 767 printf("devfs_write(): bad file type %d", ap->a_vp->v_type); 768 return (EINVAL); 769 } 770 return 0; /* not reached */ 771} 772 773/* 774 * Deviates from UFS naming convention because there is a KPI function 775 * called devfs_remove(). 776 */ 777static int 778devfs_vnop_remove(struct vnop_remove_args *ap) 779 /* struct vnop_remove_args { 780 struct vnode *a_dvp; 781 struct vnode *a_vp; 782 struct componentname *a_cnp; 783 } */ 784{ 785 struct vnode *vp = ap->a_vp; 786 struct vnode *dvp = ap->a_dvp; 787 struct componentname *cnp = ap->a_cnp; 788 devnode_t * tp; 789 devnode_t * tdp; 790 devdirent_t * tnp; 791 int doingdirectory = 0; 792 int error = 0; 793 794 /* 795 * assume that the name is null terminated as they 796 * are the end of the path. Get pointers to all our 797 * devfs structures. 798 */ 799 DEVFS_LOCK(); 800 801 tp = VTODN(vp); 802 tdp = VTODN(dvp); 803 804 805 tnp = dev_findname(tdp, cnp->cn_nameptr); 806 807 if (tnp == NULL) { 808 error = ENOENT; 809 goto abort; 810 } 811 812 /* 813 * Make sure that we don't try do something stupid 814 */ 815 if ((tp->dn_type) == DEV_DIR) { 816 /* 817 * Avoid ".", "..", and aliases of "." for obvious reasons. 818 */ 819 if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') 820 || (cnp->cn_flags&ISDOTDOT) ) { 821 error = EINVAL; 822 goto abort; 823 } 824 doingdirectory++; 825 } 826 827 /*********************************** 828 * Start actually doing things.... * 829 ***********************************/ 830 devfs_consider_time_update(tdp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD); 831 832 /* 833 * Target must be empty if a directory and have no links 834 * to it. Also, ensure source and target are compatible 835 * (both directories, or both not directories). 836 */ 837 if (( doingdirectory) && (tp->dn_links > 2)) { 838 error = ENOTEMPTY; 839 goto abort; 840 } 841 dev_free_name(tnp); 842abort: 843 DEVFS_UNLOCK(); 844 845 return (error); 846} 847 848/* 849 */ 850static int 851devfs_link(struct vnop_link_args *ap) 852 /*struct vnop_link_args { 853 struct vnode *a_tdvp; 854 struct vnode *a_vp; 855 struct componentname *a_cnp; 856 vfs_context_t a_context; 857 } */ 858{ 859 struct vnode *vp = ap->a_vp; 860 struct vnode *tdvp = ap->a_tdvp; 861 struct componentname *cnp = ap->a_cnp; 862 devnode_t * fp; 863 devnode_t * tdp; 864 devdirent_t * tnp; 865 int error = 0; 866 867 /* 868 * First catch an arbitrary restriction for this FS 869 */ 870 if (cnp->cn_namelen > DEVMAXNAMESIZE) { 871 error = ENAMETOOLONG; 872 goto out1; 873 } 874 875 /* 876 * Lock our directories and get our name pointers 877 * assume that the names are null terminated as they 878 * are the end of the path. Get pointers to all our 879 * devfs structures. 880 */ 881 /* can lookup dnode safely for tdvp outside of devfs lock as it is not aliased */ 882 tdp = VTODN(tdvp); 883 884 if (tdvp->v_mount != vp->v_mount) { 885 return (EXDEV); 886 } 887 DEVFS_LOCK(); 888 889 fp = VTODN(vp); 890 891 /*********************************** 892 * Start actually doing things.... * 893 ***********************************/ 894 dn_times_now(fp, DEVFS_UPDATE_CHANGE); 895 896 if (!error) { 897 error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp); 898 } 899out1: 900 DEVFS_UNLOCK(); 901 902 return (error); 903} 904 905/* 906 * Rename system call. Seems overly complicated to me... 907 * rename("foo", "bar"); 908 * is essentially 909 * unlink("bar"); 910 * link("foo", "bar"); 911 * unlink("foo"); 912 * but ``atomically''. 913 * 914 * When the target exists, both the directory 915 * and target vnodes are locked. 916 * the source and source-parent vnodes are referenced 917 * 918 * 919 * Basic algorithm is: 920 * 921 * 1) Bump link count on source while we're linking it to the 922 * target. This also ensure the inode won't be deleted out 923 * from underneath us while we work (it may be truncated by 924 * a concurrent `trunc' or `open' for creation). 925 * 2) Link source to destination. If destination already exists, 926 * delete it first. 927 * 3) Unlink source reference to node if still around. If a 928 * directory was moved and the parent of the destination 929 * is different from the source, patch the ".." entry in the 930 * directory. 931 */ 932static int 933devfs_rename(struct vnop_rename_args *ap) 934 /*struct vnop_rename_args { 935 struct vnode *a_fdvp; 936 struct vnode *a_fvp; 937 struct componentname *a_fcnp; 938 struct vnode *a_tdvp; 939 struct vnode *a_tvp; 940 struct componentname *a_tcnp; 941 vfs_context_t a_context; 942 } */ 943{ 944 struct vnode *tvp = ap->a_tvp; 945 struct vnode *tdvp = ap->a_tdvp; 946 struct vnode *fvp = ap->a_fvp; 947 struct vnode *fdvp = ap->a_fdvp; 948 struct componentname *tcnp = ap->a_tcnp; 949 struct componentname *fcnp = ap->a_fcnp; 950 devnode_t *fp, *fdp, *tp, *tdp; 951 devdirent_t *fnp,*tnp; 952 int doingdirectory = 0; 953 int error = 0; 954 955 DEVFS_LOCK(); 956 /* 957 * First catch an arbitrary restriction for this FS 958 */ 959 if (tcnp->cn_namelen > DEVMAXNAMESIZE) { 960 error = ENAMETOOLONG; 961 goto out; 962 } 963 964 /* 965 * assume that the names are null terminated as they 966 * are the end of the path. Get pointers to all our 967 * devfs structures. 968 */ 969 tdp = VTODN(tdvp); 970 fdp = VTODN(fdvp); 971 fp = VTODN(fvp); 972 973 fnp = dev_findname(fdp, fcnp->cn_nameptr); 974 975 if (fnp == NULL) { 976 error = ENOENT; 977 goto out; 978 } 979 tp = NULL; 980 tnp = NULL; 981 982 if (tvp) { 983 tnp = dev_findname(tdp, tcnp->cn_nameptr); 984 985 if (tnp == NULL) { 986 error = ENOENT; 987 goto out; 988 } 989 tp = VTODN(tvp); 990 } 991 992 /* 993 * Make sure that we don't try do something stupid 994 */ 995 if ((fp->dn_type) == DEV_DIR) { 996 /* 997 * Avoid ".", "..", and aliases of "." for obvious reasons. 998 */ 999 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') 1000 || (fcnp->cn_flags&ISDOTDOT) 1001 || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.') 1002 || (tcnp->cn_flags&ISDOTDOT) 1003 || (tdp == fp )) { 1004 error = EINVAL; 1005 goto out; 1006 } 1007 doingdirectory++; 1008 } 1009 1010 /* 1011 * If ".." must be changed (ie the directory gets a new 1012 * parent) then the source directory must not be in the 1013 * directory hierarchy above the target, as this would 1014 * orphan everything below the source directory. Also 1015 * the user must have write permission in the source so 1016 * as to be able to change "..". 1017 */ 1018 if (doingdirectory && (tdp != fdp)) { 1019 devnode_t * tmp, *ntmp; 1020 tmp = tdp; 1021 do { 1022 if(tmp == fp) { 1023 /* XXX unlock stuff here probably */ 1024 error = EINVAL; 1025 goto out; 1026 } 1027 ntmp = tmp; 1028 } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp); 1029 } 1030 1031 /*********************************** 1032 * Start actually doing things.... * 1033 ***********************************/ 1034 dn_times_now(fp, DEVFS_UPDATE_CHANGE); 1035 1036 /* 1037 * Check if just deleting a link name. 1038 */ 1039 if (fvp == tvp) { 1040 if (fvp->v_type == VDIR) { 1041 error = EINVAL; 1042 goto out; 1043 } 1044 /* Release destination completely. */ 1045 dev_free_name(fnp); 1046 1047 DEVFS_UNLOCK(); 1048 return 0; 1049 } 1050 /* 1051 * 1) Bump link count while we're moving stuff 1052 * around. If we crash somewhere before 1053 * completing our work, too bad :) 1054 */ 1055 fp->dn_links++; 1056 /* 1057 * If the target exists zap it (unless it's a non-empty directory) 1058 * We could do that as well but won't 1059 */ 1060 if (tp) { 1061 /* 1062 * Target must be empty if a directory and have no links 1063 * to it. Also, ensure source and target are compatible 1064 * (both directories, or both not directories). 1065 */ 1066 if (( doingdirectory) && (tp->dn_links > 2)) { 1067 error = ENOTEMPTY; 1068 goto bad; 1069 } 1070 dev_free_name(tnp); 1071 tp = NULL; 1072 } 1073 dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp); 1074 fnp->de_dnp = NULL; 1075 fp->dn_links--; /* one less link to it.. */ 1076 1077 dev_free_name(fnp); 1078bad: 1079 fp->dn_links--; /* we added one earlier*/ 1080out: 1081 DEVFS_UNLOCK(); 1082 return (error); 1083} 1084 1085static int 1086devfs_mkdir(struct vnop_mkdir_args *ap) 1087 /*struct vnop_mkdir_args { 1088 struct vnode *a_dvp; 1089 struct vnode **a_vpp; 1090 struct componentname *a_cnp; 1091 struct vnode_attr *a_vap; 1092 vfs_context_t a_context; 1093 } */ 1094{ 1095 struct componentname * cnp = ap->a_cnp; 1096 vfs_context_t ctx = cnp->cn_context; 1097 struct proc *p = vfs_context_proc(ctx); 1098 int error = 0; 1099 devnode_t * dir_p; 1100 devdirent_t * nm_p; 1101 devnode_t * dev_p; 1102 struct vnode_attr * vap = ap->a_vap; 1103 struct vnode * * vpp = ap->a_vpp; 1104 1105 DEVFS_LOCK(); 1106 1107 dir_p = VTODN(ap->a_dvp); 1108 error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_DIR, 1109 NULL, NULL, NULL, &nm_p); 1110 if (error) { 1111 goto failure; 1112 } 1113 dev_p = nm_p->de_dnp; 1114 dev_p->dn_uid = dir_p->dn_uid; 1115 dev_p->dn_gid = dir_p->dn_gid; 1116 dev_p->dn_mode = vap->va_mode; 1117 dn_copy_times(dev_p, dir_p); 1118 1119 error = devfs_dntovn(dev_p, vpp, p); 1120failure: 1121 DEVFS_UNLOCK(); 1122 1123 return error; 1124} 1125 1126/* 1127 * An rmdir is a special type of remove, which we already support; we wrap 1128 * and reexpress the arguments to call devfs_remove directly. The only 1129 * different argument is flags, which we do not set, since it's ignored. 1130 */ 1131static int 1132devfs_rmdir(struct vnop_rmdir_args *ap) 1133 /* struct vnop_rmdir_args { 1134 struct vnode *a_dvp; 1135 struct vnode *a_vp; 1136 struct componentname *a_cnp; 1137 vfs_context_t a_context; 1138 } */ 1139{ 1140 struct vnop_remove_args ra; 1141 1142 ra.a_dvp = ap->a_dvp; 1143 ra.a_vp = ap->a_vp; 1144 ra.a_cnp = ap->a_cnp; 1145 ra.a_flags = 0; /* XXX */ 1146 ra.a_context = ap->a_context; 1147 1148 return devfs_vnop_remove(&ra); 1149} 1150 1151 1152static int 1153devfs_symlink(struct vnop_symlink_args *ap) 1154 /*struct vnop_symlink_args { 1155 struct vnode *a_dvp; 1156 struct vnode **a_vpp; 1157 struct componentname *a_cnp; 1158 struct vnode_attr *a_vap; 1159 char *a_target; 1160 vfs_context_t a_context; 1161 } */ 1162{ 1163 int error; 1164 devdirent_t *newent; 1165 1166 DEVFS_LOCK(); 1167 error = devfs_make_symlink(VTODN(ap->a_dvp), ap->a_cnp->cn_nameptr, ap->a_vap->va_mode, ap->a_target, &newent); 1168 1169 if (error == 0) { 1170 error = devfs_dntovn(newent->de_dnp, ap->a_vpp, vfs_context_proc(ap->a_context)); 1171 } 1172 1173 DEVFS_UNLOCK(); 1174 1175 return error; 1176 1177} 1178 1179/* Called with devfs locked */ 1180int 1181devfs_make_symlink(devnode_t *dir_p, char *name, int mode, char *target, devdirent_t **newent) 1182{ 1183 int error = 0; 1184 devnode_type_t typeinfo; 1185 devdirent_t * nm_p; 1186 devnode_t * dev_p; 1187 1188 typeinfo.Slnk.name = target; 1189 typeinfo.Slnk.namelen = strlen(target); 1190 1191 error = dev_add_entry(name, dir_p, DEV_SLNK, 1192 &typeinfo, NULL, NULL, &nm_p); 1193 if (error) { 1194 goto failure; 1195 } 1196 dev_p = nm_p->de_dnp; 1197 dev_p->dn_uid = dir_p->dn_uid; 1198 dev_p->dn_gid = dir_p->dn_gid; 1199 dev_p->dn_mode = mode; 1200 dn_copy_times(dev_p, dir_p); 1201 1202 if (newent) { 1203 *newent = nm_p; 1204 } 1205 1206failure: 1207 1208 return error; 1209} 1210 1211/* 1212 * Mknod vnode call 1213 */ 1214static int 1215devfs_mknod(struct vnop_mknod_args *ap) 1216 /* struct vnop_mknod_args { 1217 struct vnode *a_dvp; 1218 struct vnode **a_vpp; 1219 struct componentname *a_cnp; 1220 struct vnode_attr *a_vap; 1221 vfs_context_t a_context; 1222 } */ 1223{ 1224 struct componentname * cnp = ap->a_cnp; 1225 vfs_context_t ctx = cnp->cn_context; 1226 struct proc *p = vfs_context_proc(ctx); 1227 devnode_t * dev_p; 1228 devdirent_t * devent; 1229 devnode_t * dir_p; /* devnode for parent directory */ 1230 struct vnode * dvp = ap->a_dvp; 1231 int error = 0; 1232 devnode_type_t typeinfo; 1233 struct vnode_attr * vap = ap->a_vap; 1234 struct vnode ** vpp = ap->a_vpp; 1235 1236 *vpp = NULL; 1237 if (!(vap->va_type == VBLK) && !(vap->va_type == VCHR)) { 1238 return (EINVAL); /* only support mknod of special files */ 1239 } 1240 typeinfo.dev = vap->va_rdev; 1241 1242 DEVFS_LOCK(); 1243 1244 dir_p = VTODN(dvp); 1245 1246 error = dev_add_entry(cnp->cn_nameptr, dir_p, 1247 (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV, 1248 &typeinfo, NULL, NULL, &devent); 1249 if (error) { 1250 goto failure; 1251 } 1252 dev_p = devent->de_dnp; 1253 error = devfs_dntovn(dev_p, vpp, p); 1254 if (error) 1255 goto failure; 1256 dev_p->dn_uid = vap->va_uid; 1257 dev_p->dn_gid = vap->va_gid; 1258 dev_p->dn_mode = vap->va_mode; 1259 VATTR_SET_SUPPORTED(vap, va_uid); 1260 VATTR_SET_SUPPORTED(vap, va_gid); 1261 VATTR_SET_SUPPORTED(vap, va_mode); 1262failure: 1263 DEVFS_UNLOCK(); 1264 1265 return (error); 1266} 1267 1268/* 1269 * Vnode op for readdir 1270 */ 1271static int 1272devfs_readdir(struct vnop_readdir_args *ap) 1273 /*struct vnop_readdir_args { 1274 struct vnode *a_vp; 1275 struct uio *a_uio; 1276 int a_flags; 1277 int *a_eofflag; 1278 int *a_numdirent; 1279 vfs_context_t a_context; 1280 } */ 1281{ 1282 struct vnode *vp = ap->a_vp; 1283 struct uio *uio = ap->a_uio; 1284 struct dirent dirent; 1285 devnode_t * dir_node; 1286 devdirent_t * name_node; 1287 const char *name; 1288 int error = 0; 1289 int reclen; 1290 int nodenumber; 1291 int startpos,pos; 1292 1293 if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF)) 1294 return (EINVAL); 1295 1296 /* set up refs to dir */ 1297 dir_node = VTODN(vp); 1298 if (dir_node->dn_type != DEV_DIR) 1299 return(ENOTDIR); 1300 pos = 0; 1301 startpos = uio->uio_offset; 1302 1303 DEVFS_LOCK(); 1304 1305 name_node = dir_node->dn_typeinfo.Dir.dirlist; 1306 nodenumber = 0; 1307 1308 while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0)) 1309 { 1310 switch(nodenumber) 1311 { 1312 case 0: 1313 dirent.d_fileno = dir_node->dn_ino; 1314 name = "."; 1315 dirent.d_namlen = 1; 1316 dirent.d_type = DT_DIR; 1317 break; 1318 case 1: 1319 if(dir_node->dn_typeinfo.Dir.parent) 1320 dirent.d_fileno = dir_node->dn_typeinfo.Dir.parent->dn_ino; 1321 else 1322 dirent.d_fileno = dir_node->dn_ino; 1323 name = ".."; 1324 dirent.d_namlen = 2; 1325 dirent.d_type = DT_DIR; 1326 break; 1327 default: 1328 dirent.d_fileno = name_node->de_dnp->dn_ino; 1329 dirent.d_namlen = strlen(name_node->de_name); 1330 name = name_node->de_name; 1331 switch(name_node->de_dnp->dn_type) { 1332 case DEV_BDEV: 1333 dirent.d_type = DT_BLK; 1334 break; 1335 case DEV_CDEV: 1336 dirent.d_type = DT_CHR; 1337 break; 1338 case DEV_DIR: 1339 dirent.d_type = DT_DIR; 1340 break; 1341 case DEV_SLNK: 1342 dirent.d_type = DT_LNK; 1343 break; 1344 default: 1345 dirent.d_type = DT_UNKNOWN; 1346 } 1347 } 1348#define GENERIC_DIRSIZ(dp) \ 1349 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)) 1350 1351 reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent); 1352 1353 if(pos >= startpos) /* made it to the offset yet? */ 1354 { 1355 if (uio_resid(uio) < reclen) /* will it fit? */ 1356 break; 1357 strlcpy(dirent.d_name, name, DEVMAXNAMESIZE); 1358 if ((error = uiomove ((caddr_t)&dirent, 1359 dirent.d_reclen, uio)) != 0) 1360 break; 1361 } 1362 pos += reclen; 1363 if((nodenumber >1) && name_node) 1364 name_node = name_node->de_next; 1365 nodenumber++; 1366 } 1367 DEVFS_UNLOCK(); 1368 uio->uio_offset = pos; 1369 1370 devfs_consider_time_update(dir_node, DEVFS_UPDATE_ACCESS); 1371 1372 return (error); 1373} 1374 1375 1376/* 1377 */ 1378static int 1379devfs_readlink(struct vnop_readlink_args *ap) 1380 /*struct vnop_readlink_args { 1381 struct vnode *a_vp; 1382 struct uio *a_uio; 1383 vfs_context_t a_context; 1384 } */ 1385{ 1386 struct vnode *vp = ap->a_vp; 1387 struct uio *uio = ap->a_uio; 1388 devnode_t * lnk_node; 1389 int error = 0; 1390 1391 /* set up refs to dir */ 1392 lnk_node = VTODN(vp); 1393 1394 if (lnk_node->dn_type != DEV_SLNK) { 1395 error = EINVAL; 1396 goto out; 1397 } 1398 error = uiomove(lnk_node->dn_typeinfo.Slnk.name, 1399 lnk_node->dn_typeinfo.Slnk.namelen, uio); 1400out: 1401 return error; 1402} 1403 1404static int 1405devfs_reclaim(struct vnop_reclaim_args *ap) 1406 /*struct vnop_reclaim_args { 1407 struct vnode *a_vp; 1408 } */ 1409{ 1410 struct vnode * vp = ap->a_vp; 1411 devnode_t * dnp; 1412 1413 DEVFS_LOCK(); 1414 1415 dnp = VTODN(vp); 1416 1417 if (dnp) { 1418 /* If this is a cloning device, it didn't have a dn_vn anyway */ 1419 dnp->dn_vn = NULL; 1420 vnode_clearfsnode(vp); 1421 1422 /* This could delete the node, if we are the last vnode */ 1423 devfs_rele_node(dnp); 1424 } 1425 DEVFS_UNLOCK(); 1426 1427 return(0); 1428} 1429 1430 1431/* 1432 * Get configurable pathname variables. 1433 */ 1434static int 1435devs_vnop_pathconf( 1436 struct vnop_pathconf_args /* { 1437 struct vnode *a_vp; 1438 int a_name; 1439 int *a_retval; 1440 vfs_context_t a_context; 1441 } */ *ap) 1442{ 1443 switch (ap->a_name) { 1444 case _PC_LINK_MAX: 1445 /* arbitrary limit matching HFS; devfs has no hard limit */ 1446 *ap->a_retval = 32767; 1447 break; 1448 case _PC_NAME_MAX: 1449 *ap->a_retval = DEVMAXNAMESIZE - 1; /* includes NUL */ 1450 break; 1451 case _PC_PATH_MAX: 1452 *ap->a_retval = DEVMAXPATHSIZE - 1; /* XXX nonconformant */ 1453 break; 1454 case _PC_CHOWN_RESTRICTED: 1455 *ap->a_retval = 200112; /* _POSIX_CHOWN_RESTRICTED */ 1456 break; 1457 case _PC_NO_TRUNC: 1458 *ap->a_retval = 0; 1459 break; 1460 case _PC_CASE_SENSITIVE: 1461 *ap->a_retval = 1; 1462 break; 1463 case _PC_CASE_PRESERVING: 1464 *ap->a_retval = 1; 1465 break; 1466 default: 1467 return (EINVAL); 1468 } 1469 1470 return (0); 1471} 1472 1473 1474 1475/**************************************************************************\ 1476* pseudo ops * 1477\**************************************************************************/ 1478 1479/* 1480 * 1481 * struct vnop_inactive_args { 1482 * struct vnode *a_vp; 1483 * vfs_context_t a_context; 1484 * } 1485 */ 1486 1487static int 1488devfs_inactive(__unused struct vnop_inactive_args *ap) 1489{ 1490 vnode_t vp = ap->a_vp; 1491 devnode_t *dnp = VTODN(vp); 1492 1493 /* 1494 * Cloned vnodes are not linked in anywhere, so they 1495 * can just be recycled. 1496 */ 1497 if (dnp->dn_clone != NULL) { 1498 vnode_recycle(vp); 1499 } 1500 1501 return (0); 1502} 1503 1504/* 1505 * called with DEVFS_LOCK held 1506 */ 1507static int 1508devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify) 1509{ 1510 devnode_t * ip; 1511 struct timeval now; 1512 1513 ip = VTODN(vp); 1514 if (vp->v_mount->mnt_flag & MNT_RDONLY) { 1515 ip->dn_access = 0; 1516 ip->dn_change = 0; 1517 ip->dn_update = 0; 1518 1519 return (0); 1520 } 1521 1522 DEVFS_ATTR_LOCK_SPIN(); 1523 microtime(&now); 1524 dn_times_locked(ip, access, modify, &now, DEVFS_UPDATE_ACCESS | DEVFS_UPDATE_MOD); 1525 DEVFS_ATTR_UNLOCK(); 1526 1527 return (0); 1528} 1529 1530#define VOPFUNC int (*)(void *) 1531 1532/* The following ops are used by directories and symlinks */ 1533int (**devfs_vnodeop_p)(void *); 1534static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { 1535 { &vnop_default_desc, (VOPFUNC)vn_default_error }, 1536 { &vnop_lookup_desc, (VOPFUNC)devfs_lookup }, /* lookup */ 1537 { &vnop_create_desc, (VOPFUNC)err_create }, /* create */ 1538 { &vnop_whiteout_desc, (VOPFUNC)err_whiteout }, /* whiteout */ 1539 { &vnop_mknod_desc, (VOPFUNC)devfs_mknod }, /* mknod */ 1540 { &vnop_open_desc, (VOPFUNC)nop_open }, /* open */ 1541 { &vnop_close_desc, (VOPFUNC)devfs_close }, /* close */ 1542 { &vnop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */ 1543 { &vnop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */ 1544 { &vnop_read_desc, (VOPFUNC)devfs_read }, /* read */ 1545 { &vnop_write_desc, (VOPFUNC)devfs_write }, /* write */ 1546 { &vnop_ioctl_desc, (VOPFUNC)err_ioctl }, /* ioctl */ 1547 { &vnop_select_desc, (VOPFUNC)err_select }, /* select */ 1548 { &vnop_revoke_desc, (VOPFUNC)err_revoke }, /* revoke */ 1549 { &vnop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */ 1550 { &vnop_fsync_desc, (VOPFUNC)nop_fsync }, /* fsync */ 1551 { &vnop_remove_desc, (VOPFUNC)devfs_vnop_remove }, /* remove */ 1552 { &vnop_link_desc, (VOPFUNC)devfs_link }, /* link */ 1553 { &vnop_rename_desc, (VOPFUNC)devfs_rename }, /* rename */ 1554 { &vnop_mkdir_desc, (VOPFUNC)devfs_mkdir }, /* mkdir */ 1555 { &vnop_rmdir_desc, (VOPFUNC)devfs_rmdir }, /* rmdir */ 1556 { &vnop_symlink_desc, (VOPFUNC)devfs_symlink }, /* symlink */ 1557 { &vnop_readdir_desc, (VOPFUNC)devfs_readdir }, /* readdir */ 1558 { &vnop_readlink_desc, (VOPFUNC)devfs_readlink }, /* readlink */ 1559 { &vnop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */ 1560 { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */ 1561 { &vnop_strategy_desc, (VOPFUNC)err_strategy }, /* strategy */ 1562 { &vnop_pathconf_desc, (VOPFUNC)devs_vnop_pathconf }, /* pathconf */ 1563 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */ 1564 { &vnop_bwrite_desc, (VOPFUNC)err_bwrite }, 1565 { &vnop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */ 1566 { &vnop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */ 1567 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */ 1568 { &vnop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff */ 1569 { &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */ 1570 { &vnop_blockmap_desc, (VOPFUNC)err_blockmap }, /* blockmap */ 1571#if CONFIG_MACF 1572 { &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel }, /* setlabel */ 1573#endif 1574 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 1575}; 1576struct vnodeopv_desc devfs_vnodeop_opv_desc = 1577 { &devfs_vnodeop_p, devfs_vnodeop_entries }; 1578 1579/* The following ops are used by the device nodes */ 1580int (**devfs_spec_vnodeop_p)(void *); 1581static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = { 1582 { &vnop_default_desc, (VOPFUNC)vn_default_error }, 1583 { &vnop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */ 1584 { &vnop_create_desc, (VOPFUNC)spec_create }, /* create */ 1585 { &vnop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */ 1586 { &vnop_open_desc, (VOPFUNC)spec_open }, /* open */ 1587 { &vnop_close_desc, (VOPFUNC)devfsspec_close }, /* close */ 1588 { &vnop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */ 1589 { &vnop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */ 1590 { &vnop_read_desc, (VOPFUNC)devfsspec_read }, /* read */ 1591 { &vnop_write_desc, (VOPFUNC)devfsspec_write }, /* write */ 1592 { &vnop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */ 1593 { &vnop_select_desc, (VOPFUNC)spec_select }, /* select */ 1594 { &vnop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */ 1595 { &vnop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */ 1596 { &vnop_fsync_desc, (VOPFUNC)spec_fsync }, /* fsync */ 1597 { &vnop_remove_desc, (VOPFUNC)devfs_vnop_remove }, /* remove */ 1598 { &vnop_link_desc, (VOPFUNC)devfs_link }, /* link */ 1599 { &vnop_rename_desc, (VOPFUNC)spec_rename }, /* rename */ 1600 { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */ 1601 { &vnop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */ 1602 { &vnop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */ 1603 { &vnop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */ 1604 { &vnop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */ 1605 { &vnop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */ 1606 { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */ 1607 { &vnop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */ 1608 { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */ 1609 { &vnop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */ 1610 { &vnop_bwrite_desc, (VOPFUNC)vn_bwrite }, 1611 { &vnop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */ 1612 { &vnop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */ 1613 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */ 1614 { &vnop_blktooff_desc, (VOPFUNC)spec_blktooff }, /* blktooff */ 1615 { &vnop_blktooff_desc, (VOPFUNC)spec_offtoblk }, /* blkofftoblk */ 1616 { &vnop_blockmap_desc, (VOPFUNC)spec_blockmap }, /* blockmap */ 1617#if CONFIG_MACF 1618 { &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel }, /* setlabel */ 1619#endif 1620 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 1621}; 1622struct vnodeopv_desc devfs_spec_vnodeop_opv_desc = 1623 { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries }; 1624 1625 1626#if FDESC 1627int (**devfs_devfd_vnodeop_p)(void*); 1628static struct vnodeopv_entry_desc devfs_devfd_vnodeop_entries[] = { 1629 { &vnop_default_desc, (VOPFUNC)vn_default_error }, 1630 { &vnop_lookup_desc, (VOPFUNC)devfs_devfd_lookup}, /* lookup */ 1631 { &vnop_open_desc, (VOPFUNC)nop_open }, /* open */ 1632 { &vnop_close_desc, (VOPFUNC)devfs_close }, /* close */ 1633 { &vnop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */ 1634 { &vnop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */ 1635 { &vnop_revoke_desc, (VOPFUNC)err_revoke }, /* revoke */ 1636 { &vnop_fsync_desc, (VOPFUNC)nop_fsync }, /* fsync */ 1637 { &vnop_readdir_desc, (VOPFUNC)devfs_devfd_readdir}, /* readdir */ 1638 { &vnop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */ 1639 { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */ 1640 { &vnop_pathconf_desc, (VOPFUNC)devs_vnop_pathconf }, /* pathconf */ 1641#if CONFIG_MACF 1642 { &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel }, /* setlabel */ 1643#endif 1644 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 1645}; 1646struct vnodeopv_desc devfs_devfd_vnodeop_opv_desc = 1647 { &devfs_devfd_vnodeop_p, devfs_devfd_vnodeop_entries}; 1648#endif /* FDESC */ 1649 1650 1651