X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/82252d2ba6a1f2e2cf5d86ae97f92b7df0f587e7..2f2b3b3dbaa73f43c4cff98d4932ce4a22a77604:/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 a2087631af..39bcbbc1c2 100644 --- a/usr/src/sys/kern/vfs_syscalls.c +++ b/usr/src/sys/kern/vfs_syscalls.c @@ -1,402 +1,933 @@ /* - * Copyright (c) 1982, 1986 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. * - * @(#)vfs_syscalls.c 7.2 (Berkeley) %G% + * %sccs.include.redist.c% + * + * @(#)vfs_syscalls.c 7.92 (Berkeley) %G% */ #include "param.h" #include "systm.h" -#include "dir.h" -#include "user.h" +#include "namei.h" +#include "filedesc.h" #include "kernel.h" #include "file.h" #include "stat.h" -#include "inode.h" -#include "fs.h" -#include "buf.h" +#include "vnode.h" +#include "mount.h" #include "proc.h" -#include "quota.h" #include "uio.h" -#include "socket.h" -#include "socketvar.h" -#include "mount.h" +#include "malloc.h" +#include "dirent.h" +#include + +#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 -extern struct fileops inodeops; -struct file *getinode(); +/* + * Virtual File System System Calls + */ /* - * Change current working directory (``.''). + * Mount system call. */ -chdir() +/* ARGSUSED */ +mount(p, uap, retval) + struct proc *p; + register struct args { + int type; + char *dir; + int flags; + caddr_t data; + } *uap; + int *retval; { + register struct vnode *vp; + register struct mount *mp; + int error, flag; + struct nameidata nd; + + /* + * Must be super user + */ + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + /* + * Get vnode to be covered + */ + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->dir, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (uap->flags & MNT_UPDATE) { + if ((vp->v_flag & VROOT) == 0) { + vput(vp); + return (EINVAL); + } + mp = vp->v_mount; + /* + * We allow going from read-only to read-write, + * but not from read-write to read-only. + */ + if ((mp->mnt_flag & MNT_RDONLY) == 0 && + (uap->flags & MNT_RDONLY) != 0) { + vput(vp); + return (EOPNOTSUPP); /* Needs translation */ + } + flag = mp->mnt_flag; + mp->mnt_flag |= MNT_UPDATE; + VOP_UNLOCK(vp); + goto update; + } + if (vp->v_usecount != 1) { + vput(vp); + return (EBUSY); + } + if (error = vinvalbuf(vp, 1, p->p_ucred, p)) + return (error); + if (vp->v_type != VDIR) { + vput(vp); + return (ENOTDIR); + } + if ((unsigned long)uap->type > MOUNT_MAXTYPE || + vfssw[uap->type] == (struct vfsops *)0) { + vput(vp); + return (ENODEV); + } - chdirec(&u.u_cdir); + /* + * 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]; + 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; +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; + /* + * Mount the filesystem. + */ + error = VFS_MOUNT(mp, uap->dir, uap->data, &nd, p); + if (mp->mnt_flag & MNT_UPDATE) { + mp->mnt_flag &= ~MNT_UPDATE; + vrele(vp); + if (error) + mp->mnt_flag = flag; + return (error); + } + /* + * 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) { + VOP_UNLOCK(vp); + vfs_unlock(mp); + error = VFS_START(mp, 0, p); + } else { + vfs_remove(mp); + free((caddr_t)mp, M_MOUNT); + vput(vp); + } + return (error); } /* - * Change notion of root (``/'') directory. + * Unmount system call. + * + * Note: unmount takes a path to the vnode mounted on as argument, + * not special file (as before). */ -chroot() +/* ARGSUSED */ +unmount(p, uap, retval) + struct proc *p; + register struct args { + char *pathp; + int flags; + } *uap; + int *retval; { + register struct vnode *vp; + struct mount *mp; + int error; + struct nameidata nd; + + /* + * Must be super user + */ + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); - if (suser()) - chdirec(&u.u_rdir); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->pathp, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + /* + * Must be the root of the filesystem + */ + if ((vp->v_flag & VROOT) == 0) { + vput(vp); + return (EINVAL); + } + mp = vp->v_mount; + vput(vp); + return (dounmount(mp, uap->flags, p)); } /* - * Common routine for chroot and chdir. + * Do an unmount. */ -chdirec(ipp) - register struct inode **ipp; +dounmount(mp, flags, p) + register struct mount *mp; + int flags; + struct proc *p; { - register struct inode *ip; - struct a { - char *fname; - } *uap = (struct a *)u.u_ap; - register struct nameidata *ndp = &u.u_nd; - - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - ip = namei(ndp); - if (ip == NULL) - return; - if ((ip->i_mode&IFMT) != IFDIR) { - u.u_error = ENOTDIR; - goto bad; + struct vnode *coveredvp; + int error; + + coveredvp = mp->mnt_vnodecovered; + if (vfs_busy(mp)) + return (EBUSY); + mp->mnt_flag |= MNT_UNMOUNT; + if (error = vfs_lock(mp)) + return (error); + + vnode_pager_umount(mp); /* release cached vnodes */ + cache_purgevfs(mp); /* remove cache entries for this file sys */ + if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || + (flags & MNT_FORCE)) + error = VFS_UNMOUNT(mp, flags, p); + mp->mnt_flag &= ~MNT_UNMOUNT; + vfs_unbusy(mp); + if (error) { + vfs_unlock(mp); + } else { + vrele(coveredvp); + vfs_remove(mp); + if (mp->mnt_mounth != NULL) + panic("unmount: dangling vnode"); + free((caddr_t)mp, M_MOUNT); } - if (access(ip, IEXEC)) - goto bad; - IUNLOCK(ip); - if (*ipp) - irele(*ipp); - *ipp = ip; - return; - -bad: - iput(ip); + return (error); } /* - * Open system call. + * Sync system call. + * Sync each mounted filesystem. */ -open() +/* ARGSUSED */ +sync(p, uap, retval) + struct proc *p; + void *uap; + int *retval; { - struct a { - char *fname; - int mode; - int crtmode; - } *uap = (struct a *) u.u_ap; + register struct mount *mp; + struct mount *omp; - copen(uap->mode-FOPEN, uap->crtmode, uap->fname); + mp = rootfs; + do { + /* + * 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, p->p_ucred, p); + omp = mp; + mp = mp->mnt_next; + vfs_unbusy(omp); + } else + mp = mp->mnt_next; + } while (mp != rootfs); + return (0); } /* - * Creat system call. + * Operate on filesystem quotas. */ -creat() +/* ARGSUSED */ +quotactl(p, uap, retval) + struct proc *p; + register struct args { + char *path; + int cmd; + int uid; + caddr_t arg; + } *uap; + int *retval; { - struct a { + register struct mount *mp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) + return (error); + mp = nd.ni_vp->v_mount; + vrele(nd.ni_vp); + return (VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, p)); +} + +/* + * Get filesystem statistics. + */ +/* ARGSUSED */ +statfs(p, uap, retval) + struct proc *p; + register struct args { + char *path; + struct statfs *buf; + } *uap; + int *retval; +{ + register struct mount *mp; + register struct statfs *sp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); + if (error = namei(&nd)) + return (error); + mp = nd.ni_vp->v_mount; + sp = &mp->mnt_stat; + vrele(nd.ni_vp); + if (error = VFS_STATFS(mp, sp, p)) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); +} + +/* + * Get filesystem statistics. + */ +/* ARGSUSED */ +fstatfs(p, uap, retval) + struct proc *p; + register struct args { + int fd; + struct statfs *buf; + } *uap; + int *retval; +{ + struct file *fp; + struct mount *mp; + register struct statfs *sp; + int error; + + if (error = getvnode(p->p_fd, uap->fd, &fp)) + return (error); + mp = ((struct vnode *)fp->f_data)->v_mount; + sp = &mp->mnt_stat; + if (error = VFS_STATFS(mp, sp, p)) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); +} + +/* + * Get statistics on all filesystems. + */ +getfsstat(p, uap, retval) + struct proc *p; + register struct args { + struct statfs *buf; + long bufsize; + int flags; + } *uap; + int *retval; +{ + register struct mount *mp; + 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 { + if (sfsp && count < maxcount && + ((mp->mnt_flag & MNT_MLOCK) == 0)) { + sp = &mp->mnt_stat; + /* + * If MNT_NOWAIT is specified, do not refresh the + * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. + */ + if (((uap->flags & MNT_NOWAIT) == 0 || + (uap->flags & MNT_WAIT)) && + (error = VFS_STATFS(mp, sp, p))) { + mp = mp->mnt_prev; + 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 + *retval = count; + return (0); +} + +/* + * Change current working directory to a given file descriptor. + */ +/* ARGSUSED */ +fchdir(p, uap, retval) + struct proc *p; + struct args { + int fd; + } *uap; + int *retval; +{ + register struct filedesc *fdp = p->p_fd; + register struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(fdp, uap->fd, &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + VOP_UNLOCK(vp); + if (error) + return (error); + VREF(vp); + vrele(fdp->fd_cdir); + fdp->fd_cdir = vp; + return (0); +} + +/* + * Change current working directory (``.''). + */ +/* ARGSUSED */ +chdir(p, uap, retval) + struct proc *p; + struct args { char *fname; - int fmode; - } *uap = (struct a *)u.u_ap; + } *uap; + int *retval; +{ + register struct filedesc *fdp = p->p_fd; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = chdirec(&nd, p)) + return (error); + vrele(fdp->fd_cdir); + fdp->fd_cdir = nd.ni_vp; + return (0); +} - copen(FWRITE|FCREAT|FTRUNC, uap->fmode, uap->fname); +/* + * Change notion of root (``/'') directory. + */ +/* ARGSUSED */ +chroot(p, uap, retval) + struct proc *p; + struct args { + char *fname; + } *uap; + int *retval; +{ + register struct filedesc *fdp = p->p_fd; + int error; + struct nameidata nd; + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = chdirec(&nd, p)) + return (error); + if (fdp->fd_rdir != NULL) + vrele(fdp->fd_rdir); + fdp->fd_rdir = nd.ni_vp; + return (0); +} + +/* + * Common routine for chroot and chdir. + */ +chdirec(ndp, p) + register struct nameidata *ndp; + struct proc *p; +{ + struct vnode *vp; + int error; + + if (error = namei(ndp)) + return (error); + vp = ndp->ni_vp; + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + VOP_UNLOCK(vp); + if (error) + vrele(vp); + return (error); } /* - * Common code for open and creat. + * Open system call. * Check permissions, allocate an open file structure, * and call the device open routine if any. */ -copen(mode, arg, fname) - register int mode; - int arg; - caddr_t fname; +open(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + int mode; + int crtmode; + } *uap; + int *retval; { - register struct inode *ip; + register struct filedesc *fdp = p->p_fd; register struct file *fp; - register struct nameidata *ndp = &u.u_nd; - int indx; - - fp = falloc(); - if (fp == NULL) - return; - indx = u.u_r.r_val1; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = fname; - if (mode&FCREAT) { - if (mode & FEXCL) - ndp->ni_nameiop = CREATE; - else - ndp->ni_nameiop = CREATE | FOLLOW; - ip = namei(ndp); - if (ip == NULL) { - if (u.u_error) - goto bad1; - ip = maknode(arg&07777&(~ISVTX), ndp); - if (ip == NULL) - goto bad1; - mode &= ~FTRUNC; - } else { - if (mode&FEXCL) { - u.u_error = EEXIST; - goto bad; - } - mode &= ~FCREAT; + register struct vnode *vp; + int fmode, cmode; + struct file *nfp; + 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; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, p); + p->p_dupfd = -indx - 1; /* XXX check for fdopen */ + if (error = vn_open(&nd, fmode, cmode)) { + ffree(fp); + if ((error == ENODEV || error == ENXIO) && + p->p_dupfd >= 0 && /* XXX from fdopen */ + (error = dupfdopen(fdp, indx, p->p_dupfd, + fmode, error)) == 0) { + *retval = indx; + return (0); } - } else { - ndp->ni_nameiop = LOOKUP | FOLLOW; - ip = namei(ndp); - if (ip == NULL) - goto bad1; - } - if ((ip->i_mode & IFMT) == IFSOCK) { - u.u_error = EOPNOTSUPP; - goto bad; + if (error == ERESTART) + error = EINTR; + fdp->fd_ofiles[indx] = NULL; + return (error); } - if ((mode&FCREAT) == 0) { - if (mode&FREAD) - if (access(ip, IREAD)) - goto bad; - if (mode&(FWRITE|FTRUNC)) { - if (access(ip, IWRITE)) - goto bad; - if ((ip->i_mode&IFMT) == IFDIR) { - u.u_error = EISDIR; - goto bad; - } + p->p_dupfd = 0; + vp = nd.ni_vp; + fp->f_flag = fmode & FMASK; + fp->f_type = DTYPE_VNODE; + fp->f_ops = &vnops; + fp->f_data = (caddr_t)vp; + 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; + if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) { + VOP_UNLOCK(vp); + (void) vn_close(vp, fp->f_flag, fp->f_cred, p); + ffree(fp); + fdp->fd_ofiles[indx] = NULL; + return (error); } + fp->f_flag |= FHASLOCK; } - if (mode&FTRUNC) - itrunc(ip, (u_long)0); - IUNLOCK(ip); - fp->f_flag = mode&FMASK; - fp->f_type = DTYPE_INODE; - fp->f_ops = &inodeops; - fp->f_data = (caddr_t)ip; - if (setjmp(&u.u_qsave)) { - if (u.u_error == 0) - u.u_error = EINTR; - u.u_ofile[indx] = NULL; - closef(fp); - return; - } - u.u_error = openi(ip, mode); - if (u.u_error == 0) - return; - ILOCK(ip); -bad: - iput(ip); -bad1: - u.u_ofile[indx] = NULL; - fp->f_count--; + VOP_UNLOCK(vp); + *retval = indx; + return (0); } +#ifdef COMPAT_43 /* - * Mknod system call + * Creat system call. */ -mknod() +ocreat(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + int fmode; + } *uap; + int *retval; { - register struct inode *ip; - register struct a { + struct nargs { + char *fname; + int mode; + int crtmode; + } openuap; + + openuap.fname = uap->fname; + openuap.crtmode = uap->fmode; + openuap.mode = O_WRONLY | O_CREAT | O_TRUNC; + return (open(p, &openuap, retval)); +} +#endif /* COMPAT_43 */ + +/* + * Mknod system call. + */ +/* ARGSUSED */ +mknod(p, uap, retval) + struct proc *p; + register struct args { char *fname; int fmode; int dev; - } *uap = (struct a *)u.u_ap; - register struct nameidata *ndp = &u.u_nd; - - if (!suser()) - return; - ndp->ni_nameiop = CREATE; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - ip = namei(ndp); - if (ip != NULL) { - u.u_error = EEXIST; + } *uap; + int *retval; +{ + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + CHECKPOINTREF; + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp != NULL) { + error = EEXIST; goto out; } - if (u.u_error) - return; - ip = maknode(uap->fmode, ndp); - if (ip == NULL) - return; - switch (ip->i_mode & IFMT) { - - case IFMT: /* used by badsect to flag bad sectors */ - case IFCHR: - case IFBLK: - if (uap->dev) { - /* - * Want to be able to use this to make badblock - * inodes, so don't truncate the dev number. - */ - ip->i_rdev = uap->dev; - ip->i_flag |= IACC|IUPD|ICHG; - } - } + 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; + } + vattr.va_mode = (uap->fmode & 07777) &~ p->p_fd->fd_cmask; + vattr.va_rdev = uap->dev; out: - iput(ip); + if (!error) { + LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (vp) + vrele(vp); + } + CHECKREFS("mknod"); + return (error); } /* - * link system call + * Mkfifo system call. */ -link() +/* ARGSUSED */ +mkfifo(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + int fmode; + } *uap; + int *retval; { - register struct inode *ip, *xp; - register struct a { + struct vattr vattr; + int error; + struct nameidata nd; + +#ifndef FIFO + return (EOPNOTSUPP); +#else + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + if (nd.ni_vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + return (EEXIST); + } + VATTR_NULL(&vattr); + vattr.va_type = VFIFO; + vattr.va_mode = (uap->fmode & 07777) &~ p->p_fd->fd_cmask; + LEASE_CHECK(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. + */ +/* ARGSUSED */ +link(p, uap, retval) + struct proc *p; + register struct args { char *target; char *linkname; - } *uap = (struct a *)u.u_ap; - register struct nameidata *ndp = &u.u_nd; - - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->target; - ip = namei(ndp); /* well, this routine is doomed anyhow */ - if (ip == NULL) - return; - if ((ip->i_mode&IFMT) == IFDIR && !suser()) { - iput(ip); - return; - } - ip->i_nlink++; - ip->i_flag |= ICHG; - iupdat(ip, &time, &time, 1); - IUNLOCK(ip); - ndp->ni_nameiop = CREATE; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = (caddr_t)uap->linkname; - xp = namei(ndp); + } *uap; + int *retval; +{ + register struct vnode *vp, *xp; + int error; + struct nameidata nd; + + CHECKPOINTREF; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->target, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type == VDIR && + (error = suser(p->p_ucred, &p->p_acflag))) + goto out1; + nd.ni_cnd.cn_nameiop = CREATE; + nd.ni_cnd.cn_flags = LOCKPARENT; + nd.ni_dirp = (caddr_t)uap->linkname; + if (error = namei(&nd)) + goto out1; + xp = nd.ni_vp; if (xp != NULL) { - u.u_error = EEXIST; - iput(xp); + error = EEXIST; goto out; } - if (u.u_error) - goto out; - if (ndp->ni_pdir->i_dev != ip->i_dev) { - iput(ndp->ni_pdir); - u.u_error = EXDEV; - goto out; - } - u.u_error = direnter(ip, ndp); + xp = nd.ni_dvp; out: - if (u.u_error) { - ip->i_nlink--; - ip->i_flag |= ICHG; + if (!error) { + LEASE_CHECK(xp, p, p->p_ucred, LEASE_WRITE); + LEASE_CHECK(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); } - irele(ip); +out1: + vrele(vp); + CHECKREFS("link"); + return (error); } /* - * symlink -- make a symbolic link + * Make a symbolic link. */ -symlink() -{ - register struct a { +/* ARGSUSED */ +symlink(p, uap, retval) + struct proc *p; + register struct args { char *target; char *linkname; - } *uap = (struct a *)u.u_ap; - register struct inode *ip; - register char *tp; - register c, nc; - register struct nameidata *ndp = &u.u_nd; - - tp = uap->target; - nc = 0; - while (c = fubyte(tp)) { - if (c < 0) { - u.u_error = EFAULT; - return; - } - tp++; - nc++; - } - ndp->ni_nameiop = CREATE; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->linkname; - ip = namei(ndp); - if (ip) { - iput(ip); - u.u_error = EEXIST; - return; + } *uap; + int *retval; +{ + struct vattr vattr; + char *target; + int error; + struct nameidata nd; + + CHECKPOINTREF; + MALLOC(target, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); + if (error = copyinstr(uap->target, target, MAXPATHLEN, (u_int *)0)) + goto out; + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->linkname, p); + if (error = namei(&nd)) + goto out; + if (nd.ni_vp) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + error = EEXIST; + goto out; } - if (u.u_error) - return; - ip = maknode(IFLNK | 0777, ndp); - if (ip == NULL) - return; - u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, (off_t)0, 0, - (int *)0); - /* handle u.u_error != 0 */ - iput(ip); + VATTR_NULL(&vattr); + vattr.va_mode = 0777 &~ p->p_fd->fd_cmask; + LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, target); +out: + FREE(target, M_NAMEI); + CHECKREFS("symlink"); + return (error); } /* - * Unlink system call. - * Hard to avoid races here, especially - * in unlinking directories. + * Delete a name from the filesystem. */ -unlink() +/* ARGSUSED */ +unlink(p, uap, retval) + struct proc *p; + struct args { + char *name; + } *uap; + int *retval; { - struct a { - char *fname; - } *uap = (struct a *)u.u_ap; - register struct inode *ip, *dp; - register struct nameidata *ndp = &u.u_nd; - - ndp->ni_nameiop = DELETE | LOCKPARENT; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - ip = namei(ndp); - if (ip == NULL) - return; - dp = ndp->ni_pdir; - if ((ip->i_mode&IFMT) == IFDIR && !suser()) + register struct vnode *vp; + int error; + struct nameidata nd; + + CHECKPOINTREF; + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->name, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type == VDIR && + (error = suser(p->p_ucred, &p->p_acflag))) goto out; /* - * Don't unlink a mounted file. + * The root of a mounted filesystem cannot be deleted. */ - if (ip->i_dev != dp->i_dev) { - u.u_error = EBUSY; + if (vp->v_flag & VROOT) { + error = EBUSY; goto out; } - if (ip->i_flag&ITEXT) - xrele(ip); /* try once to free text */ - if (dirremove(ndp)) { - ip->i_nlink--; - ip->i_flag |= ICHG; - } + (void) vnode_pager_uncache(vp); out: - if (dp == ip) - irele(ip); - else - iput(ip); - iput(dp); + if (!error) { + LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vput(vp); + } + CHECKREFS("unlink"); + return (error); } +struct args_lseek { + int fdes; + int pad; + off_t off; + int sbase; +}; + +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* - * Seek system call + * Seek system call. */ -lseek() -{ - register struct file *fp; - register struct a { - int fd; - off_t off; +lseek(p, uap, retval) + struct proc *p; + register struct args { + int fdes; + long off; int sbase; - } *uap = (struct a *)u.u_ap; + } *uap; + long *retval; +{ + struct args_lseek nuap; + off_t qret; + int error; + + nuap.fdes = uap->fdes; + nuap.off = uap->off; + nuap.sbase = uap->sbase; + error = __lseek(p, &nuap, &qret); + *retval = qret; + return (error); +} +#endif /* COMPAT_43 || COMPAT_SUNOS */ - GETF(fp, uap->fd); - if (fp->f_type != DTYPE_INODE) { - u.u_error = ESPIPE; - return; - } +/* + * Seek system call. + */ +__lseek(p, uap, retval) + struct proc *p; + register struct args_lseek *uap; + off_t *retval; +{ + struct ucred *cred = p->p_ucred; + register struct filedesc *fdp = p->p_fd; + register struct file *fp; + struct vattr vattr; + int error; + + if ((unsigned)uap->fdes >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[uap->fdes]) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (ESPIPE); switch (uap->sbase) { case L_INCR: @@ -404,7 +935,10 @@ lseek() break; case L_XTND: - fp->f_offset = uap->off + ((struct inode *)fp->f_data)->i_size; + if (error = VOP_GETATTR((struct vnode *)fp->f_data, + &vattr, cred, p)) + return (error); + fp->f_offset = uap->off + vattr.va_size; break; case L_SET: @@ -412,959 +946,1031 @@ lseek() break; default: - u.u_error = EINVAL; - return; + return (EINVAL); } - u.u_r.r_off = fp->f_offset; + *retval = fp->f_offset; + return (0); } /* - * Access system call + * Check access permissions. */ -saccess() -{ - register svuid, svgid; - register struct inode *ip; - register struct a { +/* ARGSUSED */ +saccess(p, uap, retval) + struct proc *p; + register struct args { char *fname; int fmode; - } *uap = (struct a *)u.u_ap; - register struct nameidata *ndp = &u.u_nd; - - svuid = u.u_uid; - svgid = u.u_gid; - u.u_uid = u.u_ruid; - u.u_gid = u.u_rgid; - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - ip = namei(ndp); - if (ip != NULL) { - if ((uap->fmode&R_OK) && access(ip, IREAD)) - goto done; - if ((uap->fmode&W_OK) && access(ip, IWRITE)) - goto done; - if ((uap->fmode&X_OK) && access(ip, IEXEC)) - goto done; -done: - iput(ip); + } *uap; + int *retval; +{ + register struct ucred *cred = p->p_ucred; + register struct vnode *vp; + int error, mode, svuid, svgid; + struct nameidata nd; + + svuid = cred->cr_uid; + svgid = cred->cr_groups[0]; + cred->cr_uid = p->p_cred->p_ruid; + cred->cr_groups[0] = p->p_cred->p_rgid; + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + goto out1; + vp = nd.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); } - u.u_uid = svuid; - u.u_gid = svgid; + vput(vp); +out1: + cred->cr_uid = svuid; + cred->cr_groups[0] = svgid; + return (error); } +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* - * Stat system call. This version follows links. + * Stat system call. + * This version follows links. */ -stat() +/* ARGSUSED */ +ostat(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + struct ostat *ub; + } *uap; + int *retval; { + struct stat sb; + struct ostat osb; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); + if (error) + return (error); + cvtstat(&sb, &osb); + error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); + return (error); +} - stat1(FOLLOW); +/* + * Lstat system call. + * This version does not follow links. + */ +/* ARGSUSED */ +olstat(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + struct ostat *ub; + } *uap; + int *retval; +{ + struct stat sb; + struct ostat osb; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); + if (error) + return (error); + 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. + * convert from an old to a new stat structure. */ -lstat() +cvtstat(st, ost) + struct stat *st; + struct ostat *ost; { - stat1(NOFOLLOW); + 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 */ -stat1(follow) - int follow; -{ - register struct inode *ip; - register struct a { +/* + * Stat system call. + * This version follows links. + */ +/* ARGSUSED */ +stat(p, uap, retval) + struct proc *p; + register struct args { char *fname; struct stat *ub; - } *uap = (struct a *)u.u_ap; + } *uap; + int *retval; +{ struct stat sb; - register struct nameidata *ndp = &u.u_nd; - - ndp->ni_nameiop = LOOKUP | follow; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - ip = namei(ndp); - if (ip == NULL) - return; - (void) ino_stat(ip, &sb); - iput(ip); - u.u_error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + 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); } /* - * Return target name of a symbolic link + * Lstat system call. + * This version does not follow links. */ -readlink() +/* ARGSUSED */ +lstat(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + struct stat *ub; + } *uap; + int *retval; { - register struct inode *ip; - register struct a { + struct stat sb; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + 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); +} + +/* + * Return target name of a symbolic link. + */ +/* ARGSUSED */ +readlink(p, uap, retval) + struct proc *p; + register struct args { char *name; char *buf; int count; - } *uap = (struct a *)u.u_ap; - register struct nameidata *ndp = &u.u_nd; - int resid; - - ndp->ni_nameiop = LOOKUP; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->name; - ip = namei(ndp); - if (ip == NULL) - return; - if ((ip->i_mode&IFMT) != IFLNK) { - u.u_error = EINVAL; + } *uap; + int *retval; +{ + register struct vnode *vp; + struct iovec aiov; + struct uio auio; + int error; + struct nameidata nd; + + CHECKPOINTREF; + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->name, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VLNK) { + error = EINVAL; goto out; } - u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, (off_t)0, 0, - &resid); + 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: - iput(ip); - u.u_r.r_val1 = uap->count - resid; + vput(vp); + *retval = uap->count - auio.uio_resid; + CHECKREFS("readlink"); + return (error); } /* - * Change mode of a file given path name. + * Change flags of a file given path name. */ -chmod() -{ - struct inode *ip; - struct a { +/* ARGSUSED */ +chflags(p, uap, retval) + struct proc *p; + register struct args { char *fname; - int fmode; - } *uap = (struct a *)u.u_ap; - - if ((ip = owner(uap->fname, FOLLOW)) == NULL) - return; - u.u_error = chmod1(ip, uap->fmode); - iput(ip); + int flags; + } *uap; + int *retval; +{ + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + error = EROFS; + goto out; + } + VATTR_NULL(&vattr); + vattr.va_flags = uap->flags; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + vput(vp); + return (error); } /* - * Change mode of a file given a file descriptor. + * Change flags of a file given a file descriptor. */ -fchmod() -{ - struct a { +/* ARGSUSED */ +fchflags(p, uap, retval) + struct proc *p; + register struct args { int fd; - int fmode; - } *uap = (struct a *)u.u_ap; - register struct inode *ip; - register struct file *fp; - - fp = getinode(uap->fd); - if (fp == NULL) - return; - ip = (struct inode *)fp->f_data; - if (u.u_uid != ip->i_uid && !suser()) - return; - ILOCK(ip); - u.u_error = chmod1(ip, uap->fmode); - IUNLOCK(ip); + int flags; + } *uap; + int *retval; +{ + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, uap->fd, &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + error = EROFS; + goto out; + } + VATTR_NULL(&vattr); + vattr.va_flags = uap->flags; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + VOP_UNLOCK(vp); + return (error); } /* - * Change the mode on a file. - * Inode must be locked before calling. + * Change mode of a file given path name. */ -chmod1(ip, mode) - register struct inode *ip; - register int mode; +/* ARGSUSED */ +chmod(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + int fmode; + } *uap; + int *retval; { + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + error = EROFS; + goto out; + } + VATTR_NULL(&vattr); + vattr.va_mode = uap->fmode & 07777; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + vput(vp); + return (error); +} - if (ip->i_fs->fs_ronly) - return (EROFS); - ip->i_mode &= ~07777; - if (u.u_uid) { - if ((ip->i_mode & IFMT) != IFDIR) - mode &= ~ISVTX; - if (!groupmember(ip->i_gid)) - mode &= ~ISGID; +/* + * Change mode of a file given a file descriptor. + */ +/* ARGSUSED */ +fchmod(p, uap, retval) + struct proc *p; + register struct args { + int fd; + int fmode; + } *uap; + int *retval; +{ + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, uap->fd, &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + error = EROFS; + goto out; } - ip->i_mode |= mode&07777; - ip->i_flag |= ICHG; - if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0) - xrele(ip); - return (0); + VATTR_NULL(&vattr); + vattr.va_mode = uap->fmode & 07777; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + VOP_UNLOCK(vp); + return (error); } /* * Set ownership given a path name. */ -chown() -{ - struct inode *ip; - struct a { +/* ARGSUSED */ +chown(p, uap, retval) + struct proc *p; + register struct args { char *fname; int uid; int gid; - } *uap = (struct a *)u.u_ap; - - if ((ip = owner(uap->fname, NOFOLLOW)) == NULL) - return; - u.u_error = chown1(ip, uap->uid, uap->gid); - iput(ip); + } *uap; + int *retval; +{ + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + error = EROFS; + goto out; + } + VATTR_NULL(&vattr); + vattr.va_uid = uap->uid; + vattr.va_gid = uap->gid; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + vput(vp); + return (error); } /* * Set ownership given a file descriptor. */ -fchown() -{ - struct a { +/* ARGSUSED */ +fchown(p, uap, retval) + struct proc *p; + register struct args { int fd; int uid; int gid; - } *uap = (struct a *)u.u_ap; - register struct inode *ip; - register struct file *fp; - - fp = getinode(uap->fd); - if (fp == NULL) - return; - ip = (struct inode *)fp->f_data; - ILOCK(ip); - u.u_error = chown1(ip, uap->uid, uap->gid); - IUNLOCK(ip); + } *uap; + int *retval; +{ + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, uap->fd, &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + error = EROFS; + goto out; + } + VATTR_NULL(&vattr); + vattr.va_uid = uap->uid; + vattr.va_gid = uap->gid; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + VOP_UNLOCK(vp); + return (error); } /* - * Perform chown operation on inode ip; - * inode must be locked prior to call. + * Set the access and modification times of a file. */ -chown1(ip, uid, gid) - register struct inode *ip; - int uid, gid; -{ -#ifdef QUOTA - register long change; -#endif - - if (ip->i_fs->fs_ronly) - return (EROFS); - if (uid == -1) - uid = ip->i_uid; - if (gid == -1) - gid = ip->i_gid; - if (uid != ip->i_uid && !suser()) - return (u.u_error); - if (gid != ip->i_gid && !groupmember((gid_t)gid) && !suser()) - return (u.u_error); -#ifdef QUOTA - if (ip->i_uid == uid) /* this just speeds things a little */ - change = 0; - else - change = ip->i_blocks; - (void) chkdq(ip, -change, 1); - (void) chkiq(ip->i_dev, ip, ip->i_uid, 1); - dqrele(ip->i_dquot); -#endif - ip->i_uid = uid; - ip->i_gid = gid; - ip->i_flag |= ICHG; - if (u.u_ruid != 0) - ip->i_mode &= ~(ISUID|ISGID); -#ifdef QUOTA - ip->i_dquot = inoquota(ip); - (void) chkdq(ip, change, 1); - (void) chkiq(ip->i_dev, (struct inode *)NULL, (uid_t)uid, 1); - return (u.u_error); /* should == 0 ALWAYS !! */ -#else - return (0); -#endif -} - -utimes() -{ - register struct a { +/* ARGSUSED */ +utimes(p, uap, retval) + struct proc *p; + register struct args { char *fname; struct timeval *tptr; - } *uap = (struct a *)u.u_ap; - register struct inode *ip; + } *uap; + int *retval; +{ + register struct vnode *vp; struct timeval tv[2]; - - if ((ip = owner(uap->fname, FOLLOW)) == NULL) - return; - if (ip->i_fs->fs_ronly) { - u.u_error = EROFS; - iput(ip); - return; - } - u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv)); - if (u.u_error == 0) { - ip->i_flag |= IACC|IUPD|ICHG; - iupdat(ip, &tv[0], &tv[1], 0); + struct vattr vattr; + int error; + struct nameidata nd; + + if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv))) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + error = EROFS; + goto out; } - iput(ip); + VATTR_NULL(&vattr); + 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; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + vput(vp); + return (error); } +struct args_truncate { + char *fname; + int pad; + off_t length; +}; + /* - * Flush any pending I/O. + * Truncate a file given its path name. */ -sync() +/* ARGSUSED */ +__truncate(p, uap, retval) + struct proc *p; + register struct args_truncate *uap; + int *retval; { + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type == VDIR) { + error = EISDIR; + goto out; + } + if ((error = vn_writechk(vp)) || + (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p))) + goto out; + VATTR_NULL(&vattr); + vattr.va_size = uap->length; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); +out: + vput(vp); + return (error); +} + +struct args_ftruncate { + int fd; + int pad; + off_t length; +}; - update(); +/* + * Truncate a file given a file descriptor. + */ +/* ARGSUSED */ +__ftruncate(p, uap, retval) + struct proc *p; + register struct args_ftruncate *uap; + int *retval; +{ + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, uap->fd, &fp)) + return (error); + if ((fp->f_flag & FWRITE) == 0) + return (EINVAL); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_type == VDIR) { + error = EISDIR; + goto out; + } + if (error = vn_writechk(vp)) + goto out; + VATTR_NULL(&vattr); + vattr.va_size = uap->length; + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + 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. */ -truncate() -{ - struct a { +/* ARGSUSED */ +truncate(p, uap, retval) + struct proc *p; + register struct args { char *fname; - off_t length; - } *uap = (struct a *)u.u_ap; - struct inode *ip; - register struct nameidata *ndp = &u.u_nd; - - ndp->ni_nameiop = LOOKUP | FOLLOW; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->fname; - ip = namei(ndp); - if (ip == NULL) - return; - if (access(ip, IWRITE)) - goto bad; - if ((ip->i_mode&IFMT) == IFDIR) { - u.u_error = EISDIR; - goto bad; - } - itrunc(ip, (u_long)uap->length); -bad: - iput(ip); + long length; + } *uap; + int *retval; +{ + struct args_truncate nuap; + + nuap.fname = uap->fname; + nuap.length = uap->length; + return (__truncate(p, &nuap, retval)); } /* * Truncate a file given a file descriptor. */ -ftruncate() -{ - struct a { +/* ARGSUSED */ +ftruncate(p, uap, retval) + struct proc *p; + register struct args { int fd; - off_t length; - } *uap = (struct a *)u.u_ap; - struct inode *ip; - struct file *fp; + long length; + } *uap; + int *retval; +{ + struct args_ftruncate nuap; - fp = getinode(uap->fd); - if (fp == NULL) - return; - if ((fp->f_flag&FWRITE) == 0) { - u.u_error = EINVAL; - return; - } - ip = (struct inode *)fp->f_data; - ILOCK(ip); - itrunc(ip, (u_long)uap->length); - IUNLOCK(ip); + nuap.fd = uap->fd; + nuap.length = uap->length; + return (__ftruncate(p, &nuap, retval)); } +#endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Synch an open file. */ -fsync() -{ - struct a { +/* ARGSUSED */ +fsync(p, uap, retval) + struct proc *p; + struct args { int fd; - } *uap = (struct a *)u.u_ap; - struct inode *ip; + } *uap; + int *retval; +{ + register struct vnode *vp; struct file *fp; - - fp = getinode(uap->fd); - if (fp == NULL) - return; - ip = (struct inode *)fp->f_data; - ILOCK(ip); - if (fp->f_flag&FWRITE) - ip->i_flag |= ICHG; - syncip(ip); - IUNLOCK(ip); + int error; + + if (error = getvnode(p->p_fd, uap->fd, &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); + VOP_UNLOCK(vp); + return (error); } /* * Rename system call. - * rename("foo", "bar"); - * is essentially - * unlink("bar"); - * link("foo", "bar"); - * unlink("foo"); - * but ``atomically''. Can't do full commit without saving state in the - * inode on disk which isn't feasible at this time. Best we can do is - * always guarantee the target exists. - * - * Basic algorithm is: - * - * 1) Bump link count on source while we're linking it to the - * target. This also insure the inode won't be deleted out - * from underneath us while we work (it may be truncated by - * a concurrent `trunc' or `open' for creation). - * 2) Link source to destination. If destination already exists, - * delete it first. - * 3) Unlink source reference to inode if still around. If a - * directory was moved and the parent of the destination - * is different from the source, patch the ".." entry in the - * directory. * * Source and destination must either both be directories, or both * not be directories. If target is a directory, it must be empty. */ -rename() -{ - struct a { +/* ARGSUSED */ +rename(p, uap, retval) + struct proc *p; + register struct args { char *from; char *to; - } *uap = (struct a *)u.u_ap; - register struct inode *ip, *xp, *dp; - struct dirtemplate dirbuf; - int doingdirectory = 0, oldparent = 0, newparent = 0; - register struct nameidata *ndp = &u.u_nd; - int error = 0; - - ndp->ni_nameiop = DELETE | LOCKPARENT; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->from; - ip = namei(ndp); - if (ip == NULL) - return; - dp = ndp->ni_pdir; - if ((ip->i_mode&IFMT) == IFDIR) { - register struct direct *d; - - d = &ndp->ni_dent; - /* - * Avoid ".", "..", and aliases of "." for obvious reasons. - */ - if ((d->d_namlen == 1 && d->d_name[0] == '.') || - (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) || - (dp == ip) || (ip->i_flag & IRENAME)) { - iput(dp); - if (dp == ip) - irele(ip); - else - iput(ip); - u.u_error = EINVAL; - return; + } *uap; + int *retval; +{ + register struct vnode *tvp, *fvp, *tdvp; + struct nameidata fromnd, tond; + int error; + + CHECKPOINTREF; + NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, + uap->from, p); + if (error = namei(&fromnd)) + return (error); + fvp = fromnd.ni_vp; + 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; + } + 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; } - ip->i_flag |= IRENAME; - oldparent = dp->i_number; - doingdirectory++; } - iput(dp); - + if (fvp == tdvp) + error = EINVAL; /* - * 1) Bump link count while we're moving stuff - * around. If we crash somewhere before - * completing our work, the link count - * may be wrong, but correctable. + * If source is the same as the destination (that is the + * same inode number with the same name in the same directory), + * then there is nothing to do. */ - ip->i_nlink++; - ip->i_flag |= ICHG; - iupdat(ip, &time, &time, 1); - IUNLOCK(ip); + if (fvp == tvp && fromnd.ni_dvp == tdvp && + fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && + !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, + fromnd.ni_cnd.cn_namelen)) + error = -1; +out: + if (!error) { + LEASE_CHECK(tdvp, p, p->p_ucred, LEASE_WRITE); + if (fromnd.ni_dvp != tdvp) + LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (tvp) + LEASE_CHECK(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.ni_dvp, &tond.ni_cnd); + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + 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_cnd.cn_pnbuf, M_NAMEI); +out1: + p->p_spare[1]--; + vrele(fromnd.ni_startdir); + FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); + CHECKREFS("rename"); + if (error == -1) + return (0); + return (error); +} - /* - * When the target exists, both the directory - * and target inodes are returned locked. - */ - ndp->ni_nameiop = CREATE | LOCKPARENT | NOCACHE; - ndp->ni_dirp = (caddr_t)uap->to; - xp = namei(ndp); - if (u.u_error) { - error = u.u_error; +/* + * Mkdir system call. + */ +/* ARGSUSED */ +mkdir(p, uap, retval) + struct proc *p; + register struct args { + char *name; + int dmode; + } *uap; + int *retval; +{ + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + CHECKPOINTREF; + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->name, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + 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; + LEASE_CHECK(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(nd.ni_vp); + CHECKREFS("mkdir2"); + return (error); +} + +/* + * Rmdir system call. + */ +/* ARGSUSED */ +rmdir(p, uap, retval) + struct proc *p; + struct args { + char *name; + } *uap; + int *retval; +{ + register struct vnode *vp; + int error; + struct nameidata nd; + + CHECKPOINTREF; + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->name, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VDIR) { + error = ENOTDIR; goto out; } - dp = ndp->ni_pdir; /* - * If ".." must be changed (ie the directory gets a new - * parent) then the source directory must not be in the - * directory heirarchy above the target, as this would - * orphan everything below the source directory. Also - * the user must have write permission in the source so - * as to be able to change "..". We must repeat the call - * to namei, as the parent directory is unlocked by the - * call to checkpath(). + * No rmdir "." please. */ - if (oldparent != dp->i_number) - newparent = dp->i_number; - if (doingdirectory && newparent) { - if (access(ip, IWRITE)) - goto bad; - do { - dp = ndp->ni_pdir; - if (xp != NULL) - iput(xp); - u.u_error = checkpath(ip, dp); - if (u.u_error) - goto out; - xp = namei(ndp); - if (u.u_error) { - error = u.u_error; - goto out; - } - } while (dp != ndp->ni_pdir); + if (nd.ni_dvp == vp) { + error = EINVAL; + goto out; } /* - * 2) If target doesn't exist, link the target - * to the source and unlink the source. - * Otherwise, rewrite the target directory - * entry to reference the source inode and - * expunge the original entry's existence. + * The root of a mounted filesystem cannot be deleted. */ - if (xp == NULL) { - if (dp->i_dev != ip->i_dev) { - error = EXDEV; - goto bad; - } - /* - * Account for ".." in new directory. - * When source and destination have the same - * parent we don't fool with the link count. - */ - if (doingdirectory && newparent) { - dp->i_nlink++; - dp->i_flag |= ICHG; - iupdat(dp, &time, &time, 1); - } - error = direnter(ip, ndp); - if (error) - goto out; + if (vp->v_flag & VROOT) + error = EBUSY; +out: + if (!error) { + LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { - if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) { - error = EXDEV; - goto bad; - } - /* - * Short circuit rename(foo, foo). - */ - if (xp->i_number == ip->i_number) - goto bad; - /* - * If the parent directory is "sticky", then the user must - * own the parent directory, or the destination of the rename, - * otherwise the destination may not be changed (except by - * root). This implements append-only directories. - */ - if ((dp->i_mode & ISVTX) && u.u_uid != 0 && - u.u_uid != dp->i_uid && xp->i_uid != u.u_uid) { - error = EPERM; - goto bad; - } - /* - * Target must be empty if a directory - * and have no links to it. - * Also, insure source and target are - * compatible (both directories, or both - * not directories). - */ - if ((xp->i_mode&IFMT) == IFDIR) { - if (!dirempty(xp, dp->i_number) || xp->i_nlink > 2) { - error = ENOTEMPTY; - goto bad; - } - if (!doingdirectory) { - error = ENOTDIR; - goto bad; - } - cacheinval(dp); - } else if (doingdirectory) { - error = EISDIR; - goto bad; - } - dirrewrite(dp, ip, ndp); - if (u.u_error) { - error = u.u_error; - goto bad1; - } - /* - * Adjust the link count of the target to - * reflect the dirrewrite above. If this is - * a directory it is empty and there are - * no links to it, so we can squash the inode and - * any space associated with it. We disallowed - * renaming over top of a directory with links to - * it above, as the remaining link would point to - * a directory without "." or ".." entries. - */ - xp->i_nlink--; - if (doingdirectory) { - if (--xp->i_nlink != 0) - panic("rename: linked directory"); - itrunc(xp, (u_long)0); - } - xp->i_flag |= ICHG; - iput(xp); - xp = NULL; + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vput(vp); } + CHECKREFS("rmdir"); + return (error); +} - /* - * 3) Unlink the source. - */ - ndp->ni_nameiop = DELETE | LOCKPARENT; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->from; - xp = namei(ndp); - if (xp != NULL) - dp = ndp->ni_pdir; - else - dp = NULL; - /* - * Insure that the directory entry still exists and has not - * changed while the new name has been entered. If the source is - * a file then the entry may have been unlinked or renamed. In - * either case there is no further work to be done. If the source - * is a directory then it cannot have been rmdir'ed; its link - * count of three would cause a rmdir to fail with ENOTEMPTY. - * The IRENAME flag insures that it cannot be moved by another - * rename. - */ - if (xp != ip) { - if (doingdirectory) - panic("rename: lost dir entry"); - } else { - /* - * If the source is a directory with a - * new parent, the link count of the old - * parent directory must be decremented - * and ".." set to point to the new parent. - */ - if (doingdirectory && newparent) { - dp->i_nlink--; - dp->i_flag |= ICHG; - error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf, - sizeof (struct dirtemplate), (off_t)0, 1, - (int *)0); - if (error == 0) { - if (dirbuf.dotdot_namlen != 2 || - dirbuf.dotdot_name[0] != '.' || - dirbuf.dotdot_name[1] != '.') { - printf("rename: mangled dir\n"); +#ifdef COMPAT_43 +/* + * Read a block of directory entries in a file system independent format. + */ +ogetdirentries(p, uap, retval) + struct proc *p; + register struct args { + int fd; + char *buf; + unsigned count; + long *basep; + } *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, readcnt; + off_t off; + + 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; + 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); + auio.uio_offset = off = fp->f_offset; +# if (BYTE_ORDER != LITTLE_ENDIAN) + if (vp->v_mount->mnt_maxsymlinklen <= 0) + error = VOP_READDIR(vp, &auio, fp->f_cred); + 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); + if (error == 0) { + readcnt = uap->count - kuio.uio_resid; + edp = (struct dirent *)&dirbuf[readcnt]; + for (dp = (struct dirent *)dirbuf; dp < edp; ) { + dp->d_type = 0; +# if (BYTE_ORDER == LITTLE_ENDIAN) + { u_char tmp = dp->d_namlen; + dp->d_namlen = dp->d_type; + dp->d_type = tmp; } +# endif + if (dp->d_reclen > 0) { + dp = (struct dirent *) + ((char *)dp + dp->d_reclen); } else { - dirbuf.dotdot_ino = newparent; - (void) rdwri(UIO_WRITE, xp, - (caddr_t)&dirbuf, - sizeof (struct dirtemplate), - (off_t)0, 1, (int *)0); - cacheinval(dp); + error = EIO; + break; } } + if (dp >= edp) + error = uiomove(dirbuf, readcnt, &auio); } - if (dirremove(ndp)) { - xp->i_nlink--; - xp->i_flag |= ICHG; - } - xp->i_flag &= ~IRENAME; - if (error == 0) /* XXX conservative */ - error = u.u_error; + FREE(dirbuf, M_TEMP); } - if (dp) - iput(dp); - if (xp) - iput(xp); - irele(ip); + fp->f_offset = auio.uio_offset; + VOP_UNLOCK(vp); if (error) - u.u_error = error; - return; - -bad: - iput(dp); -bad1: - if (xp) - iput(xp); -out: - ip->i_nlink--; - ip->i_flag |= ICHG; - irele(ip); - if (error) - u.u_error = error; + return (error); + error = copyout((caddr_t)&off, (caddr_t)uap->basep, sizeof(long)); + *retval = uap->count - auio.uio_resid; + return (error); } +#endif /* - * Make a new file. + * Read a block of directory entries in a file system independent format. */ -struct inode * -maknode(mode, ndp) - int mode; - register struct nameidata *ndp; +getdirentries(p, uap, retval) + struct proc *p; + register struct args { + int fd; + char *buf; + unsigned count; + long *basep; + } *uap; + int *retval; { - register struct inode *ip; - register struct inode *pdir = ndp->ni_pdir; - ino_t ipref; - - if ((mode & IFMT) == IFDIR) - ipref = dirpref(pdir->i_fs); - else - ipref = pdir->i_number; - ip = ialloc(pdir, ipref, mode); - if (ip == NULL) { - iput(pdir); - return (NULL); - } -#ifdef QUOTA - if (ip->i_dquot != NODQUOT) - panic("maknode: dquot"); -#endif - ip->i_flag |= IACC|IUPD|ICHG; - if ((mode & IFMT) == 0) - mode |= IFREG; - ip->i_mode = mode & ~u.u_cmask; - ip->i_nlink = 1; - ip->i_uid = u.u_uid; - ip->i_gid = pdir->i_gid; - if (ip->i_mode & ISGID && !groupmember(ip->i_gid)) - ip->i_mode &= ~ISGID; -#ifdef QUOTA - ip->i_dquot = inoquota(ip); -#endif - - /* - * Make sure inode goes to disk before directory entry. - */ - iupdat(ip, &time, &time, 1); - u.u_error = direnter(ip, ndp); - if (u.u_error) { - /* - * Write error occurred trying to update directory - * so must deallocate the inode. - */ - ip->i_nlink = 0; - ip->i_flag |= ICHG; - iput(ip); - return (NULL); - } - return (ip); + register struct vnode *vp; + struct file *fp; + struct uio auio; + struct iovec aiov; + off_t off; + int error; + + 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; + 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); + auio.uio_offset = off = fp->f_offset; + error = VOP_READDIR(vp, &auio, fp->f_cred); + fp->f_offset = auio.uio_offset; + VOP_UNLOCK(vp); + if (error) + return (error); + error = copyout((caddr_t)&off, (caddr_t)uap->basep, sizeof(long)); + *retval = uap->count - auio.uio_resid; + return (error); } /* - * A virgin directory (no blushing please). - */ -struct dirtemplate mastertemplate = { - 0, 12, 1, ".", - 0, DIRBLKSIZ - 12, 2, ".." -}; - -/* - * Mkdir system call + * Set the mode mask for creation of filesystem nodes. */ -mkdir() +mode_t +umask(p, uap, retval) + struct proc *p; + struct args { + int mask; + } *uap; + int *retval; { - struct a { - char *name; - int dmode; - } *uap = (struct a *)u.u_ap; - register struct inode *ip, *dp; - struct dirtemplate dirtemplate; - register struct nameidata *ndp = &u.u_nd; - - ndp->ni_nameiop = CREATE; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->name; - ip = namei(ndp); - if (u.u_error) - return; - if (ip != NULL) { - iput(ip); - u.u_error = EEXIST; - return; - } - dp = ndp->ni_pdir; - uap->dmode &= 0777; - uap->dmode |= IFDIR; - /* - * Must simulate part of maknode here - * in order to acquire the inode, but - * not have it entered in the parent - * directory. The entry is made later - * after writing "." and ".." entries out. - */ - ip = ialloc(dp, dirpref(dp->i_fs), uap->dmode); - if (ip == NULL) { - iput(dp); - return; - } -#ifdef QUOTA - if (ip->i_dquot != NODQUOT) - panic("mkdir: dquot"); -#endif - ip->i_flag |= IACC|IUPD|ICHG; - ip->i_mode = uap->dmode & ~u.u_cmask; - ip->i_nlink = 2; - ip->i_uid = u.u_uid; - ip->i_gid = dp->i_gid; -#ifdef QUOTA - ip->i_dquot = inoquota(ip); -#endif - iupdat(ip, &time, &time, 1); - - /* - * Bump link count in parent directory - * to reflect work done below. Should - * be done before reference is created - * so reparation is possible if we crash. - */ - dp->i_nlink++; - dp->i_flag |= ICHG; - iupdat(dp, &time, &time, 1); + register struct filedesc *fdp = p->p_fd; - /* - * Initialize directory with "." - * and ".." from static template. - */ - dirtemplate = mastertemplate; - dirtemplate.dot_ino = ip->i_number; - dirtemplate.dotdot_ino = dp->i_number; - u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate, - sizeof (dirtemplate), (off_t)0, 1, (int *)0); - if (u.u_error) { - dp->i_nlink--; - dp->i_flag |= ICHG; - goto bad; - } - if (DIRBLKSIZ > ip->i_fs->fs_fsize) - panic("mkdir: blksize"); /* XXX - should grow with bmap() */ - else - ip->i_size = DIRBLKSIZ; - /* - * Directory all set up, now - * install the entry for it in - * the parent directory. - */ - u.u_error = direnter(ip, ndp); - dp = NULL; - if (u.u_error) { - ndp->ni_nameiop = LOOKUP | NOCACHE; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->name; - dp = namei(ndp); - if (dp) { - dp->i_nlink--; - dp->i_flag |= ICHG; - } - } -bad: - /* - * No need to do an explicit itrunc here, - * irele will do this for us because we set - * the link count to 0. - */ - if (u.u_error) { - ip->i_nlink = 0; - ip->i_flag |= ICHG; - } - if (dp) - iput(dp); - iput(ip); + *retval = fdp->fd_cmask; + fdp->fd_cmask = uap->mask & 07777; + return (0); } /* - * Rmdir system call. + * Void all references to file by ripping underlying filesystem + * away from vnode. */ -rmdir() +/* ARGSUSED */ +revoke(p, uap, retval) + struct proc *p; + register struct args { + char *fname; + } *uap; + int *retval; { - struct a { - char *name; - } *uap = (struct a *)u.u_ap; - register struct inode *ip, *dp; - register struct nameidata *ndp = &u.u_nd; - - ndp->ni_nameiop = DELETE | LOCKPARENT; - ndp->ni_segflg = UIO_USERSPACE; - ndp->ni_dirp = uap->name; - ip = namei(ndp); - if (ip == NULL) - return; - dp = ndp->ni_pdir; - /* - * No rmdir "." please. - */ - if (dp == ip) { - irele(dp); - iput(ip); - u.u_error = EINVAL; - return; - } - if ((ip->i_mode&IFMT) != IFDIR) { - u.u_error = ENOTDIR; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VCHR && vp->v_type != VBLK) { + error = EINVAL; goto out; } - /* - * Don't remove a mounted on directory. - */ - if (ip->i_dev != dp->i_dev) { - u.u_error = EBUSY; + if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) goto out; - } - /* - * Verify the directory is empty (and valid). - * (Rmdir ".." won't be valid since - * ".." will contain a reference to - * the current directory and thus be - * non-empty.) - */ - if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number)) { - u.u_error = ENOTEMPTY; - goto out; - } - /* - * Delete reference to directory before purging - * inode. If we crash in between, the directory - * will be reattached to lost+found, - */ - if (dirremove(ndp) == 0) + if (p->p_ucred->cr_uid != vattr.va_uid && + (error = suser(p->p_ucred, &p->p_acflag))) goto out; - dp->i_nlink--; - dp->i_flag |= ICHG; - cacheinval(dp); - iput(dp); - dp = NULL; - /* - * Truncate inode. The only stuff left - * in the directory is "." and "..". The - * "." reference is inconsequential since - * we're quashing it. The ".." reference - * has already been adjusted above. We've - * removed the "." reference and the reference - * in the parent directory, but there may be - * other hard links so decrement by 2 and - * worry about them later. - */ - ip->i_nlink -= 2; - itrunc(ip, (u_long)0); - cacheinval(ip); + if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) + vgoneall(vp); out: - if (dp) - iput(dp); - iput(ip); -} - -struct file * -getinode(fdes) - int fdes; -{ - struct file *fp; - - if ((unsigned)fdes >= NOFILE || (fp = u.u_ofile[fdes]) == NULL) { - u.u_error = EBADF; - return ((struct file *)0); - } - if (fp->f_type != DTYPE_INODE) { - u.u_error = EINVAL; - return ((struct file *)0); - } - return (fp); + vrele(vp); + return (error); } /* - * mode mask for creation of files + * Convert a user file descriptor to a kernel file entry. */ -umask() +getvnode(fdp, fdes, fpp) + struct filedesc *fdp; + struct file **fpp; + int fdes; { - register struct a { - int mask; - } *uap = (struct a *)u.u_ap; + struct file *fp; - u.u_r.r_val1 = u.u_cmask; - u.u_cmask = uap->mask & 07777; + if ((unsigned)fdes >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[fdes]) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (EINVAL); + *fpp = fp; + return (0); }