1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/errno.h> 31 #include <sys/proc.h> 32 #include <sys/vnode.h> 33 #include <sys/vfs.h> 34 #include <sys/vfs_opreg.h> 35 #include <sys/uio.h> 36 #include <sys/cred.h> 37 #include <sys/pathname.h> 38 #include <sys/dirent.h> 39 #include <sys/debug.h> 40 #include <sys/sysmacros.h> 41 #include <sys/tiuser.h> 42 #include <sys/cmn_err.h> 43 #include <sys/stat.h> 44 #include <sys/mode.h> 45 #include <sys/policy.h> 46 #include <rpc/types.h> 47 #include <rpc/auth.h> 48 #include <rpc/clnt.h> 49 #include <sys/fs/autofs.h> 50 #include <rpcsvc/autofs_prot.h> 51 #include <fs/fs_subr.h> 52 53 /* 54 * Vnode ops for autofs 55 */ 56 static int auto_open(vnode_t **, int, cred_t *); 57 static int auto_close(vnode_t *, int, int, offset_t, cred_t *); 58 static int auto_getattr(vnode_t *, vattr_t *, int, cred_t *); 59 static int auto_setattr(vnode_t *, vattr_t *, int, cred_t *, 60 caller_context_t *); 61 static int auto_access(vnode_t *, int, int, cred_t *); 62 static int auto_lookup(vnode_t *, char *, vnode_t **, 63 pathname_t *, int, vnode_t *, cred_t *); 64 static int auto_create(vnode_t *, char *, vattr_t *, vcexcl_t, 65 int, vnode_t **, cred_t *, int); 66 static int auto_remove(vnode_t *, char *, cred_t *); 67 static int auto_link(vnode_t *, vnode_t *, char *, cred_t *); 68 static int auto_rename(vnode_t *, char *, vnode_t *, char *, cred_t *); 69 static int auto_mkdir(vnode_t *, char *, vattr_t *, vnode_t **, cred_t *); 70 static int auto_rmdir(vnode_t *, char *, vnode_t *, cred_t *); 71 static int auto_readdir(vnode_t *, uio_t *, cred_t *, int *); 72 static int auto_symlink(vnode_t *, char *, vattr_t *, char *, cred_t *); 73 static int auto_readlink(vnode_t *, struct uio *, cred_t *); 74 static int auto_fsync(vnode_t *, int, cred_t *); 75 static void auto_inactive(vnode_t *, cred_t *); 76 static int auto_rwlock(vnode_t *, int, caller_context_t *); 77 static void auto_rwunlock(vnode_t *vp, int, caller_context_t *); 78 static int auto_seek(vnode_t *vp, offset_t, offset_t *); 79 80 static int auto_trigger_mount(vnode_t *, cred_t *, vnode_t **); 81 82 vnodeops_t *auto_vnodeops; 83 84 const fs_operation_def_t auto_vnodeops_template[] = { 85 VOPNAME_OPEN, { .vop_open = auto_open }, 86 VOPNAME_CLOSE, { .vop_close = auto_close }, 87 VOPNAME_GETATTR, { .vop_getattr = auto_getattr }, 88 VOPNAME_SETATTR, { .vop_setattr = auto_setattr }, 89 VOPNAME_ACCESS, { .vop_access = auto_access }, 90 VOPNAME_LOOKUP, { .vop_lookup = auto_lookup }, 91 VOPNAME_CREATE, { .vop_create = auto_create }, 92 VOPNAME_REMOVE, { .vop_remove = auto_remove }, 93 VOPNAME_LINK, { .vop_link = auto_link }, 94 VOPNAME_RENAME, { .vop_rename = auto_rename }, 95 VOPNAME_MKDIR, { .vop_mkdir = auto_mkdir }, 96 VOPNAME_RMDIR, { .vop_rmdir = auto_rmdir }, 97 VOPNAME_READDIR, { .vop_readdir = auto_readdir }, 98 VOPNAME_SYMLINK, { .vop_symlink = auto_symlink }, 99 VOPNAME_READLINK, { .vop_readlink = auto_readlink }, 100 VOPNAME_FSYNC, { .vop_fsync = auto_fsync }, 101 VOPNAME_INACTIVE, { .vop_inactive = auto_inactive }, 102 VOPNAME_RWLOCK, { .vop_rwlock = auto_rwlock }, 103 VOPNAME_RWUNLOCK, { .vop_rwunlock = auto_rwunlock }, 104 VOPNAME_SEEK, { .vop_seek = auto_seek }, 105 VOPNAME_FRLOCK, { .error = fs_error }, 106 VOPNAME_DISPOSE, { .error = fs_error }, 107 VOPNAME_SHRLOCK, { .error = fs_error }, 108 VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support }, 109 NULL, NULL 110 }; 111 112 113 114 /* ARGSUSED */ 115 static int 116 auto_open(vnode_t **vpp, int flag, cred_t *cred) 117 { 118 vnode_t *newvp; 119 int error; 120 121 AUTOFS_DPRINT((4, "auto_open: *vpp=%p\n", (void *)*vpp)); 122 123 error = auto_trigger_mount(*vpp, cred, &newvp); 124 if (error) 125 goto done; 126 127 if (newvp != NULL) { 128 /* 129 * Node is now mounted on. 130 */ 131 VN_RELE(*vpp); 132 *vpp = newvp; 133 error = VOP_ACCESS(*vpp, VREAD, 0, cred); 134 if (!error) 135 error = VOP_OPEN(vpp, flag, cred); 136 } 137 138 done: 139 AUTOFS_DPRINT((5, "auto_open: *vpp=%p error=%d\n", (void *)*vpp, 140 error)); 141 return (error); 142 } 143 144 /* ARGSUSED */ 145 static int 146 auto_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cred) 147 { 148 return (0); 149 } 150 151 static int 152 auto_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cred) 153 { 154 fnnode_t *fnp = vntofn(vp); 155 vnode_t *newvp; 156 vfs_t *vfsp; 157 int error; 158 159 AUTOFS_DPRINT((4, "auto_getattr vp %p\n", (void *)vp)); 160 161 /* 162 * Recursive auto_getattr/mount; go to the vfsp == NULL 163 * case. 164 */ 165 if (vn_vfswlock_held(vp)) 166 goto defattr; 167 168 if (error = vn_vfsrlock_wait(vp)) 169 return (error); 170 171 vfsp = vn_mountedvfs(vp); 172 if (vfsp != NULL) { 173 /* 174 * Node is mounted on. 175 */ 176 error = VFS_ROOT(vfsp, &newvp); 177 vn_vfsunlock(vp); 178 if (error) 179 return (error); 180 mutex_enter(&fnp->fn_lock); 181 if (fnp->fn_seen == newvp && fnp->fn_thread == curthread) { 182 /* 183 * Recursive auto_getattr(); just release newvp and drop 184 * into the vfsp == NULL case. 185 */ 186 mutex_exit(&fnp->fn_lock); 187 VN_RELE(newvp); 188 } else { 189 while (fnp->fn_thread && fnp->fn_thread != curthread) { 190 fnp->fn_flags |= MF_ATTR_WAIT; 191 cv_wait(&fnp->fn_cv_mount, &fnp->fn_lock); 192 } 193 fnp->fn_thread = curthread; 194 fnp->fn_seen = newvp; 195 mutex_exit(&fnp->fn_lock); 196 error = VOP_GETATTR(newvp, vap, flags, cred); 197 VN_RELE(newvp); 198 mutex_enter(&fnp->fn_lock); 199 fnp->fn_seen = 0; 200 fnp->fn_thread = 0; 201 if (fnp->fn_flags & MF_ATTR_WAIT) { 202 fnp->fn_flags &= ~MF_ATTR_WAIT; 203 cv_broadcast(&fnp->fn_cv_mount); 204 } 205 mutex_exit(&fnp->fn_lock); 206 return (error); 207 } 208 } else { 209 vn_vfsunlock(vp); 210 } 211 212 defattr: 213 ASSERT(vp->v_type == VDIR || vp->v_type == VLNK); 214 vap->va_uid = 0; 215 vap->va_gid = 0; 216 vap->va_nlink = fnp->fn_linkcnt; 217 vap->va_nodeid = (u_longlong_t)fnp->fn_nodeid; 218 vap->va_size = fnp->fn_size; 219 vap->va_atime = fnp->fn_atime; 220 vap->va_mtime = fnp->fn_mtime; 221 vap->va_ctime = fnp->fn_ctime; 222 vap->va_type = vp->v_type; 223 vap->va_mode = fnp->fn_mode; 224 vap->va_fsid = vp->v_vfsp->vfs_dev; 225 vap->va_rdev = 0; 226 vap->va_blksize = MAXBSIZE; 227 vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size); 228 vap->va_seq = 0; 229 230 return (0); 231 } 232 233 /*ARGSUSED4*/ 234 static int 235 auto_setattr( 236 vnode_t *vp, 237 struct vattr *vap, 238 int flags, 239 cred_t *cred, 240 caller_context_t *ct) 241 { 242 vnode_t *newvp; 243 int error; 244 245 AUTOFS_DPRINT((4, "auto_setattr vp %p\n", (void *)vp)); 246 247 if (error = auto_trigger_mount(vp, cred, &newvp)) 248 goto done; 249 250 if (newvp != NULL) { 251 /* 252 * Node is mounted on. 253 */ 254 if (vn_is_readonly(newvp)) 255 error = EROFS; 256 else 257 error = VOP_SETATTR(newvp, vap, flags, cred, NULL); 258 VN_RELE(newvp); 259 } else 260 error = ENOSYS; 261 262 done: 263 AUTOFS_DPRINT((5, "auto_setattr: error=%d\n", error)); 264 return (error); 265 } 266 267 /* ARGSUSED */ 268 static int 269 auto_access(vnode_t *vp, int mode, int flags, cred_t *cred) 270 { 271 fnnode_t *fnp = vntofn(vp); 272 vnode_t *newvp; 273 int error; 274 275 AUTOFS_DPRINT((4, "auto_access: vp=%p\n", (void *)vp)); 276 277 if (error = auto_trigger_mount(vp, cred, &newvp)) 278 goto done; 279 280 if (newvp != NULL) { 281 /* 282 * Node is mounted on. 283 */ 284 error = VOP_ACCESS(newvp, mode, 0, cred); 285 VN_RELE(newvp); 286 } else { 287 int shift = 0; 288 289 /* 290 * really interested in the autofs node, check the 291 * access on it 292 */ 293 ASSERT(error == 0); 294 if (crgetuid(cred) != fnp->fn_uid) { 295 shift += 3; 296 if (groupmember(fnp->fn_gid, cred) == 0) 297 shift += 3; 298 } 299 mode &= ~(fnp->fn_mode << shift); 300 if (mode != 0) 301 error = secpolicy_vnode_access(cred, vp, fnp->fn_uid, 302 mode); 303 } 304 305 done: 306 AUTOFS_DPRINT((5, "auto_access: error=%d\n", error)); 307 return (error); 308 } 309 310 static int 311 auto_lookup( 312 vnode_t *dvp, 313 char *nm, 314 vnode_t **vpp, 315 pathname_t *pnp, 316 int flags, 317 vnode_t *rdir, 318 cred_t *cred) 319 { 320 int error = 0; 321 vnode_t *newvp = NULL; 322 vfs_t *vfsp; 323 fninfo_t *dfnip; 324 fnnode_t *dfnp = NULL; 325 fnnode_t *fnp = NULL; 326 char *searchnm; 327 int operation; /* either AUTOFS_LOOKUP or AUTOFS_MOUNT */ 328 329 dfnip = vfstofni(dvp->v_vfsp); 330 AUTOFS_DPRINT((3, "auto_lookup: dvp=%p (%s) name=%s\n", 331 (void *)dvp, dfnip->fi_map, nm)); 332 333 if (nm[0] == 0) { 334 VN_HOLD(dvp); 335 *vpp = dvp; 336 return (0); 337 } 338 339 if (error = VOP_ACCESS(dvp, VEXEC, 0, cred)) 340 return (error); 341 342 if (nm[0] == '.' && nm[1] == 0) { 343 VN_HOLD(dvp); 344 *vpp = dvp; 345 return (0); 346 } 347 348 if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) { 349 fnnode_t *pdfnp; 350 351 pdfnp = (vntofn(dvp))->fn_parent; 352 ASSERT(pdfnp != NULL); 353 354 /* 355 * Since it is legitimate to have the VROOT flag set for the 356 * subdirectories of the indirect map in autofs filesystem, 357 * rootfnnodep is checked against fnnode of dvp instead of 358 * just checking whether VROOT flag is set in dvp 359 */ 360 361 if (pdfnp == pdfnp->fn_globals->fng_rootfnnodep) { 362 vnode_t *vp; 363 364 vfs_rlock_wait(dvp->v_vfsp); 365 if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) { 366 vfs_unlock(dvp->v_vfsp); 367 return (EIO); 368 } 369 vp = dvp->v_vfsp->vfs_vnodecovered; 370 VN_HOLD(vp); 371 vfs_unlock(dvp->v_vfsp); 372 error = VOP_LOOKUP(vp, nm, vpp, pnp, flags, rdir, cred); 373 VN_RELE(vp); 374 return (error); 375 } else { 376 *vpp = fntovn(pdfnp); 377 VN_HOLD(*vpp); 378 return (0); 379 } 380 } 381 382 top: 383 dfnp = vntofn(dvp); 384 searchnm = nm; 385 operation = 0; 386 387 ASSERT(vn_matchops(dvp, auto_vnodeops)); 388 389 AUTOFS_DPRINT((3, "auto_lookup: dvp=%p dfnp=%p\n", (void *)dvp, 390 (void *)dfnp)); 391 392 /* 393 * If a lookup or mount of this node is in progress, wait for it 394 * to finish, and return whatever result it got. 395 */ 396 mutex_enter(&dfnp->fn_lock); 397 if (dfnp->fn_flags & (MF_LOOKUP | MF_INPROG)) { 398 mutex_exit(&dfnp->fn_lock); 399 error = auto_wait4mount(dfnp); 400 if (error == AUTOFS_SHUTDOWN) 401 error = ENOENT; 402 if (error == EAGAIN) 403 goto top; 404 if (error) 405 return (error); 406 } else 407 mutex_exit(&dfnp->fn_lock); 408 409 410 error = vn_vfsrlock_wait(dvp); 411 if (error) 412 return (error); 413 vfsp = vn_mountedvfs(dvp); 414 if (vfsp != NULL) { 415 error = VFS_ROOT(vfsp, &newvp); 416 vn_vfsunlock(dvp); 417 if (!error) { 418 error = VOP_LOOKUP(newvp, nm, vpp, pnp, 419 flags, rdir, cred); 420 VN_RELE(newvp); 421 } 422 return (error); 423 } 424 vn_vfsunlock(dvp); 425 426 rw_enter(&dfnp->fn_rwlock, RW_READER); 427 error = auto_search(dfnp, nm, &fnp, cred); 428 if (error) { 429 if (dfnip->fi_flags & MF_DIRECT) { 430 /* 431 * direct map. 432 */ 433 if (dfnp->fn_dirents) { 434 /* 435 * Mount previously triggered. 436 * 'nm' not found 437 */ 438 error = ENOENT; 439 } else { 440 /* 441 * I need to contact the daemon to trigger 442 * the mount. 'dfnp' will be the mountpoint. 443 */ 444 operation = AUTOFS_MOUNT; 445 VN_HOLD(fntovn(dfnp)); 446 fnp = dfnp; 447 error = 0; 448 } 449 } else if (dvp == dfnip->fi_rootvp) { 450 /* 451 * 'dfnp' is the root of the indirect AUTOFS. 452 */ 453 if (rw_tryupgrade(&dfnp->fn_rwlock) == 0) { 454 /* 455 * Could not acquire writer lock, release 456 * reader, and wait until available. We 457 * need to search for 'nm' again, since we 458 * had to release the lock before reacquiring 459 * it. 460 */ 461 rw_exit(&dfnp->fn_rwlock); 462 rw_enter(&dfnp->fn_rwlock, RW_WRITER); 463 error = auto_search(dfnp, nm, &fnp, cred); 464 } 465 466 ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock)); 467 if (error) { 468 /* 469 * create node being looked-up and request 470 * mount on it. 471 */ 472 error = auto_enter(dfnp, nm, &fnp, kcred); 473 if (!error) 474 operation = AUTOFS_LOOKUP; 475 } 476 } else if ((dfnp->fn_dirents == NULL) && 477 ((dvp->v_flag & VROOT) == 0) && 478 ((fntovn(dfnp->fn_parent))->v_flag & VROOT)) { 479 /* 480 * dfnp is the actual 'mountpoint' of indirect map, 481 * it is the equivalent of a direct mount, 482 * ie, /home/'user1' 483 */ 484 operation = AUTOFS_MOUNT; 485 VN_HOLD(fntovn(dfnp)); 486 fnp = dfnp; 487 error = 0; 488 searchnm = dfnp->fn_name; 489 } 490 } 491 492 if (error == EAGAIN) { 493 rw_exit(&dfnp->fn_rwlock); 494 goto top; 495 } 496 if (error) { 497 rw_exit(&dfnp->fn_rwlock); 498 return (error); 499 } 500 501 /* 502 * We now have the actual fnnode we're interested in. 503 * The 'MF_LOOKUP' indicates another thread is currently 504 * performing a daemon lookup of this node, therefore we 505 * wait for its completion. 506 * The 'MF_INPROG' indicates another thread is currently 507 * performing a daemon mount of this node, we wait for it 508 * to be done if we are performing a MOUNT. We don't 509 * wait for it if we are performing a LOOKUP. 510 * We can release the reader/writer lock as soon as we acquire 511 * the mutex, since the state of the lock can only change by 512 * first acquiring the mutex. 513 */ 514 mutex_enter(&fnp->fn_lock); 515 rw_exit(&dfnp->fn_rwlock); 516 if ((fnp->fn_flags & MF_LOOKUP) || 517 ((operation == AUTOFS_MOUNT) && (fnp->fn_flags & MF_INPROG))) { 518 mutex_exit(&fnp->fn_lock); 519 error = auto_wait4mount(fnp); 520 VN_RELE(fntovn(fnp)); 521 if (error == AUTOFS_SHUTDOWN) 522 error = ENOENT; 523 if (error && error != EAGAIN) 524 return (error); 525 goto top; 526 } 527 528 if (operation == 0) { 529 /* 530 * got the fnnode, check for any errors 531 * on the previous operation on that node. 532 */ 533 error = fnp->fn_error; 534 if ((error == EINTR) || (error == EAGAIN)) { 535 /* 536 * previous operation on this node was 537 * not completed, do a lookup now. 538 */ 539 operation = AUTOFS_LOOKUP; 540 } else { 541 /* 542 * previous operation completed. Return 543 * a pointer to the node only if there was 544 * no error. 545 */ 546 mutex_exit(&fnp->fn_lock); 547 if (!error) 548 *vpp = fntovn(fnp); 549 else 550 VN_RELE(fntovn(fnp)); 551 return (error); 552 } 553 } 554 555 /* 556 * Since I got to this point, it means I'm the one 557 * responsible for triggering the mount/look-up of this node. 558 */ 559 switch (operation) { 560 case AUTOFS_LOOKUP: 561 AUTOFS_BLOCK_OTHERS(fnp, MF_LOOKUP); 562 fnp->fn_error = 0; 563 mutex_exit(&fnp->fn_lock); 564 error = auto_lookup_aux(fnp, searchnm, cred); 565 if (!error) { 566 /* 567 * Return this vnode 568 */ 569 *vpp = fntovn(fnp); 570 } else { 571 /* 572 * release our reference to this vnode 573 * and return error 574 */ 575 VN_RELE(fntovn(fnp)); 576 } 577 break; 578 case AUTOFS_MOUNT: 579 AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG); 580 fnp->fn_error = 0; 581 mutex_exit(&fnp->fn_lock); 582 /* 583 * auto_new_mount_thread fires up a new thread which 584 * calls automountd finishing up the work 585 */ 586 auto_new_mount_thread(fnp, searchnm, cred); 587 588 /* 589 * At this point, we are simply another thread 590 * waiting for the mount to complete 591 */ 592 error = auto_wait4mount(fnp); 593 if (error == AUTOFS_SHUTDOWN) 594 error = ENOENT; 595 596 /* 597 * now release our reference to this vnode 598 */ 599 VN_RELE(fntovn(fnp)); 600 if (!error) 601 goto top; 602 break; 603 default: 604 auto_log(dfnp->fn_globals->fng_verbose, 605 dfnp->fn_globals->fng_zoneid, CE_WARN, 606 "auto_lookup: unknown " 607 "operation %d", operation); 608 } 609 610 AUTOFS_DPRINT((5, "auto_lookup: name=%s *vpp=%p return=%d\n", 611 nm, (void *)*vpp, error)); 612 613 return (error); 614 } 615 616 static int 617 auto_create( 618 vnode_t *dvp, 619 char *nm, 620 vattr_t *va, 621 vcexcl_t excl, 622 int mode, 623 vnode_t **vpp, 624 cred_t *cred, 625 int flag) 626 { 627 vnode_t *newvp; 628 int error; 629 630 AUTOFS_DPRINT((4, "auto_create dvp %p nm %s\n", (void *)dvp, nm)); 631 632 if (error = auto_trigger_mount(dvp, cred, &newvp)) 633 goto done; 634 635 if (newvp != NULL) { 636 /* 637 * Node is now mounted on. 638 */ 639 if (vn_is_readonly(newvp)) 640 error = EROFS; 641 else 642 error = VOP_CREATE(newvp, nm, va, excl, 643 mode, vpp, cred, flag); 644 VN_RELE(newvp); 645 } else 646 error = ENOSYS; 647 648 done: 649 AUTOFS_DPRINT((5, "auto_create: error=%d\n", error)); 650 return (error); 651 } 652 653 static int 654 auto_remove(vnode_t *dvp, char *nm, cred_t *cred) 655 { 656 vnode_t *newvp; 657 int error; 658 659 AUTOFS_DPRINT((4, "auto_remove dvp %p nm %s\n", (void *)dvp, nm)); 660 661 if (error = auto_trigger_mount(dvp, cred, &newvp)) 662 goto done; 663 664 if (newvp != NULL) { 665 /* 666 * Node is now mounted on. 667 */ 668 if (vn_is_readonly(newvp)) 669 error = EROFS; 670 else 671 error = VOP_REMOVE(newvp, nm, cred); 672 VN_RELE(newvp); 673 } else 674 error = ENOSYS; 675 676 done: 677 AUTOFS_DPRINT((5, "auto_remove: error=%d\n", error)); 678 return (error); 679 } 680 681 static int 682 auto_link(vnode_t *tdvp, vnode_t *svp, char *nm, cred_t *cred) 683 { 684 vnode_t *newvp; 685 int error; 686 687 AUTOFS_DPRINT((4, "auto_link tdvp %p svp %p nm %s\n", (void *)tdvp, 688 (void *)svp, nm)); 689 690 if (error = auto_trigger_mount(tdvp, cred, &newvp)) 691 goto done; 692 693 if (newvp == NULL) { 694 /* 695 * an autonode can not be a link to another node 696 */ 697 error = ENOSYS; 698 goto done; 699 } 700 701 if (vn_is_readonly(newvp)) { 702 error = EROFS; 703 VN_RELE(newvp); 704 goto done; 705 } 706 707 if (vn_matchops(svp, auto_vnodeops)) { 708 /* 709 * source vp can't be an autonode 710 */ 711 error = ENOSYS; 712 VN_RELE(newvp); 713 goto done; 714 } 715 716 error = VOP_LINK(newvp, svp, nm, cred); 717 VN_RELE(newvp); 718 719 done: 720 AUTOFS_DPRINT((5, "auto_link error=%d\n", error)); 721 return (error); 722 } 723 724 static int 725 auto_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) 726 { 727 vnode_t *o_newvp, *n_newvp; 728 int error; 729 730 AUTOFS_DPRINT((4, "auto_rename odvp %p onm %s to ndvp %p nnm %s\n", 731 (void *)odvp, onm, (void *)ndvp, nnm)); 732 733 /* 734 * we know odvp is an autonode, otherwise this function 735 * could not have ever been called. 736 */ 737 ASSERT(vn_matchops(odvp, auto_vnodeops)); 738 739 if (error = auto_trigger_mount(odvp, cr, &o_newvp)) 740 goto done; 741 742 if (o_newvp == NULL) { 743 /* 744 * can't rename an autonode 745 */ 746 error = ENOSYS; 747 goto done; 748 } 749 750 if (vn_matchops(ndvp, auto_vnodeops)) { 751 /* 752 * directory is AUTOFS, need to trigger the 753 * mount of the real filesystem. 754 */ 755 if (error = auto_trigger_mount(ndvp, cr, &n_newvp)) { 756 VN_RELE(o_newvp); 757 goto done; 758 } 759 760 if (n_newvp == NULL) { 761 /* 762 * target can't be an autonode 763 */ 764 error = ENOSYS; 765 VN_RELE(o_newvp); 766 goto done; 767 } 768 } else { 769 /* 770 * destination directory mount had been 771 * triggered prior to the call to this function. 772 */ 773 n_newvp = ndvp; 774 } 775 776 ASSERT(!vn_matchops(n_newvp, auto_vnodeops)); 777 778 if (vn_is_readonly(n_newvp)) { 779 error = EROFS; 780 VN_RELE(o_newvp); 781 if (n_newvp != ndvp) 782 VN_RELE(n_newvp); 783 goto done; 784 } 785 786 error = VOP_RENAME(o_newvp, onm, n_newvp, nnm, cr); 787 VN_RELE(o_newvp); 788 if (n_newvp != ndvp) 789 VN_RELE(n_newvp); 790 791 done: 792 AUTOFS_DPRINT((5, "auto_rename error=%d\n", error)); 793 return (error); 794 } 795 796 static int 797 auto_mkdir(vnode_t *dvp, char *nm, vattr_t *va, vnode_t **vpp, cred_t *cred) 798 { 799 vnode_t *newvp; 800 int error; 801 802 AUTOFS_DPRINT((4, "auto_mkdir dvp %p nm %s\n", (void *)dvp, nm)); 803 804 if (error = auto_trigger_mount(dvp, cred, &newvp)) 805 goto done; 806 807 if (newvp != NULL) { 808 /* 809 * Node is now mounted on. 810 */ 811 if (vn_is_readonly(newvp)) 812 error = EROFS; 813 else 814 error = VOP_MKDIR(newvp, nm, va, vpp, cred); 815 VN_RELE(newvp); 816 } else 817 error = ENOSYS; 818 819 done: 820 AUTOFS_DPRINT((5, "auto_mkdir: error=%d\n", error)); 821 return (error); 822 } 823 824 static int 825 auto_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cred) 826 { 827 vnode_t *newvp; 828 int error; 829 830 AUTOFS_DPRINT((4, "auto_rmdir: vp=%p nm=%s\n", (void *)dvp, nm)); 831 832 if (error = auto_trigger_mount(dvp, cred, &newvp)) 833 goto done; 834 835 if (newvp != NULL) { 836 /* 837 * Node is now mounted on. 838 */ 839 if (vn_is_readonly(newvp)) 840 error = EROFS; 841 else 842 error = VOP_RMDIR(newvp, nm, cdir, cred); 843 VN_RELE(newvp); 844 } else 845 error = ENOSYS; 846 847 done: 848 AUTOFS_DPRINT((5, "auto_rmdir: error=%d\n", error)); 849 return (error); 850 } 851 852 static int autofs_nobrowse = 0; 853 854 #ifdef nextdp 855 #undef nextdp 856 #endif 857 #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen)) 858 859 static int 860 auto_readdir(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp) 861 { 862 struct autofs_rddirargs rda; 863 autofs_rddirres rd; 864 fnnode_t *fnp = vntofn(vp); 865 fnnode_t *cfnp, *nfnp; 866 dirent64_t *dp; 867 ulong_t offset; 868 ulong_t outcount = 0, count = 0; 869 size_t namelen; 870 ulong_t alloc_count; 871 void *outbuf = NULL; 872 fninfo_t *fnip = vfstofni(vp->v_vfsp); 873 struct iovec *iovp; 874 int error = 0; 875 int reached_max = 0; 876 int myeof = 0; 877 int this_reclen; 878 struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals; 879 880 AUTOFS_DPRINT((4, "auto_readdir vp=%p offset=%lld\n", 881 (void *)vp, uiop->uio_loffset)); 882 883 if (eofp != NULL) 884 *eofp = 0; 885 886 if (uiop->uio_iovcnt != 1) 887 return (EINVAL); 888 889 iovp = uiop->uio_iov; 890 alloc_count = iovp->iov_len; 891 892 gethrestime(&fnp->fn_atime); 893 fnp->fn_ref_time = fnp->fn_atime.tv_sec; 894 895 dp = outbuf = kmem_zalloc(alloc_count, KM_SLEEP); 896 897 /* 898 * Held when getdents calls VOP_RWLOCK.... 899 */ 900 ASSERT(RW_READ_HELD(&fnp->fn_rwlock)); 901 if (uiop->uio_offset >= AUTOFS_DAEMONCOOKIE) { 902 again: 903 /* 904 * Do readdir of daemon contents only 905 * Drop readers lock and reacquire after reply. 906 */ 907 rw_exit(&fnp->fn_rwlock); 908 bzero(&rd, sizeof (struct autofs_rddirres)); 909 count = 0; 910 rda.rda_map = fnip->fi_map; 911 rda.rda_offset = (uint_t)uiop->uio_offset; 912 rd.rd_rddir.rddir_entries = dp; 913 rda.rda_count = rd.rd_rddir.rddir_size = (uint_t)alloc_count; 914 rda.uid = crgetuid(cred); 915 916 error = auto_calldaemon(fngp->fng_zoneid, 917 AUTOFS_READDIR, 918 xdr_autofs_rddirargs, 919 &rda, 920 xdr_autofs_rddirres, 921 (void *)&rd, 922 sizeof (autofs_rddirres), 923 TRUE); 924 925 /* 926 * reacquire previously dropped lock 927 */ 928 rw_enter(&fnp->fn_rwlock, RW_READER); 929 930 if (!error) { 931 error = rd.rd_status; 932 dp = rd.rd_rddir.rddir_entries; 933 } 934 935 if (error) { 936 if (error == AUTOFS_SHUTDOWN) { 937 /* 938 * treat as empty directory 939 */ 940 error = 0; 941 myeof = 1; 942 if (eofp) 943 *eofp = 1; 944 } 945 goto done; 946 } 947 if (rd.rd_rddir.rddir_size) { 948 dirent64_t *odp = dp; /* next in output buffer */ 949 dirent64_t *cdp = dp; /* current examined entry */ 950 951 /* 952 * Check for duplicates here 953 */ 954 do { 955 this_reclen = cdp->d_reclen; 956 if (auto_search(fnp, cdp->d_name, 957 NULL, cred)) { 958 /* 959 * entry not found in kernel list, 960 * include it in readdir output. 961 * 962 * If we are skipping entries. then 963 * we need to copy this entry to the 964 * correct position in the buffer 965 * to be copied out. 966 */ 967 if (cdp != odp) 968 bcopy(cdp, odp, 969 (size_t)this_reclen); 970 odp = nextdp(odp); 971 outcount += this_reclen; 972 } else { 973 /* 974 * Entry was found in the kernel 975 * list. If it is the first entry 976 * in this buffer, then just skip it 977 */ 978 if (odp == dp) { 979 dp = nextdp(dp); 980 odp = dp; 981 } 982 } 983 count += this_reclen; 984 cdp = (struct dirent64 *) 985 ((char *)cdp + this_reclen); 986 } while (count < rd.rd_rddir.rddir_size); 987 988 if (outcount) 989 error = uiomove(dp, outcount, UIO_READ, uiop); 990 uiop->uio_offset = rd.rd_rddir.rddir_offset; 991 } else { 992 if (rd.rd_rddir.rddir_eof == 0) { 993 /* 994 * alloc_count not large enough for one 995 * directory entry 996 */ 997 error = EINVAL; 998 } 999 } 1000 if (rd.rd_rddir.rddir_eof && !error) { 1001 myeof = 1; 1002 if (eofp) 1003 *eofp = 1; 1004 } 1005 if (!error && !myeof && outcount == 0) { 1006 /* 1007 * call daemon with new cookie, all previous 1008 * elements happened to be duplicates 1009 */ 1010 dp = outbuf; 1011 goto again; 1012 } 1013 goto done; 1014 } 1015 1016 if (uiop->uio_offset == 0) { 1017 /* 1018 * first time: so fudge the . and .. 1019 */ 1020 this_reclen = DIRENT64_RECLEN(1); 1021 if (alloc_count < this_reclen) { 1022 error = EINVAL; 1023 goto done; 1024 } 1025 dp->d_ino = (ino64_t)fnp->fn_nodeid; 1026 dp->d_off = (off64_t)1; 1027 dp->d_reclen = (ushort_t)this_reclen; 1028 1029 /* use strncpy(9f) to zero out uninitialized bytes */ 1030 1031 (void) strncpy(dp->d_name, ".", 1032 DIRENT64_NAMELEN(this_reclen)); 1033 outcount += dp->d_reclen; 1034 dp = nextdp(dp); 1035 1036 this_reclen = DIRENT64_RECLEN(2); 1037 if (alloc_count < outcount + this_reclen) { 1038 error = EINVAL; 1039 goto done; 1040 } 1041 dp->d_reclen = (ushort_t)this_reclen; 1042 dp->d_ino = (ino64_t)fnp->fn_parent->fn_nodeid; 1043 dp->d_off = (off64_t)2; 1044 1045 /* use strncpy(9f) to zero out uninitialized bytes */ 1046 1047 (void) strncpy(dp->d_name, "..", 1048 DIRENT64_NAMELEN(this_reclen)); 1049 outcount += dp->d_reclen; 1050 dp = nextdp(dp); 1051 } 1052 1053 offset = 2; 1054 cfnp = fnp->fn_dirents; 1055 while (cfnp != NULL) { 1056 nfnp = cfnp->fn_next; 1057 offset = cfnp->fn_offset; 1058 if ((offset >= uiop->uio_offset) && 1059 (!(cfnp->fn_flags & MF_LOOKUP))) { 1060 int reclen; 1061 1062 /* 1063 * include node only if its offset is greater or 1064 * equal to the one required and it is not in 1065 * transient state (not being looked-up) 1066 */ 1067 namelen = strlen(cfnp->fn_name); 1068 reclen = (int)DIRENT64_RECLEN(namelen); 1069 if (outcount + reclen > alloc_count) { 1070 reached_max = 1; 1071 break; 1072 } 1073 dp->d_reclen = (ushort_t)reclen; 1074 dp->d_ino = (ino64_t)cfnp->fn_nodeid; 1075 if (nfnp != NULL) { 1076 /* 1077 * get the offset of the next element 1078 */ 1079 dp->d_off = (off64_t)nfnp->fn_offset; 1080 } else { 1081 /* 1082 * This is the last element, make 1083 * offset one plus the current 1084 */ 1085 dp->d_off = (off64_t)cfnp->fn_offset + 1; 1086 } 1087 1088 /* use strncpy(9f) to zero out uninitialized bytes */ 1089 1090 (void) strncpy(dp->d_name, cfnp->fn_name, 1091 DIRENT64_NAMELEN(reclen)); 1092 outcount += dp->d_reclen; 1093 dp = nextdp(dp); 1094 } 1095 cfnp = nfnp; 1096 } 1097 1098 if (outcount) 1099 error = uiomove(outbuf, outcount, UIO_READ, uiop); 1100 if (!error) { 1101 if (reached_max) { 1102 /* 1103 * This entry did not get added to the buffer on this, 1104 * call. We need to add it on the next call therefore 1105 * set uio_offset to this entry's offset. If there 1106 * wasn't enough space for one dirent, return EINVAL. 1107 */ 1108 uiop->uio_offset = offset; 1109 if (outcount == 0) 1110 error = EINVAL; 1111 } else if (autofs_nobrowse || 1112 auto_nobrowse_option(fnip->fi_opts) || 1113 (fnip->fi_flags & MF_DIRECT) || 1114 (fnp->fn_trigger != NULL) || 1115 (((vp->v_flag & VROOT) == 0) && 1116 ((fntovn(fnp->fn_parent))->v_flag & VROOT) && 1117 (fnp->fn_dirents == NULL))) { 1118 /* 1119 * done reading directory entries 1120 */ 1121 uiop->uio_offset = offset + 1; 1122 if (eofp) 1123 *eofp = 1; 1124 } else { 1125 /* 1126 * Need to get the rest of the entries from the daemon. 1127 */ 1128 uiop->uio_offset = AUTOFS_DAEMONCOOKIE; 1129 } 1130 } 1131 1132 done: 1133 kmem_free(outbuf, alloc_count); 1134 AUTOFS_DPRINT((5, "auto_readdir vp=%p offset=%lld eof=%d\n", 1135 (void *)vp, uiop->uio_loffset, myeof)); 1136 return (error); 1137 } 1138 1139 static int 1140 auto_symlink( 1141 vnode_t *dvp, 1142 char *lnknm, /* new entry */ 1143 vattr_t *tva, 1144 char *tnm, /* existing entry */ 1145 cred_t *cred) 1146 { 1147 vnode_t *newvp; 1148 int error; 1149 1150 AUTOFS_DPRINT((4, "auto_symlink: dvp=%p lnknm=%s tnm=%s\n", 1151 (void *)dvp, lnknm, tnm)); 1152 1153 if (error = auto_trigger_mount(dvp, cred, &newvp)) 1154 goto done; 1155 1156 if (newvp != NULL) { 1157 /* 1158 * Node is mounted on. 1159 */ 1160 if (vn_is_readonly(newvp)) 1161 error = EROFS; 1162 else 1163 error = VOP_SYMLINK(newvp, lnknm, tva, tnm, cred); 1164 VN_RELE(newvp); 1165 } else 1166 error = ENOSYS; 1167 1168 done: 1169 AUTOFS_DPRINT((5, "auto_symlink: error=%d\n", error)); 1170 return (error); 1171 } 1172 1173 /* ARGSUSED */ 1174 static int 1175 auto_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) 1176 { 1177 fnnode_t *fnp = vntofn(vp); 1178 int error; 1179 timestruc_t now; 1180 1181 AUTOFS_DPRINT((4, "auto_readlink: vp=%p\n", (void *)vp)); 1182 1183 gethrestime(&now); 1184 fnp->fn_ref_time = now.tv_sec; 1185 1186 if (vp->v_type != VLNK) 1187 error = EINVAL; 1188 else { 1189 ASSERT(!(fnp->fn_flags & (MF_INPROG | MF_LOOKUP))); 1190 fnp->fn_atime = now; 1191 error = uiomove(fnp->fn_symlink, MIN(fnp->fn_symlinklen, 1192 uiop->uio_resid), UIO_READ, uiop); 1193 } 1194 1195 AUTOFS_DPRINT((5, "auto_readlink: error=%d\n", error)); 1196 return (error); 1197 } 1198 1199 /* ARGSUSED */ 1200 static int 1201 auto_fsync(vnode_t *cp, int syncflag, cred_t *cred) 1202 { 1203 return (0); 1204 } 1205 1206 /* ARGSUSED */ 1207 static void 1208 auto_inactive(vnode_t *vp, cred_t *cred) 1209 { 1210 fnnode_t *fnp = vntofn(vp); 1211 fnnode_t *dfnp = fnp->fn_parent; 1212 int count; 1213 1214 AUTOFS_DPRINT((4, "auto_inactive: vp=%p v_count=%u fn_link=%d\n", 1215 (void *)vp, vp->v_count, fnp->fn_linkcnt)); 1216 1217 /* 1218 * The rwlock should not be already held by this thread. 1219 * The assert relies on the fact that the owner field is cleared 1220 * when the lock is released. 1221 */ 1222 ASSERT(dfnp != NULL); 1223 ASSERT(rw_owner(&dfnp->fn_rwlock) != curthread); 1224 rw_enter(&dfnp->fn_rwlock, RW_WRITER); 1225 mutex_enter(&vp->v_lock); 1226 ASSERT(vp->v_count > 0); 1227 count = --vp->v_count; 1228 mutex_exit(&vp->v_lock); 1229 if (count == 0) { 1230 /* 1231 * Free only if node has no subdirectories. 1232 */ 1233 if (fnp->fn_linkcnt == 1) { 1234 auto_disconnect(dfnp, fnp); 1235 rw_exit(&dfnp->fn_rwlock); 1236 auto_freefnnode(fnp); 1237 AUTOFS_DPRINT((5, "auto_inactive: (exit) vp=%p freed\n", 1238 (void *)vp)); 1239 return; 1240 } 1241 } 1242 rw_exit(&dfnp->fn_rwlock); 1243 1244 AUTOFS_DPRINT((5, "auto_inactive: (exit) vp=%p v_count=%u fn_link=%d\n", 1245 (void *)vp, vp->v_count, fnp->fn_linkcnt)); 1246 } 1247 1248 /* ARGSUSED2 */ 1249 static int 1250 auto_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct) 1251 { 1252 fnnode_t *fnp = vntofn(vp); 1253 if (write_lock) 1254 rw_enter(&fnp->fn_rwlock, RW_WRITER); 1255 else 1256 rw_enter(&fnp->fn_rwlock, RW_READER); 1257 return (write_lock); 1258 } 1259 1260 /* ARGSUSED */ 1261 static void 1262 auto_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct) 1263 { 1264 fnnode_t *fnp = vntofn(vp); 1265 rw_exit(&fnp->fn_rwlock); 1266 } 1267 1268 1269 /* ARGSUSED */ 1270 static int 1271 auto_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) 1272 { 1273 /* 1274 * Return 0 unconditionally, since we expect 1275 * a VDIR all the time 1276 */ 1277 return (0); 1278 } 1279 1280 /* 1281 * Triggers the mount if needed. If the mount has been triggered by 1282 * another thread, it will wait for its return status, and return it. 1283 * Whether the mount is triggered by this thread, another thread, or 1284 * if the vnode was already covered, '*newvp' is a 1285 * VN_HELD vnode pointing to the root of the filesystem covering 'vp'. 1286 * If the node is not mounted on, and should not be mounted on, '*newvp' 1287 * will be NULL. 1288 * The calling routine may use '*newvp' to do the filesystem jump. 1289 */ 1290 static int 1291 auto_trigger_mount(vnode_t *vp, cred_t *cred, vnode_t **newvp) 1292 { 1293 fnnode_t *fnp = vntofn(vp); 1294 fninfo_t *fnip = vfstofni(vp->v_vfsp); 1295 vnode_t *dvp; 1296 vfs_t *vfsp; 1297 int delayed_ind; 1298 char name[AUTOFS_MAXPATHLEN]; 1299 int error; 1300 1301 AUTOFS_DPRINT((4, "auto_trigger_mount: vp=%p\n", (void *)vp)); 1302 1303 *newvp = NULL; 1304 1305 /* 1306 * Cross-zone mount triggering is disallowed. 1307 */ 1308 if (fnip->fi_zoneid != getzoneid()) 1309 return (EPERM); /* Not owner of mount */ 1310 1311 retry: 1312 error = 0; 1313 delayed_ind = 0; 1314 mutex_enter(&fnp->fn_lock); 1315 while (fnp->fn_flags & (MF_LOOKUP | MF_INPROG)) { 1316 /* 1317 * Mount or lookup in progress, 1318 * wait for it before proceeding. 1319 */ 1320 mutex_exit(&fnp->fn_lock); 1321 error = auto_wait4mount(fnp); 1322 if (error == AUTOFS_SHUTDOWN) { 1323 error = 0; 1324 goto done; 1325 } 1326 if (error && error != EAGAIN) 1327 goto done; 1328 error = 0; 1329 mutex_enter(&fnp->fn_lock); 1330 } 1331 1332 /* 1333 * If the vfslock can't be acquired for the first time. 1334 * drop the fn_lock and retry next time in blocking mode. 1335 */ 1336 if (vn_vfswlock(vp)) { 1337 /* 1338 * Lock held by another thread. 1339 * Perform blocking by dropping the 1340 * fn_lock. 1341 */ 1342 mutex_exit(&fnp->fn_lock); 1343 error = vn_vfswlock_wait(vp); 1344 if (error) 1345 goto done; 1346 /* 1347 * Because fn_lock wasn't held, the state 1348 * of the trigger node might have changed. 1349 * Need to run through the checks on trigger 1350 * node again. 1351 */ 1352 vn_vfsunlock(vp); 1353 goto retry; 1354 } 1355 1356 vfsp = vn_mountedvfs(vp); 1357 if (vfsp != NULL) { 1358 mutex_exit(&fnp->fn_lock); 1359 error = VFS_ROOT(vfsp, newvp); 1360 vn_vfsunlock(vp); 1361 goto done; 1362 } else { 1363 vn_vfsunlock(vp); 1364 if ((fnp->fn_flags & MF_MOUNTPOINT) && 1365 fnp->fn_trigger != NULL) { 1366 ASSERT(fnp->fn_dirents == NULL); 1367 /* 1368 * The filesystem that used to sit here has been 1369 * forcibly unmounted. 1370 */ 1371 mutex_exit(&fnp->fn_lock); 1372 error = EIO; 1373 goto done; 1374 } 1375 } 1376 1377 ASSERT(vp->v_type == VDIR); 1378 dvp = fntovn(fnp->fn_parent); 1379 1380 if ((fnp->fn_dirents == NULL) && 1381 ((fnip->fi_flags & MF_DIRECT) == 0) && 1382 ((vp->v_flag & VROOT) == 0) && 1383 (dvp->v_flag & VROOT)) { 1384 /* 1385 * If the parent of this node is the root of an indirect 1386 * AUTOFS filesystem, this node is remountable. 1387 */ 1388 delayed_ind = 1; 1389 } 1390 1391 if (delayed_ind || 1392 ((fnip->fi_flags & MF_DIRECT) && (fnp->fn_dirents == NULL))) { 1393 /* 1394 * Trigger mount since: 1395 * direct mountpoint with no subdirs or 1396 * delayed indirect. 1397 */ 1398 AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG); 1399 fnp->fn_error = 0; 1400 mutex_exit(&fnp->fn_lock); 1401 if (delayed_ind) 1402 (void) strcpy(name, fnp->fn_name); 1403 else 1404 (void) strcpy(name, "."); 1405 fnp->fn_ref_time = gethrestime_sec(); 1406 auto_new_mount_thread(fnp, name, cred); 1407 /* 1408 * At this point we're simply another thread waiting 1409 * for the mount to finish. 1410 */ 1411 error = auto_wait4mount(fnp); 1412 if (error == EAGAIN) 1413 goto retry; 1414 if (error == AUTOFS_SHUTDOWN) { 1415 error = 0; 1416 goto done; 1417 } 1418 if (error == 0) { 1419 if (error = vn_vfsrlock_wait(vp)) 1420 goto done; 1421 /* Reacquire after dropping locks */ 1422 vfsp = vn_mountedvfs(vp); 1423 if (vfsp != NULL) { 1424 error = VFS_ROOT(vfsp, newvp); 1425 vn_vfsunlock(vp); 1426 } else { 1427 vn_vfsunlock(vp); 1428 goto retry; 1429 } 1430 } 1431 } else 1432 mutex_exit(&fnp->fn_lock); 1433 1434 done: 1435 AUTOFS_DPRINT((5, "auto_trigger_mount: error=%d\n", error)); 1436 return (error); 1437 } 1438