ref: d4124d48f9c7e85fdaa50640035080174c94196e
dir: /sys/src/cmd/diff/test/diff-t9.expected/
--- diff-t9.1 +++ diff-t9.2 @@ -1,4 +1,5 @@ -/* $NetBSD: vfs_syscalls.c,v 1.57 1995/10/07 06:28:51 mycroft Exp $ */ +/* $OpenBSD: t9.2,v 1.2 2013/12/01 16:40:56 krw Exp $ */ +/* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ /* * Copyright (c) 1989, 1993 @@ -17,11 +18,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -53,13 +50,30 @@ #include <sys/uio.h> #include <sys/malloc.h> #include <sys/dirent.h> +#include <sys/extattr.h> #include <sys/syscallargs.h> -#include <vm/vm.h> +#include <uvm/uvm_extern.h> #include <sys/sysctl.h> -static int change_dir __P((struct nameidata *ndp, struct proc *p)); +extern int suid_clear; +int usermount = 0; /* sysctl: by default, users may not mount */ + +static int change_dir(struct nameidata *, struct proc *); + +void checkdirs(struct vnode *); + +/* + * Redirection info so we don't have to include the union fs routines in + * the kernel directly. This way, we can build unionfs as an LKM. The + * pointer gets filled in later, when we modload the LKM, or when the + * compiled-in unionfs code gets initialized. For now, we just set + * it to a stub routine. + */ + +int (*union_check_p)(struct proc *, struct vnode **, + struct file *, struct uio, int *) = NULL; /* * Virtual File System System Calls @@ -69,6 +83,7 @@ * Mount a file system. */ /* ARGSUSED */ +int sys_mount(p, v, retval) struct proc *p; void *v; @@ -78,22 +93,36 @@ syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; - syscallarg(caddr_t) data; + syscallarg(void *) data; } */ *uap = v; register struct vnode *vp; register struct mount *mp; - int error, flag; - u_long fsindex; + int error, flag = 0; +#ifdef COMPAT_43 + u_long fstypenum = 0; +#endif char fstypename[MFSNAMELEN]; + char fspath[MNAMELEN]; struct vattr va; struct nameidata nd; + struct vfsconf *vfsp; + struct timeval tv; + + if (usermount == 0 && (error = suser(p->p_ucred, &p->p_acflag))) + return (error); + + /* + * Mount points must fit in MNAMELEN, not MAXPATHLEN. + */ + error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); + if (error) + return(error); /* * Get vnode to be covered */ - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, - SCARG(uap, path), p); - if (error = namei(&nd)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p); + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (SCARG(uap, flags) & MNT_UPDATE) { @@ -113,7 +142,7 @@ return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= - SCARG(uap, flags) & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); + SCARG(uap, flags) & (MNT_RELOAD | MNT_UPDATE); /* * Only root, or the user that did the original mount is * permitted to update it. @@ -134,7 +163,11 @@ } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } - VOP_UNLOCK(vp); + if ((error = vfs_busy(mp, LK_NOWAIT, 0, p)) != 0) { + vput(vp); + return (error); + } + VOP_UNLOCK(vp, 0, p); goto update; } /* @@ -143,7 +176,7 @@ */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && - (error = suser(p->p_ucred, &p->p_acflag)))) { + (error = suser(p->p_ucred, &p->p_acflag)))) { vput(vp); return (error); } @@ -158,40 +191,47 @@ } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } - if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) + if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } - if (error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, - (size_t *)0)) { -#if defined(COMPAT_09) || defined(COMPAT_43) + error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL); + if (error) { +#ifdef COMPAT_43 /* * Historically filesystem types were identified by number. * If we get an integer for the filesystem type instead of a * string, we check to see if it matches one of the historic * filesystem types. - */ - fsindex = (u_long)SCARG(uap, type); - if (fsindex >= nvfssw || vfssw[fsindex] == NULL) { + */ + fstypenum = (u_long)SCARG(uap, type); + + for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) + if (vfsp->vfc_typenum == fstypenum) + break; + if (vfsp == NULL) { vput(vp); return (ENODEV); } - strncpy(fstypename, vfssw[fsindex]->vfs_name, MFSNAMELEN); + strncpy(fstypename, vfsp->vfc_name, MFSNAMELEN); + #else vput(vp); return (error); #endif } - for (fsindex = 0; fsindex < nvfssw; fsindex++) - if (vfssw[fsindex] != NULL && - !strncmp(vfssw[fsindex]->vfs_name, fstypename, MFSNAMELEN)) + for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) { + if (!strcmp(vfsp->vfc_name, fstypename)) break; - if (fsindex >= nvfssw) { + } + + if (vfsp == NULL) { vput(vp); - return (ENODEV); + return (EOPNOTSUPP); } + if (vp->v_mountedhere != NULL) { vput(vp); return (EBUSY); @@ -203,15 +243,14 @@ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); - mp->mnt_op = vfssw[fsindex]; - if (error = vfs_lock(mp)) { - free((caddr_t)mp, M_MOUNT); - vput(vp); - return (error); - } - /* Do this early in case we block later. */ - vfssw[fsindex]->vfs_refcount++; - vp->v_mountedhere = mp; + lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0); + /* This error never happens, but it makes auditing easier */ + if ((error = vfs_busy(mp, LK_NOWAIT, 0, p))) + return (error); + mp->mnt_op = vfsp->vfc_vfsops; + mp->mnt_vfc = vfsp; + mp->mnt_flag |= (vfsp->vfc_flags & MNT_VISFLAGMASK); + strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid; update: @@ -223,13 +262,19 @@ else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_flag |= MNT_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | - MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); + MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_SOFTDEP | + MNT_NOATIME | MNT_FORCE); mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | - MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); + MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | + MNT_SOFTDEP | MNT_NOATIME | MNT_FORCE); /* * Mount the filesystem. */ error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); + if (!error) { + microtime(&tv); + mp->mnt_stat.f_ctime = tv.tv_sec; + } if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_flag & MNT_WANTRDWR) @@ -238,23 +283,42 @@ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); if (error) mp->mnt_flag = flag; + + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + if (mp->mnt_syncer == NULL) + error = vfs_allocate_syncvnode(mp); + } else { + if (mp->mnt_syncer != NULL) + vgone(mp->mnt_syncer); + mp->mnt_syncer = NULL; + } + + vfs_unbusy(mp, p); return (error); } + + vp->v_mountedhere = mp; + /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { + vfsp->vfc_refcount++; + simple_lock(&mountlist_slock); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); + simple_unlock(&mountlist_slock); checkdirs(vp); - VOP_UNLOCK(vp); - vfs_unlock(mp); + VOP_UNLOCK(vp, 0, p); + if ((mp->mnt_flag & MNT_RDONLY) == 0) + error = vfs_allocate_syncvnode(mp); + vfs_unbusy(mp, p); (void) VFS_STATFS(mp, &mp->mnt_stat, p); - error = VFS_START(mp, 0, p); + if ((error = VFS_START(mp, 0, p)) != 0) + vrele(vp); } else { mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; - vfssw[fsindex]->vfs_refcount--; - vfs_unlock(mp); + vfs_unbusy(mp, p); free((caddr_t)mp, M_MOUNT); vput(vp); } @@ -266,6 +330,7 @@ * or root directory onto which the new filesystem has just been * mounted. If so, replace them with the new mount point. */ +void checkdirs(olddp) struct vnode *olddp; { @@ -277,7 +342,7 @@ return; if (VFS_ROOT(olddp->v_mountedhere, &newdp)) panic("mount: lost mount"); - for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { + for (p = LIST_FIRST(&allproc); p != 0; p = LIST_NEXT(p, p_list)) { fdp = p->p_fd; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); @@ -305,6 +370,7 @@ * not special file (as before). */ /* ARGSUSED */ +int sys_unmount(p, v, retval) struct proc *p; void *v; @@ -321,7 +387,7 @@ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; mp = vp->v_mount; @@ -352,50 +418,77 @@ return (EINVAL); } vput(vp); - return (dounmount(mp, SCARG(uap, flags), p)); + + if (vfs_busy(mp, LK_EXCLUSIVE, NULL, p)) + return (EBUSY); + + return (dounmount(mp, SCARG(uap, flags), p, vp)); } /* * Do the actual file system unmount. */ -dounmount(mp, flags, p) - register struct mount *mp; - int flags; - struct proc *p; +int +dounmount(struct mount *mp, int flags, struct proc *p, struct vnode *olddp) { struct vnode *coveredvp; + struct proc *p2; int error; + int hadsyncer = 0; - coveredvp = mp->mnt_vnodecovered; - if (vfs_busy(mp)) - return (EBUSY); - mp->mnt_flag |= MNT_UNMOUNT; - if (error = vfs_lock(mp)) - return (error); - - mp->mnt_flag &=~ MNT_ASYNC; - vnode_pager_umount(mp); /* release cached vnodes */ - cache_purgevfs(mp); /* remove cache entries for this file sys */ - if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || - (flags & MNT_FORCE)) - error = VFS_UNMOUNT(mp, flags, p); - mp->mnt_flag &= ~MNT_UNMOUNT; - vfs_unbusy(mp); - if (error) { - vfs_unlock(mp); - } else { - TAILQ_REMOVE(&mountlist, mp, mnt_list); - if (coveredvp != NULLVP) { - vrele(coveredvp); - coveredvp->v_mountedhere = (struct mount *)0; - } - mp->mnt_op->vfs_refcount--; - vfs_unlock(mp); - if (mp->mnt_vnodelist.lh_first != NULL) - panic("unmount: dangling vnode"); - free((caddr_t)mp, M_MOUNT); - } - return (error); + mp->mnt_flag &=~ MNT_ASYNC; + cache_purgevfs(mp); /* remove cache entries for this file sys */ + if (mp->mnt_syncer != NULL) { + hadsyncer = 1; + vgone(mp->mnt_syncer); + mp->mnt_syncer = NULL; + } + if (((mp->mnt_flag & MNT_RDONLY) || + (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || + (flags & MNT_FORCE)) + error = VFS_UNMOUNT(mp, flags, p); + simple_lock(&mountlist_slock); + if (error) { + if ((mp->mnt_flag & MNT_RDONLY) == 0 && hadsyncer) + (void) vfs_allocate_syncvnode(mp); + lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, + &mountlist_slock, p); + return (error); + } + TAILQ_REMOVE(&mountlist, mp, mnt_list); + if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { + if (olddp) { + /* + * Try to put processes back in a real directory + * after a forced unmount. + * XXX We're not holding a ref on olddp, which may + * change, so compare id numbers. + */ + LIST_FOREACH(p2, &allproc, p_list) { + struct filedesc *fdp = p2->p_fd; + if (fdp->fd_cdir && + fdp->fd_cdir->v_id == olddp->v_id) { + vrele(fdp->fd_cdir); + vref(coveredvp); + fdp->fd_cdir = coveredvp; + } + if (fdp->fd_rdir && + fdp->fd_rdir->v_id == olddp->v_id) { + vrele(fdp->fd_rdir); + vref(coveredvp); + fdp->fd_rdir = coveredvp; + } + } + } + coveredvp->v_mountedhere = NULL; + vrele(coveredvp); + } + mp->mnt_vfc->vfc_refcount--; + if (mp->mnt_vnodelist.lh_first != NULL) + panic("unmount: dangling vnode"); + lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); + free((caddr_t)mp, M_MOUNT); + return (0); } /* @@ -407,6 +500,7 @@ #endif /* ARGSUSED */ +int sys_sync(p, v, retval) struct proc *p; void *v; @@ -415,31 +509,23 @@ register struct mount *mp, *nmp; int asyncflag; - for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { - /* - * Get the next pointer in case we hang on vfs_busy - * while we are being unmounted. - */ - nmp = mp->mnt_list.cqe_next; - /* - * The lock check below is to avoid races with mount - * and unmount. - */ - if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && - !vfs_busy(mp)) { + simple_lock(&mountlist_slock); + TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mnt_list, nmp) { + if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) + continue; + if ((mp->mnt_flag & MNT_RDONLY) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; + uvm_vnp_sync(mp); VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); if (asyncflag) mp->mnt_flag |= MNT_ASYNC; - /* - * Get the next pointer again, as the next filesystem - * might have been unmounted while we were sync'ing. - */ - nmp = mp->mnt_list.cqe_next; - vfs_unbusy(mp); } + simple_lock(&mountlist_slock); + vfs_unbusy(mp, p); } + simple_unlock(&mountlist_slock); + #ifdef DEBUG if (syncprt) vfs_bufstats(); @@ -451,6 +537,7 @@ * Change filesystem quotas. */ /* ARGSUSED */ +int sys_quotactl(p, v, retval) struct proc *p; void *v; @@ -467,7 +554,7 @@ struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; vrele(nd.ni_vp); @@ -479,6 +566,7 @@ * Get filesystem statistics. */ /* ARGSUSED */ +int sys_statfs(p, v, retval) struct proc *p; void *v; @@ -492,16 +580,27 @@ register struct statfs *sp; int error; struct nameidata nd; + struct statfs sb; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; vrele(nd.ni_vp); - if (error = VFS_STATFS(mp, sp, p)) + if ((error = VFS_STATFS(mp, sp, p)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; +#if notyet + if (mp->mnt_flag & MNT_SOFTDEP) + sp->f_eflags = STATFS_SOFTUPD; +#endif + /* Don't let non-root see filesystem id (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) { + bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); + sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + sp = &sb; + } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } @@ -509,33 +608,48 @@ * Get filesystem statistics. */ /* ARGSUSED */ +int sys_fstatfs(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_fstatfs_args /* { + struct sys_fstatfs_args /* { syscallarg(int) fd; syscallarg(struct statfs *) buf; } */ *uap = v; struct file *fp; struct mount *mp; - register struct statfs *sp; + struct statfs *sp; int error; + struct statfs sb; - if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); mp = ((struct vnode *)fp->f_data)->v_mount; sp = &mp->mnt_stat; - if (error = VFS_STATFS(mp, sp, p)) + error = VFS_STATFS(mp, sp, p); + FRELE(fp); + if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; +#if notyet + if (mp->mnt_flag & MNT_SOFTDEP) + sp->f_eflags = STATFS_SOFTUPD; +#endif + /* Don't let non-root see filesystem id (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) { + bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); + sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + sp = &sb; + } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get statistics on all filesystems. */ +int sys_getfsstat(p, v, retval) struct proc *p; void *v; @@ -543,37 +657,59 @@ { register struct sys_getfsstat_args /* { syscallarg(struct statfs *) buf; - syscallarg(long) bufsize; + syscallarg(size_t) bufsize; syscallarg(int) flags; } */ *uap = v; - register struct mount *mp, *nmp; + register struct mount *mp *nmp; register struct statfs *sp; + struct statfs sb; caddr_t sfsp; - long count, maxcount, error; + size_t count, maxcount; + int error, flags = SCARG(uap, flags); maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); sfsp = (caddr_t)SCARG(uap, buf); - for (count = 0, - mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { - nmp = mp->mnt_list.cqe_next; - if (sfsp && count < maxcount && - ((mp->mnt_flag & MNT_MLOCK) == 0)) { + count = 0; + simple_lock(&mountlist_slock); + TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mnt_list, nmp) { + if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) + continue; + if (sfsp && count < maxcount) { sp = &mp->mnt_stat; - /* - * If MNT_NOWAIT is specified, do not refresh the - * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. - */ - if (((SCARG(uap, flags) & MNT_NOWAIT) == 0 || - (SCARG(uap, flags) & MNT_WAIT)) && - (error = VFS_STATFS(mp, sp, p))) - continue; + + /* Refresh stats unless MNT_NOWAIT is specified */ + if (flags != MNT_NOWAIT && + flags != MNT_LAZY && + (flags == MNT_WAIT || + flags == 0) && + (error = VFS_STATFS(mp, sp, p))) { + simple_lock(&mountlist_slock); + vfs_unbusy(mp, p); + continue; + } + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; - if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp))) +#if notyet + if (mp->mnt_flag & MNT_SOFTDEP) + sp->f_eflags = STATFS_SOFTUPD; +#endif + if (suser(p->p_ucred, &p->p_acflag)) { + bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); + sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + sp = &sb; + } + error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); + if (error) { + vfs_unbusy(mp, p); return (error); + } sfsp += sizeof(*sp); } count++; + simple_lock(&mountlist_slock); + vfs_unbusy(mp, p); } + simple_unlock(&mountlist_slock); if (sfsp && count > maxcount) *retval = maxcount; else @@ -585,6 +721,7 @@ * Change current working directory to a given file descriptor. */ /* ARGSUSED */ +int sys_fchdir(p, v, retval) struct proc *p; void *v; @@ -593,37 +730,38 @@ struct sys_fchdir_args /* { syscallarg(int) fd; } */ *uap = v; - register struct filedesc *fdp = p->p_fd; + struct filedesc *fdp = p->p_fd; struct vnode *vp, *tdp; struct mount *mp; struct file *fp; int error; - if (error = getvnode(fdp, SCARG(uap, fd), &fp)) + if ((error = getvnode(fdp, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VREF(vp); - VOP_LOCK(vp); + FRELE(fp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + while (!error && (mp = vp->v_mountedhere) != NULL) { - if (mp->mnt_flag & MNT_MLOCK) { - mp->mnt_flag |= MNT_MWAIT; - sleep((caddr_t)mp, PVFS); + if (vfs_busy(mp, 0, 0, p)) continue; - } - if (error = VFS_ROOT(mp, &tdp)) + error = VFS_ROOT(mp, &tdp); + vfs_unbusy(mp, p); + if (error) break; vput(vp); vp = tdp; } - VOP_UNLOCK(vp); if (error) { - vrele(vp); + vput(vp); return (error); } + VOP_UNLOCK(vp, 0, p); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); @@ -633,6 +771,7 @@ * Change current working directory (``.''). */ /* ARGSUSED */ +int sys_chdir(p, v, retval) struct proc *p; void *v; @@ -647,7 +786,7 @@ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = change_dir(&nd, p)) + if ((error = change_dir(&nd, p)) != 0) return (error); vrele(fdp->fd_cdir); fdp->fd_cdir = nd.ni_vp; @@ -658,6 +797,7 @@ * Change notion of root (``/'') directory. */ /* ARGSUSED */ +int sys_chroot(p, v, retval) struct proc *p; void *v; @@ -670,14 +810,22 @@ int error; struct nameidata nd; - if (error = suser(p->p_ucred, &p->p_acflag)) + if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = change_dir(&nd, p)) + if ((error = change_dir(&nd, p)) != 0) return (error); - if (fdp->fd_rdir != NULL) + if (fdp->fd_rdir != NULL) { + /* + * A chroot() done inside a changed root environment does + * an automatic chdir to avoid the out-of-tree experience. + */ vrele(fdp->fd_rdir); + vrele(fdp->fd_cdir); + VREF(nd.ni_vp); + fdp->fd_cdir = nd.ni_vp; + } fdp->fd_rdir = nd.ni_vp; return (0); } @@ -693,16 +841,17 @@ struct vnode *vp; int error; - if (error = namei(ndp)) + if ((error = namei(ndp)) != 0) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); - VOP_UNLOCK(vp); if (error) - vrele(vp); + vput(vp); + else + VOP_UNLOCK(vp, 0, p); return (error); } @@ -710,45 +859,50 @@ * Check permissions, allocate an open file structure, * and call the device open routine if any. */ +int sys_open(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_open_args /* { + struct sys_open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ *uap = v; - register struct filedesc *fdp = p->p_fd; - register struct file *fp; - register struct vnode *vp; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + struct vattr vattr; int flags, cmode; - struct file *nfp; - int type, indx, error; + int type, indx, error, localtrunc = 0; struct flock lf; struct nameidata nd; - extern struct fileops vnops; - if (error = falloc(p, &nfp, &indx)) + if ((error = falloc(p, &fp, &indx)) != 0) return (error); - fp = nfp; + flags = FFLAGS(SCARG(uap, flags)); cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ - if (error = vn_open(&nd, flags, cmode)) { - ffree(fp); + if ((flags & O_TRUNC) && (flags & (O_EXLOCK | O_SHLOCK))) { + localtrunc = 1; + flags &= ~O_TRUNC; /* Must do truncate ourselves */ + } + if ((error = vn_open(&nd, flags, cmode)) != 0) { if ((error == ENODEV || error == ENXIO) && p->p_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { + closef(fp, p); *retval = indx; return (0); } if (error == ERESTART) error = EINTR; - fdp->fd_ofiles[indx] = NULL; + fdremove(fdp, indx); + closef(fp, p); return (error); } p->p_dupfd = 0; @@ -768,189 +922,463 @@ type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; - VOP_UNLOCK(vp); - if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) { - (void) vn_close(vp, fp->f_flag, fp->f_cred, p); - ffree(fp); - fdp->fd_ofiles[indx] = NULL; + VOP_UNLOCK(vp, 0, p); + error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); + if (error) { + /* closef will vn_close the file for us. */ + fdremove(fdp, indx); + closef(fp, p); return (error); } - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); fp->f_flag |= FHASLOCK; } - VOP_UNLOCK(vp); + if (localtrunc) { + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + if ((fp->f_flag & FWRITE) == 0) + error = EACCES; + else if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = 0; + error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); + } + if (error) { + VOP_UNLOCK(vp, 0, p); + /* closef will close the file for us. */ + fdremove(fdp, indx); + closef(fp, p); + return (error); + } + } + VOP_UNLOCK(vp, 0, p); *retval = indx; + FILE_SET_MATURE(fp); return (0); } /* - * Create a special file. + * Get file handle system call */ -/* ARGSUSED */ -sys_mknod(p, v, retval) +int +sys_getfh(p, v, retval) struct proc *p; - void *v; + register void *v; register_t *retval; { - register struct sys_mknod_args /* { - syscallarg(char *) path; - syscallarg(int) mode; - syscallarg(int) dev; + register struct sys_getfh_args /* { + syscallarg(char *) fname; + syscallarg(fhandle_t *) fhp; } */ *uap = v; register struct vnode *vp; - struct vattr vattr; + fhandle_t fh; int error; - int whiteout; struct nameidata nd; - if (error = suser(p->p_ucred, &p->p_acflag)) + /* + * Must be super user + */ + error = suser(p->p_ucred, &p->p_acflag); + if (error) return (error); - NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, fname), p); + error = namei(&nd); + if (error) return (error); vp = nd.ni_vp; - if (vp != NULL) - error = EEXIST; - else { - VATTR_NULL(&vattr); - vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; - vattr.va_rdev = SCARG(uap, dev); - whiteout = 0; - - switch (SCARG(uap, mode) & S_IFMT) { - case S_IFMT: /* used by badsect to flag bad sectors */ - vattr.va_type = VBAD; - break; - case S_IFCHR: - vattr.va_type = VCHR; - break; - case S_IFBLK: - vattr.va_type = VBLK; - break; - case S_IFWHT: - whiteout = 1; - break; - default: - error = EINVAL; - break; - } - } - if (!error) { - VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); - if (whiteout) { - error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); - if (error) - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - vput(nd.ni_dvp); - } else { - error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, - &nd.ni_cnd, &vattr); - } - } else { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (vp) - vrele(vp); - } + bzero((caddr_t)&fh, sizeof(fh)); + fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; + error = VFS_VPTOFH(vp, &fh.fh_fid); + vput(vp); + if (error) + return (error); + error = copyout((caddr_t)&fh, (caddr_t)SCARG(uap, fhp), sizeof (fh)); return (error); } /* - * Create a named pipe. + * Open a file given a file handle. + * + * Check permissions, allocate an open file structure, + * and call the device open routine if any. */ -/* ARGSUSED */ -sys_mkfifo(p, v, retval) +int +sys_fhopen(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_mkfifo_args /* { - syscallarg(char *) path; - syscallarg(int) mode; + register struct sys_fhopen_args /* { + syscallarg(const fhandle_t *) fhp; + syscallarg(int) flags; } */ *uap = v; - struct vattr vattr; - int error; - struct nameidata nd; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp = NULL; + struct mount *mp; + struct ucred *cred = p->p_ucred; + int flags; + int type, indx, error=0; + struct flock lf; + struct vattr va; + fhandle_t fh; -#ifndef FIFO - return (EOPNOTSUPP); -#else - NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + /* + * Must be super user + */ + if ((error = suser(p->p_ucred, &p->p_acflag))) return (error); - if (nd.ni_vp != NULL) { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(nd.ni_vp); - return (EEXIST); - } - VATTR_NULL(&vattr); - vattr.va_type = VFIFO; - vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; - VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); - return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); -#endif /* FIFO */ -} -/* - * Make a hard file link. - */ -/* ARGSUSED */ -sys_link(p, v, retval) - struct proc *p; - void *v; - register_t *retval; -{ - register struct sys_link_args /* { - syscallarg(char *) path; - syscallarg(char *) link; - } */ *uap = v; - register struct vnode *vp; - struct nameidata nd; - int error; + flags = FFLAGS(SCARG(uap, flags)); + if ((flags & (FREAD | FWRITE)) == 0) + return (EINVAL); + if ((flags & O_CREAT)) + return (EINVAL); - NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = falloc(p, &fp, &indx)) != 0) return (error); - vp = nd.ni_vp; - if (vp->v_type != VDIR || - (error = suser(p->p_ucred, &p->p_acflag)) == 0) { - nd.ni_cnd.cn_nameiop = CREATE; - nd.ni_cnd.cn_flags = LOCKPARENT; - nd.ni_dirp = SCARG(uap, link); - if ((error = namei(&nd)) == 0) { - if (nd.ni_vp != NULL) - error = EEXIST; - if (!error) { - VOP_LEASE(nd.ni_dvp, p, p->p_ucred, - LEASE_WRITE); - VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); - } else { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (nd.ni_vp) - vrele(nd.ni_vp); - } - } + + if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) + goto bad; + + if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) { + error = ESTALE; + goto bad; } - vrele(vp); - return (error); -} + + if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp)) != 0) { + vp = NULL; /* most likely unnecessary sanity for bad: */ + goto bad; + } + + /* Now do an effective vn_open */ + + if (vp->v_type == VSOCK) { + error = EOPNOTSUPP; + goto bad; + } + if (flags & FREAD) { + if ((error = VOP_ACCESS(vp, VREAD, cred, p)) != 0) + goto bad; + } + if (flags & (FWRITE | O_TRUNC)) { + if (vp->v_type == VDIR) { + error = EISDIR; + goto bad; + } + if ((error = vn_writechk(vp)) != 0 || + (error = VOP_ACCESS(vp, VWRITE, cred, p)) != 0) + goto bad; + } + if (flags & O_TRUNC) { + VOP_UNLOCK(vp, 0, p); /* XXX */ + VOP_LEASE(vp, p, cred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ + VATTR_NULL(&va); + va.va_size = 0; + if ((error = VOP_SETATTR(vp, &va, cred, p)) != 0) + goto bad; + } + if ((error = VOP_OPEN(vp, flags, cred, p)) != 0) + goto bad; + if (flags & FWRITE) + vp->v_writecount++; + + /* done with modified vn_open, now finish what sys_open does. */ + + fp->f_flag = flags & FMASK; + fp->f_type = DTYPE_VNODE; + fp->f_ops = &vnops; + fp->f_data = (caddr_t)vp; + if (flags & (O_EXLOCK | O_SHLOCK)) { + lf.l_whence = SEEK_SET; + lf.l_start = 0; + lf.l_len = 0; + if (flags & O_EXLOCK) + lf.l_type = F_WRLCK; + else + lf.l_type = F_RDLCK; + type = F_FLOCK; + if ((flags & FNONBLOCK) == 0) + type |= F_WAIT; + VOP_UNLOCK(vp, 0, p); + error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); + if (error) { + /* closef will vn_close the file for us. */ + fdremove(fdp, indx); + closef(fp, p); + return (error); + } + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + fp->f_flag |= FHASLOCK; + } + VOP_UNLOCK(vp, 0, p); + *retval = indx; + FILE_SET_MATURE(fp); + return (0); + +bad: + fdremove(fdp, indx); + closef(fp, p); + if (vp != NULL) + vput(vp); + return (error); +} + +/* ARGSUSED */ +int +sys_fhstat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fhstat_args /* { + syscallarg(const fhandle_t *) fhp; + syscallarg(struct stat *) sb; + } */ *uap = v; + struct stat sb; + int error; + fhandle_t fh; + struct mount *mp; + struct vnode *vp; + + /* + * Must be super user + */ + if ((error = suser(p->p_ucred, &p->p_acflag))) + return (error); + + if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) + return (error); + + if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) + return (ESTALE); + if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) + return (error); + error = vn_stat(vp, &sb, p); + vput(vp); + if (error) + return (error); + error = copyout(&sb, SCARG(uap, sb), sizeof(sb)); + return (error); +} + +/* ARGSUSED */ +int +sys_fhstatfs(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fhstatfs_args /* + syscallarg(const fhandle_t *) fhp; + syscallarg(struct statfs *) buf; + } */ *uap = v; + struct statfs sp; + fhandle_t fh; + struct mount *mp; + struct vnode *vp; + int error; + + /* + * Must be super user + */ + if ((error = suser(p->p_ucred, &p->p_acflag))) + return (error); + + if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) + return (error); + + if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) + return (ESTALE); + if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) + return (error); + mp = vp->v_mount; + vput(vp); + if ((error = VFS_STATFS(mp, &sp, p)) != 0) + return (error); + sp.f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + return (copyout(&sp, SCARG(uap, buf), sizeof(sp))); +} + +/* + * Create a special file. + */ +/* ARGSUSED */ +int +sys_mknod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mknod_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + syscallarg(int) dev; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + int whiteout = 0; + struct nameidata nd; + + if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) + return (error); + if (p->p_fd->fd_rdir) + return (EINVAL); + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp != NULL) + error = EEXIST; + else { + VATTR_NULL(&vattr); + vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; + vattr.va_rdev = SCARG(uap, dev); + whiteout = 0; + + switch (SCARG(uap, mode) & S_IFMT) { + case S_IFMT: /* used by badsect to flag bad sectors */ + vattr.va_type = VBAD; + break; + case S_IFCHR: + vattr.va_type = VCHR; + break; + case S_IFBLK: + vattr.va_type = VBLK; + break; + case S_IFWHT: + whiteout = 1; + break; + default: + error = EINVAL; + break; + } + } + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (whiteout) { + error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); + if (error) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + } else { + error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, + &nd.ni_cnd, &vattr); + } + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (vp) + vrele(vp); + } + return (error); +} + +/* + * Create a named pipe. + */ +/* ARGSUSED */ +int +sys_mkfifo(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ +#ifndef FIFO + return (EOPNOTSUPP); +#else + register struct sys_mkfifo_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + } */ *uap = v; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + if (nd.ni_vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + return (EEXIST); + } + VATTR_NULL(&vattr); + vattr.va_type = VFIFO; + vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); +#endif /* FIFO */ +} + +/* + * Make a hard file link. + */ +/* ARGSUSED */ +int +sys_link(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_link_args /* { + syscallarg(char *) path; + syscallarg(char *) link; + } */ *uap = v; + register struct vnode *vp; + struct nameidata nd; + int error; + int flags; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + + flags = LOCKPARENT; + if (vp->v_type == VDIR) { + flags |= STRIPSLASHES; + } + + NDINIT(&nd, CREATE, flags, UIO_USERSPACE, SCARG(uap, link), p); + if ((error = namei(&nd)) != 0) + goto out; + if (nd.ni_vp) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + error = EEXIST; + goto out; + } + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); +out: + vrele(vp); + return (error); +} /* * Make a symbolic link. */ /* ARGSUSED */ +int sys_symlink(p, v, retval) struct proc *p; void *v; @@ -966,10 +1394,11 @@ struct nameidata nd; MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); - if (error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, (size_t *)0)) + error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, NULL); + if (error) goto out; NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, link), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); @@ -994,6 +1423,7 @@ * Delete a whiteout from the filesystem. */ /* ARGSUSED */ +int sys_undelete(p, v, retval) struct proc *p; void *v; @@ -1023,7 +1453,7 @@ } VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); - if (error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) + if ((error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) != 0) VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); return (error); @@ -1033,6 +1463,7 @@ * Delete a name from the filesystem. */ /* ARGSUSED */ +int sys_unlink(p, v, retval) struct proc *p; void *v; @@ -1045,42 +1476,39 @@ int error; struct nameidata nd; - NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; - VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); - - if (vp->v_type != VDIR || - (error = suser(p->p_ucred, &p->p_acflag)) == 0) { - /* - * The root of a mounted filesystem cannot be deleted. - */ - if (vp->v_flag & VROOT) - error = EBUSY; - else - (void)vnode_pager_uncache(vp); - } - if (!error) { - VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); - error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); - } else { + /* + * The root of a mounted filesystem cannot be deleted. + */ + if (vp->v_flag & VROOT) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); - if (vp != NULLVP) - vput(vp); + vput(vp); + error = EBUSY; + goto out; } + + (void)uvm_vnp_uncache(vp); + + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); +out: return (error); } /* * Reposition read/write file offset. */ +int sys_lseek(p, v, retval) struct proc *p; void *v; @@ -1096,24 +1524,38 @@ register struct filedesc *fdp = p->p_fd; register struct file *fp; struct vattr vattr; - int error; + struct vnode *vp; + int error, special; - if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || - (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) + if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); + vp = (struct vnode *)fp->f_data; + if (vp->v_type == VFIFO) + return (ESPIPE); + if (vp->v_type == VCHR) + special = 1; + else + special = 0; switch (SCARG(uap, whence)) { - case L_INCR: + case SEEK_CUR: + if (!special && fp->f_offset + SCARG(uap, offset) < 0) + return (EINVAL); fp->f_offset += SCARG(uap, offset); break; - case L_XTND: - if (error = - VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p)) + case SEEK_END: + error = VOP_GETATTR((struct vnode *)fp->f_data, &vattr, + cred, p); + if (error) return (error); + if (!special && (off_t)vattr.va_size + SCARG(uap, offset) < 0) + return (EINVAL); fp->f_offset = SCARG(uap, offset) + vattr.va_size; break; - case L_SET: + case SEEK_SET: + if (!special && SCARG(uap, offset) < 0) + return (EINVAL); fp->f_offset = SCARG(uap, offset); break; default: @@ -1126,6 +1568,7 @@ /* * Check access permissions. */ +int sys_access(p, v, retval) struct proc *p; void *v; @@ -1140,13 +1583,15 @@ int error, flags, t_gid, t_uid; struct nameidata nd; + if (SCARG(uap, flags) & ~(R_OK | W_OK | X_OK)) + return (EINVAL); t_uid = cred->cr_uid; t_gid = cred->cr_gid; cred->cr_uid = p->p_cred->p_ruid; cred->cr_gid = p->p_cred->p_rgid; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; @@ -1173,6 +1618,7 @@ * Get file status; this version follows links. */ /* ARGSUSED */ +int sys_stat(p, v, retval) struct proc *p; void *v; @@ -1188,12 +1634,15 @@ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); + /* Don't let non-root see generation numbers (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) + sb.st_gen = 0; error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } @@ -1202,6 +1651,7 @@ * Get file status; this version does not follow links. */ /* ARGSUSED */ +int sys_lstat(p, v, retval) struct proc *p; void *v; @@ -1211,47 +1661,21 @@ syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap = v; + struct stat sb; int error; - struct vnode *vp, *dvp; - struct stat sb, sb1; struct nameidata nd; - NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); - /* - * For symbolic links, always return the attributes of its - * containing directory, except for mode, size, and links. - */ - vp = nd.ni_vp; - dvp = nd.ni_dvp; - if (vp->v_type != VLNK) { - if (dvp == vp) - vrele(dvp); - else - vput(dvp); - error = vn_stat(vp, &sb, p); - vput(vp); - if (error) - return (error); - } else { - error = vn_stat(dvp, &sb, p); - vput(dvp); - if (error) { - vput(vp); - return (error); - } - error = vn_stat(vp, &sb1, p); - vput(vp); - if (error) - return (error); - sb.st_mode &= ~S_IFDIR; - sb.st_mode |= S_IFLNK; - sb.st_nlink = sb1.st_nlink; - sb.st_size = sb1.st_size; - sb.st_blocks = sb1.st_blocks; - } + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); + if (error) + return (error); + /* Don't let non-root see generation numbers (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) + sb.st_gen = 0; error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } @@ -1260,6 +1684,7 @@ * Get configurable pathname variables. */ /* ARGSUSED */ +int sys_pathconf(p, v, retval) struct proc *p; void *v; @@ -1274,7 +1699,7 @@ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), retval); vput(nd.ni_vp); @@ -1285,6 +1710,7 @@ * Return target name of a symbolic link. */ /* ARGSUSED */ +int sys_readlink(p, v, retval) struct proc *p; void *v; @@ -1293,7 +1719,7 @@ register struct sys_readlink_args /* { syscallarg(char *) path; syscallarg(char *) buf; - syscallarg(int) count; + syscallarg(size_t) count; } */ *uap = v; register struct vnode *vp; struct iovec aiov; @@ -1303,7 +1729,7 @@ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VLNK) @@ -1329,6 +1755,7 @@ * Change flags of a file given a path name. */ /* ARGSUSED */ +int sys_chflags(p, v, retval) struct proc *p; void *v; @@ -1336,7 +1763,7 @@ { register struct sys_chflags_args /* { syscallarg(char *) path; - syscallarg(int) flags; + syscallarg(unsigned int) flags; } */ *uap = v; register struct vnode *vp; struct vattr vattr; @@ -1344,18 +1771,29 @@ struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; + else if (SCARG(uap, flags) == VNOVAL) + error = EINVAL; else { + if (suser(p->p_ucred, &p->p_acflag)) { + if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) + goto out; + if (vattr.va_type == VCHR || vattr.va_type == VBLK) { + error = EINVAL; + goto out; + } + } VATTR_NULL(&vattr); vattr.va_flags = SCARG(uap, flags); error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } +out: vput(vp); return (error); } @@ -1364,33 +1802,47 @@ * Change flags of a file given a file descriptor. */ /* ARGSUSED */ +int sys_fchflags(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_fchflags_args /* { + struct sys_fchflags_args /* { syscallarg(int) fd; - syscallarg(int) flags; + syscallarg(unsigned int) flags; } */ *uap = v; struct vattr vattr; struct vnode *vp; struct file *fp; int error; - if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; + else if (SCARG(uap, flags) == VNOVAL) + error = EINVAL; else { + if (suser(p->p_ucred, &p->p_acflag)) { + if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) + != 0) + goto out; + if (vattr.va_type == VCHR || vattr.va_type == VBLK) { + error = EINVAL; + goto out; + } + } VATTR_NULL(&vattr); vattr.va_flags = SCARG(uap, flags); error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VOP_UNLOCK(vp); +out: + VOP_UNLOCK(vp, 0, p); + FRELE(fp); return (error); } @@ -1398,6 +1850,7 @@ * Change mode of a file given path name. */ /* ARGSUSED */ +int sys_chmod(p, v, retval) struct proc *p; void *v; @@ -1412,12 +1865,15 @@ int error; struct nameidata nd; + if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS)) + return (EINVAL); + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { @@ -1433,12 +1889,13 @@ * Change mode of a file given a file descriptor. */ /* ARGSUSED */ +int sys_fchmod(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_fchmod_args /* { + struct sys_fchmod_args /* { syscallarg(int) fd; syscallarg(int) mode; } */ *uap = v; @@ -1447,11 +1904,14 @@ struct file *fp; int error; - if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS)) + return (EINVAL); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { @@ -1459,7 +1919,8 @@ vattr.va_mode = SCARG(uap, mode) & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VOP_UNLOCK(vp); + VOP_UNLOCK(vp, 0, p); + FRELE(fp); return (error); } @@ -1467,6 +1928,7 @@ * Set ownership given a path name. */ /* ARGSUSED */ +int sys_chown(p, v, retval) struct proc *p; void *v; @@ -1481,21 +1943,87 @@ struct vattr vattr; int error; struct nameidata nd; + u_short mode; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && + (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { + error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); + if (error) + goto out; + mode = vattr.va_mode & ~(VSUID | VSGID); + if (mode == vattr.va_mode) + mode = VNOVAL; + } + else + mode = VNOVAL; + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); + vattr.va_mode = mode; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +out: + vput(vp); + return (error); +} + +/* + * Set ownership given a path name, without following links. + */ +/* ARGSUSED */ +int +sys_lchown(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_lchown_args /* { + syscallarg(char *) path; + syscallarg(int) uid; + syscallarg(int) gid; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + u_short mode; + + NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { + if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && + (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { + error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); + if (error) + goto out; + mode = vattr.va_mode & ~(VSUID | VSGID); + if (mode == vattr.va_mode) + mode = VNOVAL; + } + else + mode = VNOVAL; VATTR_NULL(&vattr); vattr.va_uid = SCARG(uap, uid); vattr.va_gid = SCARG(uap, gid); + vattr.va_mode = mode; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } +out: vput(vp); return (error); } @@ -1504,42 +2032,58 @@ * Set ownership given a file descriptor. */ /* ARGSUSED */ +int sys_fchown(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_fchown_args /* { + struct sys_fchown_args /* { syscallarg(int) fd; syscallarg(int) uid; syscallarg(int) gid; } */ *uap = v; - struct vattr vattr; struct vnode *vp; - struct file *fp; + struct vattr vattr; int error; + struct file *fp; + u_short mode; - if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { + if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && + (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { + error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); + if (error) + goto out; + mode = vattr.va_mode & ~(VSUID | VSGID); + if (mode == vattr.va_mode) + mode = VNOVAL; + } else + mode = VNOVAL; VATTR_NULL(&vattr); vattr.va_uid = SCARG(uap, uid); vattr.va_gid = SCARG(uap, gid); + vattr.va_mode = mode; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VOP_UNLOCK(vp); +out: + VOP_UNLOCK(vp, 0, p); + FRELE(fp); return (error); } /* - * Set the access and modification times of a file. + * Set the access and modification times given a path name. */ /* ARGSUSED */ +int sys_utimes(p, v, retval) struct proc *p; void *v; @@ -1560,32 +2104,97 @@ microtime(&tv[0]); tv[1] = tv[0]; vattr.va_vaflags |= VA_UTIMES_NULL; - } else if (error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, - sizeof (tv))) - return (error); + } else { + error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, + sizeof (tv)); + if (error) + return (error); + /* XXX workaround timeval matching the VFS constant VNOVAL */ + if (tv[0].tv_sec == VNOVAL) + tv[0].tv_sec = VNOVAL - 1; + if (tv[1].tv_sec == VNOVAL) + tv[1].tv_sec = VNOVAL - 1; + } NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { - vattr.va_atime.ts_sec = tv[0].tv_sec; - vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000; - vattr.va_mtime.ts_sec = tv[1].tv_sec; - vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000; + vattr.va_atime.tv_sec = tv[0].tv_sec; + vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000; + vattr.va_mtime.tv_sec = tv[1].tv_sec; + vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } + +/* + * Set the access and modification times given a file descriptor. + */ +/* ARGSUSED */ +int +sys_futimes(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_futimes_args /* { + syscallarg(int) fd; + syscallarg(struct timeval *) tptr; + } */ *uap = v; + struct vnode *vp; + struct timeval tv[2]; + struct vattr vattr; + int error; + struct file *fp; + + VATTR_NULL(&vattr); + if (SCARG(uap, tptr) == NULL) { + microtime(&tv[0]); + tv[1] = tv[0]; + vattr.va_vaflags |= VA_UTIMES_NULL; + } else { + error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, + sizeof (tv)); + if (error) + return (error); + /* XXX workaround timeval matching the VFS constant VNOVAL */ + if (tv[0].tv_sec == VNOVAL) + tv[0].tv_sec = VNOVAL - 1; + if (tv[1].tv_sec == VNOVAL) + tv[1].tv_sec = VNOVAL - 1; + } + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + vattr.va_atime.tv_sec = tv[0].tv_sec; + vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000; + vattr.va_mtime.tv_sec = tv[1].tv_sec; + vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + VOP_UNLOCK(vp, 0, p); + FRELE(fp); + return (error); +} + /* * Truncate a file given its path name. */ /* ARGSUSED */ +int sys_truncate(p, v, retval) struct proc *p; void *v; @@ -1602,11 +2211,11 @@ struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0 && @@ -1623,12 +2232,13 @@ * Truncate a file given a file descriptor. */ /* ARGSUSED */ +int sys_ftruncate(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_ftruncate_args /* { + struct sys_ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; @@ -1638,13 +2248,15 @@ struct file *fp; int error; - if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); - if ((fp->f_flag & FWRITE) == 0) - return (EINVAL); + if ((fp->f_flag & FWRITE) == 0) { + error = EINVAL; + goto bad; + } vp = (struct vnode *)fp->f_data; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0) { @@ -1652,7 +2264,9 @@ vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } - VOP_UNLOCK(vp); + VOP_UNLOCK(vp, 0, p); +bad: + FRELE(fp); return (error); } @@ -1660,6 +2274,7 @@ * Sync an open file. */ /* ARGSUSED */ +int sys_fsync(p, v, retval) struct proc *p; void *v; @@ -1668,16 +2283,22 @@ struct sys_fsync_args /* { syscallarg(int) fd; } */ *uap = v; - register struct vnode *vp; + struct vnode *vp; struct file *fp; int error; - if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); - VOP_UNLOCK(vp); +#ifdef FFS_SOFTUPDATES + if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP)) + error = softdep_fsync(vp); +#endif + + VOP_UNLOCK(vp, 0, p); + FRELE(fp); return (error); } @@ -1686,6 +2307,7 @@ * or both not be directories. If target is a directory, it must be empty. */ /* ARGSUSED */ +int sys_rename(p, v, retval) struct proc *p; void *v; @@ -1698,15 +2320,24 @@ register struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; + int flags; NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, SCARG(uap, from), p); - if (error = namei(&fromnd)) + if ((error = namei(&fromnd)) != 0) return (error); fvp = fromnd.ni_vp; - NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART, + + flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; + /* + * rename("foo/", "bar/"); is OK + */ + if (fvp->v_type == VDIR) + flags |= STRIPSLASHES; + + NDINIT(&tond, RENAME, flags, UIO_USERSPACE, SCARG(uap, to), p); - if (error = namei(&tond)) { + if ((error = namei(&tond)) != 0) { VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); @@ -1727,21 +2358,19 @@ error = EINVAL; /* * If source is the same as the destination (that is the - * same inode number with the same name in the same directory), - * then there is nothing to do. + * same inode number) */ - if (fvp == tvp && fromnd.ni_dvp == tdvp && - fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && - !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, - fromnd.ni_cnd.cn_namelen)) + if (fvp == tvp) error = -1; out: if (!error) { VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); - if (tvp) + if (tvp) { + (void)uvm_vnp_uncache(tvp); VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); + } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); } else { @@ -1771,6 +2400,7 @@ * Make a directory file. */ /* ARGSUSED */ +int sys_mkdir(p, v, retval) struct proc *p; void *v; @@ -1785,8 +2415,9 @@ int error; struct nameidata nd; - NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + NDINIT(&nd, CREATE, LOCKPARENT | STRIPSLASHES, + UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { @@ -1812,6 +2443,7 @@ * Remove a directory file. */ /* ARGSUSED */ +int sys_rmdir(p, v, retval) struct proc *p; void *v; @@ -1826,7 +2458,7 @@ NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { @@ -1837,7 +2469,7 @@ * No rmdir "." please. */ if (nd.ni_dvp == vp) { - error = EINVAL; + error = EBUSY; goto out; } /* @@ -1864,32 +2496,39 @@ /* * Read a block of directory entries in a file system independent format. */ +int sys_getdirentries(p, v, retval) struct proc *p; void *v; register_t *retval; { - register struct sys_getdirentries_args /* { + struct sys_getdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; - syscallarg(u_int) count; + syscallarg(int) count; syscallarg(long *) basep; } */ *uap = v; - register struct vnode *vp; + struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error, eofflag; - if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) - return (error); - if ((fp->f_flag & FREAD) == 0) - return (EBADF); + if (SCARG(uap, count) < 0) + return EINVAL; + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + if ((fp->f_flag & FREAD) == 0) { + error = EBADF; + goto bad; + } vp = (struct vnode *)fp->f_data; unionread: - if (vp->v_type != VDIR) - return (EINVAL); + if (vp->v_type != VDIR) { + error = EINVAL; + goto bad; + } aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; @@ -1898,57 +2537,19 @@ auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); - VOP_LOCK(vp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; - error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (u_long *)0, 0); + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, 0, 0); fp->f_offset = auio.uio_offset; - VOP_UNLOCK(vp); + VOP_UNLOCK(vp, 0, p); if (error) - return (error); - -#ifdef UNION -{ - extern int (**union_vnodeop_p)(); - extern struct vnode *union_dircache __P((struct vnode *)); - + goto bad; if ((SCARG(uap, count) == auio.uio_resid) && - (vp->v_op == union_vnodeop_p)) { - struct vnode *lvp; - - lvp = union_dircache(vp); - if (lvp != NULLVP) { - struct vattr va; - - /* - * If the directory is opaque, - * then don't show lower entries - */ - error = VOP_GETATTR(vp, &va, fp->f_cred, p); - if (va.va_flags & OPAQUE) { - vput(lvp); - lvp = NULL; - } - } - - if (lvp != NULLVP) { - error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); - VOP_UNLOCK(lvp); - - if (error) { - vrele(lvp); - return (error); - } - fp->f_data = (caddr_t) lvp; - fp->f_offset = 0; - error = vn_close(vp, FREAD, fp->f_cred, p); - if (error) - return (error); - vp = lvp; - goto unionread; - } - } -} -#endif /* UNION */ + union_check_p && + (union_check_p(p, &vp, fp, auio, &error) != 0)) + goto unionread; + if (error) + goto bad; if ((SCARG(uap, count) == auio.uio_resid) && (vp->v_flag & VROOT) && @@ -1964,6 +2565,8 @@ error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); *retval = SCARG(uap, count) - auio.uio_resid; +bad: + FRELE(fp); return (error); } @@ -1983,7 +2586,7 @@ fdp = p->p_fd; *retval = fdp->fd_cmask; - fdp->fd_cmask = SCARG(uap, newmask) & ALLPERMS; + fdp->fd_cmask = SCARG(uap, newmask) & ACCESSPERMS; return (0); } @@ -1992,6 +2595,7 @@ * away from vnode. */ /* ARGSUSED */ +int sys_revoke(p, v, retval) struct proc *p; void *v; @@ -2006,20 +2610,16 @@ struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); - if (error = namei(&nd)) + if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; - if (vp->v_type != VCHR && vp->v_type != VBLK) { - error = EINVAL; - goto out; - } - if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) + if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) goto out; if (p->p_ucred->cr_uid != vattr.va_uid && (error = suser(p->p_ucred, &p->p_acflag))) goto out; - if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) - vgoneall(vp); + if (vp->v_usecount > 1 || (vp->v_flag & (VALIASED | VLAYER))) + VOP_REVOKE(vp, REVOKEALL); out: vrele(vp); return (error); @@ -2027,7 +2627,10 @@ /* * Convert a user file descriptor to a kernel file entry. + * + * On return *fpp is FREF:ed. */ +int getvnode(fdp, fd, fpp) struct filedesc *fdp; struct file **fpp; @@ -2035,11 +2638,579 @@ { struct file *fp; - if ((u_int)fd >= fdp->fd_nfiles || - (fp = fdp->fd_ofiles[fd]) == NULL) + if ((fp = fd_getfile(fdp, fd)) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (EINVAL); + FREF(fp); *fpp = fp; + return (0); } + +/* + * Positional read system call. + */ +int +sys_pread(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_pread_args /* { + syscallarg(int) fd; + syscallarg(void *) buf; + syscallarg(size_t) nbyte; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FREAD) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + offset = SCARG(uap, offset); + + FREF(fp); + + /* dofileread() will FRELE the descriptor for us */ + return (dofileread(p, fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), + &offset, retval)); +} + +/* + * Positional scatter read system call. + */ +int +sys_preadv(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_preadv_args /* { + syscallarg(int) fd; + syscallarg(const struct iovec *) iovp; + syscallarg(int) iovcnt; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FREAD) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + FREF(fp); + + offset = SCARG(uap, offset); + + /* dofilereadv() will FRELE the descriptor for us */ + return (dofilereadv(p, fd, fp, SCARG(uap, iovp), SCARG(uap, iovcnt), + &offset, retval)); +} + +/* + * Positional write system call. + */ +int +sys_pwrite(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_pwrite_args /* { + syscallarg(int) fd; + syscallarg(const void *) buf; + syscallarg(size_t) nbyte; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FWRITE) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + FREF(fp); + + offset = SCARG(uap, offset); + + /* dofilewrite() will FRELE the descriptor for us */ + return (dofilewrite(p, fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), + &offset, retval)); +} + + +/* + * Positional gather write system call. + */ +int +sys_pwritev(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_pwritev_args /* { + syscallarg(int) fd; + syscallarg(const struct iovec *) iovp; + syscallarg(int) iovcnt; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FWRITE) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + FREF(fp); + + offset = SCARG(uap, offset); + + /* dofilewritev() will FRELE the descriptor for us */ + return (dofilewritev(p, fd, fp, SCARG(uap, iovp), SCARG(uap, iovcnt), + &offset, retval)); +} + +#ifdef UFS_EXTATTR +/* + * Syscall to push extended attribute configuration information into the + * VFS. Accepts a path, which it converts to a mountpoint, as well as + * a command (int cmd), and attribute name and misc data. For now, the + * attribute name is left in userspace for consumption by the VFS_op. + * It will probably be changed to be copied into sysspace by the + * syscall in the future, once issues with various consumers of the + * attribute code have raised their hands. + * + * Currently this is used only by UFS Extended Attributes. + */ +int +sys_extattrctl(struct proc *p, void *v, register_t *reval) +{ + struct sys_extattrctl_args /* { + syscallarg(const char *) path; + syscallarg(int) cmd; + syscallarg(const char *) filename; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct vnode *filename_vp; + struct nameidata nd; + struct mount *mp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + /* + * SCARG(uap, attrname) not always defined. We check again later + * when we invoke the VFS call so as to pass in NULL there if needed. + */ + if (SCARG(uap, attrname) != NULL) { + error = copyinstr(SCARG(uap, attrname), attrname, + EXTATTR_MAXNAMELEN, NULL); + if (error) + return (error); + } + + /* + * SCARG(uap, filename) not always defined. If it is, grab + * a vnode lock, which VFS_EXTATTRCTL() will later release. + */ + filename_vp = NULL; + if (SCARG(uap, filename) != NULL) { + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, filename), p); + if ((error = namei(&nd)) != 0) + return (error); + filename_vp = nd.ni_vp; + } + + /* SCARG(uap, path) always defined. */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) { + if (filename_vp != NULL) + vput(filename_vp); + return (error); + } + + mp = nd.ni_vp->v_mount; + if (error) { + if (filename_vp != NULL) + vput(filename_vp); + return (error); + } + + if (SCARG(uap, attrname) != NULL) { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, attrnamespace), attrname, p); + } else { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, attrnamespace), NULL, p); + } + + /* + * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, + * filename_vp, so vrele it if it is defined. + */ + if (filename_vp != NULL) + vrele(filename_vp); + + return (error); +} + +/*- + * Set a named extended attribute on a file or directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", userspace buffer + * pointer "data", buffer length "nbytes", thread "td". + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, + void *data, size_t nbytes, struct proc *p, register_t *retval) +{ + struct uio auio; + struct iovec aiov; + ssize_t cnt; + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + aiov.iov_base = data; + aiov.iov_len = nbytes; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + if (nbytes > INT_MAX) { + error = EINVAL; + goto done; + } + auio.uio_resid = nbytes; + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + cnt = nbytes; + + error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, + p->p_ucred, p); + cnt -= auio.uio_resid; + retval[0] = cnt; + +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_set_file(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_file_args /* { + syscallarg(const char *) path; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + + error = extattr_set_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} + +int +sys_extattr_set_fd(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(struct iovec *) iovp; + syscallarg(int) iovcnt; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + error = extattr_set_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, SCARG(uap, data), + SCARG(uap, nbytes), p, retval); + FRELE(fp); + + return (error); +} + +/*- + * Get a named extended attribute on a file or directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", userspace buffer + * pointer "data", buffer length "nbytes", thread "td". + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, + void *data, size_t nbytes, struct proc *p, register_t *retval) +{ + struct uio auio; + struct iovec aiov; + ssize_t cnt; + size_t size; + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_READ); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + /* + * Slightly unusual semantics: if the user provides a NULL data + * pointer, they don't want to receive the data, just the + * maximum read length. + */ + if (data != NULL) { + aiov.iov_base = data; + aiov.iov_len = nbytes; + auio.uio_iov = &aiov; + auio.uio_offset = 0; + if (nbytes > INT_MAX) { + error = EINVAL; + goto done; + } + auio.uio_resid = nbytes; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + cnt = nbytes; + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, &auio, + NULL, p->p_ucred, p); + cnt -= auio.uio_resid; + retval[0] = cnt; + } else { + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, NULL, + &size, p->p_ucred, p); + retval[0] = size; + } +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_get_file(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_get_file_args /* { + syscallarg(const char *) path; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + + error = extattr_get_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} + +int +sys_extattr_get_fd(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_get_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + error = extattr_get_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, SCARG(uap, data), + SCARG(uap, nbytes), p, retval); + FRELE(fp); + + return (error); +} + +/* + * extattr_delete_vp(): Delete a named extended attribute on a file or + * directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", proc "p" + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, + struct proc *p) +{ + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, + p->p_ucred, p); + + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_delete_file(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_delete_file_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return(error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return(error); + + error = extattr_delete_vp(nd.ni_vp, SCARG(uap, attrnamespace), + attrname, p); + + vrele(nd.ni_vp); + return(error); +} + +int +sys_extattr_delete_fd(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_delete_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + error = extattr_delete_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, p); + FRELE(fp); + + return (error); +} +#endif