X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/02c45551ed6e685f527b4a0b5f3031fe1c279d5b..b10521d67764d129e36248a51bb4124bdc33af85:/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 e72bd42183..810f5f43ca 100644 --- a/usr/src/sys/kern/vfs_syscalls.c +++ b/usr/src/sys/kern/vfs_syscalls.c @@ -1,100 +1,510 @@ -/* vfs_syscalls.c 4.59 83/05/31 */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/kernel.h" -#include "../h/file.h" -#include "../h/stat.h" -#include "../h/inode.h" -#include "../h/fs.h" -#include "../h/buf.h" -#include "../h/proc.h" -#include "../h/quota.h" -#include "../h/uio.h" -#include "../h/socket.h" -#include "../h/socketvar.h" -#include "../h/nami.h" -#include "../h/mount.h" - -extern struct fileops inodeops; -struct file *getinode(); +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)vfs_syscalls.c 7.43 (Berkeley) %G% + */ + +#include "param.h" +#include "systm.h" +#include "syscontext.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" + +#undef RETURN +#define RETURN(val) { scp->u_error = (val); if (scp->u_spare[0] != 0) panic("lock count"); return; } /* - * Change current working directory (``.''). + * Virtual File System System Calls */ -chdir() + +/* + * mount system call + */ +mount(scp) + register struct syscontext *scp; { + register struct a { + int type; + char *dir; + int flags; + caddr_t data; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + register struct mount *mp; + int error, flag; - chdirec(&u.u_cdir); + /* + * Must be super user + */ + if (error = suser(scp->sc_cred, &scp->sc_acflag)) + RETURN (error); + /* + * Get vnode to be covered + */ + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->dir; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (uap->flags & M_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->m_flag & M_RDONLY) == 0 && + (uap->flags & M_RDONLY) != 0) { + vput(vp); + RETURN (EOPNOTSUPP); /* Needs translation */ + } + flag = mp->m_flag; + mp->m_flag |= M_UPDATE; + VOP_UNLOCK(vp); + goto update; + } + vinvalbuf(vp, 1); + if (vp->v_usecount != 1) { + vput(vp); + RETURN (EBUSY); + } + 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); + } + + /* + * Allocate and initialize the file system. + */ + mp = (struct mount *)malloc((u_long)sizeof(struct mount), + M_MOUNT, M_WAITOK); + mp->m_op = vfssw[uap->type]; + mp->m_flag = 0; + mp->m_exroot = 0; + mp->m_mounth = (struct vnode *)0; + 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->m_vnodecovered = vp; +update: + /* + * Set the mount level flags. + */ + if (uap->flags & M_RDONLY) + mp->m_flag |= M_RDONLY; + else + mp->m_flag &= ~M_RDONLY; + if (uap->flags & M_NOSUID) + mp->m_flag |= M_NOSUID; + else + mp->m_flag &= ~M_NOSUID; + if (uap->flags & M_NOEXEC) + mp->m_flag |= M_NOEXEC; + else + mp->m_flag &= ~M_NOEXEC; + if (uap->flags & M_NODEV) + mp->m_flag |= M_NODEV; + else + mp->m_flag &= ~M_NODEV; + if (uap->flags & M_SYNCHRONOUS) + mp->m_flag |= M_SYNCHRONOUS; + else + mp->m_flag &= ~M_SYNCHRONOUS; + /* + * Mount the filesystem. + */ + error = VFS_MOUNT(mp, uap->dir, uap->data, ndp); + if (mp->m_flag & M_UPDATE) { + mp->m_flag &= ~M_UPDATE; + vrele(vp); + if (error) + mp->m_flag = flag; + RETURN (error); + } + /* + * Put the new filesystem on the mount list after root. + */ + mp->m_next = rootfs->m_next; + mp->m_prev = rootfs; + rootfs->m_next = mp; + mp->m_next->m_prev = mp; + cache_purge(vp); + if (!error) { + VOP_UNLOCK(vp); + vfs_unlock(mp); + error = VFS_START(mp, 0); + } 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() +unmount(scp) + register struct syscontext *scp; { + struct a { + char *pathp; + int flags; + } *uap = (struct a *)scp->sc_ap; + register struct vnode *vp; + register struct nameidata *ndp = &scp->sc_nd; + struct mount *mp; + int error; - if (suser()) - chdirec(&u.u_rdir); + /* + * Must be super user + */ + if (error = suser(scp->sc_cred, &scp->sc_acflag)) + RETURN (error); + + ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->pathp; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->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)); } /* - * Common routine for chroot and chdir. + * Do an unmount. + */ +dounmount(mp, flags) + register struct mount *mp; + int flags; +{ + struct vnode *coveredvp; + int error; + + coveredvp = mp->m_vnodecovered; + if (error = vfs_lock(mp)) + return (error); + + xumount(mp); /* remove unused sticky files from text table */ + cache_purgevfs(mp); /* remove cache entries for this file sys */ + VFS_SYNC(mp, MNT_WAIT); + + error = VFS_UNMOUNT(mp, flags); + if (error) { + vfs_unlock(mp); + } else { + vrele(coveredvp); + vfs_remove(mp); + free((caddr_t)mp, M_MOUNT); + } + return (error); +} + +/* + * Sync system call. + * Sync each mounted filesystem. + */ +/* ARGSUSED */ +sync(scp) + struct syscontext *scp; +{ + register struct mount *mp; + + mp = rootfs; + do { + /* + * The lock check below is to avoid races with mount + * and unmount. + */ + if ((mp->m_flag & (M_MLOCK|M_RDONLY)) == 0) + VFS_SYNC(mp, MNT_NOWAIT); + mp = mp->m_next; + } while (mp != rootfs); +} + +/* + * get filesystem statistics + */ +statfs(scp) + register struct syscontext *scp; +{ + struct a { + char *path; + struct statfs *buf; + } *uap = (struct a *)scp->sc_ap; + register struct mount *mp; + register struct nameidata *ndp = &scp->sc_nd; + register struct statfs *sp; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->path; + if (error = namei(ndp)) + RETURN (error); + mp = ndp->ni_vp->v_mount; + sp = &mp->m_stat; + vrele(ndp->ni_vp); + if (error = VFS_STATFS(mp, sp)) + RETURN (error); + sp->f_flags = mp->m_flag & M_VISFLAGMASK; + RETURN (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); +} + +fstatfs(scp) + register struct syscontext *scp; +{ + struct a { + int fd; + struct statfs *buf; + } *uap = (struct a *)scp->sc_ap; + struct file *fp; + struct mount *mp; + register struct statfs *sp; + int error; + + if (error = getvnode(scp->sc_ofile, uap->fd, &fp)) + RETURN (error); + mp = ((struct vnode *)fp->f_data)->v_mount; + sp = &mp->m_stat; + if (error = VFS_STATFS(mp, sp)) + RETURN (error); + sp->f_flags = mp->m_flag & M_VISFLAGMASK; + RETURN (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); +} + +/* + * get statistics on all filesystems + */ +getfsstat(scp) + register struct syscontext *scp; +{ + struct a { + struct statfs *buf; + long bufsize; + int flags; + } *uap = (struct a *)scp->sc_ap; + 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->m_flag & M_MLOCK) == 0)) { + sp = &mp->m_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))) { + mp = mp->m_prev; + continue; + } + sp->f_flags = mp->m_flag & M_VISFLAGMASK; + if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp))) + RETURN (error); + sfsp += sizeof(*sp); + } + count++; + mp = mp->m_prev; + } while (mp != rootfs); + if (sfsp && count > maxcount) + scp->sc_retval1 = maxcount; + else + scp->sc_retval1 = count; + RETURN (0); +} + +/* + * Change current working directory to a given file descriptor. + */ +fchdir(scp) + register struct syscontext *scp; +{ + struct a { + int fd; + } *uap = (struct a *)scp->sc_ap; + register struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(scp->sc_ofile, 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, scp->sc_cred); + VOP_UNLOCK(vp); + if (error) + RETURN (error); + VREF(vp); + vrele(scp->sc_cdir); + scp->sc_cdir = vp; + RETURN (0); +} + +/* + * Change current working directory (``.''). */ -chdirec(ipp) - register struct inode **ipp; +chdir(scp) + register struct syscontext *scp; { - register struct inode *ip; struct a { char *fname; - }; - - ip = namei(uchar, LOOKUP, 1); - if (ip == NULL) - return; - if ((ip->i_mode&IFMT) != IFDIR) { - u.u_error = ENOTDIR; - goto bad; - } - if (access(ip, IEXEC)) - goto bad; - iunlock(ip); - if (*ipp) - irele(*ipp); - *ipp = ip; - return; - -bad: - iput(ip); + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = chdirec(ndp)) + RETURN (error); + vrele(scp->sc_cdir); + scp->sc_cdir = ndp->ni_vp; + RETURN (0); +} + +/* + * Change notion of root (``/'') directory. + */ +chroot(scp) + register struct syscontext *scp; +{ + struct a { + char *fname; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + int error; + + if (error = suser(scp->sc_cred, &scp->sc_acflag)) + RETURN (error); + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = chdirec(ndp)) + RETURN (error); + if (scp->sc_rdir != NULL) + vrele(scp->sc_rdir); + scp->sc_rdir = ndp->ni_vp; + RETURN (0); +} + +/* + * Common routine for chroot and chdir. + */ +chdirec(ndp) + register struct nameidata *ndp; +{ + 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, ndp->ni_cred); + VOP_UNLOCK(vp); + if (error) + vrele(vp); + return (error); } /* * Open system call. */ -open() +open(scp) + register struct syscontext *scp; { struct a { char *fname; int mode; int crtmode; - } *uap = (struct a *) u.u_ap; + } *uap = (struct a *) scp->sc_ap; + struct nameidata *ndp = &scp->sc_nd; - copen(uap->mode-FOPEN, uap->crtmode); + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + RETURN (copen(scp, uap->mode-FOPEN, uap->crtmode &~ scp->sc_cmask, ndp, + &scp->sc_retval1)); } /* * Creat system call. */ -creat() +creat(scp) + register struct syscontext *scp; { struct a { char *fname; int fmode; - } *uap = (struct a *)u.u_ap; + } *uap = (struct a *)scp->sc_ap; + struct nameidata *ndp = &scp->sc_nd; - copen(FWRITE|FCREAT|FTRUNC, uap->fmode); + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + RETURN (copen(scp, FWRITE|FCREAT|FTRUNC, uap->fmode &~ scp->sc_cmask, + ndp, &scp->sc_retval1)); } /* @@ -102,218 +512,213 @@ creat() * Check permissions, allocate an open file structure, * and call the device open routine if any. */ -copen(mode, arg) - register int mode; - int arg; +copen(scp, fmode, cmode, ndp, resultfd) + register struct syscontext *scp; + int fmode, cmode; + struct nameidata *ndp; + int *resultfd; { - register struct inode *ip; register struct file *fp; - int i; - -#ifdef notdef - if ((mode&(FREAD|FWRITE)) == 0) { - u.u_error = EINVAL; - return; + struct file *nfp; + int indx, error; + extern struct fileops vnops; + + if (error = falloc(&nfp, &indx)) + return (error); + fp = nfp; + scp->sc_retval1 = indx; /* XXX for fdopen() */ + if (error = vn_open(ndp, fmode, (cmode & 07777) &~ S_ISVTX)) { + crfree(fp->f_cred); + fp->f_count--; + if (error == EJUSTRETURN) /* XXX from fdopen */ + return (0); /* XXX from fdopen */ + if (error == ERESTART) + error = EINTR; + scp->sc_ofile[indx] = NULL; + return (error); } -#endif - if (mode&FCREAT) { - ip = namei(uchar, CREATE, 1); - if (ip == NULL) { - if (u.u_error) - return; - ip = maknode(arg&07777&(~ISVTX)); - if (ip == NULL) - return; - mode &= ~FTRUNC; - } else { - if (mode&FEXCL) { - u.u_error = EEXIST; - iput(ip); - return; - } - mode &= ~FCREAT; - } - } else { - ip = namei(uchar, LOOKUP, 1); - if (ip == NULL) - return; - } - if ((ip->i_mode & IFMT) == IFSOCK) { - u.u_error = EOPNOTSUPP; - goto bad; - } - if ((mode&FCREAT) == 0) { - if (mode&FREAD) - if (access(ip, IREAD)) - goto bad; - if (mode&FWRITE) { - if (access(ip, IWRITE)) - goto bad; - if ((ip->i_mode&IFMT) == IFDIR) { - u.u_error = EISDIR; - goto bad; - } - } - } - fp = falloc(); - if (fp == NULL) - goto bad; - 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; - i = u.u_r.r_val1; -#ifdef notdef - if (setjmp(&u.u_qsave)) { - if (u.u_error == 0) - u.u_error = EINTR; - u.u_ofile[i] = NULL; - closef(fp); - return; - } -#endif - u.u_error = openi(ip, mode); - if (u.u_error == 0) - return; - u.u_ofile[i] = NULL; - fp->f_count--; - irele(ip); - return; -bad: - iput(ip); + fp->f_flag = fmode & FMASK; + fp->f_type = DTYPE_VNODE; + fp->f_ops = &vnops; + fp->f_data = (caddr_t)ndp->ni_vp; + if (resultfd) + *resultfd = indx; + return (0); } /* * Mknod system call */ -mknod() +mknod(scp) + register struct syscontext *scp; { - register struct inode *ip; register struct a { char *fname; int fmode; int dev; - } *uap; - - uap = (struct a *)u.u_ap; - if (!suser()) - return; - ip = namei(uchar, CREATE, 0); - if (ip != NULL) { - u.u_error = EEXIST; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + if (error = suser(scp->sc_cred, &scp->sc_acflag)) + RETURN (error); + ndp->ni_nameiop = CREATE | LOCKPARENT; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp != NULL) { + error = EEXIST; goto out; } - if (u.u_error) - return; - ip = maknode(uap->fmode); - if (ip == NULL) - return; - switch (ip->i_mode & IFMT) { - - 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) &~ scp->sc_cmask; + vattr.va_rdev = uap->dev; out: - iput(ip); + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_MKNOD(ndp, &vattr, ndp->ni_cred); + RETURN (error); +} + +/* + * Mkfifo system call + */ +mkfifo(scp) + register struct syscontext *scp; +{ + register struct a { + char *fname; + int fmode; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + struct vattr vattr; + int error; + +#ifndef FIFO + RETURN (EOPNOTSUPP); +#else + ndp->ni_nameiop = CREATE | LOCKPARENT; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = namei(ndp)) + RETURN (error); + if (ndp->ni_vp != NULL) { + VOP_ABORTOP(ndp); + RETURN (EEXIST); + } else { + vattr_null(&vattr); + vattr.va_type = VFIFO; + vattr.va_mode = (uap->fmode & 07777) &~ scp->sc_cmask; + } + RETURN (VOP_MKNOD(ndp, &vattr, ndp->ni_cred)); +#endif /* FIFO */ } /* * link system call */ -link() +link(scp) + register struct syscontext *scp; { - register struct inode *ip, *xp; register struct a { char *target; char *linkname; - } *uap; - - uap = (struct a *)u.u_ap; - ip = namei(uchar, LOOKUP, 1); /* 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); - u.u_dirp = (caddr_t)uap->linkname; - xp = namei(uchar, CREATE, 0); + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp, *xp; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->target; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_type == VDIR && + (error = suser(scp->sc_cred, &scp->sc_acflag))) + goto out1; + ndp->ni_nameiop = CREATE | LOCKPARENT; + ndp->ni_dirp = (caddr_t)uap->linkname; + if (error = namei(ndp)) + goto out1; + xp = ndp->ni_vp; if (xp != NULL) { - u.u_error = EEXIST; - iput(xp); - goto out; - } - if (u.u_error) - goto out; - if (u.u_pdir->i_dev != ip->i_dev) { - iput(u.u_pdir); - u.u_error = EXDEV; + error = EEXIST; goto out; } - u.u_error = direnter(ip); + xp = ndp->ni_dvp; + if (vp->v_mount != xp->v_mount) + error = EXDEV; out: - if (u.u_error) { - ip->i_nlink--; - ip->i_flag |= ICHG; - } - irele(ip); + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_LINK(vp, ndp); +out1: + vrele(vp); + RETURN (error); } /* * symlink -- make a symbolic link */ -symlink() +symlink(scp) + register struct syscontext *scp; { - register struct a { + struct a { char *target; char *linkname; - } *uap; - register struct inode *ip; - register char *tp; - register c, nc; - - uap = (struct a *)u.u_ap; - tp = uap->target; - nc = 0; - while (c = fubyte(tp)) { - if (c < 0) { - u.u_error = EFAULT; - return; - } - tp++; - nc++; - } - u.u_dirp = uap->linkname; - ip = namei(uchar, CREATE, 0); - if (ip) { - iput(ip); - u.u_error = EEXIST; - return; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + char *target; + int error; + + 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)) + goto out1; + ndp->ni_nameiop = CREATE | LOCKPARENT; + if (error = namei(ndp)) + goto out1; + vp = ndp->ni_vp; + if (vp) { + error = EEXIST; + goto out; } - if (u.u_error) - return; - ip = maknode(IFLNK | 0777); - if (ip == NULL) - return; - u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, 0, 0, (int *)0); - /* handle u.u_error != 0 */ - iput(ip); + vp = ndp->ni_dvp; + vattr_null(&vattr); + vattr.va_mode = 0777 &~ scp->sc_cmask; +out: + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_SYMLINK(ndp, &vattr, target); +out1: + FREE(target, M_NAMEI); + RETURN (error); } /* @@ -321,998 +726,796 @@ symlink() * Hard to avoid races here, especially * in unlinking directories. */ -unlink() +unlink(scp) + register struct syscontext *scp; { struct a { char *fname; - }; - register struct inode *ip, *dp; - - ip = namei(uchar, DELETE | LOCKPARENT, 0); - if (ip == NULL) - return; - dp = u.u_pdir; - if ((ip->i_mode&IFMT) == IFDIR && !suser()) + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + int error; + + ndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_type == VDIR && + (error = suser(scp->sc_cred, &scp->sc_acflag))) goto out; /* * Don't unlink a mounted file. */ - 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()) { - ip->i_nlink--; - ip->i_flag |= ICHG; - } + if (vp->v_flag & VTEXT) + xrele(vp); /* try once to free text */ out: - if (dp == ip) - irele(ip); + if (error) + VOP_ABORTOP(ndp); else - iput(ip); - iput(dp); + error = VOP_REMOVE(ndp); + RETURN (error); } /* * Seek system call */ -lseek() +lseek(scp) + register struct syscontext *scp; { register struct file *fp; register struct a { - int fd; + int fdes; off_t off; int sbase; - } *uap; - - uap = (struct a *)u.u_ap; - fp = getinode(uap->fd); - if (fp == NULL) - return; - if (uap->sbase == L_INCR) - uap->off += fp->f_offset; - else if (uap->sbase == L_XTND) - uap->off += ((struct inode *)fp->f_data)->i_size; - fp->f_offset = uap->off; - u.u_r.r_off = uap->off; + } *uap = (struct a *)scp->sc_ap; + struct vattr vattr; + int error; + + if ((unsigned)uap->fdes >= NOFILE || + (fp = scp->sc_ofile[uap->fdes]) == NULL) + RETURN (EBADF); + if (fp->f_type != DTYPE_VNODE) + RETURN (ESPIPE); + switch (uap->sbase) { + + case L_INCR: + fp->f_offset += uap->off; + break; + + case L_XTND: + if (error = VOP_GETATTR((struct vnode *)fp->f_data, + &vattr, scp->sc_cred)) + RETURN (error); + fp->f_offset = uap->off + vattr.va_size; + break; + + case L_SET: + fp->f_offset = uap->off; + break; + + default: + RETURN (EINVAL); + } + scp->sc_offset = fp->f_offset; + RETURN (0); } /* * Access system call */ -saccess() +saccess(scp) + register struct syscontext *scp; { - register svuid, svgid; - register struct inode *ip; register struct a { char *fname; int fmode; - } *uap; - - uap = (struct a *)u.u_ap; - svuid = u.u_uid; - svgid = u.u_gid; - u.u_uid = u.u_ruid; - u.u_gid = u.u_rgid; - ip = namei(uchar, LOOKUP, 1); - 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 = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + int error, mode, svuid, svgid; + + svuid = scp->sc_uid; + svgid = scp->sc_gid; + scp->sc_uid = scp->sc_ruid; + scp->sc_gid = scp->sc_rgid; + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = namei(ndp)) + 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, ndp->ni_cred); } - u.u_uid = svuid; - u.u_gid = svgid; + vput(vp); +out1: + scp->sc_uid = svuid; + scp->sc_gid = svgid; + RETURN (error); } /* * Stat system call. This version follows links. */ -stat() +stat(scp) + struct syscontext *scp; { - stat1(1); + stat1(scp, FOLLOW); } /* * Lstat system call. This version does not follow links. */ -lstat() +lstat(scp) + struct syscontext *scp; { - stat1(0); + stat1(scp, NOFOLLOW); } -stat1(follow) +stat1(scp, follow) + register struct syscontext *scp; int follow; { - register struct inode *ip; register struct a { char *fname; struct stat *ub; - } *uap; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; struct stat sb; - - uap = (struct a *)u.u_ap; - ip = namei(uchar, LOOKUP, follow); - if (ip == NULL) - return; - (void) statinode(ip, &sb); - iput(ip); - u.u_error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); + int error; + + ndp->ni_nameiop = LOOKUP | LOCKLEAF | follow; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = namei(ndp)) + RETURN (error); + error = vn_stat(ndp->ni_vp, &sb); + vput(ndp->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 */ -readlink() +readlink(scp) + register struct syscontext *scp; { - register struct inode *ip; register struct a { char *name; char *buf; int count; - } *uap = (struct a *)u.u_ap; - int resid; - - ip = namei(uchar, LOOKUP, 0); - if (ip == NULL) - return; - if ((ip->i_mode&IFMT) != IFLNK) { - u.u_error = ENXIO; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct iovec aiov; + struct uio auio; + int error; + + ndp->ni_nameiop = LOOKUP | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->name; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_type != VLNK) { + error = EINVAL; goto out; } - u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 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_resid = uap->count; + error = VOP_READLINK(vp, &auio, ndp->ni_cred); out: - iput(ip); - u.u_r.r_val1 = uap->count - resid; + vput(vp); + scp->sc_retval1 = uap->count - auio.uio_resid; + RETURN (error); } /* - * Change mode of a file given path name. + * Change flags of a file given path name. */ -chmod() +chflags(scp) + register struct syscontext *scp; { - struct inode *ip; struct a { char *fname; - int fmode; - } *uap; - - uap = (struct a *)u.u_ap; - if ((ip = owner(1)) == NULL) - return; - chmod1(ip, uap->fmode); - iput(ip); + int flags; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + vattr_null(&vattr); + vattr.va_flags = uap->flags; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_mount->m_flag & M_RDONLY) { + error = EROFS; + goto out; + } + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); +out: + vput(vp); + RETURN (error); } /* - * Change mode of a file given a file descriptor. + * Change flags of a file given a file descriptor. */ -fchmod() +fchflags(scp) + register struct syscontext *scp; { struct a { int fd; - int fmode; - } *uap; - register struct inode *ip; - register struct file *fp; - - uap = (struct a *)u.u_ap; - 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); - chmod1(ip, uap->fmode); - iunlock(ip); + int flags; + } *uap = (struct a *)scp->sc_ap; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(scp->sc_ofile, uap->fd, &fp)) + RETURN (error); + vattr_null(&vattr); + vattr.va_flags = uap->flags; + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_mount->m_flag & M_RDONLY) { + error = EROFS; + goto out; + } + error = VOP_SETATTR(vp, &vattr, fp->f_cred); +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; +chmod(scp) + register struct syscontext *scp; { + struct a { + char *fname; + int fmode; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + vattr_null(&vattr); + vattr.va_mode = uap->fmode & 07777; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_mount->m_flag & M_RDONLY) { + error = EROFS; + goto out; + } + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); +out: + vput(vp); + RETURN (error); +} - ip->i_mode &= ~07777; - if (u.u_uid) { - mode &= ~ISVTX; - if (!groupmember(ip->i_gid)) - mode &= ~ISGID; +/* + * Change mode of a file given a file descriptor. + */ +fchmod(scp) + register struct syscontext *scp; +{ + struct a { + int fd; + int fmode; + } *uap = (struct a *)scp->sc_ap; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(scp->sc_ofile, uap->fd, &fp)) + RETURN (error); + vattr_null(&vattr); + vattr.va_mode = uap->fmode & 07777; + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_mount->m_flag & M_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); + error = VOP_SETATTR(vp, &vattr, fp->f_cred); +out: + VOP_UNLOCK(vp); + RETURN (error); } /* * Set ownership given a path name. */ -chown() +chown(scp) + register struct syscontext *scp; { - struct inode *ip; struct a { char *fname; int uid; int gid; - } *uap; - - uap = (struct a *)u.u_ap; - if (!suser() || (ip = owner(0)) == NULL) - return; - u.u_error = chown1(ip, uap->uid, uap->gid); - iput(ip); + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + ndp->ni_nameiop = LOOKUP | NOFOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + vattr_null(&vattr); + vattr.va_uid = uap->uid; + vattr.va_gid = uap->gid; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_mount->m_flag & M_RDONLY) { + error = EROFS; + goto out; + } + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); +out: + vput(vp); + RETURN (error); } /* * Set ownership given a file descriptor. */ -fchown() +fchown(scp) + register struct syscontext *scp; { struct a { int fd; int uid; int gid; - } *uap; - register struct inode *ip; - register struct file *fp; - - uap = (struct a *)u.u_ap; - fp = getinode(uap->fd); - if (fp == NULL) - return; - ip = (struct inode *)fp->f_data; - if (!suser()) - return; - ilock(ip); - u.u_error = chown1(ip, uap->uid, uap->gid); - iunlock(ip); -} - -/* - * Perform chown operation on inode ip; - * inode must be locked prior to call. - */ -chown1(ip, uid, gid) - register struct inode *ip; - int uid, gid; -{ -#ifdef QUOTA - register long change; -#endif - - if (uid == -1) - uid = ip->i_uid; - if (gid == -1) - gid = ip->i_gid; -#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, 1); - return (u.u_error); /* should == 0 ALWAYS !! */ -#else - return (0); -#endif + } *uap = (struct a *)scp->sc_ap; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(scp->sc_ofile, uap->fd, &fp)) + RETURN (error); + vattr_null(&vattr); + vattr.va_uid = uap->uid; + vattr.va_gid = uap->gid; + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_mount->m_flag & M_RDONLY) { + error = EROFS; + goto out; + } + error = VOP_SETATTR(vp, &vattr, fp->f_cred); +out: + VOP_UNLOCK(vp); + RETURN (error); } -utimes() +utimes(scp) + register struct syscontext *scp; { register struct a { char *fname; struct timeval *tptr; - } *uap = (struct a *)u.u_ap; - register struct inode *ip; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; struct timeval tv[2]; - - if ((ip = owner(1)) == NULL) - 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); - } - iput(ip); -} - -/* - * Flush any pending I/O. - */ -sync() -{ - - update(); -} - -/* - * Apply an advisory lock on a file descriptor. - */ -flock() -{ - register struct a { - int fd; - int how; - } *uap = (struct a *)u.u_ap; - register struct file *fp; - register int cmd, flags; - - fp = getinode(uap->fd); - if (fp == NULL) - return; - cmd = uap->how; - flags = u.u_pofile[uap->fd] & (UF_SHLOCK|UF_EXLOCK); - if (cmd&LOCK_UN) { - if (flags == 0) { - u.u_error = EINVAL; - return; - } - funlocki((struct inode *)fp->f_data, flags); - u.u_pofile[uap->fd] &= ~(UF_SHLOCK|UF_EXLOCK); - return; + struct vattr vattr; + int error; + + if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv))) + RETURN (error); + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + vattr_null(&vattr); + vattr.va_atime = tv[0]; + vattr.va_mtime = tv[1]; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_mount->m_flag & M_RDONLY) { + error = EROFS; + goto out; } - /* - * No reason to write lock a file we've already - * write locked, similarly with a read lock. - */ - if ((flags&UF_EXLOCK) && (cmd&LOCK_EX) || - (flags&UF_SHLOCK) && (cmd&LOCK_SH)) - return; - u.u_pofile[uap->fd] = - flocki((struct inode *)fp->f_data, u.u_pofile[uap->fd], cmd); + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); +out: + vput(vp); + RETURN (error); } /* * Truncate a file given its path name. */ -truncate() +truncate(scp) + register struct syscontext *scp; { struct a { char *fname; - u_long length; - } *uap = (struct a *)u.u_ap; - struct inode *ip; - - ip = namei(uchar, LOOKUP, 1); - if (ip == NULL) - return; - if (access(ip, IWRITE)) - goto bad; - if ((ip->i_mode&IFMT) == IFDIR) { - u.u_error = EISDIR; - goto bad; + off_t length; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + vattr_null(&vattr); + vattr.va_size = uap->length; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_type == VDIR) { + error = EISDIR; + goto out; } - itrunc(ip, uap->length); -bad: - iput(ip); + if ((error = vn_writechk(vp)) || + (error = VOP_ACCESS(vp, VWRITE, ndp->ni_cred))) + goto out; + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); +out: + vput(vp); + RETURN (error); } /* * Truncate a file given a file descriptor. */ -ftruncate() +ftruncate(scp) + register struct syscontext *scp; { struct a { int fd; - u_long length; - } *uap = (struct a *)u.u_ap; - struct inode *ip; + off_t length; + } *uap = (struct a *)scp->sc_ap; + struct vattr vattr; + struct vnode *vp; struct file *fp; - - fp = getinode(uap->fd); - if (fp == NULL) - return; - if ((fp->f_flag&FWRITE) == 0) { - u.u_error = EINVAL; - return; + int error; + + if (error = getvnode(scp->sc_ofile, uap->fd, &fp)) + RETURN (error); + if ((fp->f_flag & FWRITE) == 0) + RETURN (EINVAL); + vattr_null(&vattr); + vattr.va_size = uap->length; + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + if (vp->v_type == VDIR) { + error = EISDIR; + goto out; } - ip = (struct inode *)fp->f_data; - ilock(ip); - itrunc(ip, uap->length); - iunlock(ip); + if (error = vn_writechk(vp)) + goto out; + error = VOP_SETATTR(vp, &vattr, fp->f_cred); +out: + VOP_UNLOCK(vp); + RETURN (error); } /* * Synch an open file. */ -fsync() +fsync(scp) + register struct syscontext *scp; { struct a { int fd; - } *uap = (struct a *)u.u_ap; - struct inode *ip; + } *uap = (struct a *)scp->sc_ap; + register struct vnode *vp; struct file *fp; - - fp = getinode(uap->fd); - if (fp == NULL) - return; - ip = (struct inode *)fp->f_data; - ilock(ip); - syncip(ip); - iunlock(ip); + int error; + + if (error = getvnode(scp->sc_ofile, uap->fd, &fp)) + RETURN (error); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + error = VOP_FSYNC(vp, fp->f_flag, fp->f_cred, MNT_WAIT); + 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. - * 2) Link source to destination. If destination already exists, - * delete it first. - * 3) Unlink source reference to inode if still around. - * 4) 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() +rename(scp) + register struct syscontext *scp; { struct a { char *from; char *to; - } *uap; - register struct inode *ip, *xp, *dp; - int oldparent, parentdifferent, doingdirectory; - int error = 0; - - uap = (struct a *)u.u_ap; - ip = namei(uchar, DELETE | LOCKPARENT, 0); - if (ip == NULL) - return; - dp = u.u_pdir; - oldparent = 0, doingdirectory = 0; - if ((ip->i_mode&IFMT) == IFDIR) { - register struct direct *d; - - d = &u.u_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)) { - iput(dp); - if (dp == ip) - irele(ip); - else - iput(ip); - u.u_error = EINVAL; - return; - } - oldparent = dp->i_number; - doingdirectory++; - } - iput(dp); - - /* - * 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. - */ - ip->i_nlink++; - ip->i_flag |= ICHG; - iupdat(ip, &time, &time, 1); - iunlock(ip); - - /* - * When the target exists, both the directory - * and target inodes are returned locked. - */ - u.u_dirp = (caddr_t)uap->to; - xp = namei(uchar, CREATE | LOCKPARENT, 0); - if (u.u_error) { - error = u.u_error; - goto out; - } - dp = u.u_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(). - */ - parentdifferent = oldparent != dp->i_number; - if (doingdirectory && parentdifferent) { - if (access(ip, IWRITE)) - goto bad; - do { - dp = u.u_pdir; - if (xp != NULL) - iput(xp); - u.u_error = checkpath(ip, dp); - if (u.u_error) - goto out; - u.u_dirp = (caddr_t)uap->to; - xp = namei(uchar, CREATE | LOCKPARENT, 0); - if (u.u_error) { - error = u.u_error; - goto out; - } - } while (dp != u.u_pdir); - } - /* - * 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. - */ - if (xp == NULL) { - if (dp->i_dev != ip->i_dev) { - error = EXDEV; - goto bad; - } - /* - * Account for ".." in directory. - * When source and destination have the - * same parent we don't fool with the - * link count -- this isn't required - * because we do a similar check below. - */ - if (doingdirectory && parentdifferent) { - dp->i_nlink++; - dp->i_flag |= ICHG; - iupdat(dp, &time, &time, 1); - } - error = direnter(ip); - if (error) + } *uap = (struct a *)scp->sc_ap; + register struct vnode *tvp, *fvp, *tdvp; + register struct nameidata *ndp = &scp->sc_nd; + struct nameidata tond; + int error; + + ndp->ni_nameiop = DELETE | WANTPARENT; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->from; + if (error = namei(ndp)) + RETURN (error); + fvp = ndp->ni_vp; + nddup(ndp, &tond); + tond.ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE; + tond.ni_segflg = UIO_USERSPACE; + tond.ni_dirp = uap->to; + error = namei(&tond); + tdvp = tond.ni_dvp; + tvp = tond.ni_vp; + if (tvp != NULL) { + if (fvp->v_type == VDIR && tvp->v_type != VDIR) { + error = ENOTDIR; goto out; - } else { - if (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; - /* - * 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) || xp->i_nlink > 2) { - error = ENOTEMPTY; - goto bad; - } - if (!doingdirectory) { - error = ENOTDIR; - goto bad; - } - } else if (doingdirectory) { + } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; - goto bad; - } - dirrewrite(dp, ip); - 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 we've no way to determine if - * we've got a link or the directory itself, and - * if we get a link, then ".." will be screwed up. - */ - 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; - } - - /* - * 3) Unlink the source. - */ - u.u_dirp = uap->from; - dp = namei(uchar, DELETE, 0); - /* - * Insure directory entry still exists and - * has not changed since the start of all - * this. If either has occured, forget about - * about deleting the original entry and just - * adjust the link count in the inode. - */ - if (dp == NULL || u.u_dent.d_ino != ip->i_number) { - ip->i_nlink--; - ip->i_flag |= ICHG; - } else { - /* - * If source is a directory, must adjust - * link count of parent directory also. - * If target didn't exist and source and - * target have the same parent, then we - * needn't touch the link count, it all - * balances out in the end. Otherwise, we - * must do so to reflect deletion of ".." - * done above. - */ - if (doingdirectory && (xp != NULL || parentdifferent)) { - dp->i_nlink--; - dp->i_flag |= ICHG; - } - if (dirremove()) { - ip->i_nlink--; - ip->i_flag |= ICHG; + goto out; } - if (error == 0) /* conservative */ - error = u.u_error; } - irele(ip); - if (dp) - iput(dp); - - /* - * 4) Renaming a directory with the parent - * different requires ".." to be rewritten. - * The window is still there for ".." to - * be inconsistent, but this is unavoidable, - * and a lot shorter than when it was done - * in a user process. - */ - if (doingdirectory && parentdifferent && error == 0) { - struct dirtemplate dirbuf; - - u.u_dirp = uap->to; - ip = namei(uchar, LOOKUP | LOCKPARENT, 0); - if (ip == NULL) { - printf("rename: .. went away\n"); - return; - } - dp = u.u_pdir; - if ((ip->i_mode&IFMT) != IFDIR) { - printf("rename: .. not a directory\n"); - goto stuck; - } - error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf, - sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); - if (error == 0) { - dirbuf.dotdot_ino = dp->i_number; - (void) rdwri(UIO_WRITE, ip, (caddr_t)&dirbuf, - sizeof (struct dirtemplate), (off_t)0, 1, (int *)0); - } -stuck: - irele(dp); - iput(ip); + if (error) { + VOP_ABORTOP(ndp); + goto out1; } - goto done; - -bad: - iput(dp); -bad1: - if (xp) - iput(xp); -out: - ip->i_nlink--; - ip->i_flag |= ICHG; - irele(ip); -done: - if (error) - u.u_error = error; -} - -/* - * Make a new file. - */ -struct inode * -maknode(mode) - int mode; -{ - register struct inode *ip; - ino_t ipref; - - if ((mode & IFMT) == IFDIR) - ipref = dirpref(u.u_pdir->i_fs); - else - ipref = u.u_pdir->i_number; - ip = ialloc(u.u_pdir, ipref, mode); - if (ip == NULL) { - iput(u.u_pdir); - return (NULL); + if (fvp->v_mount != tdvp->v_mount) { + error = EXDEV; + goto out; } -#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 = u.u_pdir->i_gid; - if (ip->i_mode & ISGID && !groupmember(ip->i_gid)) - ip->i_mode &= ~ISGID; -#ifdef QUOTA - ip->i_dquot = inoquota(ip); -#endif - + if (fvp == tdvp) + error = EINVAL; /* - * Make sure inode goes to disk before directory entry. + * If source is the same as the destination, + * then there is nothing to do. */ - iupdat(ip, &time, &time, 1); - u.u_error = direnter(ip); - 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); + if (fvp == tvp) + error = -1; +out: + if (error) { + VOP_ABORTOP(&tond); + VOP_ABORTOP(ndp); + } else { + error = VOP_RENAME(ndp, &tond); } - return (ip); +out1: + ndrele(&tond); + if (error == -1) + RETURN (0); + RETURN (error); } -/* - * A virgin directory (no blushing please). - */ -struct dirtemplate mastertemplate = { - 0, 12, 1, ".", - 0, DIRBLKSIZ - 12, 2, ".." -}; - /* * Mkdir system call */ -mkdir() +mkdir(scp) + register struct syscontext *scp; { struct a { char *name; int dmode; - } *uap; - register struct inode *ip, *dp; - struct dirtemplate dirtemplate; - - uap = (struct a *)u.u_ap; - ip = namei(uchar, CREATE, 0); - if (u.u_error) - return; - if (ip != NULL) { - iput(ip); - u.u_error = EEXIST; - return; - } - dp = u.u_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; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + ndp->ni_nameiop = CREATE | LOCKPARENT; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->name; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp != NULL) { + VOP_ABORTOP(ndp); + RETURN (EEXIST); } -#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); - - /* - * 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; - } - /* - * Directory all set up, now - * install the entry for it in - * the parent directory. - */ - u.u_error = direnter(ip); - dp = NULL; - if (u.u_error) { - u.u_dirp = uap->name; - dp = namei(uchar, LOOKUP, 0); - 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); + vattr_null(&vattr); + vattr.va_type = VDIR; + vattr.va_mode = (uap->dmode & 0777) &~ scp->sc_cmask; + error = VOP_MKDIR(ndp, &vattr); + if (!error) + vput(ndp->ni_vp); + RETURN (error); } /* * Rmdir system call. */ -rmdir() +rmdir(scp) + register struct syscontext *scp; { struct a { char *name; - }; - register struct inode *ip, *dp; - - ip = namei(uchar, DELETE | LOCKPARENT, 0); - if (ip == NULL) - return; - dp = u.u_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; - goto out; - } - /* - * Don't remove a mounted on directory. - */ - if (ip->i_dev != dp->i_dev) { - u.u_error = EBUSY; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + int error; + + ndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->name; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_type != VDIR) { + error = ENOTDIR; 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.) + * No rmdir "." please. */ - if (ip->i_nlink != 2 || !dirempty(ip)) { - u.u_error = ENOTEMPTY; + if (ndp->ni_dvp == vp) { + error = EINVAL; goto out; } /* - * Delete reference to directory before purging - * inode. If we crash in between, the directory - * will be reattached to lost+found, - */ - if (dirremove() == 0) - goto out; - dp->i_nlink--; - dp->i_flag |= ICHG; - 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. + * Don't unlink a mounted file. */ - ip->i_nlink -= 2; - itrunc(ip, (u_long)0); + if (vp->v_flag & VROOT) + error = EBUSY; out: - if (dp) - iput(dp); - iput(ip); + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_RMDIR(ndp); + RETURN (error); } -struct file * -getinode(fdes) - int fdes; +/* + * Read a block of directory entries in a file system independent format + */ +getdirentries(scp) + register struct syscontext *scp; { - register struct file *fp; - - fp = getf(fdes); - if (fp == 0) - return (0); - if (fp->f_type != DTYPE_INODE) { - u.u_error = EINVAL; - return (0); - } - return (fp); + register struct a { + int fd; + char *buf; + unsigned count; + long *basep; + } *uap = (struct a *)scp->sc_ap; + register struct vnode *vp; + struct file *fp; + struct uio auio; + struct iovec aiov; + off_t off; + int error, eofflag; + + if (error = getvnode(scp->sc_ofile, 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_resid = uap->count; + VOP_LOCK(vp); + auio.uio_offset = off = fp->f_offset; + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag); + fp->f_offset = auio.uio_offset; + VOP_UNLOCK(vp); + if (error) + RETURN (error); + error = copyout((caddr_t)&off, (caddr_t)uap->basep, sizeof(long)); + scp->sc_retval1 = uap->count - auio.uio_resid; + RETURN (error); } /* * mode mask for creation of files */ -umask() +umask(scp) + register struct syscontext *scp; { register struct a { int mask; - } *uap; + } *uap = (struct a *)scp->sc_ap; + + scp->sc_retval1 = scp->sc_cmask; + scp->sc_cmask = uap->mask & 07777; + RETURN (0); +} + +/* + * Void all references to file by ripping underlying filesystem + * away from vnode. + */ +revoke(scp) + register struct syscontext *scp; +{ + struct a { + char *fname; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (vp->v_type != VCHR && vp->v_type != VBLK) { + error = EINVAL; + goto out; + } + if (error = VOP_GETATTR(vp, &vattr, scp->sc_cred)) + goto out; + if (scp->sc_uid != vattr.va_uid || + (error = suser(scp->sc_cred, &scp->sc_acflag))) + goto out; + if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) + vgoneall(vp); +out: + vrele(vp); + RETURN (error); +} + +getvnode(ofile, fdes, fpp) + struct file *ofile[]; + struct file **fpp; + int fdes; +{ + struct file *fp; - uap = (struct a *)u.u_ap; - u.u_r.r_val1 = u.u_cmask; - u.u_cmask = uap->mask & 07777; + if ((unsigned)fdes >= NOFILE || (fp = ofile[fdes]) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (EINVAL); + *fpp = fp; + return (0); }