X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/3d27a326c8dc6e3f5d1150b02d6edabd8053edcd..0e30f634db0bf5e9f6480faa9648024a0f7ed7b2:/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 da535f307b..6fc96aedd4 100644 --- a/usr/src/sys/kern/vfs_syscalls.c +++ b/usr/src/sys/kern/vfs_syscalls.c @@ -1,33 +1,306 @@ -/* vfs_syscalls.c 6.3 83/09/30 */ - -#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.13 (Berkeley) %G% + */ + +#include "param.h" +#include "systm.h" +#include "syscontext.h" +#include "kernel.h" +#include "file.h" +#include "stat.h" +#include "vnode.h" +#include "../ufs/inode.h" +#include "mount.h" +#include "proc.h" +#include "uio.h" +#include "malloc.h" + +/* + * Virtual File System System Calls + */ + +/* + * mount system call + */ +mount() +{ + register struct a { + int type; + char *dir; + int flags; + caddr_t data; + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_nd; + struct vnode *vp; + struct mount *mp; + int error; + + /* + * Must be super user + */ + if (error = suser(u.u_cred, &u.u_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 (vp->v_count != 1) { + vput(vp); + RETURN (EBUSY); + } + if (vp->v_type != VDIR) { + vput(vp); + RETURN (ENOTDIR); + } + if (uap->type > MOUNT_MAXTYPE || + vfssw[uap->type] == (struct vfsops *)0) { + vput(vp); + RETURN (ENODEV); + } + + /* + * Mount the filesystem. + */ + 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; + error = vfs_add(vp, mp, uap->flags); + if (!error) + error = VFS_MOUNT(mp, uap->dir, uap->data, ndp); + cache_purge(vp); + VOP_UNLOCK(vp); + if (!error) { + vfs_unlock(mp); + } else { + vfs_remove(mp); + free((caddr_t)mp, M_MOUNT); + vrele(vp); + } + RETURN (error); +} + +/* + * Unmount system call. + * + * Note: unmount takes a path to the vnode mounted on as argument, + * not special file (as before). + */ +unmount() +{ + struct a { + char *pathp; + int flags; + } *uap = (struct a *)u.u_ap; + register struct vnode *vp; + register struct mount *mp; + register struct nameidata *ndp = &u.u_nd; + struct vnode *coveredvp; + int error; + + /* + * Must be super user + */ + if (error = suser(u.u_cred, &u.u_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); + /* + * Do the unmount. + */ + 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, uap->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. + */ +sync() +{ + register struct mount *mp; + + mp = rootfs; + do { + if ((mp->m_flag & M_RDONLY) == 0) + VFS_SYNC(mp, MNT_NOWAIT); + mp = mp->m_next; + } while (mp != rootfs); +} + +/* + * get filesystem statistics + */ +statfs() +{ + struct a { + char *path; + struct statfs *buf; + } *uap = (struct a *)u.u_ap; + register struct vnode *vp; + register struct nameidata *ndp = &u.u_nd; + struct statfs sb; + int error; + + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->path; + if (error = namei(ndp)) + RETURN (error); + vp = ndp->ni_vp; + if (error = VFS_STATFS(vp->v_mount, &sb)) + goto out; + error = copyout((caddr_t)&sb, (caddr_t)uap->buf, sizeof(sb)); +out: + vput(vp); + RETURN (error); +} + +fstatfs() +{ + struct a { + int fd; + struct statfs *buf; + } *uap = (struct a *)u.u_ap; + struct file *fp; + struct statfs sb; + int error; + + if (error = getvnode(uap->fd, &fp)) + RETURN (error); + if (error = VFS_STATFS(((struct vnode *)fp->f_data)->v_mount, &sb)) + RETURN (error); + RETURN (copyout((caddr_t)&sb, (caddr_t)uap->buf, sizeof(sb))); +} + +/* + * get statistics on all filesystems + */ +getfsstat() +{ + struct a { + struct statfs *buf; + long bufsize; + } *uap = (struct a *)u.u_ap; + register struct mount *mp; + register struct statfs *sfsp; + long count, maxcount, error; + + maxcount = uap->bufsize / sizeof(struct statfs); + sfsp = uap->buf; + mp = rootfs; + count = 0; + do { + count++; + if (sfsp && count <= maxcount) { + if (error = VFS_STATFS(mp, sfsp)) + RETURN (error); + sfsp++; + } + mp = mp->m_prev; + } while (mp != rootfs); + if (sfsp && count > maxcount) + u.u_r.r_val1 = maxcount; + else + u.u_r.r_val1 = count; + RETURN (0); +} + +/* + * Change current working directory to a given file descriptor. + */ +fchdir() +{ + struct a { + int fd; + } *uap = (struct a *)u.u_ap; + register struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(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, u.u_cred); + VOP_UNLOCK(vp); + vrele(u.u_cdir); + u.u_cdir = vp; + RETURN (error); +} /* * Change current working directory (``.''). */ chdir() { - - chdirec(&u.u_cdir); + struct a { + char *fname; + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_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(u.u_cdir); + u.u_cdir = ndp->ni_vp; + RETURN (0); } /* @@ -35,39 +308,44 @@ chdir() */ chroot() { - - if (suser()) - chdirec(&u.u_rdir); + struct a { + char *fname; + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_nd; + int error; + + if (error = suser(u.u_cred, &u.u_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); + vrele(u.u_rdir); + u.u_rdir = ndp->ni_vp; + RETURN (0); } /* * Common routine for chroot and chdir. */ -chdirec(ipp) - register struct inode **ipp; +chdirec(ndp) + register struct nameidata *ndp; { - 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); + 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); } /* @@ -80,8 +358,12 @@ open() int mode; int crtmode; } *uap = (struct a *) u.u_ap; + struct nameidata *ndp = &u.u_nd; - copen(uap->mode-FOPEN, uap->crtmode); + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + RETURN (copen(uap->mode-FOPEN, uap->crtmode &~ u.u_cmask, ndp, + &u.u_r.r_val1)); } /* @@ -93,8 +375,12 @@ creat() char *fname; int fmode; } *uap = (struct a *)u.u_ap; + struct nameidata *ndp = &u.u_nd; - copen(FWRITE|FCREAT|FTRUNC, uap->fmode); + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + RETURN (copen(FWRITE|FCREAT|FTRUNC, uap->fmode &~ u.u_cmask, ndp, + &u.u_r.r_val1)); } /* @@ -102,86 +388,33 @@ 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(fmode, cmode, ndp, resultfd) + 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; - } -#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; + struct file *nfp; + int indx, error; + extern struct fileops vnops; + + if (error = falloc(&nfp, &indx)) + return (error); + fp = nfp; + u.u_r.r_val1 = indx; /* XXX for fdopen() */ + if (error = vn_open(ndp, fmode, (cmode & 07777) &~ ISVTX)) { + u.u_ofile[indx] = NULL; + crfree(fp->f_cred); + fp->f_count--; + return (error); } - 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; - if (setjmp(&u.u_qsave)) { - if (u.u_error == 0) - u.u_error = EINTR; - u.u_ofile[i] = NULL; - closef(fp); - return; - } - 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); } /* @@ -189,43 +422,52 @@ bad: */ mknod() { - 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 *)u.u_ap; + register struct nameidata *ndp = &u.u_nd; + register struct vnode *vp; + struct vattr vattr; + int error; + + if (error = suser(u.u_cred, &u.u_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) { + vattr_null(&vattr); + switch (uap->fmode & IFMT) { case IFMT: /* used by badsect to flag bad sectors */ + vattr.va_type = VBAD; + break; case IFCHR: + vattr.va_type = VCHR; + break; 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.va_type = VBLK; + break; + default: + error = EINVAL; + goto out; } - + vattr.va_mode = (uap->fmode & 07777) &~ u.u_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); } /* @@ -233,45 +475,43 @@ out: */ link() { - 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 *)u.u_ap; + register struct nameidata *ndp = &u.u_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(u.u_cred, &u.u_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); + error = EEXIST; 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; - 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); } /* @@ -279,40 +519,40 @@ out: */ symlink() { - 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 *)u.u_ap; + register struct nameidata *ndp = &u.u_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 &~ u.u_cmask; +out: + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_SYMLINK(ndp, &vattr, target); +out1: + FREE(target, M_NAMEI); + RETURN (error); } /* @@ -324,34 +564,35 @@ unlink() { 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 *)u.u_ap; + register struct nameidata *ndp = &u.u_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(u.u_cred, &u.u_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); } /* @@ -361,15 +602,18 @@ lseek() { 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; + } *uap = (struct a *)u.u_ap; + struct vattr vattr; + int error; + + if ((unsigned)uap->fdes >= NOFILE || + (fp = u.u_ofile[uap->fdes]) == NULL) + RETURN (EBADF); + if (fp->f_type != DTYPE_VNODE) + RETURN (ESPIPE); switch (uap->sbase) { case L_INCR: @@ -377,7 +621,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, u.u_cred)) + RETURN (error); + fp->f_offset = uap->off + vattr.va_size; break; case L_SET: @@ -385,10 +632,10 @@ lseek() break; default: - u.u_error = EINVAL; - return; + RETURN (EINVAL); } u.u_r.r_off = fp->f_offset; + RETURN (0); } /* @@ -396,31 +643,43 @@ lseek() */ saccess() { - register svuid, svgid; - register struct inode *ip; register struct a { char *fname; int fmode; - } *uap; + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_nd; + register struct vnode *vp; + int error, mode, svuid, svgid; - 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); + 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 ((error = vn_writechk(vp)) == 0) + error = VOP_ACCESS(vp, mode, ndp->ni_cred); } + vput(vp); +out1: u.u_uid = svuid; u.u_gid = svgid; + RETURN (error); } /* @@ -429,7 +688,7 @@ done: stat() { - stat1(1); + stat1(FOLLOW); } /* @@ -438,26 +697,31 @@ stat() lstat() { - stat1(0); + stat1(NOFOLLOW); } stat1(follow) int follow; { - register struct inode *ip; register struct a { char *fname; struct stat *ub; - } *uap; + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_nd; struct stat sb; - - uap = (struct a *)u.u_ap; - ip = namei(uchar, LOOKUP, follow); - 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; + + 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); } /* @@ -465,25 +729,102 @@ stat1(follow) */ readlink() { - register struct inode *ip; register struct a { char *name; char *buf; int count; } *uap = (struct a *)u.u_ap; - int resid; + register struct nameidata *ndp = &u.u_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; + } + 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: + vput(vp); + u.u_r.r_val1 = uap->count - auio.uio_resid; + RETURN (error); +} - ip = namei(uchar, LOOKUP, 0); - if (ip == NULL) - return; - if ((ip->i_mode&IFMT) != IFLNK) { - u.u_error = ENXIO; +/* + * Change flags of a file given path name. + */ +chflags() +{ + struct a { + char *fname; + int flags; + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_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; } - u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid); + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); out: - iput(ip); - u.u_r.r_val1 = uap->count - resid; + vput(vp); + RETURN (error); +} + +/* + * Change flags of a file given a file descriptor. + */ +fchflags() +{ + struct a { + int fd; + int flags; + } *uap = (struct a *)u.u_ap; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(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); } /* @@ -491,17 +832,31 @@ out: */ chmod() { - 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); + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_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); } /* @@ -512,41 +867,26 @@ fchmod() 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); -} - -/* - * Change the mode on a file. - * Inode must be locked before calling. - */ -chmod1(ip, mode) - register struct inode *ip; - register int mode; -{ - - ip->i_mode &= ~07777; - if (u.u_uid) { - mode &= ~ISVTX; - if (!groupmember(ip->i_gid)) - mode &= ~ISGID; + } *uap = (struct a *)u.u_ap; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(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); } /* @@ -554,18 +894,33 @@ chmod1(ip, mode) */ chown() { - 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 *)u.u_ap; + register struct nameidata *ndp = &u.u_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); } /* @@ -577,60 +932,27 @@ fchown() 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 *)u.u_ap; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(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() @@ -639,26 +961,31 @@ utimes() char *fname; struct timeval *tptr; } *uap = (struct a *)u.u_ap; - register struct inode *ip; + register struct nameidata *ndp = &u.u_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); + 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; } - iput(ip); -} - -/* - * Flush any pending I/O. - */ -sync() -{ - - update(); + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); +out: + vput(vp); + RETURN (error); } /* @@ -668,22 +995,32 @@ truncate() { struct a { char *fname; - u_long length; + off_t 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; + register struct nameidata *ndp = &u.u_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); } /* @@ -693,22 +1030,31 @@ ftruncate() { struct a { int fd; - u_long length; + off_t length; } *uap = (struct a *)u.u_ap; - struct inode *ip; + 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(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); } /* @@ -719,40 +1065,17 @@ fsync() struct a { int fd; } *uap = (struct a *)u.u_ap; - struct inode *ip; struct file *fp; + int error; - fp = getinode(uap->fd); - if (fp == NULL) - return; - ip = (struct inode *)fp->f_data; - ilock(ip); - syncip(ip); - iunlock(ip); + if (error = getvnode(uap->fd, &fp)) + RETURN (error); + error = VOP_FSYNC((struct vnode *)fp->f_data, fp->f_flag, fp->f_cred); + 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. @@ -762,327 +1085,56 @@ rename() 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) - 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) { + } *uap = (struct a *)u.u_ap; + register struct vnode *tvp, *fvp, *tdvp; + register struct nameidata *ndp = &u.u_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 = 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); + goto out; + } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { + error = ENOTDIR; + goto out; } - 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; - } - if (error == 0) /* conservative */ - error = u.u_error; + if (error) { + VOP_ABORTOP(ndp); + goto out1; } - 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 (fvp->v_mount != tdvp->v_mount) { + error = EXDEV; + goto out; } - goto done; - -bad: - iput(dp); -bad1: - if (xp) - iput(xp); + if (fvp == tdvp || fvp == tvp) + error = EINVAL; 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); - } -#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 - - /* - * Make sure inode goes to disk before directory entry. - */ - 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 (error) { + VOP_ABORTOP(&tond); + VOP_ABORTOP(ndp); + } else { + error = VOP_RENAME(ndp, &tond); } - return (ip); +out1: + ndrele(&tond); + RETURN (error); } -/* - * A virgin directory (no blushing please). - */ -struct dirtemplate mastertemplate = { - 0, 12, 1, ".", - 0, DIRBLKSIZ - 12, 2, ".." -}; - /* * Mkdir system call */ @@ -1091,100 +1143,29 @@ mkdir() 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; - } -#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; + } *uap = (struct a *)u.u_ap; + register struct nameidata *ndp = &u.u_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); } - if (dp) - iput(dp); - iput(ip); + vattr_null(&vattr); + vattr.va_type = VDIR; + vattr.va_mode = (uap->dmode & 0777) &~ u.u_cmask; + error = VOP_MKDIR(ndp, &vattr); + if (!error) + vput(ndp->ni_vp); + RETURN (error); } /* @@ -1194,88 +1175,77 @@ rmdir() { 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 *)u.u_ap; + register struct nameidata *ndp = &u.u_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() { - 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 *)u.u_ap; + struct file *fp; + struct uio auio; + struct iovec aiov; + off_t off; + int error; + + if (error = getvnode(uap->fd, &fp)) + RETURN (error); + if ((fp->f_flag & FREAD) == 0) + RETURN (EBADF); + 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; + off = fp->f_offset; + if (error = VOP_READDIR((struct vnode *)fp->f_data, &auio, + &(fp->f_offset), fp->f_cred)) + RETURN (error); + error = copyout((caddr_t)&off, (caddr_t)uap->basep, + sizeof(long)); + u.u_r.r_val1 = uap->count - auio.uio_resid; + RETURN (error); } /* @@ -1285,9 +1255,23 @@ umask() { register struct a { int mask; - } *uap; + } *uap = (struct a *)u.u_ap; - uap = (struct a *)u.u_ap; u.u_r.r_val1 = u.u_cmask; u.u_cmask = uap->mask & 07777; + RETURN (0); +} + +getvnode(fdes, fpp) + struct file **fpp; + int fdes; +{ + struct file *fp; + + if ((unsigned)fdes >= NOFILE || (fp = u.u_ofile[fdes]) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (EINVAL); + *fpp = fp; + return (0); }