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