X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/c03959f987d61b669d055be0f875228a7bc8ac59..13f48dee70ec0b79753383e9734854a57464d8fa:/usr/src/sys/kern/vfs_syscalls.c diff --git a/usr/src/sys/kern/vfs_syscalls.c b/usr/src/sys/kern/vfs_syscalls.c index 91fb4ac390..4063304af6 100644 --- a/usr/src/sys/kern/vfs_syscalls.c +++ b/usr/src/sys/kern/vfs_syscalls.c @@ -1,152 +1,192 @@ /* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. + * 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. * * %sccs.include.redist.c% * - * @(#)vfs_syscalls.c 7.70.1.1 (Berkeley) %G% + * @(#)vfs_syscalls.c 8.28 (Berkeley) %G% */ -#include "param.h" -#include "systm.h" -#include "namei.h" -#include "filedesc.h" -#include "kernel.h" -#include "file.h" -#include "stat.h" -#include "vnode.h" -#include "mount.h" -#include "proc.h" -#include "uio.h" -#include "malloc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int change_dir __P((struct nameidata *ndp, struct proc *p)); + +#ifdef REF_DIAGNOSTIC +#define CURCOUNT (curproc ? curproc->p_spare[0] : 0) +#define CHECKPOINTREF int oldrefcount = CURCOUNT; +#define CHECKREFS(F) if (oldrefcount != CURCOUNT) \ + printf("REFCOUNT: %s, old=%d, new=%d\n", (F), oldrefcount, CURCOUNT); +#else +#define CHECKPOINTREF +#define CHECKREFS(D) +#endif /* * Virtual File System System Calls */ /* - * Mount system call. + * Mount a file system. */ +struct mount_args { + int type; + char *path; + int flags; + caddr_t data; +}; /* ARGSUSED */ mount(p, uap, retval) struct proc *p; - register struct args { - int type; - char *dir; - int flags; - caddr_t data; - } *uap; + register struct mount_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; register struct mount *mp; int error, flag; + struct vattr va; struct nameidata nd; - /* - * Must be super user - */ - if (error = suser(p->p_ucred, &p->p_acflag)) - return (error); /* * Get vnode to be covered */ - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->dir; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; + vp = nd.ni_vp; if (uap->flags & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; + flag = mp->mnt_flag; /* - * We allow going from read-only to read-write, - * but not from read-write to read-only. + * We only allow the filesystem to be reloaded if it + * is currently mounted read-only. */ - if ((mp->mnt_flag & MNT_RDONLY) == 0 && - (uap->flags & MNT_RDONLY) != 0) { + if ((uap->flags & MNT_RELOAD) && + ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } - flag = mp->mnt_flag; - mp->mnt_flag |= MNT_UPDATE; + mp->mnt_flag |= + uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); + /* + * Only root, or the user that did the original mount is + * permitted to update it. + */ + if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && + (error = suser(p->p_ucred, &p->p_acflag))) { + vput(vp); + return (error); + } + /* + * Do not allow NFS export by non-root users. Silently + * enforce MNT_NOSUID and MNT_NODEV for non-root users. + */ + if (p->p_ucred->cr_uid != 0) { + if (uap->flags & MNT_EXPORTED) { + vput(vp); + return (EPERM); + } + uap->flags |= MNT_NOSUID | MNT_NODEV; + } VOP_UNLOCK(vp); goto update; } - vinvalbuf(vp, 1); - if (vp->v_usecount != 1) { + /* + * If the user is not root, ensure that they own the directory + * onto which we are attempting to mount. + */ + if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || + (va.va_uid != p->p_ucred->cr_uid && + (error = suser(p->p_ucred, &p->p_acflag)))) { vput(vp); - return (EBUSY); + return (error); } + /* + * Do not allow NFS export by non-root users. Silently + * enforce MNT_NOSUID and MNT_NODEV for non-root users. + */ + if (p->p_ucred->cr_uid != 0) { + if (uap->flags & MNT_EXPORTED) { + vput(vp); + return (EPERM); + } + uap->flags |= MNT_NOSUID | MNT_NODEV; + } + if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) + return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } - if ((unsigned long)uap->type > MOUNT_MAXTYPE || - vfssw[uap->type] == (struct vfsops *)0) { + if ((u_long)uap->type > MOUNT_MAXTYPE || vfssw[uap->type] == NULL) { vput(vp); return (ENODEV); } + if (vp->v_mountedhere != NULL) { + vput(vp); + return (EBUSY); + } /* * Allocate and initialize the file system. */ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); + bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = vfssw[uap->type]; - mp->mnt_flag = 0; - mp->mnt_exroot = 0; - mp->mnt_mounth = NULLVP; if (error = vfs_lock(mp)) { free((caddr_t)mp, M_MOUNT); vput(vp); return (error); } - if (vp->v_mountedhere != (struct mount *)0) { - vfs_unlock(mp); - free((caddr_t)mp, M_MOUNT); - vput(vp); - return (EBUSY); - } vp->v_mountedhere = mp; mp->mnt_vnodecovered = vp; + mp->mnt_stat.f_owner = p->p_ucred->cr_uid; update: /* * Set the mount level flags. */ if (uap->flags & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; - else - mp->mnt_flag &= ~MNT_RDONLY; - if (uap->flags & MNT_NOSUID) - mp->mnt_flag |= MNT_NOSUID; - else - mp->mnt_flag &= ~MNT_NOSUID; - if (uap->flags & MNT_NOEXEC) - mp->mnt_flag |= MNT_NOEXEC; - else - mp->mnt_flag &= ~MNT_NOEXEC; - if (uap->flags & MNT_NODEV) - mp->mnt_flag |= MNT_NODEV; - else - mp->mnt_flag &= ~MNT_NODEV; - if (uap->flags & MNT_SYNCHRONOUS) - mp->mnt_flag |= MNT_SYNCHRONOUS; - else - mp->mnt_flag &= ~MNT_SYNCHRONOUS; + else if (mp->mnt_flag & MNT_RDONLY) + mp->mnt_flag |= MNT_WANTRDWR; + mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | + MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); + mp->mnt_flag |= uap->flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | + MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); /* * Mount the filesystem. */ - error = VFS_MOUNT(mp, uap->dir, uap->data, ndp, p); + error = VFS_MOUNT(mp, uap->path, uap->data, &nd, p); if (mp->mnt_flag & MNT_UPDATE) { - mp->mnt_flag &= ~MNT_UPDATE; vrele(vp); + if (mp->mnt_flag & MNT_WANTRDWR) + mp->mnt_flag &= ~MNT_RDONLY; + mp->mnt_flag &=~ + (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); if (error) mp->mnt_flag = flag; return (error); @@ -154,17 +194,16 @@ update: /* * Put the new filesystem on the mount list after root. */ - mp->mnt_next = rootfs->mnt_next; - mp->mnt_prev = rootfs; - rootfs->mnt_next = mp; - mp->mnt_next->mnt_prev = mp; cache_purge(vp); if (!error) { + TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); + checkdirs(vp); VOP_UNLOCK(vp); vfs_unlock(mp); error = VFS_START(mp, 0, p); } else { - vfs_remove(mp); + mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; + vfs_unlock(mp); free((caddr_t)mp, M_MOUNT); vput(vp); } @@ -172,39 +211,79 @@ update: } /* - * Unmount system call. + * Scan all active processes to see if any of them have a current + * or root directory onto which the new filesystem has just been + * mounted. If so, replace them with the new mount point. + */ +checkdirs(olddp) + struct vnode *olddp; +{ + struct filedesc *fdp; + struct vnode *newdp; + struct proc *p; + + if (olddp->v_usecount == 1) + return; + if (VFS_ROOT(olddp->v_mountedhere, &newdp)) + panic("mount: lost mount"); + for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { + fdp = p->p_fd; + if (fdp->fd_cdir == olddp) { + vrele(fdp->fd_cdir); + VREF(newdp); + fdp->fd_cdir = newdp; + } + if (fdp->fd_rdir == olddp) { + vrele(fdp->fd_rdir); + VREF(newdp); + fdp->fd_rdir = newdp; + } + } + if (rootvnode == olddp) { + vrele(rootvnode); + VREF(newdp); + rootvnode = newdp; + } + vput(newdp); +} + +/* + * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ +struct unmount_args { + char *path; + int flags; +}; /* ARGSUSED */ unmount(p, uap, retval) struct proc *p; - register struct args { - char *pathp; - int flags; - } *uap; + register struct unmount_args *uap; int *retval; { register struct vnode *vp; - register struct nameidata *ndp; struct mount *mp; int error; struct nameidata nd; + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + mp = vp->v_mount; + /* - * Must be super user + * Only root, or the user that did the original mount is + * permitted to unmount this filesystem. */ - if (error = suser(p->p_ucred, &p->p_acflag)) + if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && + (error = suser(p->p_ucred, &p->p_acflag))) { + vput(vp); return (error); + } - ndp = &nd; - ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->pathp; - if (error = namei(ndp, p)) - return (error); - vp = ndp->ni_vp; /* * Must be the root of the filesystem */ @@ -212,13 +291,12 @@ unmount(p, uap, retval) vput(vp); return (EINVAL); } - mp = vp->v_mount; vput(vp); return (dounmount(mp, uap->flags, p)); } /* - * Do an unmount. + * Do the actual file system unmount. */ dounmount(mp, flags, p) register struct mount *mp; @@ -235,9 +313,11 @@ dounmount(mp, flags, p) if (error = vfs_lock(mp)) return (error); + mp->mnt_flag &=~ MNT_ASYNC; vnode_pager_umount(mp); /* release cached vnodes */ cache_purgevfs(mp); /* remove cache entries for this file sys */ - if ((error = VFS_SYNC(mp, MNT_WAIT)) == 0 || (flags & MNT_FORCE)) + if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || + (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); mp->mnt_flag &= ~MNT_UNMOUNT; vfs_unbusy(mp); @@ -245,100 +325,119 @@ dounmount(mp, flags, p) vfs_unlock(mp); } else { vrele(coveredvp); - vfs_remove(mp); + TAILQ_REMOVE(&mountlist, mp, mnt_list); + mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; + vfs_unlock(mp); + if (mp->mnt_vnodelist.lh_first != NULL) + panic("unmount: dangling vnode"); free((caddr_t)mp, M_MOUNT); } return (error); } /* - * Sync system call. * Sync each mounted filesystem. */ +#ifdef DEBUG +int syncprt = 0; +struct ctldebug debug0 = { "syncprt", &syncprt }; +#endif + +struct sync_args { + int dummy; +}; /* ARGSUSED */ sync(p, uap, retval) struct proc *p; - void *uap; + struct sync_args *uap; int *retval; { - register struct mount *mp; - struct mount *omp; + register struct mount *mp, *nmp; + int asyncflag; - mp = rootfs; - do { + for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) { + /* + * Get the next pointer in case we hang on vfs_busy + * while we are being unmounted. + */ + nmp = mp->mnt_list.tqe_next; /* * The lock check below is to avoid races with mount * and unmount. */ if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && !vfs_busy(mp)) { - VFS_SYNC(mp, MNT_NOWAIT); - omp = mp; - mp = mp->mnt_next; - vfs_unbusy(omp); - } else - mp = mp->mnt_next; - } while (mp != rootfs); + asyncflag = mp->mnt_flag & MNT_ASYNC; + mp->mnt_flag &= ~MNT_ASYNC; + VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); + if (asyncflag) + mp->mnt_flag |= MNT_ASYNC; + /* + * Get the next pointer again, as the next filesystem + * might have been unmounted while we were sync'ing. + */ + nmp = mp->mnt_list.tqe_next; + vfs_unbusy(mp); + } + } +#ifdef DIAGNOSTIC + if (syncprt) + vfs_bufstats(); +#endif /* DIAGNOSTIC */ return (0); } /* - * Operate on filesystem quotas. + * Change filesystem quotas. */ +struct quotactl_args { + char *path; + int cmd; + int uid; + caddr_t arg; +}; /* ARGSUSED */ quotactl(p, uap, retval) struct proc *p; - register struct args { - char *path; - int cmd; - int uid; - caddr_t arg; - } *uap; + register struct quotactl_args *uap; int *retval; { register struct mount *mp; - register struct nameidata *ndp; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->path; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - mp = ndp->ni_vp->v_mount; - vrele(ndp->ni_vp); + mp = nd.ni_vp->v_mount; + vrele(nd.ni_vp); return (VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, p)); } /* * Get filesystem statistics. */ +struct statfs_args { + char *path; + struct statfs *buf; +}; /* ARGSUSED */ statfs(p, uap, retval) struct proc *p; - register struct args { - char *path; - struct statfs *buf; - } *uap; + register struct statfs_args *uap; int *retval; { register struct mount *mp; - register struct nameidata *ndp; register struct statfs *sp; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->path; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - mp = ndp->ni_vp->v_mount; + mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; - vrele(ndp->ni_vp); + vrele(nd.ni_vp); if (error = VFS_STATFS(mp, sp, p)) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; @@ -348,13 +447,14 @@ statfs(p, uap, retval) /* * Get filesystem statistics. */ +struct fstatfs_args { + int fd; + struct statfs *buf; +}; /* ARGSUSED */ fstatfs(p, uap, retval) struct proc *p; - register struct args { - int fd; - struct statfs *buf; - } *uap; + register struct fstatfs_args *uap; int *retval; { struct file *fp; @@ -375,25 +475,25 @@ fstatfs(p, uap, retval) /* * Get statistics on all filesystems. */ +struct getfsstat_args { + struct statfs *buf; + long bufsize; + int flags; +}; getfsstat(p, uap, retval) struct proc *p; - register struct args { - struct statfs *buf; - long bufsize; - int flags; - } *uap; + register struct getfsstat_args *uap; int *retval; { - register struct mount *mp; + register struct mount *mp, *nmp; register struct statfs *sp; caddr_t sfsp; long count, maxcount, error; maxcount = uap->bufsize / sizeof(struct statfs); sfsp = (caddr_t)uap->buf; - mp = rootfs; - count = 0; - do { + for (count = 0, mp = mountlist.tqh_first; mp != NULL; mp = nmp) { + nmp = mp->mnt_list.tqe_next; if (sfsp && count < maxcount && ((mp->mnt_flag & MNT_MLOCK) == 0)) { sp = &mp->mnt_stat; @@ -403,18 +503,15 @@ getfsstat(p, uap, retval) */ if (((uap->flags & MNT_NOWAIT) == 0 || (uap->flags & MNT_WAIT)) && - (error = VFS_STATFS(mp, sp, p))) { - mp = mp->mnt_prev; + (error = VFS_STATFS(mp, sp, p))) continue; - } sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp))) return (error); sfsp += sizeof(*sp); } count++; - mp = mp->mnt_prev; - } while (mp != rootfs); + } if (sfsp && count > maxcount) *retval = maxcount; else @@ -425,31 +522,46 @@ getfsstat(p, uap, retval) /* * Change current working directory to a given file descriptor. */ +struct fchdir_args { + int fd; +}; /* ARGSUSED */ fchdir(p, uap, retval) struct proc *p; - struct args { - int fd; - } *uap; + struct fchdir_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; - register struct vnode *vp; + struct vnode *vp, *tdp; + struct mount *mp; struct file *fp; int error; if (error = getvnode(fdp, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; + VREF(vp); VOP_LOCK(vp); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + while (!error && (mp = vp->v_mountedhere) != NULL) { + if (mp->mnt_flag & MNT_MLOCK) { + mp->mnt_flag |= MNT_MWAIT; + sleep((caddr_t)mp, PVFS); + continue; + } + if (error = VFS_ROOT(mp, &tdp)) + break; + vput(vp); + vp = tdp; + } VOP_UNLOCK(vp); - if (error) + if (error) { + vrele(vp); return (error); - VREF(vp); + } vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); @@ -458,71 +570,66 @@ fchdir(p, uap, retval) /* * Change current working directory (``.''). */ +struct chdir_args { + char *path; +}; /* ARGSUSED */ chdir(p, uap, retval) struct proc *p; - struct args { - char *fname; - } *uap; + struct chdir_args *uap; int *retval; { - register struct nameidata *ndp; register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = chdirec(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = change_dir(&nd, p)) return (error); vrele(fdp->fd_cdir); - fdp->fd_cdir = ndp->ni_vp; + fdp->fd_cdir = nd.ni_vp; return (0); } /* * Change notion of root (``/'') directory. */ +struct chroot_args { + char *path; +}; /* ARGSUSED */ chroot(p, uap, retval) struct proc *p; - struct args { - char *fname; - } *uap; + struct chroot_args *uap; int *retval; { - register struct nameidata *ndp; register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; if (error = suser(p->p_ucred, &p->p_acflag)) return (error); - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = chdirec(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = change_dir(&nd, p)) return (error); if (fdp->fd_rdir != NULL) vrele(fdp->fd_rdir); - fdp->fd_rdir = ndp->ni_vp; + fdp->fd_rdir = nd.ni_vp; return (0); } /* * Common routine for chroot and chdir. */ -chdirec(ndp, p) - struct nameidata *ndp; +static int +change_dir(ndp, p) + register struct nameidata *ndp; struct proc *p; { struct vnode *vp; int error; - if (error = namei(ndp, p)) + if (error = namei(ndp)) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) @@ -536,43 +643,42 @@ chdirec(ndp, p) } /* - * Open system call. * Check permissions, allocate an open file structure, * and call the device open routine if any. */ +struct open_args { + char *path; + int flags; + int mode; +}; open(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int mode; - int crtmode; - } *uap; + register struct open_args *uap; int *retval; { - struct nameidata *ndp; register struct filedesc *fdp = p->p_fd; register struct file *fp; - int fmode, cmode; + register struct vnode *vp; + int flags, cmode; struct file *nfp; - int indx, error; + int type, indx, error; + struct flock lf; struct nameidata nd; extern struct fileops vnops; if (error = falloc(p, &nfp, &indx)) return (error); fp = nfp; - fmode = FFLAGS(uap->mode); - cmode = ((uap->crtmode &~ fdp->fd_cmask) & 07777) &~ S_ISVTX; - ndp = &nd; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; + flags = FFLAGS(uap->flags); + cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ - if (error = vn_open(ndp, p, fmode, cmode)) { - crfree(fp->f_cred); - fp->f_count--; - if (error == ENODEV && /* XXX from fdopen */ - p->p_dupfd >= 0 && - (error = dupfdopen(fdp, indx, p->p_dupfd, fmode)) == 0) { + if (error = vn_open(&nd, flags, cmode)) { + ffree(fp); + if ((error == ENODEV || error == ENXIO) && + p->p_dupfd >= 0 && /* XXX from fdopen */ + (error = + dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { *retval = indx; return (0); } @@ -581,117 +687,150 @@ open(p, uap, retval) fdp->fd_ofiles[indx] = NULL; return (error); } - fp->f_flag = fmode & FMASK; + p->p_dupfd = 0; + vp = nd.ni_vp; + fp->f_flag = flags & FMASK; fp->f_type = DTYPE_VNODE; fp->f_ops = &vnops; - fp->f_data = (caddr_t)ndp->ni_vp; + fp->f_data = (caddr_t)vp; + if (flags & (O_EXLOCK | O_SHLOCK)) { + lf.l_whence = SEEK_SET; + lf.l_start = 0; + lf.l_len = 0; + if (flags & O_EXLOCK) + lf.l_type = F_WRLCK; + else + lf.l_type = F_RDLCK; + type = F_FLOCK; + if ((flags & FNONBLOCK) == 0) + type |= F_WAIT; + VOP_UNLOCK(vp); + if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) { + (void) vn_close(vp, fp->f_flag, fp->f_cred, p); + ffree(fp); + fdp->fd_ofiles[indx] = NULL; + return (error); + } + VOP_LOCK(vp); + fp->f_flag |= FHASLOCK; + } + VOP_UNLOCK(vp); *retval = indx; return (0); } #ifdef COMPAT_43 /* - * Creat system call. + * Create a file. */ +struct ocreat_args { + char *path; + int mode; +}; ocreat(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct ocreat_args *uap; int *retval; { - struct args { - char *fname; - int mode; - int crtmode; - } openuap; - - openuap.fname = uap->fname; - openuap.crtmode = uap->fmode; - openuap.mode = O_WRONLY | O_CREAT | O_TRUNC; + struct open_args openuap; + + openuap.path = uap->path; + openuap.mode = uap->mode; + openuap.flags = O_WRONLY | O_CREAT | O_TRUNC; return (open(p, &openuap, retval)); } #endif /* COMPAT_43 */ /* - * Mknod system call. + * Create a special file. */ +struct mknod_args { + char *path; + int mode; + int dev; +}; /* ARGSUSED */ mknod(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - int dev; - } *uap; + register struct mknod_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct vattr vattr; int error; + int whiteout; struct nameidata nd; + CHECKPOINTREF; if (error = suser(p->p_ucred, &p->p_acflag)) return (error); - ndp = &nd; - ndp->ni_nameiop = CREATE | LOCKPARENT; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp != NULL) { + vp = nd.ni_vp; + if (vp != NULL) error = EEXIST; - goto out; - } - VATTR_NULL(&vattr); - switch (uap->fmode & 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; - default: - error = EINVAL; - goto out; + else { + VATTR_NULL(&vattr); + vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; + vattr.va_rdev = uap->dev; + whiteout = 0; + + switch (uap->mode & S_IFMT) { + case S_IFMT: /* used by badsect to flag bad sectors */ + vattr.va_type = VBAD; + break; + case S_IFCHR: + vattr.va_type = VCHR; + break; + case S_IFBLK: + vattr.va_type = VBLK; + break; + case S_IFWHT: + whiteout = 1; + break; + default: + error = EINVAL; + break; + } } - vattr.va_mode = (uap->fmode & 07777) &~ p->p_fd->fd_cmask; - vattr.va_rdev = uap->dev; -out: if (!error) { - error = VOP_MKNOD(ndp, &vattr, p->p_ucred, p); + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (whiteout) { + error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); + if (error) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + } else { + error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, + &nd.ni_cnd, &vattr); + } } else { - VOP_ABORTOP(ndp); - if (ndp->ni_dvp == vp) - vrele(ndp->ni_dvp); + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); else - vput(ndp->ni_dvp); + vput(nd.ni_dvp); if (vp) vrele(vp); } + CHECKREFS("mknod"); return (error); } /* - * Mkfifo system call. + * Create named pipe. */ +struct mkfifo_args { + char *path; + int mode; +}; /* ARGSUSED */ mkfifo(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct mkfifo_args *uap; int *retval; { - register struct nameidata *ndp; struct vattr vattr; int error; struct nameidata nd; @@ -699,188 +838,224 @@ mkfifo(p, uap, retval) #ifndef FIFO return (EOPNOTSUPP); #else - ndp = &nd; - ndp->ni_nameiop = CREATE | LOCKPARENT; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - if (ndp->ni_vp != NULL) { - VOP_ABORTOP(ndp); - if (ndp->ni_dvp == ndp->ni_vp) - vrele(ndp->ni_dvp); + if (nd.ni_vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); else - vput(ndp->ni_dvp); - vrele(ndp->ni_vp); + vput(nd.ni_dvp); + vrele(nd.ni_vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VFIFO; - vattr.va_mode = (uap->fmode & 07777) &~ p->p_fd->fd_cmask; - return (VOP_MKNOD(ndp, &vattr, p->p_ucred, p)); + vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); #endif /* FIFO */ } /* - * Link system call. + * Make a hard file link. */ +struct link_args { + char *path; + char *link; +}; /* ARGSUSED */ link(p, uap, retval) struct proc *p; - register struct args { - char *target; - char *linkname; - } *uap; + register struct link_args *uap; int *retval; { - register struct nameidata *ndp; - register struct vnode *vp, *xp; - int error; + register struct vnode *vp; struct nameidata nd; + int error; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->target; - if (error = namei(ndp, p)) + CHECKPOINTREF; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_type == VDIR && - (error = suser(p->p_ucred, &p->p_acflag))) - goto out1; - ndp->ni_nameiop = CREATE | LOCKPARENT; - ndp->ni_dirp = (caddr_t)uap->linkname; - if (error = namei(ndp, p)) - goto out1; - xp = ndp->ni_vp; - if (xp != NULL) { - error = EEXIST; - goto out; - } - xp = ndp->ni_dvp; - if (vp->v_mount != xp->v_mount) - error = EXDEV; -out: - if (!error) { - error = VOP_LINK(vp, ndp, p); - } else { - VOP_ABORTOP(ndp); - if (ndp->ni_dvp == ndp->ni_vp) - vrele(ndp->ni_dvp); - else - vput(ndp->ni_dvp); - if (ndp->ni_vp) - vrele(ndp->ni_vp); + vp = nd.ni_vp; + if (vp->v_type != VDIR || + (error = suser(p->p_ucred, &p->p_acflag)) == 0) { + nd.ni_cnd.cn_nameiop = CREATE; + nd.ni_cnd.cn_flags = LOCKPARENT; + nd.ni_dirp = uap->link; + if ((error = namei(&nd)) == 0) { + if (nd.ni_vp != NULL) + error = EEXIST; + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, + LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (nd.ni_vp) + vrele(nd.ni_vp); + } + } } -out1: vrele(vp); + CHECKREFS("link"); return (error); } /* * Make a symbolic link. */ +struct symlink_args { + char *path; + char *link; +}; /* ARGSUSED */ symlink(p, uap, retval) struct proc *p; - register struct args { - char *target; - char *linkname; - } *uap; + register struct symlink_args *uap; int *retval; { - register struct nameidata *ndp; struct vattr vattr; - char *target; + char *path; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->linkname; - MALLOC(target, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); - if (error = copyinstr(uap->target, target, MAXPATHLEN, (u_int *)0)) + CHECKPOINTREF; + MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); + if (error = copyinstr(uap->path, path, MAXPATHLEN, NULL)) goto out; - ndp->ni_nameiop = CREATE | LOCKPARENT; - if (error = namei(ndp, p)) + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->link, p); + if (error = namei(&nd)) goto out; - if (ndp->ni_vp) { - VOP_ABORTOP(ndp); - if (ndp->ni_dvp == ndp->ni_vp) - vrele(ndp->ni_dvp); + if (nd.ni_vp) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); else - vput(ndp->ni_dvp); - vrele(ndp->ni_vp); + vput(nd.ni_dvp); + vrele(nd.ni_vp); error = EEXIST; goto out; } VATTR_NULL(&vattr); - vattr.va_mode = 0777 &~ p->p_fd->fd_cmask; - error = VOP_SYMLINK(ndp, &vattr, target, p); + vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); out: - FREE(target, M_NAMEI); + FREE(path, M_NAMEI); + CHECKREFS("symlink"); + return (error); +} + +/* + * Delete a whiteout from the filesystem. + */ +struct undelete_args { + char *path; +}; +/* ARGSUSED */ +undelete(p, uap, retval) + struct proc *p; + struct undelete_args *uap; + int *retval; +{ + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, uap->path, p); + error = namei(&nd); + if (error) + return (error); + + if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (nd.ni_vp) + vrele(nd.ni_vp); + return (EEXIST); + } + + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); return (error); } /* * Delete a name from the filesystem. */ +struct unlink_args { + char *path; +}; /* ARGSUSED */ unlink(p, uap, retval) struct proc *p; - struct args { - char *fname; - } *uap; + struct unlink_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + CHECKPOINTREF; + NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_type == VDIR && - (error = suser(p->p_ucred, &p->p_acflag))) - goto out; - /* - * The root of a mounted filesystem cannot be deleted. - */ - if (vp->v_flag & VROOT) { - error = EBUSY; - goto out; + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + + if (vp->v_type != VDIR || + (error = suser(p->p_ucred, &p->p_acflag)) == 0) { + /* + * The root of a mounted filesystem cannot be deleted. + */ + if (vp->v_flag & VROOT) + error = EBUSY; + else + (void)vnode_pager_uncache(vp); } - (void) vnode_pager_uncache(vp); -out: + if (!error) { - error = VOP_REMOVE(ndp, p); + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { - VOP_ABORTOP(ndp); - if (ndp->ni_dvp == vp) - vrele(ndp->ni_dvp); + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); else - vput(ndp->ni_dvp); - vput(vp); + vput(nd.ni_dvp); + if (vp != NULLVP) + vput(vp); } + CHECKREFS("unlink"); return (error); } /* - * Seek system call. + * Reposition read/write file offset. */ +struct lseek_args { + int fd; + int pad; + off_t offset; + int whence; +}; lseek(p, uap, retval) struct proc *p; - register struct args { - int fdes; - off_t off; - int sbase; - } *uap; - off_t *retval; + register struct lseek_args *uap; + int *retval; { struct ucred *cred = p->p_ucred; register struct filedesc *fdp = p->p_fd; @@ -888,229 +1063,409 @@ lseek(p, uap, retval) struct vattr vattr; int error; - if ((unsigned)uap->fdes >= fdp->fd_nfiles || - (fp = fdp->fd_ofiles[uap->fdes]) == NULL) + if ((u_int)uap->fd >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[uap->fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); - switch (uap->sbase) { - + switch (uap->whence) { case L_INCR: - fp->f_offset += uap->off; + fp->f_offset += uap->offset; break; - case L_XTND: - if (error = VOP_GETATTR((struct vnode *)fp->f_data, - &vattr, cred, p)) + if (error = + VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p)) return (error); - fp->f_offset = uap->off + vattr.va_size; + fp->f_offset = uap->offset + vattr.va_size; break; - case L_SET: - fp->f_offset = uap->off; + fp->f_offset = uap->offset; break; - default: return (EINVAL); } - *retval = fp->f_offset; + *(off_t *)retval = fp->f_offset; return (0); } +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +/* + * Reposition read/write file offset. + */ +struct olseek_args { + int fd; + long offset; + int whence; +}; +olseek(p, uap, retval) + struct proc *p; + register struct olseek_args *uap; + int *retval; +{ + struct lseek_args nuap; + off_t qret; + int error; + + nuap.fd = uap->fd; + nuap.offset = uap->offset; + nuap.whence = uap->whence; + error = lseek(p, &nuap, &qret); + *(long *)retval = qret; + return (error); +} +#endif /* COMPAT_43 */ + /* * Check access permissions. */ -/* ARGSUSED */ -saccess(p, uap, retval) +struct access_args { + char *path; + int flags; +}; +access(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct access_args *uap; int *retval; { - register struct nameidata *ndp; register struct ucred *cred = p->p_ucred; register struct vnode *vp; - int error, mode, svuid, svgid; + int error, flags, t_gid, t_uid; struct nameidata nd; - ndp = &nd; - svuid = cred->cr_uid; - svgid = cred->cr_groups[0]; + t_uid = cred->cr_uid; + t_gid = cred->cr_groups[0]; cred->cr_uid = p->p_cred->p_ruid; cred->cr_groups[0] = p->p_cred->p_rgid; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) goto out1; - vp = ndp->ni_vp; - /* - * fmode == 0 means only check for exist - */ - if (uap->fmode) { - mode = 0; - if (uap->fmode & R_OK) - mode |= VREAD; - if (uap->fmode & W_OK) - mode |= VWRITE; - if (uap->fmode & X_OK) - mode |= VEXEC; - if ((mode & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) - error = VOP_ACCESS(vp, mode, cred, p); + vp = nd.ni_vp; + + /* Flags == 0 means only check for existence. */ + if (uap->flags) { + flags = 0; + if (uap->flags & R_OK) + flags |= VREAD; + if (uap->flags & W_OK) + flags |= VWRITE; + if (uap->flags & X_OK) + flags |= VEXEC; + if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) + error = VOP_ACCESS(vp, flags, cred, p); } vput(vp); out1: - cred->cr_uid = svuid; - cred->cr_groups[0] = svgid; + cred->cr_uid = t_uid; + cred->cr_groups[0] = t_gid; return (error); } +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* - * Stat system call. - * This version follows links. + * Get file status; this version follows links. */ +struct ostat_args { + char *path; + struct ostat *ub; +}; /* ARGSUSED */ -stat(p, uap, retval) +ostat(p, uap, retval) struct proc *p; - register struct args { - char *fname; - struct stat *ub; - } *uap; + register struct ostat_args *uap; int *retval; { - register struct nameidata *ndp; struct stat sb; + struct ostat osb; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - error = vn_stat(ndp->ni_vp, &sb, p); - vput(ndp->ni_vp); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); if (error) return (error); - error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); + cvtstat(&sb, &osb); + error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); return (error); } /* - * Lstat system call. - * This version does not follow links. + * Get file status; this version does not follow links. */ +struct olstat_args { + char *path; + struct ostat *ub; +}; /* ARGSUSED */ -lstat(p, uap, retval) +olstat(p, uap, retval) struct proc *p; - register struct args { - char *fname; - struct stat *ub; - } *uap; + register struct olstat_args *uap; + int *retval; +{ + struct vnode *vp, *dvp; + struct stat sb, sb1; + struct ostat osb; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, + uap->path, p); + if (error = namei(&nd)) + return (error); + /* + * For symbolic links, always return the attributes of its + * containing directory, except for mode, size, and links. + */ + vp = nd.ni_vp; + dvp = nd.ni_dvp; + if (vp->v_type != VLNK) { + if (dvp == vp) + vrele(dvp); + else + vput(dvp); + error = vn_stat(vp, &sb, p); + vput(vp); + if (error) + return (error); + } else { + error = vn_stat(dvp, &sb, p); + vput(dvp); + if (error) { + vput(vp); + return (error); + } + error = vn_stat(vp, &sb1, p); + vput(vp); + if (error) + return (error); + sb.st_mode &= ~S_IFDIR; + sb.st_mode |= S_IFLNK; + sb.st_nlink = sb1.st_nlink; + sb.st_size = sb1.st_size; + sb.st_blocks = sb1.st_blocks; + } + cvtstat(&sb, &osb); + error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); + return (error); +} + +/* + * Convert from an old to a new stat structure. + */ +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 || COMPAT_SUNOS */ + +/* + * Get file status; this version follows links. + */ +struct stat_args { + char *path; + struct stat *ub; +}; +/* ARGSUSED */ +stat(p, uap, retval) + struct proc *p; + register struct stat_args *uap; int *retval; { - register struct nameidata *ndp; struct stat sb; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | LOCKLEAF | NOFOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - error = vn_stat(ndp->ni_vp, &sb, p); - vput(ndp->ni_vp); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); return (error); } +/* + * Get file status; this version does not follow links. + */ +struct lstat_args { + char *path; + struct stat *ub; +}; +/* ARGSUSED */ +lstat(p, uap, retval) + struct proc *p; + register struct lstat_args *uap; + int *retval; +{ + int error; + struct vnode *vp, *dvp; + struct stat sb, sb1; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, + uap->path, p); + if (error = namei(&nd)) + return (error); + /* + * For symbolic links, always return the attributes of its + * containing directory, except for mode, size, and links. + */ + vp = nd.ni_vp; + dvp = nd.ni_dvp; + if (vp->v_type != VLNK) { + if (dvp == vp) + vrele(dvp); + else + vput(dvp); + error = vn_stat(vp, &sb, p); + vput(vp); + if (error) + return (error); + } else { + error = vn_stat(dvp, &sb, p); + vput(dvp); + if (error) { + vput(vp); + return (error); + } + error = vn_stat(vp, &sb1, p); + vput(vp); + if (error) + return (error); + sb.st_mode &= ~S_IFDIR; + sb.st_mode |= S_IFLNK; + sb.st_nlink = sb1.st_nlink; + sb.st_size = sb1.st_size; + sb.st_blocks = sb1.st_blocks; + } + error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); + return (error); +} + +/* + * Get configurable pathname variables. + */ +struct pathconf_args { + char *path; + int name; +}; +/* ARGSUSED */ +pathconf(p, uap, retval) + struct proc *p; + register struct pathconf_args *uap; + int *retval; +{ + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) + return (error); + error = VOP_PATHCONF(nd.ni_vp, uap->name, retval); + vput(nd.ni_vp); + return (error); +} + /* * Return target name of a symbolic link. */ +struct readlink_args { + char *path; + char *buf; + int count; +}; /* ARGSUSED */ readlink(p, uap, retval) struct proc *p; - register struct args { - char *name; - char *buf; - int count; - } *uap; + register struct readlink_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->name; - if (error = namei(ndp, p)) + CHECKPOINTREF; + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_type != VLNK) { + vp = nd.ni_vp; + if (vp->v_type != VLNK) error = EINVAL; - goto out; + else { + aiov.iov_base = uap->buf; + aiov.iov_len = uap->count; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auio.uio_resid = uap->count; + error = VOP_READLINK(vp, &auio, p->p_ucred); } - aiov.iov_base = uap->buf; - aiov.iov_len = uap->count; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - auio.uio_offset = 0; - auio.uio_rw = UIO_READ; - auio.uio_segflg = UIO_USERSPACE; - auio.uio_procp = p; - auio.uio_resid = uap->count; - error = VOP_READLINK(vp, &auio, p->p_ucred); -out: vput(vp); *retval = uap->count - auio.uio_resid; + CHECKREFS("readlink"); return (error); } /* - * Change flags of a file given path name. + * Change flags of a file given a path name. */ +struct chflags_args { + char *path; + int flags; +}; /* ARGSUSED */ chflags(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int flags; - } *uap; + register struct chflags_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_mount->mnt_flag & MNT_RDONLY) { + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; - goto out; + else { + VATTR_NULL(&vattr); + vattr.va_flags = uap->flags; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VATTR_NULL(&vattr); - vattr.va_flags = uap->flags; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: vput(vp); return (error); } @@ -1118,13 +1473,14 @@ out: /* * Change flags of a file given a file descriptor. */ +struct fchflags_args { + int fd; + int flags; +}; /* ARGSUSED */ fchflags(p, uap, retval) struct proc *p; - register struct args { - int fd; - int flags; - } *uap; + register struct fchflags_args *uap; int *retval; { struct vattr vattr; @@ -1135,15 +1491,15 @@ fchflags(p, uap, retval) if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); - if (vp->v_mount->mnt_flag & MNT_RDONLY) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; - goto out; + else { + VATTR_NULL(&vattr); + vattr.va_flags = uap->flags; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VATTR_NULL(&vattr); - vattr.va_flags = uap->flags; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: VOP_UNLOCK(vp); return (error); } @@ -1151,36 +1507,34 @@ out: /* * Change mode of a file given path name. */ +struct chmod_args { + char *path; + int mode; +}; /* ARGSUSED */ chmod(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int fmode; - } *uap; + register struct chmod_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_mount->mnt_flag & MNT_RDONLY) { + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; - goto out; + else { + VATTR_NULL(&vattr); + vattr.va_mode = uap->mode & ALLPERMS; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VATTR_NULL(&vattr); - vattr.va_mode = uap->fmode & 07777; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: vput(vp); return (error); } @@ -1188,13 +1542,14 @@ out: /* * Change mode of a file given a file descriptor. */ +struct fchmod_args { + int fd; + int mode; +}; /* ARGSUSED */ fchmod(p, uap, retval) struct proc *p; - register struct args { - int fd; - int fmode; - } *uap; + register struct fchmod_args *uap; int *retval; { struct vattr vattr; @@ -1205,15 +1560,15 @@ fchmod(p, uap, retval) if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); - if (vp->v_mount->mnt_flag & MNT_RDONLY) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; - goto out; + else { + VATTR_NULL(&vattr); + vattr.va_mode = uap->mode & ALLPERMS; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VATTR_NULL(&vattr); - vattr.va_mode = uap->fmode & 07777; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: VOP_UNLOCK(vp); return (error); } @@ -1221,38 +1576,36 @@ out: /* * Set ownership given a path name. */ +struct chown_args { + char *path; + int uid; + int gid; +}; /* ARGSUSED */ chown(p, uap, retval) struct proc *p; - register struct args { - char *fname; - int uid; - int gid; - } *uap; + register struct chown_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | NOFOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_mount->mnt_flag & MNT_RDONLY) { + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; - goto out; + else { + VATTR_NULL(&vattr); + vattr.va_uid = uap->uid; + vattr.va_gid = uap->gid; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VATTR_NULL(&vattr); - vattr.va_uid = uap->uid; - vattr.va_gid = uap->gid; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: vput(vp); return (error); } @@ -1260,14 +1613,15 @@ out: /* * Set ownership given a file descriptor. */ +struct fchown_args { + int fd; + int uid; + int gid; +}; /* ARGSUSED */ fchown(p, uap, retval) struct proc *p; - register struct args { - int fd; - int uid; - int gid; - } *uap; + register struct fchown_args *uap; int *retval; { struct vattr vattr; @@ -1278,16 +1632,16 @@ fchown(p, uap, retval) if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); - if (vp->v_mount->mnt_flag & MNT_RDONLY) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; - goto out; + else { + VATTR_NULL(&vattr); + vattr.va_uid = uap->uid; + vattr.va_gid = uap->gid; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VATTR_NULL(&vattr); - vattr.va_uid = uap->uid; - vattr.va_gid = uap->gid; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: VOP_UNLOCK(vp); return (error); } @@ -1295,40 +1649,44 @@ out: /* * Set the access and modification times of a file. */ +struct utimes_args { + char *path; + struct timeval *tptr; +}; /* ARGSUSED */ utimes(p, uap, retval) struct proc *p; - register struct args { - char *fname; - struct timeval *tptr; - } *uap; + register struct utimes_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct timeval tv[2]; struct vattr vattr; int error; struct nameidata nd; - if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv))) - return (error); - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + VATTR_NULL(&vattr); + if (uap->tptr == NULL) { + microtime(&tv[0]); + tv[1] = tv[0]; + vattr.va_vaflags |= VA_UTIMES_NULL; + } else if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv))) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_mount->mnt_flag & MNT_RDONLY) { + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; - goto out; + else { + vattr.va_atime.ts_sec = tv[0].tv_sec; + vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000; + vattr.va_mtime.ts_sec = tv[1].tv_sec; + vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - VATTR_NULL(&vattr); - vattr.va_atime = tv[0]; - vattr.va_mtime = tv[1]; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: vput(vp); return (error); } @@ -1336,39 +1694,36 @@ out: /* * Truncate a file given its path name. */ +struct truncate_args { + char *path; + int pad; + off_t length; +}; /* ARGSUSED */ truncate(p, uap, retval) struct proc *p; - register struct args { - char *fname; - off_t length; - } *uap; + register struct truncate_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; - if (vp->v_type == VDIR) { + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_type == VDIR) error = EISDIR; - goto out; + else if ((error = vn_writechk(vp)) == 0 && + (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = uap->length; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } - if ((error = vn_writechk(vp)) || - (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p))) - goto out; - VATTR_NULL(&vattr); - vattr.va_size = uap->length; - error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); -out: vput(vp); return (error); } @@ -1376,13 +1731,15 @@ out: /* * Truncate a file given a file descriptor. */ +struct ftruncate_args { + int fd; + int pad; + off_t length; +}; /* ARGSUSED */ ftruncate(p, uap, retval) struct proc *p; - register struct args { - int fd; - off_t length; - } *uap; + register struct ftruncate_args *uap; int *retval; { struct vattr vattr; @@ -1395,30 +1752,71 @@ ftruncate(p, uap, retval) if ((fp->f_flag & FWRITE) == 0) return (EINVAL); vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); - if (vp->v_type == VDIR) { + if (vp->v_type == VDIR) error = EISDIR; - goto out; + else if ((error = vn_writechk(vp)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = uap->length; + error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } - if (error = vn_writechk(vp)) - goto out; - VATTR_NULL(&vattr); - vattr.va_size = uap->length; - error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); -out: VOP_UNLOCK(vp); return (error); } +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) +/* + * Truncate a file given its path name. + */ +struct otruncate_args { + char *path; + long length; +}; +/* ARGSUSED */ +otruncate(p, uap, retval) + struct proc *p; + register struct otruncate_args *uap; + int *retval; +{ + struct truncate_args nuap; + + nuap.path = uap->path; + nuap.length = uap->length; + return (truncate(p, &nuap, retval)); +} + +/* + * Truncate a file given a file descriptor. + */ +struct oftruncate_args { + int fd; + long length; +}; +/* ARGSUSED */ +oftruncate(p, uap, retval) + struct proc *p; + register struct oftruncate_args *uap; + int *retval; +{ + struct ftruncate_args nuap; + + nuap.fd = uap->fd; + nuap.length = uap->length; + return (ftruncate(p, &nuap, retval)); +} +#endif /* COMPAT_43 || COMPAT_SUNOS */ + /* - * Synch an open file. + * Sync an open file. */ +struct fsync_args { + int fd; +}; /* ARGSUSED */ fsync(p, uap, retval) struct proc *p; - struct args { - int fd; - } *uap; + struct fsync_args *uap; int *retval; { register struct vnode *vp; @@ -1429,41 +1827,39 @@ fsync(p, uap, retval) return (error); vp = (struct vnode *)fp->f_data; VOP_LOCK(vp); - error = VOP_FSYNC(vp, fp->f_flag, fp->f_cred, MNT_WAIT, p); + error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); VOP_UNLOCK(vp); return (error); } /* - * Rename system call. - * - * Source and destination must either both be directories, or both - * not be directories. If target is a directory, it must be empty. + * Rename files. Source and destination must either both be directories, + * or both not be directories. If target is a directory, it must be empty. */ +struct rename_args { + char *from; + char *to; +}; /* ARGSUSED */ rename(p, uap, retval) struct proc *p; - register struct args { - char *from; - char *to; - } *uap; + register struct rename_args *uap; int *retval; { register struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; - fromnd.ni_nameiop = DELETE | WANTPARENT | SAVESTART; - fromnd.ni_segflg = UIO_USERSPACE; - fromnd.ni_dirp = uap->from; - if (error = namei(&fromnd, p)) + CHECKPOINTREF; + NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, + uap->from, p); + if (error = namei(&fromnd)) return (error); fvp = fromnd.ni_vp; - tond.ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; - tond.ni_segflg = UIO_USERSPACE; - tond.ni_dirp = uap->to; - if (error = namei(&tond, p)) { - VOP_ABORTOP(&fromnd); + NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART, + UIO_USERSPACE, uap->to, p); + if (error = namei(&tond)) { + VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; @@ -1478,14 +1874,6 @@ rename(p, uap, retval) error = EISDIR; goto out; } - if (fvp->v_mount != tvp->v_mount) { - error = EXDEV; - goto out; - } - } - if (fvp->v_mount != tdvp->v_mount) { - error = EXDEV; - goto out; } if (fvp == tdvp) error = EINVAL; @@ -1495,102 +1883,110 @@ rename(p, uap, retval) * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && - fromnd.ni_namelen == tond.ni_namelen && - !bcmp(fromnd.ni_ptr, tond.ni_ptr, fromnd.ni_namelen)) + fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && + !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, + fromnd.ni_cnd.cn_namelen)) error = -1; out: if (!error) { - error = VOP_RENAME(&fromnd, &tond, p); + VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); + if (fromnd.ni_dvp != tdvp) + VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (tvp) + VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, + tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); } else { - VOP_ABORTOP(&tond); + VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); - VOP_ABORTOP(&fromnd); + VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); } p->p_spare[1]--; vrele(tond.ni_startdir); - FREE(tond.ni_pnbuf, M_NAMEI); + FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); out1: p->p_spare[1]--; - vrele(fromnd.ni_startdir); - FREE(fromnd.ni_pnbuf, M_NAMEI); + if (fromnd.ni_startdir) + vrele(fromnd.ni_startdir); + FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); + CHECKREFS("rename"); if (error == -1) return (0); return (error); } /* - * Mkdir system call. + * Make a directory file. */ +struct mkdir_args { + char *path; + int mode; +}; /* ARGSUSED */ mkdir(p, uap, retval) struct proc *p; - register struct args { - char *name; - int dmode; - } *uap; + register struct mkdir_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = CREATE | LOCKPARENT; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->name; - if (error = namei(ndp, p)) + CHECKPOINTREF; + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; + vp = nd.ni_vp; if (vp != NULL) { - VOP_ABORTOP(ndp); - if (ndp->ni_dvp == vp) - vrele(ndp->ni_dvp); + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); else - vput(ndp->ni_dvp); + vput(nd.ni_dvp); vrele(vp); + CHECKREFS("mkdir1"); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VDIR; - vattr.va_mode = (uap->dmode & 0777) &~ p->p_fd->fd_cmask; - error = VOP_MKDIR(ndp, &vattr, p); + vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (!error) - vput(ndp->ni_vp); + vput(nd.ni_vp); + CHECKREFS("mkdir2"); return (error); } /* - * Rmdir system call. + * Remove a directory file. */ +struct rmdir_args { + char *path; +}; /* ARGSUSED */ rmdir(p, uap, retval) struct proc *p; - struct args { - char *name; - } *uap; + struct rmdir_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->name; - if (error = namei(ndp, p)) + CHECKPOINTREF; + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; + vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; @@ -1598,7 +1994,7 @@ rmdir(p, uap, retval) /* * No rmdir "." please. */ - if (ndp->ni_dvp == vp) { + if (nd.ni_dvp == vp) { error = EINVAL; goto out; } @@ -1609,36 +2005,198 @@ rmdir(p, uap, retval) error = EBUSY; out: if (!error) { - error = VOP_RMDIR(ndp, p); + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { - VOP_ABORTOP(ndp); - if (ndp->ni_dvp == vp) - vrele(ndp->ni_dvp); + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); else - vput(ndp->ni_dvp); + vput(nd.ni_dvp); vput(vp); } + CHECKREFS("rmdir"); + return (error); +} + +#ifdef COMPAT_43 +/* + * Read a block of directory entries in a file system independent format. + */ +struct ogetdirentries_args { + int fd; + char *buf; + u_int count; + long *basep; +}; +ogetdirentries(p, uap, retval) + struct proc *p; + register struct ogetdirentries_args *uap; + int *retval; +{ + register 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; + long loff; + + if (error = getvnode(p->p_fd, uap->fd, &fp)) + return (error); + if ((fp->f_flag & FREAD) == 0) + return (EBADF); + vp = (struct vnode *)fp->f_data; +unionread: + if (vp->v_type != VDIR) + 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_procp = p; + auio.uio_resid = uap->count; + VOP_LOCK(vp); + loff = auio.uio_offset = fp->f_offset; +# if (BYTE_ORDER != LITTLE_ENDIAN) + if (vp->v_mount->mnt_maxsymlinklen <= 0) { + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, + (u_long *)0, 0); + 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, + (u_long *)0, 0); + 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); + if (error) + return (error); + +#ifdef UNION +{ + extern int (**union_vnodeop_p)(); + extern struct vnode *union_dircache __P((struct vnode *)); + + if ((uap->count == auio.uio_resid) && + (vp->v_op == union_vnodeop_p)) { + struct vnode *lvp; + + lvp = union_dircache(vp); + if (lvp != NULLVP) { + struct vattr va; + + /* + * If the directory is opaque, + * then don't show lower entries + */ + error = VOP_GETATTR(vp, &va, fp->f_cred, p); + if (va.va_flags & OPAQUE) { + vput(lvp); + lvp = NULL; + } + } + + if (lvp != NULLVP) { + error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); + VOP_UNLOCK(lvp); + + if (error) { + vrele(lvp); + return (error); + } + fp->f_data = (caddr_t) lvp; + fp->f_offset = 0; + error = vn_close(vp, FREAD, fp->f_cred, p); + if (error) + return (error); + vp = lvp; + goto unionread; + } + } +} +#endif /* UNION */ + + if ((uap->count == auio.uio_resid) && + (vp->v_flag & VROOT) && + (vp->v_mount->mnt_flag & MNT_UNION)) { + struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; + VREF(vp); + fp->f_data = (caddr_t) vp; + fp->f_offset = 0; + vrele(tvp); + goto unionread; + } + error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); + *retval = uap->count - auio.uio_resid; return (error); } +#endif /* COMPAT_43 */ /* * Read a block of directory entries in a file system independent format. */ +struct getdirentries_args { + int fd; + char *buf; + u_int count; + long *basep; +}; getdirentries(p, uap, retval) struct proc *p; - register struct args { - int fd; - char *buf; - unsigned count; - long *basep; - } *uap; + register struct getdirentries_args *uap; int *retval; { register struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; - off_t off; + long loff; int error, eofflag; if (error = getvnode(p->p_fd, uap->fd, &fp)) @@ -1646,6 +2204,7 @@ getdirentries(p, uap, retval) if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; +unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = uap->buf; @@ -1657,13 +2216,69 @@ getdirentries(p, uap, retval) auio.uio_procp = p; auio.uio_resid = uap->count; VOP_LOCK(vp); - auio.uio_offset = off = fp->f_offset; - error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag); + loff = auio.uio_offset = fp->f_offset; + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (u_long *)0, 0); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp); if (error) return (error); - error = copyout((caddr_t)&off, (caddr_t)uap->basep, sizeof(long)); + +#ifdef UNION +{ + extern int (**union_vnodeop_p)(); + extern struct vnode *union_dircache __P((struct vnode *)); + + if ((uap->count == auio.uio_resid) && + (vp->v_op == union_vnodeop_p)) { + struct vnode *lvp; + + lvp = union_dircache(vp); + if (lvp != NULLVP) { + struct vattr va; + + /* + * If the directory is opaque, + * then don't show lower entries + */ + error = VOP_GETATTR(vp, &va, fp->f_cred, p); + if (va.va_flags & OPAQUE) { + vput(lvp); + lvp = NULL; + } + } + + if (lvp != NULLVP) { + error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); + VOP_UNLOCK(lvp); + + if (error) { + vrele(lvp); + return (error); + } + fp->f_data = (caddr_t) lvp; + fp->f_offset = 0; + error = vn_close(vp, FREAD, fp->f_cred, p); + if (error) + return (error); + vp = lvp; + goto unionread; + } + } +} +#endif + + if ((uap->count == auio.uio_resid) && + (vp->v_flag & VROOT) && + (vp->v_mount->mnt_flag & MNT_UNION)) { + struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; + VREF(vp); + fp->f_data = (caddr_t) vp; + fp->f_offset = 0; + vrele(tvp); + goto unionread; + } + error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); *retval = uap->count - auio.uio_resid; return (error); } @@ -1671,18 +2286,20 @@ getdirentries(p, uap, retval) /* * Set the mode mask for creation of filesystem nodes. */ -mode_t +struct umask_args { + int newmask; +}; +mode_t /* XXX */ umask(p, uap, retval) struct proc *p; - struct args { - int mask; - } *uap; + struct umask_args *uap; int *retval; { - register struct filedesc *fdp = p->p_fd; + register struct filedesc *fdp; + fdp = p->p_fd; *retval = fdp->fd_cmask; - fdp->fd_cmask = uap->mask & 07777; + fdp->fd_cmask = uap->newmask & ALLPERMS; return (0); } @@ -1690,27 +2307,24 @@ umask(p, uap, retval) * Void all references to file by ripping underlying filesystem * away from vnode. */ +struct revoke_args { + char *path; +}; /* ARGSUSED */ revoke(p, uap, retval) struct proc *p; - register struct args { - char *fname; - } *uap; + register struct revoke_args *uap; int *retval; { - register struct nameidata *ndp; register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; - ndp = &nd; - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - if (error = namei(ndp, p)) + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) return (error); - vp = ndp->ni_vp; + vp = nd.ni_vp; if (vp->v_type != VCHR && vp->v_type != VBLK) { error = EINVAL; goto out; @@ -1730,15 +2344,15 @@ out: /* * Convert a user file descriptor to a kernel file entry. */ -getvnode(fdp, fdes, fpp) +getvnode(fdp, fd, fpp) struct filedesc *fdp; struct file **fpp; - int fdes; + int fd; { struct file *fp; - if ((unsigned)fdes >= fdp->fd_nfiles || - (fp = fdp->fd_ofiles[fdes]) == NULL) + if ((u_int)fd >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (EINVAL);