1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 39 * $Id: vfs_syscalls.c,v 1.34 1995/10/08 00:06:10 swallace Exp $ 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/namei.h> 45 #include <sys/filedesc.h> 46 #include <sys/kernel.h> 47 #include <sys/file.h> 48 #include <sys/stat.h> 49 #include <sys/vnode.h> 50 #include <sys/mount.h> 51 #include <sys/proc.h> 52 #include <sys/uio.h> 53 #include <sys/malloc.h> 54 #include <sys/dirent.h> 55 56 #ifdef UNION 57 #include <miscfs/union/union.h> 58 #endif 59 60 #include <vm/vm.h> 61 #include <sys/sysctl.h> 62 63 static int change_dir __P((struct nameidata *ndp, struct proc *p)); 64 int getvnode __P((struct filedesc *, int, struct file **)); 65 66 /* 67 * Virtual File System System Calls 68 */ 69 70 /* 71 * Mount a file system. 72 */ 73 struct mount_args { 74 int type; 75 char *path; 76 int flags; 77 caddr_t data; 78 }; 79 /* ARGSUSED */ 80 int 81 mount(p, uap, retval) 82 struct proc *p; 83 register struct mount_args *uap; 84 int *retval; 85 { 86 register struct vnode *vp; 87 register struct mount *mp; 88 int error, flag = 0; 89 struct nameidata nd; 90 91 /* 92 * Must be super user 93 */ 94 error = suser(p->p_ucred, &p->p_acflag); 95 if (error) 96 return (error); 97 /* 98 * Get vnode to be covered 99 */ 100 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 101 error = namei(&nd); 102 if (error) 103 return (error); 104 vp = nd.ni_vp; 105 if (uap->flags & MNT_UPDATE) { 106 if ((vp->v_flag & VROOT) == 0) { 107 vput(vp); 108 return (EINVAL); 109 } 110 mp = vp->v_mount; 111 flag = mp->mnt_flag; 112 /* 113 * We only allow the filesystem to be reloaded if it 114 * is currently mounted read-only. 115 */ 116 if ((uap->flags & MNT_RELOAD) && 117 ((mp->mnt_flag & MNT_RDONLY) == 0)) { 118 vput(vp); 119 return (EOPNOTSUPP); /* Needs translation */ 120 } 121 mp->mnt_flag |= 122 uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); 123 VOP_UNLOCK(vp); 124 goto update; 125 } 126 error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0); 127 if (error) 128 return (error); 129 if (vp->v_type != VDIR) { 130 vput(vp); 131 return (ENOTDIR); 132 } 133 if ((u_long)uap->type > MOUNT_MAXTYPE || vfssw[uap->type] == NULL) { 134 vput(vp); 135 return (ENODEV); 136 } 137 138 /* 139 * Allocate and initialize the file system. 140 */ 141 mp = (struct mount *)malloc((u_long)sizeof(struct mount), 142 M_MOUNT, M_WAITOK); 143 bzero((char *)mp, (u_long)sizeof(struct mount)); 144 mp->mnt_op = vfssw[uap->type]; 145 mp->mnt_vfc = vfsconf[uap->type]; 146 error = vfs_lock(mp); 147 if (error) { 148 free((caddr_t)mp, M_MOUNT); 149 vput(vp); 150 return (error); 151 } 152 if (vp->v_mountedhere != NULL) { 153 vfs_unlock(mp); 154 free((caddr_t)mp, M_MOUNT); 155 vput(vp); 156 return (EBUSY); 157 } 158 vp->v_mountedhere = mp; 159 mp->mnt_vnodecovered = vp; 160 vfsconf[uap->type]->vfc_refcount++; 161 162 update: 163 /* 164 * Set the mount level flags. 165 */ 166 if (uap->flags & MNT_RDONLY) 167 mp->mnt_flag |= MNT_RDONLY; 168 else if (mp->mnt_flag & MNT_RDONLY) 169 mp->mnt_flag |= MNT_WANTRDWR; 170 mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | 171 MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); 172 mp->mnt_flag |= uap->flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | 173 MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_FORCE); 174 /* 175 * Mount the filesystem. 176 */ 177 error = VFS_MOUNT(mp, uap->path, uap->data, &nd, p); 178 if (mp->mnt_flag & MNT_UPDATE) { 179 vrele(vp); 180 if (mp->mnt_flag & MNT_WANTRDWR) 181 mp->mnt_flag &= ~MNT_RDONLY; 182 mp->mnt_flag &=~ 183 (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); 184 if (error) 185 mp->mnt_flag = flag; 186 return (error); 187 } 188 /* 189 * Put the new filesystem on the mount list after root. 190 */ 191 cache_purge(vp); 192 if (!error) { 193 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); 194 VOP_UNLOCK(vp); 195 vfs_unlock(mp); 196 error = VFS_START(mp, 0, p); 197 if (error) 198 vrele(vp); 199 } else { 200 mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; 201 vfs_unlock(mp); 202 free((caddr_t)mp, M_MOUNT); 203 vput(vp); 204 vfsconf[uap->type]->vfc_refcount--; 205 } 206 return (error); 207 } 208 209 /* 210 * Unmount a file system. 211 * 212 * Note: unmount takes a path to the vnode mounted on as argument, 213 * not special file (as before). 214 */ 215 struct unmount_args { 216 char *path; 217 int flags; 218 }; 219 /* ARGSUSED */ 220 int 221 unmount(p, uap, retval) 222 struct proc *p; 223 register struct unmount_args *uap; 224 int *retval; 225 { 226 register struct vnode *vp; 227 struct mount *mp; 228 int error; 229 struct nameidata nd; 230 231 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 232 error = namei(&nd); 233 if (error) 234 return (error); 235 vp = nd.ni_vp; 236 237 /* 238 * Unless this is a user mount, then must 239 * have suser privilege. 240 */ 241 if (((vp->v_mount->mnt_flag & MNT_USER) == 0) && 242 (error = suser(p->p_ucred, &p->p_acflag))) { 243 vput(vp); 244 return (error); 245 } 246 247 /* 248 * Must be the root of the filesystem 249 */ 250 if ((vp->v_flag & VROOT) == 0) { 251 vput(vp); 252 return (EINVAL); 253 } 254 mp = vp->v_mount; 255 vput(vp); 256 257 /* 258 * Don't allow unmount of the root filesystem 259 */ 260 if (mp->mnt_flag & MNT_ROOTFS) 261 return (EINVAL); 262 263 return (dounmount(mp, uap->flags, p)); 264 } 265 266 /* 267 * Do the actual file system unmount. 268 */ 269 int 270 dounmount(mp, flags, p) 271 register struct mount *mp; 272 int flags; 273 struct proc *p; 274 { 275 struct vnode *coveredvp; 276 int error; 277 278 coveredvp = mp->mnt_vnodecovered; 279 if (vfs_busy(mp)) 280 return (EBUSY); 281 mp->mnt_flag |= MNT_UNMOUNT; 282 error = vfs_lock(mp); 283 if (error) 284 return (error); 285 286 mp->mnt_flag &=~ MNT_ASYNC; 287 vfs_msync(mp, MNT_NOWAIT); 288 vnode_pager_umount(mp); /* release cached vnodes */ 289 cache_purgevfs(mp); /* remove cache entries for this file sys */ 290 if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || 291 (flags & MNT_FORCE)) 292 error = VFS_UNMOUNT(mp, flags, p); 293 mp->mnt_flag &= ~MNT_UNMOUNT; 294 vfs_unbusy(mp); 295 if (error) { 296 vfs_unlock(mp); 297 } else { 298 vrele(coveredvp); 299 CIRCLEQ_REMOVE(&mountlist, mp, mnt_list); 300 mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; 301 vfs_unlock(mp); 302 mp->mnt_vfc->vfc_refcount--; 303 if (mp->mnt_vnodelist.lh_first != NULL) 304 panic("unmount: dangling vnode"); 305 free((caddr_t)mp, M_MOUNT); 306 } 307 return (error); 308 } 309 310 /* 311 * Sync each mounted filesystem. 312 */ 313 #ifdef DIAGNOSTIC 314 int syncprt = 0; 315 struct ctldebug debug0 = { "syncprt", &syncprt }; 316 #endif 317 318 struct sync_args { 319 int dummy; 320 }; 321 322 /* ARGSUSED */ 323 int 324 sync(p, uap, retval) 325 struct proc *p; 326 struct sync_args *uap; 327 int *retval; 328 { 329 register struct mount *mp; 330 int asyncflag; 331 332 for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = mp->mnt_list.cqe_next) { 333 /* 334 * The lock check below is to avoid races with mount 335 * and unmount. 336 */ 337 if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && 338 !vfs_busy(mp)) { 339 asyncflag = mp->mnt_flag & MNT_ASYNC; 340 mp->mnt_flag &= ~MNT_ASYNC; 341 vfs_msync(mp, MNT_NOWAIT); 342 VFS_SYNC(mp, MNT_NOWAIT, p != NULL ? p->p_ucred : NOCRED, p); 343 if (asyncflag) 344 mp->mnt_flag |= MNT_ASYNC; 345 vfs_unbusy(mp); 346 } 347 } 348 return (0); 349 } 350 351 /* 352 * Change filesystem quotas. 353 */ 354 struct quotactl_args { 355 char *path; 356 int cmd; 357 int uid; 358 caddr_t arg; 359 }; 360 /* ARGSUSED */ 361 int 362 quotactl(p, uap, retval) 363 struct proc *p; 364 register struct quotactl_args *uap; 365 int *retval; 366 { 367 register struct mount *mp; 368 int error; 369 struct nameidata nd; 370 371 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 372 error = namei(&nd); 373 if (error) 374 return (error); 375 mp = nd.ni_vp->v_mount; 376 vrele(nd.ni_vp); 377 return (VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, p)); 378 } 379 380 /* 381 * Get filesystem statistics. 382 */ 383 struct statfs_args { 384 char *path; 385 struct statfs *buf; 386 }; 387 /* ARGSUSED */ 388 int 389 statfs(p, uap, retval) 390 struct proc *p; 391 register struct statfs_args *uap; 392 int *retval; 393 { 394 register struct mount *mp; 395 register struct statfs *sp; 396 int error; 397 struct nameidata nd; 398 399 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 400 error = namei(&nd); 401 if (error) 402 return (error); 403 mp = nd.ni_vp->v_mount; 404 sp = &mp->mnt_stat; 405 vrele(nd.ni_vp); 406 error = VFS_STATFS(mp, sp, p); 407 if (error) 408 return (error); 409 sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; 410 return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); 411 } 412 413 /* 414 * Get filesystem statistics. 415 */ 416 struct fstatfs_args { 417 int fd; 418 struct statfs *buf; 419 }; 420 /* ARGSUSED */ 421 int 422 fstatfs(p, uap, retval) 423 struct proc *p; 424 register struct fstatfs_args *uap; 425 int *retval; 426 { 427 struct file *fp; 428 struct mount *mp; 429 register struct statfs *sp; 430 int error; 431 432 error = getvnode(p->p_fd, uap->fd, &fp); 433 if (error) 434 return (error); 435 mp = ((struct vnode *)fp->f_data)->v_mount; 436 sp = &mp->mnt_stat; 437 error = VFS_STATFS(mp, sp, p); 438 if (error) 439 return (error); 440 sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; 441 return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); 442 } 443 444 /* 445 * Get statistics on all filesystems. 446 */ 447 struct getfsstat_args { 448 struct statfs *buf; 449 long bufsize; 450 int flags; 451 }; 452 int 453 getfsstat(p, uap, retval) 454 struct proc *p; 455 register struct getfsstat_args *uap; 456 int *retval; 457 { 458 register struct mount *mp, *nmp; 459 register struct statfs *sp; 460 caddr_t sfsp; 461 long count, maxcount, error; 462 463 maxcount = uap->bufsize / sizeof(struct statfs); 464 sfsp = (caddr_t)uap->buf; 465 count = 0; 466 for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { 467 nmp = mp->mnt_list.cqe_next; 468 if (sfsp && count < maxcount && 469 ((mp->mnt_flag & MNT_MLOCK) == 0)) { 470 sp = &mp->mnt_stat; 471 /* 472 * If MNT_NOWAIT is specified, do not refresh the 473 * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. 474 */ 475 if (((uap->flags & MNT_NOWAIT) == 0 || 476 (uap->flags & MNT_WAIT)) && 477 (error = VFS_STATFS(mp, sp, p))) 478 continue; 479 sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; 480 error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); 481 if (error) 482 return (error); 483 sfsp += sizeof(*sp); 484 } 485 count++; 486 } 487 if (sfsp && count > maxcount) 488 *retval = maxcount; 489 else 490 *retval = count; 491 return (0); 492 } 493 494 /* 495 * Change current working directory to a given file descriptor. 496 */ 497 struct fchdir_args { 498 int fd; 499 }; 500 /* ARGSUSED */ 501 int 502 fchdir(p, uap, retval) 503 struct proc *p; 504 struct fchdir_args *uap; 505 int *retval; 506 { 507 register struct filedesc *fdp = p->p_fd; 508 register struct vnode *vp; 509 struct file *fp; 510 int error; 511 512 error = getvnode(fdp, uap->fd, &fp); 513 if (error) 514 return (error); 515 vp = (struct vnode *)fp->f_data; 516 VOP_LOCK(vp); 517 if (vp->v_type != VDIR) 518 error = ENOTDIR; 519 else 520 error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); 521 VOP_UNLOCK(vp); 522 if (error) 523 return (error); 524 VREF(vp); 525 vrele(fdp->fd_cdir); 526 fdp->fd_cdir = vp; 527 return (0); 528 } 529 530 /* 531 * Change current working directory (``.''). 532 */ 533 struct chdir_args { 534 char *path; 535 }; 536 /* ARGSUSED */ 537 int 538 chdir(p, uap, retval) 539 struct proc *p; 540 struct chdir_args *uap; 541 int *retval; 542 { 543 register struct filedesc *fdp = p->p_fd; 544 int error; 545 struct nameidata nd; 546 547 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 548 error = change_dir(&nd, p); 549 if (error) 550 return (error); 551 vrele(fdp->fd_cdir); 552 fdp->fd_cdir = nd.ni_vp; 553 return (0); 554 } 555 556 /* 557 * Change notion of root (``/'') directory. 558 */ 559 struct chroot_args { 560 char *path; 561 }; 562 /* ARGSUSED */ 563 int 564 chroot(p, uap, retval) 565 struct proc *p; 566 struct chroot_args *uap; 567 int *retval; 568 { 569 register struct filedesc *fdp = p->p_fd; 570 int error; 571 struct nameidata nd; 572 573 error = suser(p->p_ucred, &p->p_acflag); 574 if (error) 575 return (error); 576 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 577 error = change_dir(&nd, p); 578 if (error) 579 return (error); 580 if (fdp->fd_rdir != NULL) 581 vrele(fdp->fd_rdir); 582 fdp->fd_rdir = nd.ni_vp; 583 return (0); 584 } 585 586 /* 587 * Common routine for chroot and chdir. 588 */ 589 static int 590 change_dir(ndp, p) 591 register struct nameidata *ndp; 592 struct proc *p; 593 { 594 struct vnode *vp; 595 int error; 596 597 error = namei(ndp); 598 if (error) 599 return (error); 600 vp = ndp->ni_vp; 601 if (vp->v_type != VDIR) 602 error = ENOTDIR; 603 else 604 error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); 605 VOP_UNLOCK(vp); 606 if (error) 607 vrele(vp); 608 return (error); 609 } 610 611 /* 612 * Check permissions, allocate an open file structure, 613 * and call the device open routine if any. 614 */ 615 struct open_args { 616 char *path; 617 int flags; 618 int mode; 619 }; 620 int 621 open(p, uap, retval) 622 struct proc *p; 623 register struct open_args *uap; 624 int *retval; 625 { 626 register struct filedesc *fdp = p->p_fd; 627 register struct file *fp; 628 register struct vnode *vp; 629 int flags, cmode; 630 struct file *nfp; 631 int type, indx, error; 632 struct flock lf; 633 struct nameidata nd; 634 635 error = falloc(p, &nfp, &indx); 636 if (error) 637 return (error); 638 fp = nfp; 639 flags = FFLAGS(uap->flags); 640 cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; 641 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 642 p->p_dupfd = -indx - 1; /* XXX check for fdopen */ 643 error = vn_open(&nd, flags, cmode); 644 if (error) { 645 ffree(fp); 646 if ((error == ENODEV || error == ENXIO) && 647 p->p_dupfd >= 0 && /* XXX from fdopen */ 648 (error = 649 dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { 650 *retval = indx; 651 return (0); 652 } 653 if (error == ERESTART) 654 error = EINTR; 655 fdp->fd_ofiles[indx] = NULL; 656 return (error); 657 } 658 p->p_dupfd = 0; 659 vp = nd.ni_vp; 660 fp->f_flag = flags & FMASK; 661 fp->f_type = DTYPE_VNODE; 662 fp->f_ops = &vnops; 663 fp->f_data = (caddr_t)vp; 664 if (flags & (O_EXLOCK | O_SHLOCK)) { 665 lf.l_whence = SEEK_SET; 666 lf.l_start = 0; 667 lf.l_len = 0; 668 if (flags & O_EXLOCK) 669 lf.l_type = F_WRLCK; 670 else 671 lf.l_type = F_RDLCK; 672 type = F_FLOCK; 673 if ((flags & FNONBLOCK) == 0) 674 type |= F_WAIT; 675 VOP_UNLOCK(vp); 676 error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); 677 if (error) { 678 (void) vn_close(vp, fp->f_flag, fp->f_cred, p); 679 ffree(fp); 680 fdp->fd_ofiles[indx] = NULL; 681 return (error); 682 } 683 VOP_LOCK(vp); 684 fp->f_flag |= FHASLOCK; 685 } 686 VOP_UNLOCK(vp); 687 *retval = indx; 688 return (0); 689 } 690 691 #ifdef COMPAT_43 692 /* 693 * Create a file. 694 */ 695 struct ocreat_args { 696 char *path; 697 int mode; 698 }; 699 int 700 ocreat(p, uap, retval) 701 struct proc *p; 702 register struct ocreat_args *uap; 703 int *retval; 704 { 705 struct open_args openuap; 706 707 openuap.path = uap->path; 708 openuap.mode = uap->mode; 709 openuap.flags = O_WRONLY | O_CREAT | O_TRUNC; 710 return (open(p, &openuap, retval)); 711 } 712 #endif /* COMPAT_43 */ 713 714 /* 715 * Create a special file. 716 */ 717 struct mknod_args { 718 char *path; 719 int mode; 720 int dev; 721 }; 722 /* ARGSUSED */ 723 int 724 mknod(p, uap, retval) 725 struct proc *p; 726 register struct mknod_args *uap; 727 int *retval; 728 { 729 register struct vnode *vp; 730 struct vattr vattr; 731 int error; 732 struct nameidata nd; 733 734 error = suser(p->p_ucred, &p->p_acflag); 735 if (error) 736 return (error); 737 NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); 738 error = namei(&nd); 739 if (error) 740 return (error); 741 vp = nd.ni_vp; 742 if (vp != NULL) 743 error = EEXIST; 744 else { 745 VATTR_NULL(&vattr); 746 vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; 747 vattr.va_rdev = uap->dev; 748 749 switch (uap->mode & S_IFMT) { 750 case S_IFMT: /* used by badsect to flag bad sectors */ 751 vattr.va_type = VBAD; 752 break; 753 case S_IFCHR: 754 vattr.va_type = VCHR; 755 break; 756 case S_IFBLK: 757 vattr.va_type = VBLK; 758 break; 759 default: 760 error = EINVAL; 761 break; 762 } 763 } 764 if (!error) { 765 LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 766 error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); 767 } else { 768 VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); 769 if (nd.ni_dvp == vp) 770 vrele(nd.ni_dvp); 771 else 772 vput(nd.ni_dvp); 773 if (vp) 774 vrele(vp); 775 } 776 return (error); 777 } 778 779 /* 780 * Create named pipe. 781 */ 782 struct mkfifo_args { 783 char *path; 784 int mode; 785 }; 786 /* ARGSUSED */ 787 int 788 mkfifo(p, uap, retval) 789 struct proc *p; 790 register struct mkfifo_args *uap; 791 int *retval; 792 { 793 struct vattr vattr; 794 int error; 795 struct nameidata nd; 796 797 NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); 798 error = namei(&nd); 799 if (error) 800 return (error); 801 if (nd.ni_vp != NULL) { 802 VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); 803 if (nd.ni_dvp == nd.ni_vp) 804 vrele(nd.ni_dvp); 805 else 806 vput(nd.ni_dvp); 807 vrele(nd.ni_vp); 808 return (EEXIST); 809 } 810 VATTR_NULL(&vattr); 811 vattr.va_type = VFIFO; 812 vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; 813 LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 814 return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); 815 } 816 817 /* 818 * Make a hard file link. 819 */ 820 struct link_args { 821 char *path; 822 char *link; 823 }; 824 /* ARGSUSED */ 825 int 826 link(p, uap, retval) 827 struct proc *p; 828 register struct link_args *uap; 829 int *retval; 830 { 831 register struct vnode *vp; 832 struct nameidata nd; 833 int error; 834 835 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 836 error = namei(&nd); 837 if (error) 838 return (error); 839 vp = nd.ni_vp; 840 if (vp->v_type != VDIR || 841 (error = suser(p->p_ucred, &p->p_acflag)) == 0) { 842 NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->link, p); 843 if (vp->v_type == VDIR) 844 nd.ni_cnd.cn_flags |= WILLBEDIR; 845 error = namei(&nd); 846 if (!error) { 847 if (nd.ni_vp != NULL) { 848 VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); 849 if (nd.ni_dvp == nd.ni_vp) 850 vrele(nd.ni_dvp); 851 else 852 vput(nd.ni_dvp); 853 if (nd.ni_vp) 854 vrele(nd.ni_vp); 855 error = EEXIST; 856 } else { 857 LEASE_CHECK(nd.ni_dvp, 858 p, p->p_ucred, LEASE_WRITE); 859 LEASE_CHECK(vp, 860 p, p->p_ucred, LEASE_WRITE); 861 error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); 862 } 863 } 864 } 865 vrele(vp); 866 return (error); 867 } 868 869 /* 870 * Make a symbolic link. 871 */ 872 struct symlink_args { 873 char *path; 874 char *link; 875 }; 876 /* ARGSUSED */ 877 int 878 symlink(p, uap, retval) 879 struct proc *p; 880 register struct symlink_args *uap; 881 int *retval; 882 { 883 struct vattr vattr; 884 char *path; 885 int error; 886 struct nameidata nd; 887 888 MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); 889 error = copyinstr(uap->path, path, MAXPATHLEN, NULL); 890 if (error) 891 goto out; 892 NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->link, p); 893 error = namei(&nd); 894 if (error) 895 goto out; 896 if (nd.ni_vp) { 897 VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); 898 if (nd.ni_dvp == nd.ni_vp) 899 vrele(nd.ni_dvp); 900 else 901 vput(nd.ni_dvp); 902 vrele(nd.ni_vp); 903 error = EEXIST; 904 goto out; 905 } 906 VATTR_NULL(&vattr); 907 vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; 908 LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 909 error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); 910 out: 911 FREE(path, M_NAMEI); 912 return (error); 913 } 914 915 /* 916 * Delete a name from the filesystem. 917 */ 918 struct unlink_args { 919 char *path; 920 }; 921 /* ARGSUSED */ 922 int 923 unlink(p, uap, retval) 924 struct proc *p; 925 struct unlink_args *uap; 926 int *retval; 927 { 928 register struct vnode *vp; 929 int error; 930 struct nameidata nd; 931 932 NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, uap->path, p); 933 error = namei(&nd); 934 if (error) 935 return (error); 936 vp = nd.ni_vp; 937 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 938 VOP_LOCK(vp); 939 940 if (vp->v_type != VDIR || 941 (error = suser(p->p_ucred, &p->p_acflag)) == 0) { 942 /* 943 * The root of a mounted filesystem cannot be deleted. 944 */ 945 if (vp->v_flag & VROOT) 946 error = EBUSY; 947 else 948 (void) vnode_pager_uncache(vp); 949 } 950 951 if (!error) { 952 LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 953 error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); 954 } else { 955 VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); 956 if (nd.ni_dvp == vp) 957 vrele(nd.ni_dvp); 958 else 959 vput(nd.ni_dvp); 960 vput(vp); 961 } 962 return (error); 963 } 964 965 /* 966 * Reposition read/write file offset. 967 */ 968 struct lseek_args { 969 int fd; 970 int pad; 971 off_t offset; 972 int whence; 973 }; 974 int 975 lseek(p, uap, retval) 976 struct proc *p; 977 register struct lseek_args *uap; 978 int *retval; 979 { 980 struct ucred *cred = p->p_ucred; 981 register struct filedesc *fdp = p->p_fd; 982 register struct file *fp; 983 struct vattr vattr; 984 int error; 985 986 if ((u_int)uap->fd >= fdp->fd_nfiles || 987 (fp = fdp->fd_ofiles[uap->fd]) == NULL) 988 return (EBADF); 989 if (fp->f_type != DTYPE_VNODE) 990 return (ESPIPE); 991 switch (uap->whence) { 992 case L_INCR: 993 fp->f_offset += uap->offset; 994 break; 995 case L_XTND: 996 error=VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p); 997 if (error) 998 return (error); 999 fp->f_offset = uap->offset + vattr.va_size; 1000 break; 1001 case L_SET: 1002 fp->f_offset = uap->offset; 1003 break; 1004 default: 1005 return (EINVAL); 1006 } 1007 *(off_t *)retval = fp->f_offset; 1008 return (0); 1009 } 1010 1011 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 1012 /* 1013 * Reposition read/write file offset. 1014 */ 1015 struct olseek_args { 1016 int fd; 1017 long offset; 1018 int whence; 1019 }; 1020 int 1021 olseek(p, uap, retval) 1022 struct proc *p; 1023 register struct olseek_args *uap; 1024 int *retval; 1025 { 1026 struct lseek_args nuap; 1027 off_t qret; 1028 int error; 1029 1030 nuap.fd = uap->fd; 1031 nuap.offset = uap->offset; 1032 nuap.whence = uap->whence; 1033 error = lseek(p, &nuap, &qret); 1034 *(long *)retval = qret; 1035 return (error); 1036 } 1037 #endif /* COMPAT_43 */ 1038 1039 /* 1040 * Check access permissions. 1041 */ 1042 struct access_args { 1043 char *path; 1044 int flags; 1045 }; 1046 int 1047 access(p, uap, retval) 1048 struct proc *p; 1049 register struct access_args *uap; 1050 int *retval; 1051 { 1052 register struct ucred *cred = p->p_ucred; 1053 register struct vnode *vp; 1054 int error, flags, t_gid, t_uid; 1055 struct nameidata nd; 1056 1057 t_uid = cred->cr_uid; 1058 t_gid = cred->cr_groups[0]; 1059 cred->cr_uid = p->p_cred->p_ruid; 1060 cred->cr_groups[0] = p->p_cred->p_rgid; 1061 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 1062 error = namei(&nd); 1063 if (error) 1064 goto out1; 1065 vp = nd.ni_vp; 1066 1067 /* Flags == 0 means only check for existence. */ 1068 if (uap->flags) { 1069 flags = 0; 1070 if (uap->flags & R_OK) 1071 flags |= VREAD; 1072 if (uap->flags & W_OK) 1073 flags |= VWRITE; 1074 if (uap->flags & X_OK) 1075 flags |= VEXEC; 1076 if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) 1077 error = VOP_ACCESS(vp, flags, cred, p); 1078 } 1079 vput(vp); 1080 out1: 1081 cred->cr_uid = t_uid; 1082 cred->cr_groups[0] = t_gid; 1083 return (error); 1084 } 1085 1086 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 1087 /* 1088 * Get file status; this version follows links. 1089 */ 1090 struct ostat_args { 1091 char *path; 1092 struct ostat *ub; 1093 }; 1094 /* ARGSUSED */ 1095 int 1096 ostat(p, uap, retval) 1097 struct proc *p; 1098 register struct ostat_args *uap; 1099 int *retval; 1100 { 1101 struct stat sb; 1102 struct ostat osb; 1103 int error; 1104 struct nameidata nd; 1105 1106 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 1107 error = namei(&nd); 1108 if (error) 1109 return (error); 1110 error = vn_stat(nd.ni_vp, &sb, p); 1111 vput(nd.ni_vp); 1112 if (error) 1113 return (error); 1114 cvtstat(&sb, &osb); 1115 error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); 1116 return (error); 1117 } 1118 1119 /* 1120 * Get file status; this version does not follow links. 1121 */ 1122 struct olstat_args { 1123 char *path; 1124 struct ostat *ub; 1125 }; 1126 /* ARGSUSED */ 1127 int 1128 olstat(p, uap, retval) 1129 struct proc *p; 1130 register struct olstat_args *uap; 1131 int *retval; 1132 { 1133 struct vnode *vp, *dvp; 1134 struct stat sb, sb1; 1135 struct ostat osb; 1136 int error; 1137 struct nameidata nd; 1138 1139 NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, 1140 uap->path, p); 1141 error = namei(&nd); 1142 if (error) 1143 return (error); 1144 /* 1145 * For symbolic links, always return the attributes of its 1146 * containing directory, except for mode, size, and links. 1147 */ 1148 vp = nd.ni_vp; 1149 dvp = nd.ni_dvp; 1150 if (vp->v_type != VLNK) { 1151 if (dvp == vp) 1152 vrele(dvp); 1153 else 1154 vput(dvp); 1155 error = vn_stat(vp, &sb, p); 1156 vput(vp); 1157 if (error) 1158 return (error); 1159 } else { 1160 error = vn_stat(dvp, &sb, p); 1161 vput(dvp); 1162 if (error) { 1163 vput(vp); 1164 return (error); 1165 } 1166 error = vn_stat(vp, &sb1, p); 1167 vput(vp); 1168 if (error) 1169 return (error); 1170 sb.st_mode &= ~S_IFDIR; 1171 sb.st_mode |= S_IFLNK; 1172 sb.st_nlink = sb1.st_nlink; 1173 sb.st_size = sb1.st_size; 1174 sb.st_blocks = sb1.st_blocks; 1175 } 1176 cvtstat(&sb, &osb); 1177 error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); 1178 return (error); 1179 } 1180 1181 /* 1182 * Convert from an old to a new stat structure. 1183 */ 1184 void 1185 cvtstat(st, ost) 1186 struct stat *st; 1187 struct ostat *ost; 1188 { 1189 1190 ost->st_dev = st->st_dev; 1191 ost->st_ino = st->st_ino; 1192 ost->st_mode = st->st_mode; 1193 ost->st_nlink = st->st_nlink; 1194 ost->st_uid = st->st_uid; 1195 ost->st_gid = st->st_gid; 1196 ost->st_rdev = st->st_rdev; 1197 if (st->st_size < (quad_t)1 << 32) 1198 ost->st_size = st->st_size; 1199 else 1200 ost->st_size = -2; 1201 ost->st_atime = st->st_atime; 1202 ost->st_mtime = st->st_mtime; 1203 ost->st_ctime = st->st_ctime; 1204 ost->st_blksize = st->st_blksize; 1205 ost->st_blocks = st->st_blocks; 1206 ost->st_flags = st->st_flags; 1207 ost->st_gen = st->st_gen; 1208 } 1209 #endif /* COMPAT_43 || COMPAT_SUNOS */ 1210 1211 /* 1212 * Get file status; this version follows links. 1213 */ 1214 struct stat_args { 1215 char *path; 1216 struct stat *ub; 1217 }; 1218 /* ARGSUSED */ 1219 int 1220 stat(p, uap, retval) 1221 struct proc *p; 1222 register struct stat_args *uap; 1223 int *retval; 1224 { 1225 struct stat sb; 1226 int error; 1227 struct nameidata nd; 1228 1229 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 1230 error = namei(&nd); 1231 if (error) 1232 return (error); 1233 error = vn_stat(nd.ni_vp, &sb, p); 1234 vput(nd.ni_vp); 1235 if (error) 1236 return (error); 1237 error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); 1238 return (error); 1239 } 1240 1241 /* 1242 * Get file status; this version does not follow links. 1243 */ 1244 struct lstat_args { 1245 char *path; 1246 struct stat *ub; 1247 }; 1248 /* ARGSUSED */ 1249 int 1250 lstat(p, uap, retval) 1251 struct proc *p; 1252 register struct lstat_args *uap; 1253 int *retval; 1254 { 1255 int error; 1256 struct vnode *vp, *dvp; 1257 struct stat sb, sb1; 1258 struct nameidata nd; 1259 1260 NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, 1261 uap->path, p); 1262 error = namei(&nd); 1263 if (error) 1264 return (error); 1265 /* 1266 * For symbolic links, always return the attributes of its 1267 * containing directory, except for mode, size, and links. 1268 */ 1269 vp = nd.ni_vp; 1270 dvp = nd.ni_dvp; 1271 if (vp->v_type != VLNK) { 1272 if (dvp == vp) 1273 vrele(dvp); 1274 else 1275 vput(dvp); 1276 error = vn_stat(vp, &sb, p); 1277 vput(vp); 1278 if (error) 1279 return (error); 1280 } else { 1281 error = vn_stat(dvp, &sb, p); 1282 vput(dvp); 1283 if (error) { 1284 vput(vp); 1285 return (error); 1286 } 1287 error = vn_stat(vp, &sb1, p); 1288 vput(vp); 1289 if (error) 1290 return (error); 1291 sb.st_mode &= ~S_IFDIR; 1292 sb.st_mode |= S_IFLNK; 1293 sb.st_nlink = sb1.st_nlink; 1294 sb.st_size = sb1.st_size; 1295 sb.st_blocks = sb1.st_blocks; 1296 } 1297 error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); 1298 return (error); 1299 } 1300 1301 /* 1302 * Get configurable pathname variables. 1303 */ 1304 struct pathconf_args { 1305 char *path; 1306 int name; 1307 }; 1308 /* ARGSUSED */ 1309 int 1310 pathconf(p, uap, retval) 1311 struct proc *p; 1312 register struct pathconf_args *uap; 1313 int *retval; 1314 { 1315 int error; 1316 struct nameidata nd; 1317 1318 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 1319 error = namei(&nd); 1320 if (error) 1321 return (error); 1322 error = VOP_PATHCONF(nd.ni_vp, uap->name, retval); 1323 vput(nd.ni_vp); 1324 return (error); 1325 } 1326 1327 /* 1328 * Return target name of a symbolic link. 1329 */ 1330 struct readlink_args { 1331 char *path; 1332 char *buf; 1333 int count; 1334 }; 1335 /* ARGSUSED */ 1336 int 1337 readlink(p, uap, retval) 1338 struct proc *p; 1339 register struct readlink_args *uap; 1340 int *retval; 1341 { 1342 register struct vnode *vp; 1343 struct iovec aiov; 1344 struct uio auio; 1345 int error; 1346 struct nameidata nd; 1347 1348 NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); 1349 error = namei(&nd); 1350 if (error) 1351 return (error); 1352 vp = nd.ni_vp; 1353 if (vp->v_type != VLNK) 1354 error = EINVAL; 1355 else { 1356 aiov.iov_base = uap->buf; 1357 aiov.iov_len = uap->count; 1358 auio.uio_iov = &aiov; 1359 auio.uio_iovcnt = 1; 1360 auio.uio_offset = 0; 1361 auio.uio_rw = UIO_READ; 1362 auio.uio_segflg = UIO_USERSPACE; 1363 auio.uio_procp = p; 1364 auio.uio_resid = uap->count; 1365 error = VOP_READLINK(vp, &auio, p->p_ucred); 1366 } 1367 vput(vp); 1368 *retval = uap->count - auio.uio_resid; 1369 return (error); 1370 } 1371 1372 /* 1373 * Change flags of a file given a path name. 1374 */ 1375 struct chflags_args { 1376 char *path; 1377 int flags; 1378 }; 1379 /* ARGSUSED */ 1380 int 1381 chflags(p, uap, retval) 1382 struct proc *p; 1383 register struct chflags_args *uap; 1384 int *retval; 1385 { 1386 register struct vnode *vp; 1387 struct vattr vattr; 1388 int error; 1389 struct nameidata nd; 1390 1391 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 1392 error = namei(&nd); 1393 if (error) 1394 return (error); 1395 vp = nd.ni_vp; 1396 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1397 VOP_LOCK(vp); 1398 VATTR_NULL(&vattr); 1399 vattr.va_flags = uap->flags; 1400 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1401 vput(vp); 1402 return (error); 1403 } 1404 1405 /* 1406 * Change flags of a file given a file descriptor. 1407 */ 1408 struct fchflags_args { 1409 int fd; 1410 int flags; 1411 }; 1412 /* ARGSUSED */ 1413 int 1414 fchflags(p, uap, retval) 1415 struct proc *p; 1416 register struct fchflags_args *uap; 1417 int *retval; 1418 { 1419 struct vattr vattr; 1420 struct vnode *vp; 1421 struct file *fp; 1422 int error; 1423 1424 error = getvnode(p->p_fd, uap->fd, &fp); 1425 if (error) 1426 return (error); 1427 vp = (struct vnode *)fp->f_data; 1428 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1429 VOP_LOCK(vp); 1430 VATTR_NULL(&vattr); 1431 vattr.va_flags = uap->flags; 1432 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1433 VOP_UNLOCK(vp); 1434 return (error); 1435 } 1436 1437 /* 1438 * Change mode of a file given path name. 1439 */ 1440 struct chmod_args { 1441 char *path; 1442 int mode; 1443 }; 1444 /* ARGSUSED */ 1445 int 1446 chmod(p, uap, retval) 1447 struct proc *p; 1448 register struct chmod_args *uap; 1449 int *retval; 1450 { 1451 register struct vnode *vp; 1452 struct vattr vattr; 1453 int error; 1454 struct nameidata nd; 1455 1456 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 1457 error = namei(&nd); 1458 if (error) 1459 return (error); 1460 vp = nd.ni_vp; 1461 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1462 VOP_LOCK(vp); 1463 VATTR_NULL(&vattr); 1464 vattr.va_mode = uap->mode & ALLPERMS; 1465 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1466 vput(vp); 1467 return (error); 1468 } 1469 1470 /* 1471 * Change mode of a file given a file descriptor. 1472 */ 1473 struct fchmod_args { 1474 int fd; 1475 int mode; 1476 }; 1477 /* ARGSUSED */ 1478 int 1479 fchmod(p, uap, retval) 1480 struct proc *p; 1481 register struct fchmod_args *uap; 1482 int *retval; 1483 { 1484 struct vattr vattr; 1485 struct vnode *vp; 1486 struct file *fp; 1487 int error; 1488 1489 error = getvnode(p->p_fd, uap->fd, &fp); 1490 if (error) 1491 return (error); 1492 vp = (struct vnode *)fp->f_data; 1493 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1494 VOP_LOCK(vp); 1495 VATTR_NULL(&vattr); 1496 vattr.va_mode = uap->mode & ALLPERMS; 1497 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1498 VOP_UNLOCK(vp); 1499 return (error); 1500 } 1501 1502 /* 1503 * Set ownership given a path name. 1504 */ 1505 struct chown_args { 1506 char *path; 1507 int uid; 1508 int gid; 1509 }; 1510 /* ARGSUSED */ 1511 int 1512 chown(p, uap, retval) 1513 struct proc *p; 1514 register struct chown_args *uap; 1515 int *retval; 1516 { 1517 register struct vnode *vp; 1518 struct vattr vattr; 1519 int error; 1520 struct nameidata nd; 1521 1522 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 1523 error = namei(&nd); 1524 if (error) 1525 return (error); 1526 vp = nd.ni_vp; 1527 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1528 VOP_LOCK(vp); 1529 VATTR_NULL(&vattr); 1530 vattr.va_uid = uap->uid; 1531 vattr.va_gid = uap->gid; 1532 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1533 vput(vp); 1534 return (error); 1535 } 1536 1537 /* 1538 * Set ownership given a file descriptor. 1539 */ 1540 struct fchown_args { 1541 int fd; 1542 int uid; 1543 int gid; 1544 }; 1545 /* ARGSUSED */ 1546 int 1547 fchown(p, uap, retval) 1548 struct proc *p; 1549 register struct fchown_args *uap; 1550 int *retval; 1551 { 1552 struct vattr vattr; 1553 struct vnode *vp; 1554 struct file *fp; 1555 int error; 1556 1557 error = getvnode(p->p_fd, uap->fd, &fp); 1558 if (error) 1559 return (error); 1560 vp = (struct vnode *)fp->f_data; 1561 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1562 VOP_LOCK(vp); 1563 VATTR_NULL(&vattr); 1564 vattr.va_uid = uap->uid; 1565 vattr.va_gid = uap->gid; 1566 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1567 VOP_UNLOCK(vp); 1568 return (error); 1569 } 1570 1571 /* 1572 * Set the access and modification times of a file. 1573 */ 1574 struct utimes_args { 1575 char *path; 1576 struct timeval *tptr; 1577 }; 1578 /* ARGSUSED */ 1579 int 1580 utimes(p, uap, retval) 1581 struct proc *p; 1582 register struct utimes_args *uap; 1583 int *retval; 1584 { 1585 register struct vnode *vp; 1586 struct timeval tv[2]; 1587 struct vattr vattr; 1588 int error; 1589 struct nameidata nd; 1590 1591 VATTR_NULL(&vattr); 1592 if (uap->tptr == NULL) { 1593 microtime(&tv[0]); 1594 tv[1] = tv[0]; 1595 vattr.va_vaflags |= VA_UTIMES_NULL; 1596 } else { 1597 error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv)); 1598 if (error) 1599 return (error); 1600 } 1601 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 1602 error = namei(&nd); 1603 if (error) 1604 return (error); 1605 vp = nd.ni_vp; 1606 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1607 VOP_LOCK(vp); 1608 vattr.va_atime.ts_sec = tv[0].tv_sec; 1609 vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000; 1610 vattr.va_mtime.ts_sec = tv[1].tv_sec; 1611 vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000; 1612 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1613 vput(vp); 1614 return (error); 1615 } 1616 1617 /* 1618 * Truncate a file given its path name. 1619 */ 1620 struct truncate_args { 1621 char *path; 1622 int pad; 1623 off_t length; 1624 }; 1625 /* ARGSUSED */ 1626 int 1627 truncate(p, uap, retval) 1628 struct proc *p; 1629 register struct truncate_args *uap; 1630 int *retval; 1631 { 1632 register struct vnode *vp; 1633 struct vattr vattr; 1634 int error; 1635 struct nameidata nd; 1636 1637 if (uap->length < 0) 1638 return(EINVAL); 1639 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 1640 error = namei(&nd); 1641 if (error) 1642 return (error); 1643 vp = nd.ni_vp; 1644 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1645 VOP_LOCK(vp); 1646 if (vp->v_type == VDIR) 1647 error = EISDIR; 1648 else if ((error = vn_writechk(vp)) == 0 && 1649 (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { 1650 VATTR_NULL(&vattr); 1651 vattr.va_size = uap->length; 1652 error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); 1653 } 1654 vput(vp); 1655 return (error); 1656 } 1657 1658 /* 1659 * Truncate a file given a file descriptor. 1660 */ 1661 struct ftruncate_args { 1662 int fd; 1663 int pad; 1664 off_t length; 1665 }; 1666 /* ARGSUSED */ 1667 int 1668 ftruncate(p, uap, retval) 1669 struct proc *p; 1670 register struct ftruncate_args *uap; 1671 int *retval; 1672 { 1673 struct vattr vattr; 1674 struct vnode *vp; 1675 struct file *fp; 1676 int error; 1677 1678 if (uap->length < 0) 1679 return(EINVAL); 1680 error = getvnode(p->p_fd, uap->fd, &fp); 1681 if (error) 1682 return (error); 1683 if ((fp->f_flag & FWRITE) == 0) 1684 return (EINVAL); 1685 vp = (struct vnode *)fp->f_data; 1686 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1687 VOP_LOCK(vp); 1688 if (vp->v_type == VDIR) 1689 error = EISDIR; 1690 else if ((error = vn_writechk(vp)) == 0) { 1691 VATTR_NULL(&vattr); 1692 vattr.va_size = uap->length; 1693 error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); 1694 } 1695 VOP_UNLOCK(vp); 1696 return (error); 1697 } 1698 1699 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 1700 /* 1701 * Truncate a file given its path name. 1702 */ 1703 struct otruncate_args { 1704 char *path; 1705 long length; 1706 }; 1707 /* ARGSUSED */ 1708 int 1709 otruncate(p, uap, retval) 1710 struct proc *p; 1711 register struct otruncate_args *uap; 1712 int *retval; 1713 { 1714 struct truncate_args nuap; 1715 1716 nuap.path = uap->path; 1717 nuap.length = uap->length; 1718 return (truncate(p, &nuap, retval)); 1719 } 1720 1721 /* 1722 * Truncate a file given a file descriptor. 1723 */ 1724 struct oftruncate_args { 1725 int fd; 1726 long length; 1727 }; 1728 /* ARGSUSED */ 1729 int 1730 oftruncate(p, uap, retval) 1731 struct proc *p; 1732 register struct oftruncate_args *uap; 1733 int *retval; 1734 { 1735 struct ftruncate_args nuap; 1736 1737 nuap.fd = uap->fd; 1738 nuap.length = uap->length; 1739 return (ftruncate(p, &nuap, retval)); 1740 } 1741 #endif /* COMPAT_43 || COMPAT_SUNOS */ 1742 1743 /* 1744 * Sync an open file. 1745 */ 1746 struct fsync_args { 1747 int fd; 1748 }; 1749 /* ARGSUSED */ 1750 int 1751 fsync(p, uap, retval) 1752 struct proc *p; 1753 struct fsync_args *uap; 1754 int *retval; 1755 { 1756 register struct vnode *vp; 1757 struct file *fp; 1758 int error; 1759 1760 error = getvnode(p->p_fd, uap->fd, &fp); 1761 if (error) 1762 return (error); 1763 vp = (struct vnode *)fp->f_data; 1764 VOP_LOCK(vp); 1765 if (vp->v_object) { 1766 vm_object_page_clean(vp->v_object, 0, 0 ,0, FALSE); 1767 } 1768 error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); 1769 VOP_UNLOCK(vp); 1770 return (error); 1771 } 1772 1773 /* 1774 * Rename files. Source and destination must either both be directories, 1775 * or both not be directories. If target is a directory, it must be empty. 1776 */ 1777 struct rename_args { 1778 char *from; 1779 char *to; 1780 }; 1781 /* ARGSUSED */ 1782 int 1783 rename(p, uap, retval) 1784 struct proc *p; 1785 register struct rename_args *uap; 1786 int *retval; 1787 { 1788 register struct vnode *tvp, *fvp, *tdvp; 1789 struct nameidata fromnd, tond; 1790 int error; 1791 1792 NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, 1793 uap->from, p); 1794 error = namei(&fromnd); 1795 if (error) 1796 return (error); 1797 fvp = fromnd.ni_vp; 1798 NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART, 1799 UIO_USERSPACE, uap->to, p); 1800 if (fromnd.ni_vp->v_type == VDIR) 1801 tond.ni_cnd.cn_flags |= WILLBEDIR; 1802 error = namei(&tond); 1803 if (error) { 1804 VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); 1805 vrele(fromnd.ni_dvp); 1806 vrele(fvp); 1807 goto out1; 1808 } 1809 tdvp = tond.ni_dvp; 1810 tvp = tond.ni_vp; 1811 if (tvp != NULL) { 1812 if (fvp->v_type == VDIR && tvp->v_type != VDIR) { 1813 error = ENOTDIR; 1814 goto out; 1815 } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { 1816 error = EISDIR; 1817 goto out; 1818 } 1819 } 1820 if (fvp == tdvp) 1821 error = EINVAL; 1822 /* 1823 * If source is the same as the destination (that is the 1824 * same inode number with the same name in the same directory), 1825 * then there is nothing to do. 1826 */ 1827 if (fvp == tvp && fromnd.ni_dvp == tdvp && 1828 fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && 1829 !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, 1830 fromnd.ni_cnd.cn_namelen)) 1831 error = -1; 1832 out: 1833 if (!error) { 1834 LEASE_CHECK(tdvp, p, p->p_ucred, LEASE_WRITE); 1835 if (fromnd.ni_dvp != tdvp) { 1836 LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 1837 } 1838 if (tvp) { 1839 LEASE_CHECK(tvp, p, p->p_ucred, LEASE_WRITE); 1840 (void) vnode_pager_uncache(tvp); 1841 } 1842 error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, 1843 tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); 1844 } else { 1845 VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); 1846 if (tdvp == tvp) 1847 vrele(tdvp); 1848 else 1849 vput(tdvp); 1850 if (tvp) 1851 vput(tvp); 1852 VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); 1853 vrele(fromnd.ni_dvp); 1854 vrele(fvp); 1855 } 1856 vrele(tond.ni_startdir); 1857 FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); 1858 out1: 1859 if (fromnd.ni_startdir) 1860 vrele(fromnd.ni_startdir); 1861 FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); 1862 if (error == -1) 1863 return (0); 1864 return (error); 1865 } 1866 1867 /* 1868 * Make a directory file. 1869 */ 1870 struct mkdir_args { 1871 char *path; 1872 int mode; 1873 }; 1874 /* ARGSUSED */ 1875 int 1876 mkdir(p, uap, retval) 1877 struct proc *p; 1878 register struct mkdir_args *uap; 1879 int *retval; 1880 { 1881 register struct vnode *vp; 1882 struct vattr vattr; 1883 int error; 1884 struct nameidata nd; 1885 1886 NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); 1887 nd.ni_cnd.cn_flags |= WILLBEDIR; 1888 error = namei(&nd); 1889 if (error) 1890 return (error); 1891 vp = nd.ni_vp; 1892 if (vp != NULL) { 1893 VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); 1894 if (nd.ni_dvp == vp) 1895 vrele(nd.ni_dvp); 1896 else 1897 vput(nd.ni_dvp); 1898 vrele(vp); 1899 return (EEXIST); 1900 } 1901 VATTR_NULL(&vattr); 1902 vattr.va_type = VDIR; 1903 vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask; 1904 LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 1905 error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); 1906 if (!error) 1907 vput(nd.ni_vp); 1908 return (error); 1909 } 1910 1911 /* 1912 * Remove a directory file. 1913 */ 1914 struct rmdir_args { 1915 char *path; 1916 }; 1917 /* ARGSUSED */ 1918 int 1919 rmdir(p, uap, retval) 1920 struct proc *p; 1921 struct rmdir_args *uap; 1922 int *retval; 1923 { 1924 register struct vnode *vp; 1925 int error; 1926 struct nameidata nd; 1927 1928 NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->path, p); 1929 error = namei(&nd); 1930 if (error) 1931 return (error); 1932 vp = nd.ni_vp; 1933 if (vp->v_type != VDIR) { 1934 error = ENOTDIR; 1935 goto out; 1936 } 1937 /* 1938 * No rmdir "." please. 1939 */ 1940 if (nd.ni_dvp == vp) { 1941 error = EINVAL; 1942 goto out; 1943 } 1944 /* 1945 * The root of a mounted filesystem cannot be deleted. 1946 */ 1947 if (vp->v_flag & VROOT) 1948 error = EBUSY; 1949 out: 1950 if (!error) { 1951 LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); 1952 LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); 1953 error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); 1954 } else { 1955 VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); 1956 if (nd.ni_dvp == vp) 1957 vrele(nd.ni_dvp); 1958 else 1959 vput(nd.ni_dvp); 1960 vput(vp); 1961 } 1962 return (error); 1963 } 1964 1965 #ifdef COMPAT_43 1966 /* 1967 * Read a block of directory entries in a file system independent format. 1968 */ 1969 struct ogetdirentries_args { 1970 int fd; 1971 char *buf; 1972 u_int count; 1973 long *basep; 1974 }; 1975 int 1976 ogetdirentries(p, uap, retval) 1977 struct proc *p; 1978 register struct ogetdirentries_args *uap; 1979 int *retval; 1980 { 1981 register struct vnode *vp; 1982 struct file *fp; 1983 struct uio auio, kuio; 1984 struct iovec aiov, kiov; 1985 struct dirent *dp, *edp; 1986 caddr_t dirbuf; 1987 int error, readcnt; 1988 long loff; 1989 1990 error = getvnode(p->p_fd, uap->fd, &fp); 1991 if (error) 1992 return (error); 1993 if ((fp->f_flag & FREAD) == 0) 1994 return (EBADF); 1995 vp = (struct vnode *)fp->f_data; 1996 if (vp->v_type != VDIR) 1997 return (EINVAL); 1998 aiov.iov_base = uap->buf; 1999 aiov.iov_len = uap->count; 2000 auio.uio_iov = &aiov; 2001 auio.uio_iovcnt = 1; 2002 auio.uio_rw = UIO_READ; 2003 auio.uio_segflg = UIO_USERSPACE; 2004 auio.uio_procp = p; 2005 auio.uio_resid = uap->count; 2006 VOP_LOCK(vp); 2007 loff = auio.uio_offset = fp->f_offset; 2008 # if (BYTE_ORDER != LITTLE_ENDIAN) 2009 if (vp->v_mount->mnt_maxsymlinklen <= 0) { 2010 error = VOP_READDIR(vp, &auio, fp->f_cred, NULL, NULL, NULL); 2011 fp->f_offset = auio.uio_offset; 2012 } else 2013 # endif 2014 { 2015 kuio = auio; 2016 kuio.uio_iov = &kiov; 2017 kuio.uio_segflg = UIO_SYSSPACE; 2018 kiov.iov_len = uap->count; 2019 MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); 2020 kiov.iov_base = dirbuf; 2021 error = VOP_READDIR(vp, &kuio, fp->f_cred, NULL, NULL, NULL); 2022 fp->f_offset = kuio.uio_offset; 2023 if (error == 0) { 2024 readcnt = uap->count - kuio.uio_resid; 2025 edp = (struct dirent *)&dirbuf[readcnt]; 2026 for (dp = (struct dirent *)dirbuf; dp < edp; ) { 2027 # if (BYTE_ORDER == LITTLE_ENDIAN) 2028 /* 2029 * The expected low byte of 2030 * dp->d_namlen is our dp->d_type. 2031 * The high MBZ byte of dp->d_namlen 2032 * is our dp->d_namlen. 2033 */ 2034 dp->d_type = dp->d_namlen; 2035 dp->d_namlen = 0; 2036 # else 2037 /* 2038 * The dp->d_type is the high byte 2039 * of the expected dp->d_namlen, 2040 * so must be zero'ed. 2041 */ 2042 dp->d_type = 0; 2043 # endif 2044 if (dp->d_reclen > 0) { 2045 dp = (struct dirent *) 2046 ((char *)dp + dp->d_reclen); 2047 } else { 2048 error = EIO; 2049 break; 2050 } 2051 } 2052 if (dp >= edp) 2053 error = uiomove(dirbuf, readcnt, &auio); 2054 } 2055 FREE(dirbuf, M_TEMP); 2056 } 2057 VOP_UNLOCK(vp); 2058 if (error) 2059 return (error); 2060 error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); 2061 *retval = uap->count - auio.uio_resid; 2062 return (error); 2063 } 2064 #endif 2065 2066 /* 2067 * Read a block of directory entries in a file system independent format. 2068 */ 2069 struct getdirentries_args { 2070 int fd; 2071 char *buf; 2072 u_int count; 2073 long *basep; 2074 }; 2075 int 2076 getdirentries(p, uap, retval) 2077 struct proc *p; 2078 register struct getdirentries_args *uap; 2079 int *retval; 2080 { 2081 register struct vnode *vp; 2082 struct file *fp; 2083 struct uio auio; 2084 struct iovec aiov; 2085 long loff; 2086 int error; 2087 2088 error = getvnode(p->p_fd, uap->fd, &fp); 2089 if (error) 2090 return (error); 2091 if ((fp->f_flag & FREAD) == 0) 2092 return (EBADF); 2093 vp = (struct vnode *)fp->f_data; 2094 unionread: 2095 if (vp->v_type != VDIR) 2096 return (EINVAL); 2097 aiov.iov_base = uap->buf; 2098 aiov.iov_len = uap->count; 2099 auio.uio_iov = &aiov; 2100 auio.uio_iovcnt = 1; 2101 auio.uio_rw = UIO_READ; 2102 auio.uio_segflg = UIO_USERSPACE; 2103 auio.uio_procp = p; 2104 auio.uio_resid = uap->count; 2105 VOP_LOCK(vp); 2106 loff = auio.uio_offset = fp->f_offset; 2107 error = VOP_READDIR(vp, &auio, fp->f_cred, NULL, NULL, NULL); 2108 fp->f_offset = auio.uio_offset; 2109 VOP_UNLOCK(vp); 2110 if (error) 2111 return (error); 2112 2113 #ifdef UNION 2114 { 2115 if ((uap->count == auio.uio_resid) && 2116 (vp->v_op == union_vnodeop_p)) { 2117 struct vnode *tvp = vp; 2118 2119 vp = union_lowervp(vp); 2120 if (vp != NULLVP) { 2121 VOP_LOCK(vp); 2122 error = VOP_OPEN(vp, FREAD, fp->f_cred, p); 2123 VOP_UNLOCK(vp); 2124 2125 if (error) { 2126 vrele(vp); 2127 return (error); 2128 } 2129 fp->f_data = (caddr_t) vp; 2130 fp->f_offset = 0; 2131 error = vn_close(tvp, FREAD, fp->f_cred, p); 2132 if (error) 2133 return (error); 2134 goto unionread; 2135 } 2136 } 2137 } 2138 #endif 2139 2140 if ((uap->count == auio.uio_resid) && 2141 vp && 2142 (vp->v_flag & VROOT) && 2143 (vp->v_mount->mnt_flag & MNT_UNION)) { 2144 struct vnode *tvp = vp; 2145 vp = vp->v_mount->mnt_vnodecovered; 2146 VREF(vp); 2147 fp->f_data = (caddr_t) vp; 2148 fp->f_offset = 0; 2149 vrele(tvp); 2150 goto unionread; 2151 } 2152 error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); 2153 *retval = uap->count - auio.uio_resid; 2154 return (error); 2155 } 2156 2157 /* 2158 * Set the mode mask for creation of filesystem nodes. 2159 */ 2160 struct umask_args { 2161 int newmask; 2162 }; 2163 mode_t /* XXX */ 2164 umask(p, uap, retval) 2165 struct proc *p; 2166 struct umask_args *uap; 2167 int *retval; 2168 { 2169 register struct filedesc *fdp; 2170 2171 fdp = p->p_fd; 2172 *retval = fdp->fd_cmask; 2173 fdp->fd_cmask = uap->newmask & ALLPERMS; 2174 return (0); 2175 } 2176 2177 /* 2178 * Void all references to file by ripping underlying filesystem 2179 * away from vnode. 2180 */ 2181 struct revoke_args { 2182 char *path; 2183 }; 2184 /* ARGSUSED */ 2185 int 2186 revoke(p, uap, retval) 2187 struct proc *p; 2188 register struct revoke_args *uap; 2189 int *retval; 2190 { 2191 register struct vnode *vp; 2192 struct vattr vattr; 2193 int error; 2194 struct nameidata nd; 2195 2196 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); 2197 error = namei(&nd); 2198 if (error) 2199 return (error); 2200 vp = nd.ni_vp; 2201 if (vp->v_type != VCHR && vp->v_type != VBLK) { 2202 error = EINVAL; 2203 goto out; 2204 } 2205 error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); 2206 if (error) 2207 goto out; 2208 if (p->p_ucred->cr_uid != vattr.va_uid && 2209 (error = suser(p->p_ucred, &p->p_acflag))) 2210 goto out; 2211 if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) 2212 vgoneall(vp); 2213 out: 2214 vrele(vp); 2215 return (error); 2216 } 2217 2218 /* 2219 * Convert a user file descriptor to a kernel file entry. 2220 */ 2221 int 2222 getvnode(fdp, fd, fpp) 2223 struct filedesc *fdp; 2224 struct file **fpp; 2225 int fd; 2226 { 2227 struct file *fp; 2228 2229 if ((u_int)fd >= fdp->fd_nfiles || 2230 (fp = fdp->fd_ofiles[fd]) == NULL) 2231 return (EBADF); 2232 if (fp->f_type != DTYPE_VNODE) 2233 return (EINVAL); 2234 *fpp = fp; 2235 return (0); 2236 } 2237