diff options
author | rwatson <rwatson@FreeBSD.org> | 2006-12-23 00:10:36 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2006-12-23 00:10:36 +0000 |
commit | 520b5875d288d27361242d78147311b89ecbcd78 (patch) | |
tree | 92024bd7b5575f8bcd138b47115f6f6c1b20e6ee /sys/kern/vfs_extattr.c | |
parent | 5aa6dce0256290d16feabf001f4f1b709e3e69a4 (diff) | |
download | FreeBSD-src-520b5875d288d27361242d78147311b89ecbcd78.zip FreeBSD-src-520b5875d288d27361242d78147311b89ecbcd78.tar.gz |
Following a repo-copy of vfs_syscalls.c to vfs_extattr.c, remove
non-extattr functions from vfs_extattr.c, and extattr functions from
vfs_syscalls.c.
Change copyright/license on vfs_extattr.c to my copyright/license on
the extended attribute implementation (from extattr.h).
Clean up includes a bit.
Obtained from: TrustedBSD Project
Diffstat (limited to 'sys/kern/vfs_extattr.c')
-rw-r--r-- | sys/kern/vfs_extattr.c | 4321 |
1 files changed, 7 insertions, 4314 deletions
diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c index bb51d01..628abd9 100644 --- a/sys/kern/vfs_extattr.c +++ b/sys/kern/vfs_extattr.c @@ -1,11 +1,8 @@ /*- - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. + * Copyright (c) 1999-2001 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -15,14 +12,11 @@ * 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. - * 4. 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. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -30,4330 +24,29 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include "opt_compat.h" #include "opt_mac.h" #include <sys/param.h> #include <sys/systm.h> -#include <sys/bio.h> -#include <sys/buf.h> -#include <sys/sysent.h> -#include <sys/malloc.h> +#include <sys/lock.h> #include <sys/mount.h> #include <sys/mutex.h> #include <sys/sysproto.h> #include <sys/namei.h> #include <sys/filedesc.h> -#include <sys/kernel.h> -#include <sys/fcntl.h> -#include <sys/file.h> #include <sys/limits.h> -#include <sys/linker.h> -#include <sys/stat.h> -#include <sys/sx.h> -#include <sys/unistd.h> #include <sys/vnode.h> -#include <sys/priv.h> #include <sys/proc.h> -#include <sys/dirent.h> #include <sys/extattr.h> -#include <sys/jail.h> -#include <sys/syscallsubr.h> -#include <sys/sysctl.h> - -#include <machine/stdarg.h> #include <security/audit/audit.h> #include <security/mac/mac_framework.h> -#include <vm/vm.h> -#include <vm/vm_object.h> -#include <vm/vm_page.h> -#include <vm/uma.h> - -static int chroot_refuse_vdir_fds(struct filedesc *fdp); -static int getutimes(const struct timeval *, enum uio_seg, struct timespec *); -static int setfown(struct thread *td, struct vnode *, uid_t, gid_t); -static int setfmode(struct thread *td, struct vnode *, int); -static int setfflags(struct thread *td, struct vnode *, int); -static int setutimes(struct thread *td, struct vnode *, - const struct timespec *, int, int); -static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred, - struct thread *td); - -static int extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, - size_t nbytes, struct thread *td); - -int (*union_dircheckp)(struct thread *td, struct vnode **, struct file *); - -/* - * The module initialization routine for POSIX asynchronous I/O will - * set this to the version of AIO that it implements. (Zero means - * that it is not implemented.) This value is used here by pathconf() - * and in kern_descrip.c by fpathconf(). - */ -int async_io_version; - -/* - * Sync each mounted filesystem. - */ -#ifndef _SYS_SYSPROTO_H_ -struct sync_args { - int dummy; -}; -#endif - -#ifdef DEBUG -static int syncprt = 0; -SYSCTL_INT(_debug, OID_AUTO, syncprt, CTLFLAG_RW, &syncprt, 0, ""); -#endif - -/* ARGSUSED */ -int -sync(td, uap) - struct thread *td; - struct sync_args *uap; -{ - struct mount *mp, *nmp; - int vfslocked; - - mtx_lock(&mountlist_mtx); - for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { - if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { - nmp = TAILQ_NEXT(mp, mnt_list); - continue; - } - vfslocked = VFS_LOCK_GIANT(mp); - if ((mp->mnt_flag & MNT_RDONLY) == 0 && - vn_start_write(NULL, &mp, V_NOWAIT) == 0) { - MNT_ILOCK(mp); - mp->mnt_noasync++; - mp->mnt_kern_flag &= ~MNTK_ASYNC; - MNT_IUNLOCK(mp); - vfs_msync(mp, MNT_NOWAIT); - VFS_SYNC(mp, MNT_NOWAIT, td); - MNT_ILOCK(mp); - mp->mnt_noasync--; - if ((mp->mnt_flag & MNT_ASYNC) != 0 && - mp->mnt_noasync == 0) - mp->mnt_kern_flag |= MNTK_ASYNC; - MNT_IUNLOCK(mp); - vn_finished_write(mp); - } - VFS_UNLOCK_GIANT(vfslocked); - mtx_lock(&mountlist_mtx); - nmp = TAILQ_NEXT(mp, mnt_list); - vfs_unbusy(mp, td); - } - mtx_unlock(&mountlist_mtx); - return (0); -} - -/* XXX PRISON: could be per prison flag */ -static int prison_quotas; -#if 0 -SYSCTL_INT(_kern_prison, OID_AUTO, quotas, CTLFLAG_RW, &prison_quotas, 0, ""); -#endif - -/* - * Change filesystem quotas. - * - * MP SAFE - */ -#ifndef _SYS_SYSPROTO_H_ -struct quotactl_args { - char *path; - int cmd; - int uid; - caddr_t arg; -}; -#endif -int -quotactl(td, uap) - struct thread *td; - register struct quotactl_args /* { - char *path; - int cmd; - int uid; - caddr_t arg; - } */ *uap; -{ - struct mount *mp, *vmp; - int vfslocked; - int error; - struct nameidata nd; - - AUDIT_ARG(cmd, uap->cmd); - AUDIT_ARG(uid, uap->uid); - if (jailed(td->td_ucred) && !prison_quotas) - return (EPERM); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, - UIO_USERSPACE, uap->path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = vn_start_write(nd.ni_vp, &vmp, V_WAIT | PCATCH); - mp = nd.ni_vp->v_mount; - vrele(nd.ni_vp); - if (error) - goto out; - error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, td); - vn_finished_write(vmp); -out: - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Get filesystem statistics. - */ -#ifndef _SYS_SYSPROTO_H_ -struct statfs_args { - char *path; - struct statfs *buf; -}; -#endif -int -statfs(td, uap) - struct thread *td; - register struct statfs_args /* { - char *path; - struct statfs *buf; - } */ *uap; -{ - struct statfs sf; - int error; - - error = kern_statfs(td, uap->path, UIO_USERSPACE, &sf); - if (error == 0) - error = copyout(&sf, uap->buf, sizeof(sf)); - return (error); -} - -int -kern_statfs(struct thread *td, char *path, enum uio_seg pathseg, - struct statfs *buf) -{ - struct mount *mp; - struct statfs *sp, sb; - int vfslocked; - int error; - struct nameidata nd; - - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - error = namei(&nd); - if (error) - return (error); - vfslocked = NDHASGIANT(&nd); - mp = nd.ni_vp->v_mount; - vfs_ref(mp); - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_vp); -#ifdef MAC - error = mac_check_mount_stat(td->td_ucred, mp); - if (error) - goto out; -#endif - /* - * Set these in case the underlying filesystem fails to do so. - */ - sp = &mp->mnt_stat; - sp->f_version = STATFS_VERSION; - sp->f_namemax = NAME_MAX; - sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; - error = VFS_STATFS(mp, sp, td); - if (error) - goto out; - if (priv_check(td, PRIV_VFS_GENERATION)) { - bcopy(sp, &sb, sizeof(sb)); - sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; - prison_enforce_statfs(td->td_ucred, mp, &sb); - sp = &sb; - } - *buf = *sp; -out: - vfs_rel(mp); - VFS_UNLOCK_GIANT(vfslocked); - if (mtx_owned(&Giant)) - printf("statfs(%d): %s: %d\n", vfslocked, path, error); - return (error); -} - -/* - * Get filesystem statistics. - */ -#ifndef _SYS_SYSPROTO_H_ -struct fstatfs_args { - int fd; - struct statfs *buf; -}; -#endif -int -fstatfs(td, uap) - struct thread *td; - register struct fstatfs_args /* { - int fd; - struct statfs *buf; - } */ *uap; -{ - struct statfs sf; - int error; - - error = kern_fstatfs(td, uap->fd, &sf); - if (error == 0) - error = copyout(&sf, uap->buf, sizeof(sf)); - return (error); -} - -int -kern_fstatfs(struct thread *td, int fd, struct statfs *buf) -{ - struct file *fp; - struct mount *mp; - struct statfs *sp, sb; - int vfslocked; - struct vnode *vp; - int error; - - AUDIT_ARG(fd, fd); - error = getvnode(td->td_proc->p_fd, fd, &fp); - if (error) - return (error); - vp = fp->f_vnode; - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); -#ifdef AUDIT - AUDIT_ARG(vnode, vp, ARG_VNODE1); -#endif - mp = vp->v_mount; - if (mp) - vfs_ref(mp); - VOP_UNLOCK(vp, 0, td); - fdrop(fp, td); - if (vp->v_iflag & VI_DOOMED) { - error = EBADF; - goto out; - } -#ifdef MAC - error = mac_check_mount_stat(td->td_ucred, mp); - if (error) - goto out; -#endif - /* - * Set these in case the underlying filesystem fails to do so. - */ - sp = &mp->mnt_stat; - sp->f_version = STATFS_VERSION; - sp->f_namemax = NAME_MAX; - sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; - error = VFS_STATFS(mp, sp, td); - if (error) - goto out; - if (priv_check(td, PRIV_VFS_GENERATION)) { - bcopy(sp, &sb, sizeof(sb)); - sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; - prison_enforce_statfs(td->td_ucred, mp, &sb); - sp = &sb; - } - *buf = *sp; -out: - if (mp) - vfs_rel(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Get statistics on all filesystems. - */ -#ifndef _SYS_SYSPROTO_H_ -struct getfsstat_args { - struct statfs *buf; - long bufsize; - int flags; -}; -#endif -int -getfsstat(td, uap) - struct thread *td; - register struct getfsstat_args /* { - struct statfs *buf; - long bufsize; - int flags; - } */ *uap; -{ - - return (kern_getfsstat(td, &uap->buf, uap->bufsize, UIO_USERSPACE, - uap->flags)); -} - -/* - * If (bufsize > 0 && bufseg == UIO_SYSSPACE) - * The caller is responsible for freeing memory which will be allocated - * in '*buf'. - */ -int -kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize, - enum uio_seg bufseg, int flags) -{ - struct mount *mp, *nmp; - struct statfs *sfsp, *sp, sb; - size_t count, maxcount; - int vfslocked; - int error; - - maxcount = bufsize / sizeof(struct statfs); - if (bufsize == 0) - sfsp = NULL; - else if (bufseg == UIO_USERSPACE) - sfsp = *buf; - else /* if (bufseg == UIO_SYSSPACE) */ { - count = 0; - mtx_lock(&mountlist_mtx); - TAILQ_FOREACH(mp, &mountlist, mnt_list) { - count++; - } - mtx_unlock(&mountlist_mtx); - if (maxcount > count) - maxcount = count; - sfsp = *buf = malloc(maxcount * sizeof(struct statfs), M_TEMP, - M_WAITOK); - } - count = 0; - mtx_lock(&mountlist_mtx); - for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { - if (prison_canseemount(td->td_ucred, mp) != 0) { - nmp = TAILQ_NEXT(mp, mnt_list); - continue; - } -#ifdef MAC - if (mac_check_mount_stat(td->td_ucred, mp) != 0) { - nmp = TAILQ_NEXT(mp, mnt_list); - continue; - } -#endif - if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) { - nmp = TAILQ_NEXT(mp, mnt_list); - continue; - } - vfslocked = VFS_LOCK_GIANT(mp); - if (sfsp && count < maxcount) { - sp = &mp->mnt_stat; - /* - * Set these in case the underlying filesystem - * fails to do so. - */ - sp->f_version = STATFS_VERSION; - sp->f_namemax = NAME_MAX; - sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; - /* - * If MNT_NOWAIT or MNT_LAZY is specified, do not - * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY - * overrides MNT_WAIT. - */ - if (((flags & (MNT_LAZY|MNT_NOWAIT)) == 0 || - (flags & MNT_WAIT)) && - (error = VFS_STATFS(mp, sp, td))) { - VFS_UNLOCK_GIANT(vfslocked); - mtx_lock(&mountlist_mtx); - nmp = TAILQ_NEXT(mp, mnt_list); - vfs_unbusy(mp, td); - continue; - } - if (priv_check(td, PRIV_VFS_GENERATION)) { - bcopy(sp, &sb, sizeof(sb)); - sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; - prison_enforce_statfs(td->td_ucred, mp, &sb); - sp = &sb; - } - if (bufseg == UIO_SYSSPACE) - bcopy(sp, sfsp, sizeof(*sp)); - else /* if (bufseg == UIO_USERSPACE) */ { - error = copyout(sp, sfsp, sizeof(*sp)); - if (error) { - vfs_unbusy(mp, td); - VFS_UNLOCK_GIANT(vfslocked); - return (error); - } - } - sfsp++; - } - VFS_UNLOCK_GIANT(vfslocked); - count++; - mtx_lock(&mountlist_mtx); - nmp = TAILQ_NEXT(mp, mnt_list); - vfs_unbusy(mp, td); - } - mtx_unlock(&mountlist_mtx); - if (sfsp && count > maxcount) - td->td_retval[0] = maxcount; - else - td->td_retval[0] = count; - return (0); -} - -#ifdef COMPAT_FREEBSD4 -/* - * Get old format filesystem statistics. - */ -static void cvtstatfs(struct statfs *, struct ostatfs *); - -#ifndef _SYS_SYSPROTO_H_ -struct freebsd4_statfs_args { - char *path; - struct ostatfs *buf; -}; -#endif -int -freebsd4_statfs(td, uap) - struct thread *td; - struct freebsd4_statfs_args /* { - char *path; - struct ostatfs *buf; - } */ *uap; -{ - struct ostatfs osb; - struct statfs sf; - int error; - - error = kern_statfs(td, uap->path, UIO_USERSPACE, &sf); - if (error) - return (error); - cvtstatfs(&sf, &osb); - return (copyout(&osb, uap->buf, sizeof(osb))); -} - -/* - * Get filesystem statistics. - */ -#ifndef _SYS_SYSPROTO_H_ -struct freebsd4_fstatfs_args { - int fd; - struct ostatfs *buf; -}; -#endif -int -freebsd4_fstatfs(td, uap) - struct thread *td; - struct freebsd4_fstatfs_args /* { - int fd; - struct ostatfs *buf; - } */ *uap; -{ - struct ostatfs osb; - struct statfs sf; - int error; - - error = kern_fstatfs(td, uap->fd, &sf); - if (error) - return (error); - cvtstatfs(&sf, &osb); - return (copyout(&osb, uap->buf, sizeof(osb))); -} - -/* - * Get statistics on all filesystems. - */ -#ifndef _SYS_SYSPROTO_H_ -struct freebsd4_getfsstat_args { - struct ostatfs *buf; - long bufsize; - int flags; -}; -#endif -int -freebsd4_getfsstat(td, uap) - struct thread *td; - register struct freebsd4_getfsstat_args /* { - struct ostatfs *buf; - long bufsize; - int flags; - } */ *uap; -{ - struct statfs *buf, *sp; - struct ostatfs osb; - size_t count, size; - int error; - - count = uap->bufsize / sizeof(struct ostatfs); - size = count * sizeof(struct statfs); - error = kern_getfsstat(td, &buf, size, UIO_SYSSPACE, uap->flags); - if (size > 0) { - count = td->td_retval[0]; - sp = buf; - while (count > 0 && error == 0) { - cvtstatfs(sp, &osb); - error = copyout(&osb, uap->buf, sizeof(osb)); - sp++; - uap->buf++; - count--; - } - free(buf, M_TEMP); - } - return (error); -} - -/* - * Implement fstatfs() for (NFS) file handles. - */ -#ifndef _SYS_SYSPROTO_H_ -struct freebsd4_fhstatfs_args { - struct fhandle *u_fhp; - struct ostatfs *buf; -}; -#endif -int -freebsd4_fhstatfs(td, uap) - struct thread *td; - struct freebsd4_fhstatfs_args /* { - struct fhandle *u_fhp; - struct ostatfs *buf; - } */ *uap; -{ - struct ostatfs osb; - struct statfs sf; - fhandle_t fh; - int error; - - error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); - if (error) - return (error); - error = kern_fhstatfs(td, fh, &sf); - if (error) - return (error); - cvtstatfs(&sf, &osb); - return (copyout(&osb, uap->buf, sizeof(osb))); -} - -/* - * Convert a new format statfs structure to an old format statfs structure. - */ -static void -cvtstatfs(nsp, osp) - struct statfs *nsp; - struct ostatfs *osp; -{ - - bzero(osp, sizeof(*osp)); - osp->f_bsize = MIN(nsp->f_bsize, LONG_MAX); - osp->f_iosize = MIN(nsp->f_iosize, LONG_MAX); - osp->f_blocks = MIN(nsp->f_blocks, LONG_MAX); - osp->f_bfree = MIN(nsp->f_bfree, LONG_MAX); - osp->f_bavail = MIN(nsp->f_bavail, LONG_MAX); - osp->f_files = MIN(nsp->f_files, LONG_MAX); - osp->f_ffree = MIN(nsp->f_ffree, LONG_MAX); - osp->f_owner = nsp->f_owner; - osp->f_type = nsp->f_type; - osp->f_flags = nsp->f_flags; - osp->f_syncwrites = MIN(nsp->f_syncwrites, LONG_MAX); - osp->f_asyncwrites = MIN(nsp->f_asyncwrites, LONG_MAX); - osp->f_syncreads = MIN(nsp->f_syncreads, LONG_MAX); - osp->f_asyncreads = MIN(nsp->f_asyncreads, LONG_MAX); - strlcpy(osp->f_fstypename, nsp->f_fstypename, - MIN(MFSNAMELEN, OMFSNAMELEN)); - strlcpy(osp->f_mntonname, nsp->f_mntonname, - MIN(MNAMELEN, OMNAMELEN)); - strlcpy(osp->f_mntfromname, nsp->f_mntfromname, - MIN(MNAMELEN, OMNAMELEN)); - osp->f_fsid = nsp->f_fsid; -} -#endif /* COMPAT_FREEBSD4 */ - -/* - * Change current working directory to a given file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct fchdir_args { - int fd; -}; -#endif -int -fchdir(td, uap) - struct thread *td; - struct fchdir_args /* { - int fd; - } */ *uap; -{ - register struct filedesc *fdp = td->td_proc->p_fd; - struct vnode *vp, *tdp, *vpold; - struct mount *mp; - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, uap->fd); - if ((error = getvnode(fdp, uap->fd, &fp)) != 0) - return (error); - vp = fp->f_vnode; - VREF(vp); - fdrop(fp, td); - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, vp, ARG_VNODE1); - error = change_dir(vp, td); - while (!error && (mp = vp->v_mountedhere) != NULL) { - int tvfslocked; - if (vfs_busy(mp, 0, 0, td)) - continue; - tvfslocked = VFS_LOCK_GIANT(mp); - error = VFS_ROOT(mp, LK_EXCLUSIVE, &tdp, td); - vfs_unbusy(mp, td); - if (error) { - VFS_UNLOCK_GIANT(tvfslocked); - break; - } - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - vp = tdp; - vfslocked = tvfslocked; - } - if (error) { - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); - } - VOP_UNLOCK(vp, 0, td); - VFS_UNLOCK_GIANT(vfslocked); - FILEDESC_LOCK_FAST(fdp); - vpold = fdp->fd_cdir; - fdp->fd_cdir = vp; - FILEDESC_UNLOCK_FAST(fdp); - vfslocked = VFS_LOCK_GIANT(vpold->v_mount); - vrele(vpold); - VFS_UNLOCK_GIANT(vfslocked); - return (0); -} - -/* - * Change current working directory (``.''). - */ -#ifndef _SYS_SYSPROTO_H_ -struct chdir_args { - char *path; -}; -#endif -int -chdir(td, uap) - struct thread *td; - struct chdir_args /* { - char *path; - } */ *uap; -{ - - return (kern_chdir(td, uap->path, UIO_USERSPACE)); -} - -int -kern_chdir(struct thread *td, char *path, enum uio_seg pathseg) -{ - register struct filedesc *fdp = td->td_proc->p_fd; - int error; - struct nameidata nd; - struct vnode *vp; - int vfslocked; - - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1 | MPSAFE, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - if ((error = change_dir(nd.ni_vp, td)) != 0) { - vput(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - NDFREE(&nd, NDF_ONLY_PNBUF); - return (error); - } - VOP_UNLOCK(nd.ni_vp, 0, td); - VFS_UNLOCK_GIANT(vfslocked); - NDFREE(&nd, NDF_ONLY_PNBUF); - FILEDESC_LOCK_FAST(fdp); - vp = fdp->fd_cdir; - fdp->fd_cdir = nd.ni_vp; - FILEDESC_UNLOCK_FAST(fdp); - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - vrele(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (0); -} - -/* - * Helper function for raised chroot(2) security function: Refuse if - * any filedescriptors are open directories. - */ -static int -chroot_refuse_vdir_fds(fdp) - struct filedesc *fdp; -{ - struct vnode *vp; - struct file *fp; - int fd; - - FILEDESC_LOCK_ASSERT(fdp, MA_OWNED); - for (fd = 0; fd < fdp->fd_nfiles ; fd++) { - fp = fget_locked(fdp, fd); - if (fp == NULL) - continue; - if (fp->f_type == DTYPE_VNODE) { - vp = fp->f_vnode; - if (vp->v_type == VDIR) - return (EPERM); - } - } - return (0); -} - -/* - * This sysctl determines if we will allow a process to chroot(2) if it - * has a directory open: - * 0: disallowed for all processes. - * 1: allowed for processes that were not already chroot(2)'ed. - * 2: allowed for all processes. - */ - -static int chroot_allow_open_directories = 1; - -SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, - &chroot_allow_open_directories, 0, ""); - -/* - * Change notion of root (``/'') directory. - */ -#ifndef _SYS_SYSPROTO_H_ -struct chroot_args { - char *path; -}; -#endif -int -chroot(td, uap) - struct thread *td; - struct chroot_args /* { - char *path; - } */ *uap; -{ - int error; - struct nameidata nd; - int vfslocked; - - error = priv_check_cred(td->td_ucred, PRIV_VFS_CHROOT, - SUSER_ALLOWJAIL); - if (error) - return (error); - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - UIO_USERSPACE, uap->path, td); - error = namei(&nd); - if (error) - goto error; - vfslocked = NDHASGIANT(&nd); - if ((error = change_dir(nd.ni_vp, td)) != 0) - goto e_vunlock; -#ifdef MAC - if ((error = mac_check_vnode_chroot(td->td_ucred, nd.ni_vp))) - goto e_vunlock; -#endif - VOP_UNLOCK(nd.ni_vp, 0, td); - error = change_root(nd.ni_vp, td); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - NDFREE(&nd, NDF_ONLY_PNBUF); - return (error); -e_vunlock: - vput(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); -error: - NDFREE(&nd, NDF_ONLY_PNBUF); - return (error); -} - -/* - * Common routine for chroot and chdir. Callers must provide a locked vnode - * instance. - */ -int -change_dir(vp, td) - struct vnode *vp; - struct thread *td; -{ - int error; - - ASSERT_VOP_LOCKED(vp, "change_dir(): vp not locked"); - if (vp->v_type != VDIR) - return (ENOTDIR); -#ifdef MAC - error = mac_check_vnode_chdir(td->td_ucred, vp); - if (error) - return (error); -#endif - error = VOP_ACCESS(vp, VEXEC, td->td_ucred, td); - return (error); -} - -/* - * Common routine for kern_chroot() and jail_attach(). The caller is - * responsible for invoking priv_check() and mac_check_chroot() to authorize - * this operation. - */ -int -change_root(vp, td) - struct vnode *vp; - struct thread *td; -{ - struct filedesc *fdp; - struct vnode *oldvp; - int vfslocked; - int error; - - VFS_ASSERT_GIANT(vp->v_mount); - fdp = td->td_proc->p_fd; - FILEDESC_LOCK(fdp); - if (chroot_allow_open_directories == 0 || - (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) { - error = chroot_refuse_vdir_fds(fdp); - if (error) { - FILEDESC_UNLOCK(fdp); - return (error); - } - } - oldvp = fdp->fd_rdir; - fdp->fd_rdir = vp; - VREF(fdp->fd_rdir); - if (!fdp->fd_jdir) { - fdp->fd_jdir = vp; - VREF(fdp->fd_jdir); - } - FILEDESC_UNLOCK(fdp); - vfslocked = VFS_LOCK_GIANT(oldvp->v_mount); - vrele(oldvp); - VFS_UNLOCK_GIANT(vfslocked); - return (0); -} - -/* - * Check permissions, allocate an open file structure, - * and call the device open routine if any. - * - * MP SAFE - */ -#ifndef _SYS_SYSPROTO_H_ -struct open_args { - char *path; - int flags; - int mode; -}; -#endif -int -open(td, uap) - struct thread *td; - register struct open_args /* { - char *path; - int flags; - int mode; - } */ *uap; -{ - - return kern_open(td, uap->path, UIO_USERSPACE, uap->flags, uap->mode); -} - -int -kern_open(struct thread *td, char *path, enum uio_seg pathseg, int flags, - int mode) -{ - struct proc *p = td->td_proc; - struct filedesc *fdp = p->p_fd; - struct file *fp; - struct vnode *vp; - struct vattr vat; - struct mount *mp; - int cmode; - struct file *nfp; - int type, indx, error; - struct flock lf; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(fflags, flags); - AUDIT_ARG(mode, mode); - if ((flags & O_ACCMODE) == O_ACCMODE) - return (EINVAL); - flags = FFLAGS(flags); - error = falloc(td, &nfp, &indx); - if (error) - return (error); - /* An extra reference on `nfp' has been held for us by falloc(). */ - fp = nfp; - cmode = ((mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1 | MPSAFE, pathseg, path, td); - td->td_dupfd = -1; /* XXX check for fdopen */ - error = vn_open(&nd, &flags, cmode, indx); - if (error) { - /* - * If the vn_open replaced the method vector, something - * wonderous happened deep below and we just pass it up - * pretending we know what we do. - */ - if (error == ENXIO && fp->f_ops != &badfileops) { - fdrop(fp, td); - td->td_retval[0] = indx; - return (0); - } - - /* - * release our own reference - */ - fdrop(fp, td); - - /* - * handle special fdopen() case. bleh. dupfdopen() is - * responsible for dropping the old contents of ofiles[indx] - * if it succeeds. - */ - if ((error == ENODEV || error == ENXIO) && - td->td_dupfd >= 0 && /* XXX from fdopen */ - (error = - dupfdopen(td, fdp, indx, td->td_dupfd, flags, error)) == 0) { - td->td_retval[0] = indx; - return (0); - } - /* - * Clean up the descriptor, but only if another thread hadn't - * replaced or closed it. - */ - fdclose(fdp, fp, indx, td); - - if (error == ERESTART) - error = EINTR; - return (error); - } - td->td_dupfd = 0; - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; - - /* - * There should be 2 references on the file, one from the descriptor - * table, and one for us. - * - * Handle the case where someone closed the file (via its file - * descriptor) while we were blocked. The end result should look - * like opening the file succeeded but it was immediately closed. - * We call vn_close() manually because we haven't yet hooked up - * the various 'struct file' fields. - */ - FILEDESC_LOCK(fdp); - FILE_LOCK(fp); - if (fp->f_count == 1) { - mp = vp->v_mount; - KASSERT(fdp->fd_ofiles[indx] != fp, - ("Open file descriptor lost all refs")); - FILE_UNLOCK(fp); - FILEDESC_UNLOCK(fdp); - VOP_UNLOCK(vp, 0, td); - vn_close(vp, flags & FMASK, fp->f_cred, td); - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - td->td_retval[0] = indx; - return (0); - } - fp->f_vnode = vp; - if (fp->f_data == NULL) - fp->f_data = vp; - fp->f_flag = flags & FMASK; - if (fp->f_ops == &badfileops) - fp->f_ops = &vnops; - fp->f_seqcount = 1; - fp->f_type = (vp->v_type == VFIFO ? DTYPE_FIFO : DTYPE_VNODE); - FILE_UNLOCK(fp); - FILEDESC_UNLOCK(fdp); - - VOP_UNLOCK(vp, 0, td); - 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; - if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, - type)) != 0) - goto bad; - fp->f_flag |= FHASLOCK; - } - if (flags & O_TRUNC) { - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - goto bad; - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - VATTR_NULL(&vat); - vat.va_size = 0; - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); -#ifdef MAC - error = mac_check_vnode_write(td->td_ucred, fp->f_cred, vp); - if (error == 0) -#endif - error = VOP_SETATTR(vp, &vat, td->td_ucred, td); - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); - if (error) - goto bad; - } - VFS_UNLOCK_GIANT(vfslocked); - /* - * Release our private reference, leaving the one associated with - * the descriptor table intact. - */ - fdrop(fp, td); - td->td_retval[0] = indx; - return (0); -bad: - VFS_UNLOCK_GIANT(vfslocked); - fdclose(fdp, fp, indx, td); - fdrop(fp, td); - return (error); -} - -#ifdef COMPAT_43 -/* - * Create a file. - * - * MP SAFE - */ -#ifndef _SYS_SYSPROTO_H_ -struct ocreat_args { - char *path; - int mode; -}; -#endif -int -ocreat(td, uap) - struct thread *td; - register struct ocreat_args /* { - char *path; - int mode; - } */ *uap; -{ - - return (kern_open(td, uap->path, UIO_USERSPACE, - O_WRONLY | O_CREAT | O_TRUNC, uap->mode)); -} -#endif /* COMPAT_43 */ - -/* - * Create a special file. - */ -#ifndef _SYS_SYSPROTO_H_ -struct mknod_args { - char *path; - int mode; - int dev; -}; -#endif -int -mknod(td, uap) - struct thread *td; - register struct mknod_args /* { - char *path; - int mode; - int dev; - } */ *uap; -{ - - return (kern_mknod(td, uap->path, UIO_USERSPACE, uap->mode, uap->dev)); -} - -int -kern_mknod(struct thread *td, char *path, enum uio_seg pathseg, int mode, - int dev) -{ - struct vnode *vp; - struct mount *mp; - struct vattr vattr; - int error; - int whiteout = 0; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(mode, mode); - AUDIT_ARG(dev, dev); - switch (mode & S_IFMT) { - case S_IFCHR: - case S_IFBLK: - error = priv_check(td, PRIV_VFS_MKNOD_DEV); - break; - case S_IFMT: - error = priv_check(td, PRIV_VFS_MKNOD_BAD); - break; - case S_IFWHT: - error = priv_check(td, PRIV_VFS_MKNOD_WHT); - break; - default: - error = EINVAL; - break; - } - if (error) - return (error); -restart: - bwillwrite(); - NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - if (vp != NULL) { - NDFREE(&nd, NDF_ONLY_PNBUF); - if (vp == nd.ni_dvp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (EEXIST); - } else { - VATTR_NULL(&vattr); - FILEDESC_LOCK_FAST(td->td_proc->p_fd); - vattr.va_mode = (mode & ALLPERMS) & - ~td->td_proc->p_fd->fd_cmask; - FILEDESC_UNLOCK_FAST(td->td_proc->p_fd); - vattr.va_rdev = dev; - whiteout = 0; - - switch (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: - panic("kern_mknod: invalid mode"); - } - } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - VFS_UNLOCK_GIANT(vfslocked); - if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) - return (error); - goto restart; - } -#ifdef MAC - if (error == 0 && !whiteout) - error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, - &nd.ni_cnd, &vattr); -#endif - if (!error) { - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - if (whiteout) - error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); - else { - error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, - &nd.ni_cnd, &vattr); - if (error == 0) - vput(nd.ni_vp); - } - } - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - vn_finished_write(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Create a named pipe. - */ -#ifndef _SYS_SYSPROTO_H_ -struct mkfifo_args { - char *path; - int mode; -}; -#endif -int -mkfifo(td, uap) - struct thread *td; - register struct mkfifo_args /* { - char *path; - int mode; - } */ *uap; -{ - - return (kern_mkfifo(td, uap->path, UIO_USERSPACE, uap->mode)); -} - -int -kern_mkfifo(struct thread *td, char *path, enum uio_seg pathseg, int mode) -{ - struct mount *mp; - struct vattr vattr; - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(mode, mode); -restart: - bwillwrite(); - NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - if (nd.ni_vp != NULL) { - NDFREE(&nd, NDF_ONLY_PNBUF); - if (nd.ni_vp == nd.ni_dvp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (EEXIST); - } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - VFS_UNLOCK_GIANT(vfslocked); - if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) - return (error); - goto restart; - } - VATTR_NULL(&vattr); - vattr.va_type = VFIFO; - FILEDESC_LOCK_FAST(td->td_proc->p_fd); - vattr.va_mode = (mode & ALLPERMS) & ~td->td_proc->p_fd->fd_cmask; - FILEDESC_UNLOCK_FAST(td->td_proc->p_fd); -#ifdef MAC - error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, - &vattr); - if (error) - goto out; -#endif - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); - if (error == 0) - vput(nd.ni_vp); -#ifdef MAC -out: -#endif - vput(nd.ni_dvp); - vn_finished_write(mp); - VFS_UNLOCK_GIANT(vfslocked); - NDFREE(&nd, NDF_ONLY_PNBUF); - return (error); -} - -/* - * Make a hard file link. - */ -#ifndef _SYS_SYSPROTO_H_ -struct link_args { - char *path; - char *link; -}; -#endif -int -link(td, uap) - struct thread *td; - register struct link_args /* { - char *path; - char *link; - } */ *uap; -{ - int error; - - error = kern_link(td, uap->path, uap->link, UIO_USERSPACE); - return (error); -} - -static int hardlink_check_uid = 0; -SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, - &hardlink_check_uid, 0, - "Unprivileged processes cannot create hard links to files owned by other " - "users"); -static int hardlink_check_gid = 0; -SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_gid, CTLFLAG_RW, - &hardlink_check_gid, 0, - "Unprivileged processes cannot create hard links to files owned by other " - "groups"); - -static int -can_hardlink(struct vnode *vp, struct thread *td, struct ucred *cred) -{ - struct vattr va; - int error; - - if (!hardlink_check_uid && !hardlink_check_gid) - return (0); - - error = VOP_GETATTR(vp, &va, cred, td); - if (error != 0) - return (error); - - if (hardlink_check_uid && cred->cr_uid != va.va_uid) { - error = priv_check_cred(cred, PRIV_VFS_LINK, - SUSER_ALLOWJAIL); - if (error) - return (error); - } - - if (hardlink_check_gid && !groupmember(va.va_gid, cred)) { - error = priv_check_cred(cred, PRIV_VFS_LINK, - SUSER_ALLOWJAIL); - if (error) - return (error); - } - - return (0); -} - -int -kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) -{ - struct vnode *vp; - struct mount *mp; - struct nameidata nd; - int vfslocked; - int lvfslocked; - int error; - - bwillwrite(); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, segflg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; - if (vp->v_type == VDIR) { - vrele(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (EPERM); /* POSIX */ - } - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { - vrele(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); - } - NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE2, - segflg, link, td); - if ((error = namei(&nd)) == 0) { - lvfslocked = NDHASGIANT(&nd); - if (nd.ni_vp != NULL) { - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(nd.ni_vp); - error = EEXIST; - } else if ((error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td)) - == 0) { - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - error = can_hardlink(vp, td, td->td_ucred); - if (error == 0) -#ifdef MAC - error = mac_check_vnode_link(td->td_ucred, - nd.ni_dvp, vp, &nd.ni_cnd); - if (error == 0) -#endif - error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); - VOP_UNLOCK(vp, 0, td); - vput(nd.ni_dvp); - } - NDFREE(&nd, NDF_ONLY_PNBUF); - VFS_UNLOCK_GIANT(lvfslocked); - } - vrele(vp); - vn_finished_write(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Make a symbolic link. - */ -#ifndef _SYS_SYSPROTO_H_ -struct symlink_args { - char *path; - char *link; -}; -#endif -int -symlink(td, uap) - struct thread *td; - register struct symlink_args /* { - char *path; - char *link; - } */ *uap; -{ - - return (kern_symlink(td, uap->path, uap->link, UIO_USERSPACE)); -} - -int -kern_symlink(struct thread *td, char *path, char *link, enum uio_seg segflg) -{ - struct mount *mp; - struct vattr vattr; - char *syspath; - int error; - struct nameidata nd; - int vfslocked; - - if (segflg == UIO_SYSSPACE) { - syspath = path; - } else { - syspath = uma_zalloc(namei_zone, M_WAITOK); - if ((error = copyinstr(path, syspath, MAXPATHLEN, NULL)) != 0) - goto out; - } - AUDIT_ARG(text, syspath); -restart: - bwillwrite(); - NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE1, - segflg, link, td); - if ((error = namei(&nd)) != 0) - goto out; - vfslocked = NDHASGIANT(&nd); - if (nd.ni_vp) { - NDFREE(&nd, NDF_ONLY_PNBUF); - if (nd.ni_vp == nd.ni_dvp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - error = EEXIST; - goto out; - } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - VFS_UNLOCK_GIANT(vfslocked); - if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) - goto out; - goto restart; - } - VATTR_NULL(&vattr); - FILEDESC_LOCK_FAST(td->td_proc->p_fd); - vattr.va_mode = ACCESSPERMS &~ td->td_proc->p_fd->fd_cmask; - FILEDESC_UNLOCK_FAST(td->td_proc->p_fd); -#ifdef MAC - vattr.va_type = VLNK; - error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, - &vattr); - if (error) - goto out2; -#endif - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, syspath); - if (error == 0) - vput(nd.ni_vp); -#ifdef MAC -out2: -#endif - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - vn_finished_write(mp); - VFS_UNLOCK_GIANT(vfslocked); -out: - if (segflg != UIO_SYSSPACE) - uma_zfree(namei_zone, syspath); - return (error); -} - -/* - * Delete a whiteout from the filesystem. - */ -int -undelete(td, uap) - struct thread *td; - register struct undelete_args /* { - char *path; - } */ *uap; -{ - int error; - struct mount *mp; - struct nameidata nd; - int vfslocked; - -restart: - bwillwrite(); - NDINIT(&nd, DELETE, LOCKPARENT | DOWHITEOUT | MPSAFE | AUDITVNODE1, - UIO_USERSPACE, uap->path, td); - error = namei(&nd); - if (error) - return (error); - vfslocked = NDHASGIANT(&nd); - - if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { - NDFREE(&nd, NDF_ONLY_PNBUF); - if (nd.ni_vp == nd.ni_dvp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (nd.ni_vp) - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (EEXIST); - } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - VFS_UNLOCK_GIANT(vfslocked); - if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) - return (error); - goto restart; - } - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - vn_finished_write(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Delete a name from the filesystem. - */ -#ifndef _SYS_SYSPROTO_H_ -struct unlink_args { - char *path; -}; -#endif -int -unlink(td, uap) - struct thread *td; - struct unlink_args /* { - char *path; - } */ *uap; -{ - int error; - - error = kern_unlink(td, uap->path, UIO_USERSPACE); - return (error); -} - -int -kern_unlink(struct thread *td, char *path, enum uio_seg pathseg) -{ - struct mount *mp; - struct vnode *vp; - int error; - struct nameidata nd; - int vfslocked; - -restart: - bwillwrite(); - NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error == EINVAL ? EPERM : error); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - if (vp->v_type == VDIR) - error = EPERM; /* POSIX */ - else { - /* - * The root of a mounted filesystem cannot be deleted. - * - * XXX: can this only be a VDIR case? - */ - if (vp->v_vflag & VV_ROOT) - error = EBUSY; - } - if (error == 0) { - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - if (vp == nd.ni_dvp) - vrele(vp); - else - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - if ((error = vn_start_write(NULL, &mp, - V_XSLEEP | PCATCH)) != 0) - return (error); - goto restart; - } -#ifdef MAC - error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp, - &nd.ni_cnd); - if (error) - goto out; -#endif - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); -#ifdef MAC -out: -#endif - vn_finished_write(mp); - } - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - if (vp == nd.ni_dvp) - vrele(vp); - else - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Reposition read/write file offset. - */ -#ifndef _SYS_SYSPROTO_H_ -struct lseek_args { - int fd; - int pad; - off_t offset; - int whence; -}; -#endif -int -lseek(td, uap) - struct thread *td; - register struct lseek_args /* { - int fd; - int pad; - off_t offset; - int whence; - } */ *uap; -{ - struct ucred *cred = td->td_ucred; - struct file *fp; - struct vnode *vp; - struct vattr vattr; - off_t offset; - int error, noneg; - int vfslocked; - - if ((error = fget(td, uap->fd, &fp)) != 0) - return (error); - if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) { - fdrop(fp, td); - return (ESPIPE); - } - vp = fp->f_vnode; - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - noneg = (vp->v_type != VCHR); - offset = uap->offset; - switch (uap->whence) { - case L_INCR: - if (noneg && - (fp->f_offset < 0 || - (offset > 0 && fp->f_offset > OFF_MAX - offset))) { - error = EOVERFLOW; - break; - } - offset += fp->f_offset; - break; - case L_XTND: - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - error = VOP_GETATTR(vp, &vattr, cred, td); - VOP_UNLOCK(vp, 0, td); - if (error) - break; - if (noneg && - (vattr.va_size > OFF_MAX || - (offset > 0 && vattr.va_size > OFF_MAX - offset))) { - error = EOVERFLOW; - break; - } - offset += vattr.va_size; - break; - case L_SET: - break; - default: - error = EINVAL; - } - if (error == 0 && noneg && offset < 0) - error = EINVAL; - if (error != 0) - goto drop; - fp->f_offset = offset; - *(off_t *)(td->td_retval) = fp->f_offset; -drop: - fdrop(fp, td); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -#if defined(COMPAT_43) -/* - * Reposition read/write file offset. - */ -#ifndef _SYS_SYSPROTO_H_ -struct olseek_args { - int fd; - long offset; - int whence; -}; -#endif -int -olseek(td, uap) - struct thread *td; - register struct olseek_args /* { - int fd; - long offset; - int whence; - } */ *uap; -{ - struct lseek_args /* { - int fd; - int pad; - off_t offset; - int whence; - } */ nuap; - int error; - - nuap.fd = uap->fd; - nuap.offset = uap->offset; - nuap.whence = uap->whence; - error = lseek(td, &nuap); - return (error); -} -#endif /* COMPAT_43 */ - -/* - * Check access permissions using passed credentials. - */ -static int -vn_access(vp, user_flags, cred, td) - struct vnode *vp; - int user_flags; - struct ucred *cred; - struct thread *td; -{ - int error, flags; - - /* Flags == 0 means only check for existence. */ - error = 0; - if (user_flags) { - flags = 0; - if (user_flags & R_OK) - flags |= VREAD; - if (user_flags & W_OK) - flags |= VWRITE; - if (user_flags & X_OK) - flags |= VEXEC; -#ifdef MAC - error = mac_check_vnode_access(cred, vp, flags); - if (error) - return (error); -#endif - if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) - error = VOP_ACCESS(vp, flags, cred, td); - } - return (error); -} - -/* - * Check access permissions using "real" credentials. - */ -#ifndef _SYS_SYSPROTO_H_ -struct access_args { - char *path; - int flags; -}; -#endif -int -access(td, uap) - struct thread *td; - register struct access_args /* { - char *path; - int flags; - } */ *uap; -{ - - return (kern_access(td, uap->path, UIO_USERSPACE, uap->flags)); -} - -int -kern_access(struct thread *td, char *path, enum uio_seg pathseg, int flags) -{ - struct ucred *cred, *tmpcred; - register struct vnode *vp; - struct nameidata nd; - int vfslocked; - int error; - - /* - * Create and modify a temporary credential instead of one that - * is potentially shared. This could also mess up socket - * buffer accounting which can run in an interrupt context. - */ - cred = td->td_ucred; - tmpcred = crdup(cred); - tmpcred->cr_uid = cred->cr_ruid; - tmpcred->cr_groups[0] = cred->cr_rgid; - td->td_ucred = tmpcred; - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - goto out1; - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - - error = vn_access(vp, flags, tmpcred, td); - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); -out1: - td->td_ucred = cred; - crfree(tmpcred); - return (error); -} - -/* - * Check access permissions using "effective" credentials. - */ -#ifndef _SYS_SYSPROTO_H_ -struct eaccess_args { - char *path; - int flags; -}; -#endif -int -eaccess(td, uap) - struct thread *td; - register struct eaccess_args /* { - char *path; - int flags; - } */ *uap; -{ - - return (kern_eaccess(td, uap->path, UIO_USERSPACE, uap->flags)); -} - -int -kern_eaccess(struct thread *td, char *path, enum uio_seg pathseg, int flags) -{ - struct nameidata nd; - struct vnode *vp; - int vfslocked; - int error; - - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vp = nd.ni_vp; - vfslocked = NDHASGIANT(&nd); - error = vn_access(vp, flags, td->td_ucred, td); - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -#if defined(COMPAT_43) -/* - * Get file status; this version follows links. - */ -#ifndef _SYS_SYSPROTO_H_ -struct ostat_args { - char *path; - struct ostat *ub; -}; -#endif -int -ostat(td, uap) - struct thread *td; - register struct ostat_args /* { - char *path; - struct ostat *ub; - } */ *uap; -{ - struct stat sb; - struct ostat osb; - int error; - - error = kern_stat(td, uap->path, UIO_USERSPACE, &sb); - if (error) - return (error); - cvtstat(&sb, &osb); - error = copyout(&osb, uap->ub, sizeof (osb)); - return (error); -} - -/* - * Get file status; this version does not follow links. - */ -#ifndef _SYS_SYSPROTO_H_ -struct olstat_args { - char *path; - struct ostat *ub; -}; -#endif -int -olstat(td, uap) - struct thread *td; - register struct olstat_args /* { - char *path; - struct ostat *ub; - } */ *uap; -{ - struct stat sb; - struct ostat osb; - int error; - - error = kern_lstat(td, uap->path, UIO_USERSPACE, &sb); - if (error) - return (error); - cvtstat(&sb, &osb); - error = copyout(&osb, uap->ub, sizeof (osb)); - return (error); -} - -/* - * Convert from an old to a new stat structure. - */ -void -cvtstat(st, ost) - struct stat *st; - struct ostat *ost; -{ - - ost->st_dev = st->st_dev; - ost->st_ino = st->st_ino; - ost->st_mode = st->st_mode; - ost->st_nlink = st->st_nlink; - ost->st_uid = st->st_uid; - ost->st_gid = st->st_gid; - ost->st_rdev = st->st_rdev; - if (st->st_size < (quad_t)1 << 32) - ost->st_size = st->st_size; - else - ost->st_size = -2; - ost->st_atime = st->st_atime; - ost->st_mtime = st->st_mtime; - ost->st_ctime = st->st_ctime; - ost->st_blksize = st->st_blksize; - ost->st_blocks = st->st_blocks; - ost->st_flags = st->st_flags; - ost->st_gen = st->st_gen; -} -#endif /* COMPAT_43 */ - -/* - * Get file status; this version follows links. - */ -#ifndef _SYS_SYSPROTO_H_ -struct stat_args { - char *path; - struct stat *ub; -}; -#endif -int -stat(td, uap) - struct thread *td; - register struct stat_args /* { - char *path; - struct stat *ub; - } */ *uap; -{ - struct stat sb; - int error; - - error = kern_stat(td, uap->path, UIO_USERSPACE, &sb); - if (error == 0) - error = copyout(&sb, uap->ub, sizeof (sb)); - return (error); -} - -int -kern_stat(struct thread *td, char *path, enum uio_seg pathseg, struct stat *sbp) -{ - struct nameidata nd; - struct stat sb; - int error, vfslocked; - - NDINIT(&nd, LOOKUP, - FOLLOW | LOCKSHARED | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - error = vn_stat(nd.ni_vp, &sb, td->td_ucred, NOCRED, td); - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - if (mtx_owned(&Giant)) - printf("stat(%d): %s\n", vfslocked, path); - if (error) - return (error); - *sbp = sb; - return (0); -} - -/* - * Get file status; this version does not follow links. - */ -#ifndef _SYS_SYSPROTO_H_ -struct lstat_args { - char *path; - struct stat *ub; -}; -#endif -int -lstat(td, uap) - struct thread *td; - register struct lstat_args /* { - char *path; - struct stat *ub; - } */ *uap; -{ - struct stat sb; - int error; - - error = kern_lstat(td, uap->path, UIO_USERSPACE, &sb); - if (error == 0) - error = copyout(&sb, uap->ub, sizeof (sb)); - return (error); -} - -int -kern_lstat(struct thread *td, char *path, enum uio_seg pathseg, struct stat *sbp) -{ - struct vnode *vp; - struct stat sb; - struct nameidata nd; - int error, vfslocked; - - NDINIT(&nd, LOOKUP, - NOFOLLOW | LOCKLEAF | LOCKSHARED | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - if (error) - return (error); - *sbp = sb; - return (0); -} - -/* - * Implementation of the NetBSD [l]stat() functions. - */ -void -cvtnstat(sb, nsb) - struct stat *sb; - struct nstat *nsb; -{ - bzero(nsb, sizeof *nsb); - nsb->st_dev = sb->st_dev; - nsb->st_ino = sb->st_ino; - nsb->st_mode = sb->st_mode; - nsb->st_nlink = sb->st_nlink; - nsb->st_uid = sb->st_uid; - nsb->st_gid = sb->st_gid; - nsb->st_rdev = sb->st_rdev; - nsb->st_atimespec = sb->st_atimespec; - nsb->st_mtimespec = sb->st_mtimespec; - nsb->st_ctimespec = sb->st_ctimespec; - nsb->st_size = sb->st_size; - nsb->st_blocks = sb->st_blocks; - nsb->st_blksize = sb->st_blksize; - nsb->st_flags = sb->st_flags; - nsb->st_gen = sb->st_gen; - nsb->st_birthtimespec = sb->st_birthtimespec; -} - -#ifndef _SYS_SYSPROTO_H_ -struct nstat_args { - char *path; - struct nstat *ub; -}; -#endif -int -nstat(td, uap) - struct thread *td; - register struct nstat_args /* { - char *path; - struct nstat *ub; - } */ *uap; -{ - struct stat sb; - struct nstat nsb; - int error; - - error = kern_stat(td, uap->path, UIO_USERSPACE, &sb); - if (error) - return (error); - cvtnstat(&sb, &nsb); - error = copyout(&nsb, uap->ub, sizeof (nsb)); - return (error); -} - -/* - * NetBSD lstat. Get file status; this version does not follow links. - */ -#ifndef _SYS_SYSPROTO_H_ -struct lstat_args { - char *path; - struct stat *ub; -}; -#endif -int -nlstat(td, uap) - struct thread *td; - register struct nlstat_args /* { - char *path; - struct nstat *ub; - } */ *uap; -{ - struct stat sb; - struct nstat nsb; - int error; - - error = kern_lstat(td, uap->path, UIO_USERSPACE, &sb); - if (error) - return (error); - cvtnstat(&sb, &nsb); - error = copyout(&nsb, uap->ub, sizeof (nsb)); - return (error); -} - -/* - * Get configurable pathname variables. - */ -#ifndef _SYS_SYSPROTO_H_ -struct pathconf_args { - char *path; - int name; -}; -#endif -int -pathconf(td, uap) - struct thread *td; - register struct pathconf_args /* { - char *path; - int name; - } */ *uap; -{ - - return (kern_pathconf(td, uap->path, UIO_USERSPACE, uap->name)); -} - -int -kern_pathconf(struct thread *td, char *path, enum uio_seg pathseg, int name) -{ - struct nameidata nd; - int error, vfslocked; - - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - - /* If asynchronous I/O is available, it works for all files. */ - if (name == _PC_ASYNC_IO) - td->td_retval[0] = async_io_version; - else - error = VOP_PATHCONF(nd.ni_vp, name, td->td_retval); - vput(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Return target name of a symbolic link. - */ -#ifndef _SYS_SYSPROTO_H_ -struct readlink_args { - char *path; - char *buf; - int count; -}; -#endif -int -readlink(td, uap) - struct thread *td; - register struct readlink_args /* { - char *path; - char *buf; - int count; - } */ *uap; -{ - - return (kern_readlink(td, uap->path, UIO_USERSPACE, uap->buf, - UIO_USERSPACE, uap->count)); -} - -int -kern_readlink(struct thread *td, char *path, enum uio_seg pathseg, char *buf, - enum uio_seg bufseg, int count) -{ - register struct vnode *vp; - struct iovec aiov; - struct uio auio; - int error; - struct nameidata nd; - int vfslocked; - - NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - NDFREE(&nd, NDF_ONLY_PNBUF); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; -#ifdef MAC - error = mac_check_vnode_readlink(td->td_ucred, vp); - if (error) { - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); - } -#endif - if (vp->v_type != VLNK) - error = EINVAL; - else { - aiov.iov_base = buf; - aiov.iov_len = count; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - auio.uio_offset = 0; - auio.uio_rw = UIO_READ; - auio.uio_segflg = bufseg; - auio.uio_td = td; - auio.uio_resid = count; - error = VOP_READLINK(vp, &auio, td->td_ucred); - } - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - td->td_retval[0] = count - auio.uio_resid; - return (error); -} - -/* - * Common implementation code for chflags() and fchflags(). - */ -static int -setfflags(td, vp, flags) - struct thread *td; - struct vnode *vp; - int flags; -{ - int error; - struct mount *mp; - struct vattr vattr; - - /* - * Prevent non-root users from setting flags on devices. When - * a device is reused, users can retain ownership of the device - * if they are allowed to set flags and programs assume that - * chown can't fail when done as root. - */ - if (vp->v_type == VCHR || vp->v_type == VBLK) { - error = priv_check_cred(td->td_ucred, PRIV_VFS_CHFLAGS_DEV, - SUSER_ALLOWJAIL); - if (error) - return (error); - } - - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - return (error); - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - VATTR_NULL(&vattr); - vattr.va_flags = flags; -#ifdef MAC - error = mac_check_vnode_setflags(td->td_ucred, vp, vattr.va_flags); - if (error == 0) -#endif - error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); - return (error); -} - -/* - * Change flags of a file given a path name. - */ -#ifndef _SYS_SYSPROTO_H_ -struct chflags_args { - char *path; - int flags; -}; -#endif -int -chflags(td, uap) - struct thread *td; - register struct chflags_args /* { - char *path; - int flags; - } */ *uap; -{ - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(fflags, uap->flags); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, UIO_USERSPACE, - uap->path, td); - if ((error = namei(&nd)) != 0) - return (error); - NDFREE(&nd, NDF_ONLY_PNBUF); - vfslocked = NDHASGIANT(&nd); - error = setfflags(td, nd.ni_vp, uap->flags); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Same as chflags() but doesn't follow symlinks. - */ -int -lchflags(td, uap) - struct thread *td; - register struct lchflags_args /* { - char *path; - int flags; - } */ *uap; -{ - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(fflags, uap->flags); - NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE | AUDITVNODE1, UIO_USERSPACE, - uap->path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setfflags(td, nd.ni_vp, uap->flags); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Change flags of a file given a file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct fchflags_args { - int fd; - int flags; -}; -#endif -int -fchflags(td, uap) - struct thread *td; - register struct fchflags_args /* { - int fd; - int flags; - } */ *uap; -{ - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, uap->fd); - AUDIT_ARG(fflags, uap->flags); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); -#ifdef AUDIT - vn_lock(fp->f_vnode, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, fp->f_vnode, ARG_VNODE1); - VOP_UNLOCK(fp->f_vnode, 0, td); -#endif - error = setfflags(td, fp->f_vnode, uap->flags); - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} - -/* - * Common implementation code for chmod(), lchmod() and fchmod(). - */ -static int -setfmode(td, vp, mode) - struct thread *td; - struct vnode *vp; - int mode; -{ - int error; - struct mount *mp; - struct vattr vattr; - - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - return (error); - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - VATTR_NULL(&vattr); - vattr.va_mode = mode & ALLPERMS; -#ifdef MAC - error = mac_check_vnode_setmode(td->td_ucred, vp, vattr.va_mode); - if (error == 0) -#endif - error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); - return (error); -} - -/* - * Change mode of a file given path name. - */ -#ifndef _SYS_SYSPROTO_H_ -struct chmod_args { - char *path; - int mode; -}; -#endif -int -chmod(td, uap) - struct thread *td; - register struct chmod_args /* { - char *path; - int mode; - } */ *uap; -{ - - return (kern_chmod(td, uap->path, UIO_USERSPACE, uap->mode)); -} - -int -kern_chmod(struct thread *td, char *path, enum uio_seg pathseg, int mode) -{ - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(mode, mode); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setfmode(td, nd.ni_vp, mode); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Change mode of a file given path name (don't follow links.) - */ -#ifndef _SYS_SYSPROTO_H_ -struct lchmod_args { - char *path; - int mode; -}; -#endif -int -lchmod(td, uap) - struct thread *td; - register struct lchmod_args /* { - char *path; - int mode; - } */ *uap; -{ - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(mode, (mode_t)uap->mode); - NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE | AUDITVNODE1, UIO_USERSPACE, - uap->path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setfmode(td, nd.ni_vp, uap->mode); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Change mode of a file given a file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct fchmod_args { - int fd; - int mode; -}; -#endif -int -fchmod(td, uap) - struct thread *td; - register struct fchmod_args /* { - int fd; - int mode; - } */ *uap; -{ - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, uap->fd); - AUDIT_ARG(mode, uap->mode); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); -#ifdef AUDIT - vn_lock(fp->f_vnode, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, fp->f_vnode, ARG_VNODE1); - VOP_UNLOCK(fp->f_vnode, 0, td); -#endif - error = setfmode(td, fp->f_vnode, uap->mode); - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} - -/* - * Common implementation for chown(), lchown(), and fchown() - */ -static int -setfown(td, vp, uid, gid) - struct thread *td; - struct vnode *vp; - uid_t uid; - gid_t gid; -{ - int error; - struct mount *mp; - struct vattr vattr; - - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - return (error); - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - VATTR_NULL(&vattr); - vattr.va_uid = uid; - vattr.va_gid = gid; -#ifdef MAC - error = mac_check_vnode_setowner(td->td_ucred, vp, vattr.va_uid, - vattr.va_gid); - if (error == 0) -#endif - error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); - return (error); -} - -/* - * Set ownership given a path name. - */ -#ifndef _SYS_SYSPROTO_H_ -struct chown_args { - char *path; - int uid; - int gid; -}; -#endif -int -chown(td, uap) - struct thread *td; - register struct chown_args /* { - char *path; - int uid; - int gid; - } */ *uap; -{ - - return (kern_chown(td, uap->path, UIO_USERSPACE, uap->uid, uap->gid)); -} - -int -kern_chown(struct thread *td, char *path, enum uio_seg pathseg, int uid, - int gid) -{ - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(owner, uid, gid); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setfown(td, nd.ni_vp, uid, gid); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Set ownership given a path name, do not cross symlinks. - */ -#ifndef _SYS_SYSPROTO_H_ -struct lchown_args { - char *path; - int uid; - int gid; -}; -#endif -int -lchown(td, uap) - struct thread *td; - register struct lchown_args /* { - char *path; - int uid; - int gid; - } */ *uap; -{ - - return (kern_lchown(td, uap->path, UIO_USERSPACE, uap->uid, uap->gid)); -} - -int -kern_lchown(struct thread *td, char *path, enum uio_seg pathseg, int uid, - int gid) -{ - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(owner, uid, gid); - NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE | AUDITVNODE1, pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setfown(td, nd.ni_vp, uid, gid); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Set ownership given a file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct fchown_args { - int fd; - int uid; - int gid; -}; -#endif -int -fchown(td, uap) - struct thread *td; - register struct fchown_args /* { - int fd; - int uid; - int gid; - } */ *uap; -{ - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, uap->fd); - AUDIT_ARG(owner, uap->uid, uap->gid); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); -#ifdef AUDIT - vn_lock(fp->f_vnode, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, fp->f_vnode, ARG_VNODE1); - VOP_UNLOCK(fp->f_vnode, 0, td); -#endif - error = setfown(td, fp->f_vnode, uap->uid, uap->gid); - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} - -/* - * Common implementation code for utimes(), lutimes(), and futimes(). - */ -static int -getutimes(usrtvp, tvpseg, tsp) - const struct timeval *usrtvp; - enum uio_seg tvpseg; - struct timespec *tsp; -{ - struct timeval tv[2]; - const struct timeval *tvp; - int error; - - if (usrtvp == NULL) { - microtime(&tv[0]); - TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); - tsp[1] = tsp[0]; - } else { - if (tvpseg == UIO_SYSSPACE) { - tvp = usrtvp; - } else { - if ((error = copyin(usrtvp, tv, sizeof(tv))) != 0) - return (error); - tvp = tv; - } - - if (tvp[0].tv_usec < 0 || tvp[0].tv_usec >= 1000000 || - tvp[1].tv_usec < 0 || tvp[1].tv_usec >= 1000000) - return (EINVAL); - TIMEVAL_TO_TIMESPEC(&tvp[0], &tsp[0]); - TIMEVAL_TO_TIMESPEC(&tvp[1], &tsp[1]); - } - return (0); -} - -/* - * Common implementation code for utimes(), lutimes(), and futimes(). - */ -static int -setutimes(td, vp, ts, numtimes, nullflag) - struct thread *td; - struct vnode *vp; - const struct timespec *ts; - int numtimes; - int nullflag; -{ - int error, setbirthtime; - struct mount *mp; - struct vattr vattr; - - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - return (error); - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - setbirthtime = 0; - if (numtimes < 3 && VOP_GETATTR(vp, &vattr, td->td_ucred, td) == 0 && - timespeccmp(&ts[1], &vattr.va_birthtime, < )) - setbirthtime = 1; - VATTR_NULL(&vattr); - vattr.va_atime = ts[0]; - vattr.va_mtime = ts[1]; - if (setbirthtime) - vattr.va_birthtime = ts[1]; - if (numtimes > 2) - vattr.va_birthtime = ts[2]; - if (nullflag) - vattr.va_vaflags |= VA_UTIMES_NULL; -#ifdef MAC - error = mac_check_vnode_setutimes(td->td_ucred, vp, vattr.va_atime, - vattr.va_mtime); -#endif - if (error == 0) - error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); - return (error); -} - -/* - * Set the access and modification times of a file. - */ -#ifndef _SYS_SYSPROTO_H_ -struct utimes_args { - char *path; - struct timeval *tptr; -}; -#endif -int -utimes(td, uap) - struct thread *td; - register struct utimes_args /* { - char *path; - struct timeval *tptr; - } */ *uap; -{ - - return (kern_utimes(td, uap->path, UIO_USERSPACE, uap->tptr, - UIO_USERSPACE)); -} - -int -kern_utimes(struct thread *td, char *path, enum uio_seg pathseg, - struct timeval *tptr, enum uio_seg tptrseg) -{ - struct timespec ts[2]; - int error; - struct nameidata nd; - int vfslocked; - - if ((error = getutimes(tptr, tptrseg, ts)) != 0) - return (error); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Set the access and modification times of a file. - */ -#ifndef _SYS_SYSPROTO_H_ -struct lutimes_args { - char *path; - struct timeval *tptr; -}; -#endif -int -lutimes(td, uap) - struct thread *td; - register struct lutimes_args /* { - char *path; - struct timeval *tptr; - } */ *uap; -{ - - return (kern_lutimes(td, uap->path, UIO_USERSPACE, uap->tptr, - UIO_USERSPACE)); -} - -int -kern_lutimes(struct thread *td, char *path, enum uio_seg pathseg, - struct timeval *tptr, enum uio_seg tptrseg) -{ - struct timespec ts[2]; - int error; - struct nameidata nd; - int vfslocked; - - if ((error = getutimes(tptr, tptrseg, ts)) != 0) - return (error); - NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE | AUDITVNODE1, pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - error = setutimes(td, nd.ni_vp, ts, 2, tptr == NULL); - vrele(nd.ni_vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Set the access and modification times of a file. - */ -#ifndef _SYS_SYSPROTO_H_ -struct futimes_args { - int fd; - struct timeval *tptr; -}; -#endif -int -futimes(td, uap) - struct thread *td; - register struct futimes_args /* { - int fd; - struct timeval *tptr; - } */ *uap; -{ - - return (kern_futimes(td, uap->fd, uap->tptr, UIO_USERSPACE)); -} - -int -kern_futimes(struct thread *td, int fd, struct timeval *tptr, - enum uio_seg tptrseg) -{ - struct timespec ts[2]; - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, fd); - if ((error = getutimes(tptr, tptrseg, ts)) != 0) - return (error); - if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0) - return (error); - vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); -#ifdef AUDIT - vn_lock(fp->f_vnode, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, fp->f_vnode, ARG_VNODE1); - VOP_UNLOCK(fp->f_vnode, 0, td); -#endif - error = setutimes(td, fp->f_vnode, ts, 2, tptr == NULL); - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} - -/* - * Truncate a file given its path name. - */ -#ifndef _SYS_SYSPROTO_H_ -struct truncate_args { - char *path; - int pad; - off_t length; -}; -#endif -int -truncate(td, uap) - struct thread *td; - register struct truncate_args /* { - char *path; - int pad; - off_t length; - } */ *uap; -{ - - return (kern_truncate(td, uap->path, UIO_USERSPACE, uap->length)); -} - -int -kern_truncate(struct thread *td, char *path, enum uio_seg pathseg, off_t length) -{ - struct mount *mp; - struct vnode *vp; - struct vattr vattr; - int error; - struct nameidata nd; - int vfslocked; - - if (length < 0) - return(EINVAL); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { - vrele(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); - } - NDFREE(&nd, NDF_ONLY_PNBUF); - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - if (vp->v_type == VDIR) - error = EISDIR; -#ifdef MAC - else if ((error = mac_check_vnode_write(td->td_ucred, NOCRED, vp))) { - } -#endif - else if ((error = vn_writechk(vp)) == 0 && - (error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td)) == 0) { - VATTR_NULL(&vattr); - vattr.va_size = length; - error = VOP_SETATTR(vp, &vattr, td->td_ucred, td); - } - vput(vp); - vn_finished_write(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Truncate a file given a file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct ftruncate_args { - int fd; - int pad; - off_t length; -}; -#endif -int -ftruncate(td, uap) - struct thread *td; - register struct ftruncate_args /* { - int fd; - int pad; - off_t length; - } */ *uap; -{ - struct mount *mp; - struct vattr vattr; - struct vnode *vp; - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, uap->fd); - if (uap->length < 0) - return(EINVAL); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - if ((fp->f_flag & FWRITE) == 0) { - fdrop(fp, td); - return (EINVAL); - } - vp = fp->f_vnode; - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - goto drop; - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, vp, ARG_VNODE1); - if (vp->v_type == VDIR) - error = EISDIR; -#ifdef MAC - else if ((error = mac_check_vnode_write(td->td_ucred, fp->f_cred, - vp))) { - } -#endif - else if ((error = vn_writechk(vp)) == 0) { - VATTR_NULL(&vattr); - vattr.va_size = uap->length; - error = VOP_SETATTR(vp, &vattr, fp->f_cred, td); - } - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); -drop: - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} - -#if defined(COMPAT_43) -/* - * Truncate a file given its path name. - */ -#ifndef _SYS_SYSPROTO_H_ -struct otruncate_args { - char *path; - long length; -}; -#endif -int -otruncate(td, uap) - struct thread *td; - register struct otruncate_args /* { - char *path; - long length; - } */ *uap; -{ - struct truncate_args /* { - char *path; - int pad; - off_t length; - } */ nuap; - - nuap.path = uap->path; - nuap.length = uap->length; - return (truncate(td, &nuap)); -} - -/* - * Truncate a file given a file descriptor. - */ -#ifndef _SYS_SYSPROTO_H_ -struct oftruncate_args { - int fd; - long length; -}; -#endif -int -oftruncate(td, uap) - struct thread *td; - register struct oftruncate_args /* { - int fd; - long length; - } */ *uap; -{ - struct ftruncate_args /* { - int fd; - int pad; - off_t length; - } */ nuap; - - nuap.fd = uap->fd; - nuap.length = uap->length; - return (ftruncate(td, &nuap)); -} -#endif /* COMPAT_43 */ - -/* - * Sync an open file. - */ -#ifndef _SYS_SYSPROTO_H_ -struct fsync_args { - int fd; -}; -#endif -int -fsync(td, uap) - struct thread *td; - struct fsync_args /* { - int fd; - } */ *uap; -{ - struct vnode *vp; - struct mount *mp; - struct file *fp; - int vfslocked; - int error; - - AUDIT_ARG(fd, uap->fd); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - vp = fp->f_vnode; - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) - goto drop; - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, vp, ARG_VNODE1); - if (vp->v_object != NULL) { - VM_OBJECT_LOCK(vp->v_object); - vm_object_page_clean(vp->v_object, 0, 0, 0); - VM_OBJECT_UNLOCK(vp->v_object); - } - error = VOP_FSYNC(vp, MNT_WAIT, td); - - VOP_UNLOCK(vp, 0, td); - vn_finished_write(mp); -drop: - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} - -/* - * Rename files. Source and destination must either both be directories, - * or both not be directories. If target is a directory, it must be empty. - */ -#ifndef _SYS_SYSPROTO_H_ -struct rename_args { - char *from; - char *to; -}; -#endif -int -rename(td, uap) - struct thread *td; - register struct rename_args /* { - char *from; - char *to; - } */ *uap; -{ - - return (kern_rename(td, uap->from, uap->to, UIO_USERSPACE)); -} - -int -kern_rename(struct thread *td, char *from, char *to, enum uio_seg pathseg) -{ - struct mount *mp = NULL; - struct vnode *tvp, *fvp, *tdvp; - struct nameidata fromnd, tond; - int tvfslocked; - int fvfslocked; - int error; - - bwillwrite(); -#ifdef MAC - NDINIT(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART | MPSAFE | - AUDITVNODE1, pathseg, from, td); -#else - NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART | MPSAFE | - AUDITVNODE1, pathseg, from, td); -#endif - if ((error = namei(&fromnd)) != 0) - return (error); - fvfslocked = NDHASGIANT(&fromnd); - tvfslocked = 0; -#ifdef MAC - error = mac_check_vnode_rename_from(td->td_ucred, fromnd.ni_dvp, - fromnd.ni_vp, &fromnd.ni_cnd); - VOP_UNLOCK(fromnd.ni_dvp, 0, td); - if (fromnd.ni_dvp != fromnd.ni_vp) - VOP_UNLOCK(fromnd.ni_vp, 0, td); -#endif - fvp = fromnd.ni_vp; - if (error == 0) - error = vn_start_write(fvp, &mp, V_WAIT | PCATCH); - if (error != 0) { - NDFREE(&fromnd, NDF_ONLY_PNBUF); - vrele(fromnd.ni_dvp); - vrele(fvp); - goto out1; - } - NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | - MPSAFE | AUDITVNODE2, pathseg, to, td); - if (fromnd.ni_vp->v_type == VDIR) - tond.ni_cnd.cn_flags |= WILLBEDIR; - if ((error = namei(&tond)) != 0) { - /* Translate error code for rename("dir1", "dir2/."). */ - if (error == EISDIR && fvp->v_type == VDIR) - error = EINVAL; - NDFREE(&fromnd, NDF_ONLY_PNBUF); - vrele(fromnd.ni_dvp); - vrele(fvp); - vn_finished_write(mp); - goto out1; - } - tvfslocked = NDHASGIANT(&tond); - tdvp = tond.ni_dvp; - tvp = tond.ni_vp; - if (tvp != NULL) { - if (fvp->v_type == VDIR && tvp->v_type != VDIR) { - error = ENOTDIR; - goto out; - } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { - error = EISDIR; - goto out; - } - } - if (fvp == tdvp) - error = EINVAL; - /* - * If the source is the same as the destination (that is, if they - * are links to the same vnode), then there is nothing to do. - */ - if (fvp == tvp) - error = -1; -#ifdef MAC - else - error = mac_check_vnode_rename_to(td->td_ucred, tdvp, - tond.ni_vp, fromnd.ni_dvp == tdvp, &tond.ni_cnd); -#endif -out: - if (!error) { - VOP_LEASE(tdvp, td, td->td_ucred, LEASE_WRITE); - if (fromnd.ni_dvp != tdvp) { - VOP_LEASE(fromnd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - } - if (tvp) { - VOP_LEASE(tvp, td, td->td_ucred, LEASE_WRITE); - } - error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, - tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); - NDFREE(&fromnd, NDF_ONLY_PNBUF); - NDFREE(&tond, NDF_ONLY_PNBUF); - } else { - NDFREE(&fromnd, NDF_ONLY_PNBUF); - NDFREE(&tond, NDF_ONLY_PNBUF); - if (tvp) - vput(tvp); - if (tdvp == tvp) - vrele(tdvp); - else - vput(tdvp); - vrele(fromnd.ni_dvp); - vrele(fvp); - } - vrele(tond.ni_startdir); - vn_finished_write(mp); -out1: - if (fromnd.ni_startdir) - vrele(fromnd.ni_startdir); - VFS_UNLOCK_GIANT(fvfslocked); - VFS_UNLOCK_GIANT(tvfslocked); - if (error == -1) - return (0); - return (error); -} - -/* - * Make a directory file. - */ -#ifndef _SYS_SYSPROTO_H_ -struct mkdir_args { - char *path; - int mode; -}; -#endif -int -mkdir(td, uap) - struct thread *td; - register struct mkdir_args /* { - char *path; - int mode; - } */ *uap; -{ - - return (kern_mkdir(td, uap->path, UIO_USERSPACE, uap->mode)); -} - -int -kern_mkdir(struct thread *td, char *path, enum uio_seg segflg, int mode) -{ - struct mount *mp; - struct vnode *vp; - struct vattr vattr; - int error; - struct nameidata nd; - int vfslocked; - - AUDIT_ARG(mode, mode); -restart: - bwillwrite(); - NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE1, - segflg, path, td); - nd.ni_cnd.cn_flags |= WILLBEDIR; - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - if (vp != NULL) { - NDFREE(&nd, NDF_ONLY_PNBUF); - /* - * XXX namei called with LOCKPARENT but not LOCKLEAF has - * the strange behaviour of leaving the vnode unlocked - * if the target is the same vnode as the parent. - */ - if (vp == nd.ni_dvp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (EEXIST); - } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - VFS_UNLOCK_GIANT(vfslocked); - if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) - return (error); - goto restart; - } - VATTR_NULL(&vattr); - vattr.va_type = VDIR; - FILEDESC_LOCK_FAST(td->td_proc->p_fd); - vattr.va_mode = (mode & ACCESSPERMS) &~ td->td_proc->p_fd->fd_cmask; - FILEDESC_UNLOCK_FAST(td->td_proc->p_fd); -#ifdef MAC - error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd, - &vattr); - if (error) - goto out; -#endif - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); -#ifdef MAC -out: -#endif - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(nd.ni_dvp); - if (!error) - vput(nd.ni_vp); - vn_finished_write(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Remove a directory file. - */ -#ifndef _SYS_SYSPROTO_H_ -struct rmdir_args { - char *path; -}; -#endif -int -rmdir(td, uap) - struct thread *td; - struct rmdir_args /* { - char *path; - } */ *uap; -{ - - return (kern_rmdir(td, uap->path, UIO_USERSPACE)); -} - -int -kern_rmdir(struct thread *td, char *path, enum uio_seg pathseg) -{ - struct mount *mp; - struct vnode *vp; - int error; - struct nameidata nd; - int vfslocked; - -restart: - bwillwrite(); - NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF | MPSAFE | AUDITVNODE1, - pathseg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - if (vp->v_type != VDIR) { - error = ENOTDIR; - goto out; - } - /* - * No rmdir "." please. - */ - if (nd.ni_dvp == vp) { - error = EINVAL; - goto out; - } - /* - * The root of a mounted filesystem cannot be deleted. - */ - if (vp->v_vflag & VV_ROOT) { - error = EBUSY; - goto out; - } -#ifdef MAC - error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp, - &nd.ni_cnd); - if (error) - goto out; -#endif - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(vp); - if (nd.ni_dvp == vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - VFS_UNLOCK_GIANT(vfslocked); - if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) - return (error); - goto restart; - } - VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE); - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); - vn_finished_write(mp); -out: - NDFREE(&nd, NDF_ONLY_PNBUF); - vput(vp); - if (nd.ni_dvp == vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -#ifdef COMPAT_43 -/* - * Read a block of directory entries in a filesystem independent format. - */ -#ifndef _SYS_SYSPROTO_H_ -struct ogetdirentries_args { - int fd; - char *buf; - u_int count; - long *basep; -}; -#endif -int -ogetdirentries(td, uap) - struct thread *td; - register struct ogetdirentries_args /* { - int fd; - char *buf; - u_int count; - long *basep; - } */ *uap; -{ - struct vnode *vp; - struct file *fp; - struct uio auio, kuio; - struct iovec aiov, kiov; - struct dirent *dp, *edp; - caddr_t dirbuf; - int error, eofflag, readcnt, vfslocked; - long loff; - - /* XXX arbitrary sanity limit on `count'. */ - if (uap->count > 64 * 1024) - return (EINVAL); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - if ((fp->f_flag & FREAD) == 0) { - fdrop(fp, td); - return (EBADF); - } - vp = fp->f_vnode; -unionread: - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - if (vp->v_type != VDIR) { - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (EINVAL); - } - aiov.iov_base = uap->buf; - aiov.iov_len = uap->count; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - auio.uio_rw = UIO_READ; - auio.uio_segflg = UIO_USERSPACE; - auio.uio_td = td; - auio.uio_resid = uap->count; - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - loff = auio.uio_offset = fp->f_offset; -#ifdef MAC - error = mac_check_vnode_readdir(td->td_ucred, vp); - if (error) { - VOP_UNLOCK(vp, 0, td); - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); - } -#endif -# if (BYTE_ORDER != LITTLE_ENDIAN) - if (vp->v_mount->mnt_maxsymlinklen <= 0) { - error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, - NULL, NULL); - fp->f_offset = auio.uio_offset; - } else -# endif - { - kuio = auio; - kuio.uio_iov = &kiov; - kuio.uio_segflg = UIO_SYSSPACE; - kiov.iov_len = uap->count; - MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); - kiov.iov_base = dirbuf; - error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, - NULL, NULL); - fp->f_offset = kuio.uio_offset; - if (error == 0) { - readcnt = uap->count - kuio.uio_resid; - edp = (struct dirent *)&dirbuf[readcnt]; - for (dp = (struct dirent *)dirbuf; dp < edp; ) { -# if (BYTE_ORDER == LITTLE_ENDIAN) - /* - * The expected low byte of - * dp->d_namlen is our dp->d_type. - * The high MBZ byte of dp->d_namlen - * is our dp->d_namlen. - */ - dp->d_type = dp->d_namlen; - dp->d_namlen = 0; -# else - /* - * The dp->d_type is the high byte - * of the expected dp->d_namlen, - * so must be zero'ed. - */ - dp->d_type = 0; -# endif - if (dp->d_reclen > 0) { - dp = (struct dirent *) - ((char *)dp + dp->d_reclen); - } else { - error = EIO; - break; - } - } - if (dp >= edp) - error = uiomove(dirbuf, readcnt, &auio); - } - FREE(dirbuf, M_TEMP); - } - VOP_UNLOCK(vp, 0, td); - if (error) { - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); - } - if (uap->count == auio.uio_resid) { - if (union_dircheckp) { - error = union_dircheckp(td, &vp, fp); - if (error == -1) { - VFS_UNLOCK_GIANT(vfslocked); - goto unionread; - } - if (error) { - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); - } - } - /* - * XXX We could delay dropping the lock above but - * union_dircheckp complicates things. - */ - vn_lock(vp, LK_EXCLUSIVE|LK_RETRY, td); - if ((vp->v_vflag & VV_ROOT) && - (vp->v_mount->mnt_flag & MNT_UNION)) { - struct vnode *tvp = vp; - vp = vp->v_mount->mnt_vnodecovered; - VREF(vp); - fp->f_vnode = vp; - fp->f_data = vp; - fp->f_offset = 0; - vput(tvp); - VFS_UNLOCK_GIANT(vfslocked); - goto unionread; - } - VOP_UNLOCK(vp, 0, td); - } - VFS_UNLOCK_GIANT(vfslocked); - error = copyout(&loff, uap->basep, sizeof(long)); - fdrop(fp, td); - td->td_retval[0] = uap->count - auio.uio_resid; - return (error); -} -#endif /* COMPAT_43 */ - -/* - * Read a block of directory entries in a filesystem independent format. - */ -#ifndef _SYS_SYSPROTO_H_ -struct getdirentries_args { - int fd; - char *buf; - u_int count; - long *basep; -}; -#endif -int -getdirentries(td, uap) - struct thread *td; - register struct getdirentries_args /* { - int fd; - char *buf; - u_int count; - long *basep; - } */ *uap; -{ - struct vnode *vp; - struct file *fp; - struct uio auio; - struct iovec aiov; - int vfslocked; - long loff; - int error, eofflag; - - AUDIT_ARG(fd, uap->fd); - if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) - return (error); - if ((fp->f_flag & FREAD) == 0) { - fdrop(fp, td); - return (EBADF); - } - vp = fp->f_vnode; -unionread: - vfslocked = VFS_LOCK_GIANT(vp->v_mount); - if (vp->v_type != VDIR) { - error = EINVAL; - goto fail; - } - aiov.iov_base = uap->buf; - aiov.iov_len = uap->count; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - auio.uio_rw = UIO_READ; - auio.uio_segflg = UIO_USERSPACE; - auio.uio_td = td; - auio.uio_resid = uap->count; - /* vn_lock(vp, LK_SHARED | LK_RETRY, td); */ - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - AUDIT_ARG(vnode, vp, ARG_VNODE1); - loff = auio.uio_offset = fp->f_offset; -#ifdef MAC - error = mac_check_vnode_readdir(td->td_ucred, vp); - if (error == 0) -#endif - error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, - NULL); - fp->f_offset = auio.uio_offset; - VOP_UNLOCK(vp, 0, td); - if (error) - goto fail; - if (uap->count == auio.uio_resid) { - if (union_dircheckp) { - error = union_dircheckp(td, &vp, fp); - if (error == -1) { - VFS_UNLOCK_GIANT(vfslocked); - goto unionread; - } - if (error) - goto fail; - } - /* - * XXX We could delay dropping the lock above but - * union_dircheckp complicates things. - */ - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - if ((vp->v_vflag & VV_ROOT) && - (vp->v_mount->mnt_flag & MNT_UNION)) { - struct vnode *tvp = vp; - vp = vp->v_mount->mnt_vnodecovered; - VREF(vp); - fp->f_vnode = vp; - fp->f_data = vp; - fp->f_offset = 0; - vput(tvp); - VFS_UNLOCK_GIANT(vfslocked); - goto unionread; - } - VOP_UNLOCK(vp, 0, td); - } - if (uap->basep != NULL) { - error = copyout(&loff, uap->basep, sizeof(long)); - } - td->td_retval[0] = uap->count - auio.uio_resid; -fail: - VFS_UNLOCK_GIANT(vfslocked); - fdrop(fp, td); - return (error); -} -#ifndef _SYS_SYSPROTO_H_ -struct getdents_args { - int fd; - char *buf; - size_t count; -}; -#endif -int -getdents(td, uap) - struct thread *td; - register struct getdents_args /* { - int fd; - char *buf; - u_int count; - } */ *uap; -{ - struct getdirentries_args ap; - ap.fd = uap->fd; - ap.buf = uap->buf; - ap.count = uap->count; - ap.basep = NULL; - return (getdirentries(td, &ap)); -} - -/* - * Set the mode mask for creation of filesystem nodes. - * - * MP SAFE - */ -#ifndef _SYS_SYSPROTO_H_ -struct umask_args { - int newmask; -}; -#endif -int -umask(td, uap) - struct thread *td; - struct umask_args /* { - int newmask; - } */ *uap; -{ - register struct filedesc *fdp; - - FILEDESC_LOCK_FAST(td->td_proc->p_fd); - fdp = td->td_proc->p_fd; - td->td_retval[0] = fdp->fd_cmask; - fdp->fd_cmask = uap->newmask & ALLPERMS; - FILEDESC_UNLOCK_FAST(td->td_proc->p_fd); - return (0); -} - -/* - * Void all references to file by ripping underlying filesystem - * away from vnode. - */ -#ifndef _SYS_SYSPROTO_H_ -struct revoke_args { - char *path; -}; -#endif -int -revoke(td, uap) - struct thread *td; - register struct revoke_args /* { - char *path; - } */ *uap; -{ - struct vnode *vp; - struct vattr vattr; - int error; - struct nameidata nd; - int vfslocked; - - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - UIO_USERSPACE, uap->path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - vp = nd.ni_vp; - NDFREE(&nd, NDF_ONLY_PNBUF); - if (vp->v_type != VCHR) { - error = EINVAL; - goto out; - } -#ifdef MAC - error = mac_check_vnode_revoke(td->td_ucred, vp); - if (error) - goto out; -#endif - error = VOP_GETATTR(vp, &vattr, td->td_ucred, td); - if (error) - goto out; - if (td->td_ucred->cr_uid != vattr.va_uid) { - error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN, - SUSER_ALLOWJAIL); - if (error) - goto out; - } - if (vcount(vp) > 1) - VOP_REVOKE(vp, REVOKEALL); -out: - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Convert a user file descriptor to a kernel file entry. - * A reference on the file entry is held upon returning. - */ -int -getvnode(fdp, fd, fpp) - struct filedesc *fdp; - int fd; - struct file **fpp; -{ - int error; - struct file *fp; - - fp = NULL; - if (fdp == NULL) - error = EBADF; - else { - FILEDESC_LOCK(fdp); - if ((u_int)fd >= fdp->fd_nfiles || - (fp = fdp->fd_ofiles[fd]) == NULL) - error = EBADF; - else if (fp->f_vnode == NULL) { - fp = NULL; - error = EINVAL; - } else { - fhold(fp); - error = 0; - } - FILEDESC_UNLOCK(fdp); - } - *fpp = fp; - return (error); -} - -/* - * Get (NFS) file handle - */ -#ifndef _SYS_SYSPROTO_H_ -struct lgetfh_args { - char *fname; - fhandle_t *fhp; -}; -#endif -int -lgetfh(td, uap) - struct thread *td; - register struct lgetfh_args *uap; -{ - struct nameidata nd; - fhandle_t fh; - register struct vnode *vp; - int vfslocked; - int error; - - error = priv_check(td, PRIV_VFS_GETFH); - if (error) - return (error); - NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - UIO_USERSPACE, uap->fname, td); - error = namei(&nd); - if (error) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; - bzero(&fh, sizeof(fh)); - fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fh.fh_fid); - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - if (error) - return (error); - error = copyout(&fh, uap->fhp, sizeof (fh)); - return (error); -} - -#ifndef _SYS_SYSPROTO_H_ -struct getfh_args { - char *fname; - fhandle_t *fhp; -}; -#endif -int -getfh(td, uap) - struct thread *td; - register struct getfh_args *uap; -{ - struct nameidata nd; - fhandle_t fh; - register struct vnode *vp; - int vfslocked; - int error; - - error = priv_check(td, PRIV_VFS_GETFH); - if (error) - return (error); - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, - UIO_USERSPACE, uap->fname, td); - error = namei(&nd); - if (error) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; - bzero(&fh, sizeof(fh)); - fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fh.fh_fid); - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - if (error) - return (error); - error = copyout(&fh, uap->fhp, sizeof (fh)); - return (error); -} - -/* - * syscall for the rpc.lockd to use to translate a NFS file handle into an - * open descriptor. - * - * warning: do not remove the priv_check() call or this becomes one giant - * security hole. - * - * MP SAFE - */ -#ifndef _SYS_SYSPROTO_H_ -struct fhopen_args { - const struct fhandle *u_fhp; - int flags; -}; -#endif -int -fhopen(td, uap) - struct thread *td; - struct fhopen_args /* { - const struct fhandle *u_fhp; - int flags; - } */ *uap; -{ - struct proc *p = td->td_proc; - struct mount *mp; - struct vnode *vp; - struct fhandle fhp; - struct vattr vat; - struct vattr *vap = &vat; - struct flock lf; - struct file *fp; - register struct filedesc *fdp = p->p_fd; - int fmode, mode, error, type; - struct file *nfp; - int vfslocked; - int indx; - - error = priv_check(td, PRIV_VFS_FHOPEN); - if (error) - return (error); - fmode = FFLAGS(uap->flags); - /* why not allow a non-read/write open for our lockd? */ - if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) - return (EINVAL); - error = copyin(uap->u_fhp, &fhp, sizeof(fhp)); - if (error) - return(error); - /* find the mount point */ - mp = vfs_getvfs(&fhp.fh_fsid); - if (mp == NULL) - return (ESTALE); - vfslocked = VFS_LOCK_GIANT(mp); - /* now give me my vnode, it gets returned to me locked */ - error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); - if (error) - goto out; - /* - * from now on we have to make sure not - * to forget about the vnode - * any error that causes an abort must vput(vp) - * just set error = err and 'goto bad;'. - */ - - /* - * from vn_open - */ - if (vp->v_type == VLNK) { - error = EMLINK; - goto bad; - } - if (vp->v_type == VSOCK) { - error = EOPNOTSUPP; - goto bad; - } - mode = 0; - if (fmode & (FWRITE | O_TRUNC)) { - if (vp->v_type == VDIR) { - error = EISDIR; - goto bad; - } - error = vn_writechk(vp); - if (error) - goto bad; - mode |= VWRITE; - } - if (fmode & FREAD) - mode |= VREAD; - if (fmode & O_APPEND) - mode |= VAPPEND; -#ifdef MAC - error = mac_check_vnode_open(td->td_ucred, vp, mode); - if (error) - goto bad; -#endif - if (mode) { - error = VOP_ACCESS(vp, mode, td->td_ucred, td); - if (error) - goto bad; - } - if (fmode & O_TRUNC) { - VOP_UNLOCK(vp, 0, td); /* XXX */ - if ((error = vn_start_write(NULL, &mp, V_WAIT | PCATCH)) != 0) { - vrele(vp); - goto out; - } - VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); /* XXX */ -#ifdef MAC - /* - * We don't yet have fp->f_cred, so use td->td_ucred, which - * should be right. - */ - error = mac_check_vnode_write(td->td_ucred, td->td_ucred, vp); - if (error == 0) { -#endif - VATTR_NULL(vap); - vap->va_size = 0; - error = VOP_SETATTR(vp, vap, td->td_ucred, td); -#ifdef MAC - } -#endif - vn_finished_write(mp); - if (error) - goto bad; - } - error = VOP_OPEN(vp, fmode, td->td_ucred, td, -1); - if (error) - goto bad; - - if (fmode & FWRITE) - vp->v_writecount++; - - /* - * end of vn_open code - */ - - if ((error = falloc(td, &nfp, &indx)) != 0) { - if (fmode & FWRITE) - vp->v_writecount--; - goto bad; - } - /* An extra reference on `nfp' has been held for us by falloc(). */ - fp = nfp; - - nfp->f_vnode = vp; - nfp->f_data = vp; - nfp->f_flag = fmode & FMASK; - nfp->f_ops = &vnops; - nfp->f_type = DTYPE_VNODE; - if (fmode & (O_EXLOCK | O_SHLOCK)) { - lf.l_whence = SEEK_SET; - lf.l_start = 0; - lf.l_len = 0; - if (fmode & O_EXLOCK) - lf.l_type = F_WRLCK; - else - lf.l_type = F_RDLCK; - type = F_FLOCK; - if ((fmode & FNONBLOCK) == 0) - type |= F_WAIT; - VOP_UNLOCK(vp, 0, td); - if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, - type)) != 0) { - /* - * The lock request failed. Normally close the - * descriptor but handle the case where someone might - * have dup()d or close()d it when we weren't looking. - */ - fdclose(fdp, fp, indx, td); - - /* - * release our private reference - */ - fdrop(fp, td); - goto out; - } - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); - fp->f_flag |= FHASLOCK; - } - - VOP_UNLOCK(vp, 0, td); - fdrop(fp, td); - vfs_rel(mp); - VFS_UNLOCK_GIANT(vfslocked); - td->td_retval[0] = indx; - return (0); - -bad: - vput(vp); -out: - vfs_rel(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - -/* - * Stat an (NFS) file handle. - * - * MP SAFE - */ -#ifndef _SYS_SYSPROTO_H_ -struct fhstat_args { - struct fhandle *u_fhp; - struct stat *sb; -}; -#endif -int -fhstat(td, uap) - struct thread *td; - register struct fhstat_args /* { - struct fhandle *u_fhp; - struct stat *sb; - } */ *uap; -{ - struct stat sb; - fhandle_t fh; - struct mount *mp; - struct vnode *vp; - int vfslocked; - int error; - - error = priv_check(td, PRIV_VFS_FHSTAT); - if (error) - return (error); - error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); - if (error) - return (error); - if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) - return (ESTALE); - vfslocked = VFS_LOCK_GIANT(mp); - if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) { - vfs_rel(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); - } - error = vn_stat(vp, &sb, td->td_ucred, NOCRED, td); - vput(vp); - vfs_rel(mp); - VFS_UNLOCK_GIANT(vfslocked); - if (error) - return (error); - error = copyout(&sb, uap->sb, sizeof(sb)); - return (error); -} - -/* - * Implement fstatfs() for (NFS) file handles. - * - * MP SAFE - */ -#ifndef _SYS_SYSPROTO_H_ -struct fhstatfs_args { - struct fhandle *u_fhp; - struct statfs *buf; -}; -#endif -int -fhstatfs(td, uap) - struct thread *td; - struct fhstatfs_args /* { - struct fhandle *u_fhp; - struct statfs *buf; - } */ *uap; -{ - struct statfs sf; - fhandle_t fh; - int error; - - error = copyin(uap->u_fhp, &fh, sizeof(fhandle_t)); - if (error) - return (error); - error = kern_fhstatfs(td, fh, &sf); - if (error) - return (error); - return (copyout(&sf, uap->buf, sizeof(sf))); -} - -int -kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf) -{ - struct statfs *sp; - struct mount *mp; - struct vnode *vp; - int vfslocked; - int error; - - error = priv_check(td, PRIV_VFS_FHSTATFS); - if (error) - return (error); - if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) - return (ESTALE); - vfslocked = VFS_LOCK_GIANT(mp); - error = VFS_FHTOVP(mp, &fh.fh_fid, &vp); - if (error) { - VFS_UNLOCK_GIANT(vfslocked); - vfs_rel(mp); - return (error); - } - vput(vp); - error = prison_canseemount(td->td_ucred, mp); - if (error) - goto out; -#ifdef MAC - error = mac_check_mount_stat(td->td_ucred, mp); - if (error) - goto out; -#endif - /* - * Set these in case the underlying filesystem fails to do so. - */ - sp = &mp->mnt_stat; - sp->f_version = STATFS_VERSION; - sp->f_namemax = NAME_MAX; - sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; - error = VFS_STATFS(mp, sp, td); - if (error == 0) - *buf = *sp; -out: - vfs_rel(mp); - VFS_UNLOCK_GIANT(vfslocked); - return (error); -} - /* * Syscall to push extended attribute configuration information into the * VFS. Accepts a path, which it converts to a mountpoint, as well as |