X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/09c429458a747f6a58ffcd910786efb086ce40c8..58c3cad7f8a4347f214f4ce7a05bbba221a07af0:/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 f293a3070f..ee35805762 100644 --- a/usr/src/sys/kern/vfs_syscalls.c +++ b/usr/src/sys/kern/vfs_syscalls.c @@ -1,102 +1,397 @@ -/* vfs_syscalls.c 4.24 82/06/04 */ +/* + * 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.14 (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(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; + struct vnode *vp; + struct mount *mp; + int error; + + /* + * 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 (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(scp) + register struct syscontext *scp; +{ + struct a { + char *pathp; + int flags; + } *uap = (struct a *)scp->sc_ap; + register struct vnode *vp; + register struct mount *mp; + register struct nameidata *ndp = &scp->sc_nd; + struct vnode *coveredvp; + int error; + + /* + * 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); + /* + * Do the unmount. + */ + coveredvp = mp->m_vnodecovered; + if (error = vfs_lock(mp)) + RETURN (error); -#ifdef SIMFS -#include "../h/sysrenam.h" -#endif -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/dir.h" -#include "../h/user.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/inline.h" + xumount(mp); /* remove unused sticky files from text table */ + cache_purgevfs(mp); /* remove cache entries for this file sys */ + VFS_SYNC(mp, MNT_WAIT); -chdir() + 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(scp) + register struct syscontext *scp; +{ + 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(scp) + register struct syscontext *scp; { + struct a { + char *path; + struct statfs *buf; + } *uap = (struct a *)scp->sc_ap; + register struct vnode *vp; + register struct nameidata *ndp = &scp->sc_nd; + struct statfs sb; + int error; - chdirec(&u.u_cdir); + 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); } -chroot() +fstatfs(scp) + register struct syscontext *scp; { + struct a { + int fd; + struct statfs *buf; + } *uap = (struct a *)scp->sc_ap; + struct file *fp; + struct statfs sb; + int error; - if (suser()) - chdirec(&u.u_rdir); + if (error = getvnode(scp->sc_ofile, 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))); } -chdirec(ipp) -register struct inode **ipp; +/* + * get statistics on all filesystems + */ +getfsstat(scp) + register struct syscontext *scp; +{ + struct a { + struct statfs *buf; + long bufsize; + } *uap = (struct a *)scp->sc_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) + 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); + vrele(scp->sc_cdir); + scp->sc_cdir = vp; + RETURN (error); +} + +/* + * Change current working directory (``.''). + */ +chdir(scp) + register struct syscontext *scp; { - register struct inode *ip; struct a { char *fname; - }; - - ip = namei(uchar, 0, 1); - if(ip == NULL) - return; - if((ip->i_mode&IFMT) != IFDIR) { - u.u_error = ENOTDIR; - goto bad; - } - if(access(ip, IEXEC)) - goto bad; - irele(ip); - if (*ipp) { - ilock(*ipp); - iput(*ipp); - } - *ipp = ip; - return; + } *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); +} -bad: - iput(ip); +/* + * 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); + 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; { - register struct inode *ip; - register struct a { + struct a { char *fname; - int rwmode; - } *uap; + int mode; + int crtmode; + } *uap = (struct a *) scp->sc_ap; + struct nameidata *ndp = &scp->sc_nd; - uap = (struct a *)u.u_ap; - ip = namei(uchar, 0, 1); - if (ip == NULL) - return; - open1(ip, ++uap->rwmode, 0); + 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; { - register struct inode *ip; - register struct a { + struct a { char *fname; int fmode; - } *uap; + } *uap = (struct a *)scp->sc_ap; + struct nameidata *ndp = &scp->sc_nd; - uap = (struct a *)u.u_ap; - ip = namei(uchar, 1, 1); - if (ip == NULL) { - if (u.u_error) - return; - ip = maknode(uap->fmode&07777&(~ISVTX)); - if (ip==NULL) - return; - open1(ip, FWRITE, 2); - } else - open1(ip, FWRITE, 1); + 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)); } /* @@ -104,167 +399,175 @@ creat() * Check permissions, allocate an open file structure, * and call the device open routine if any. */ -open1(ip, mode, trf) - register struct inode *ip; - register mode; +copen(scp, fmode, cmode, ndp, resultfd) + register struct syscontext *scp; + int fmode, cmode; + struct nameidata *ndp; + int *resultfd; { register struct file *fp; - int i; - - if (trf != 2) { - if (mode&FREAD) - (void) access(ip, IREAD); - if (mode&FWRITE) { - (void) access(ip, IWRITE); - if ((ip->i_mode&IFMT) == IFDIR) - u.u_error = EISDIR; - } + 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) &~ ISVTX)) { + scp->sc_ofile[indx] = NULL; + crfree(fp->f_cred); + fp->f_count--; + return (error); } - if (u.u_error) - goto out; - if (trf == 1) - itrunc(ip); - irele(ip); - if ((fp = falloc()) == NULL) - goto out; - fp->f_flag = mode&(FREAD|FWRITE); - i = u.u_r.r_val1; - fp->f_inode = ip; - openi(ip, mode&(FREAD|FWRITE)); - if (u.u_error == 0) - return; - u.u_ofile[i] = NULL; - fp->f_count--; -out: - if (ip != NULL) - 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 *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; - uap = (struct a *)u.u_ap; - if (suser()) { - ip = namei(uchar, 1, 0); - if (ip != NULL) { - u.u_error = EEXIST; - goto out; - } - } - if (u.u_error) - return; - ip = maknode(uap->fmode); - if (ip == NULL) - return; - 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; + 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; } + 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: + 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); } /* * 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 *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp, *xp; + int error; - uap = (struct a *)u.u_ap; - ip = namei(uchar, 0, 1); /* well, this routine is doomed anyhow */ - if (ip == NULL) - return; - if ((ip->i_mode&IFMT)==IFDIR && !suser()) + 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; - ip->i_nlink++; - ip->i_flag |= ICHG; - iupdat(ip, &time, &time, 1); - irele(ip); - u.u_dirp = (caddr_t)uap->linkname; - xp = namei(uchar, 1, 0); + 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; - } - wdir(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; - } + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_LINK(vp, ndp); out1: - iput(ip); + 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, 1, 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_base = uap->target; - u.u_count = nc; - u.u_offset = 0; - u.u_segflg = 0; - writei(ip); - 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); } /* @@ -272,328 +575,741 @@ symlink() * Hard to avoid races here, especially * in unlinking directories. */ -unlink() +unlink(scp) + register struct syscontext *scp; { - register struct inode *ip, *pp; struct a { char *fname; - }; - struct fs *fs; - struct buf *bp; - int lbn, bn, base; - - pp = namei(uchar, 2, 0); - if(pp == NULL) - return; - /* - * Check for unlink(".") - * to avoid hanging on the iget - */ - if (pp->i_number == u.u_dent.d_ino) { - ip = pp; - ip->i_count++; - } else - ip = iget(pp->i_dev, pp->i_fs, u.u_dent.d_ino); - if(ip == NULL) - goto out1; - 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 != pp->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 ((ip->i_flag&ITEXT) && ip->i_nlink==1) { - u.u_error = ETXTBSY; - goto out; - } -*/ - if (u.u_count == 0) { - /* - * first entry in block, so set d_ino to zero. - */ - u.u_base = (caddr_t)&u.u_dent; - u.u_count = DIRSIZ(&u.u_dent); - u.u_dent.d_ino = 0; - writei(pp); - } else { - /* - * updating preceeding entry to skip over current entry. - */ - fs = pp->i_fs; - lbn = lblkno(fs, u.u_offset); - base = blkoff(fs, u.u_offset); - bn = fsbtodb(fs, bmap(pp, lbn, B_WRITE, base + u.u_count)); - bp = bread(pp->i_dev, bn, blksize(fs, pp, lbn)); - if (bp->b_flags & B_ERROR) { - brelse(bp); - goto out; - } - ((struct direct *)(bp->b_un.b_addr + base))->d_reclen += - u.u_dent.d_reclen; - bwrite(bp); - pp->i_flag |= IUPD|ICHG; - } - ip->i_nlink--; - ip->i_flag |= ICHG; - + if (vp->v_flag & VTEXT) + xrele(vp); /* try once to free text */ out: - iput(ip); -out1: - iput(pp); + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_REMOVE(ndp); + RETURN (error); } /* * Seek system call */ -seek() +lseek(scp) + register struct syscontext *scp; { register struct file *fp; register struct a { int fdes; off_t off; int sbase; - } *uap; - - uap = (struct a *)u.u_ap; - fp = getf(uap->fdes); - if (fp == NULL) - return; - if (fp->f_flag&FSOCKET) { - u.u_error = ESPIPE; - return; + } *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); } - if (uap->sbase == 1) - uap->off += fp->f_offset; - else if (uap->sbase == 2) - uap->off += fp->f_inode->i_size; - fp->f_offset = uap->off; - u.u_r.r_off = uap->off; + 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, 0, 1); - if (ip != NULL) { - if (uap->fmode&(IREAD>>6)) - (void) access(ip, IREAD); - if (uap->fmode&(IWRITE>>6)) - (void) access(ip, IWRITE); - if (uap->fmode&(IEXEC>>6)) - (void) access(ip, IEXEC); - 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 ((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); } /* - * the fstat system call. + * Stat system call. This version follows links. */ -fstat() +stat(scp) + struct syscontext *scp; { - register struct file *fp; - register struct a { - int fdes; - struct stat *sb; - } *uap; - - uap = (struct a *)u.u_ap; - fp = getf(uap->fdes); - if (fp == NULL) - return; - if (fp->f_flag & FSOCKET) - u.u_error = sostat(fp->f_socket, uap->sb); - else - stat1(fp->f_inode, uap->sb); + + stat1(scp, FOLLOW); } /* - * Stat system call. This version follows links. + * Lstat system call. This version does not follow links. */ -stat() +lstat(scp) + struct syscontext *scp; +{ + + stat1(scp, NOFOLLOW); +} + +stat1(scp, follow) + register struct syscontext *scp; + int follow; { - register struct inode *ip; register struct a { char *fname; - struct stat *sb; - } *uap; + struct stat *ub; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + struct stat sb; + int error; - uap = (struct a *)u.u_ap; - ip = namei(uchar, 0, 1); - if (ip == NULL) - return; - stat1(ip, uap->sb); - iput(ip); + 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); } /* - * Lstat system call. This version does not follow links. + * Return target name of a symbolic link */ -lstat() +readlink(scp) + register struct syscontext *scp; { - register struct inode *ip; register struct a { - char *fname; - struct stat *sb; - } *uap; + char *name; + char *buf; + int count; + } *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; - uap = (struct a *)u.u_ap; - ip = namei(uchar, 0, 0); - if (ip == NULL) - return; - stat1(ip, uap->sb); - iput(ip); + 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); + scp->sc_retval1 = uap->count - auio.uio_resid; + RETURN (error); } /* - * The basic routine for fstat and stat: - * get the inode and pass appropriate parts back. + * Change flags of a file given path name. */ -stat1(ip, ub) - register struct inode *ip; - struct stat *ub; +chflags(scp) + register struct syscontext *scp; { - struct stat ds; + struct a { + char *fname; + int flags; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; - IUPDAT(ip, &time, &time, 0); - /* - * Copy from inode table - */ - ds.st_dev = ip->i_dev; - ds.st_ino = ip->i_number; - ds.st_mode = ip->i_mode; - ds.st_nlink = ip->i_nlink; - ds.st_uid = ip->i_uid; - ds.st_gid = ip->i_gid; - ds.st_rdev = (dev_t)ip->i_rdev; - ds.st_size = ip->i_size; - ds.st_atime = ip->i_atime; - ds.st_mtime = ip->i_mtime; - ds.st_ctime = ip->i_ctime; - ds.st_blksize = ip->i_fs->fs_bsize; - if (copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)) < 0) - u.u_error = EFAULT; + 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); } /* - * Return target name of a symbolic link + * Change flags of a file given a file descriptor. */ -readlink() +fchflags(scp) + register struct syscontext *scp; { - register struct inode *ip; - register struct a { - char *name; - char *buf; - int count; - } *uap; + struct a { + int fd; + int flags; + } *uap = (struct a *)scp->sc_ap; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; - ip = namei(uchar, 0, 0); - if (ip == NULL) - return; - if ((ip->i_mode&IFMT) != IFLNK) { - u.u_error = ENXIO; + 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; } - uap = (struct a *)u.u_ap; - u.u_offset = 0; - u.u_base = uap->buf; - u.u_count = uap->count; - u.u_segflg = 0; - readi(ip); + error = VOP_SETATTR(vp, &vattr, fp->f_cred); out: - iput(ip); - u.u_r.r_val1 = uap->count - u.u_count; + VOP_UNLOCK(vp); + RETURN (error); } -chmod() +/* + * Change mode of a file given path name. + */ +chmod(scp) + register struct syscontext *scp; { - register struct inode *ip; - register struct a { + struct a { char *fname; int fmode; - } *uap; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; - uap = (struct a *)u.u_ap; - if ((ip = owner(1)) == NULL) - return; - ip->i_mode &= ~07777; - if (u.u_uid) - uap->fmode &= ~ISVTX; - ip->i_mode |= uap->fmode&07777; - ip->i_flag |= ICHG; - if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0) - xrele(ip); - iput(ip); + 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); } -chown() +/* + * Change mode of a file given a file descriptor. + */ +fchmod(scp) + register struct syscontext *scp; { - register struct inode *ip; - register struct a { + 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; + } + error = VOP_SETATTR(vp, &vattr, fp->f_cred); +out: + VOP_UNLOCK(vp); + RETURN (error); +} + +/* + * Set ownership given a path name. + */ +chown(scp) + register struct syscontext *scp; +{ + struct a { char *fname; int uid; int gid; - } *uap; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct vattr vattr; + int error; - uap = (struct a *)u.u_ap; - if (!suser() || (ip = owner(0)) == NULL) - return; - ip->i_uid = uap->uid; - ip->i_gid = uap->gid; - ip->i_flag |= ICHG; - if (u.u_ruid != 0) - ip->i_mode &= ~(ISUID|ISGID); - iput(ip); + 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 IUPD and IACC times on file. - * Can't set ICHG. + * Set ownership given a file descriptor. */ -utime() +fchown(scp) + register struct syscontext *scp; +{ + struct a { + int fd; + int uid; + int gid; + } *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(scp) + register struct syscontext *scp; { register struct a { char *fname; - time_t *tptr; - } *uap; - register struct inode *ip; - time_t tv[2]; - - uap = (struct a *)u.u_ap; - if ((ip = owner(1)) == NULL) - return; - if (copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof(tv))) { - u.u_error = EFAULT; + struct timeval *tptr; + } *uap = (struct a *)scp->sc_ap; + register struct nameidata *ndp = &scp->sc_nd; + register struct vnode *vp; + struct timeval tv[2]; + 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; + } + error = VOP_SETATTR(vp, &vattr, ndp->ni_cred); +out: + vput(vp); + RETURN (error); +} + +/* + * Truncate a file given its path name. + */ +truncate(scp) + register struct syscontext *scp; +{ + struct a { + char *fname; + 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; + } + 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(scp) + register struct syscontext *scp; +{ + struct a { + int fd; + off_t length; + } *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); + 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; + } + 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(scp) + register struct syscontext *scp; +{ + struct a { + int fd; + } *uap = (struct a *)scp->sc_ap; + struct file *fp; + int error; + + if (error = getvnode(scp->sc_ofile, uap->fd, &fp)) + RETURN (error); + error = VOP_FSYNC((struct vnode *)fp->f_data, fp->f_flag, fp->f_cred); + RETURN (error); +} + +/* + * Rename system call. + * + * Source and destination must either both be directories, or both + * not be directories. If target is a directory, it must be empty. + */ +rename(scp) + register struct syscontext *scp; +{ + struct a { + char *from; + char *to; + } *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 = EISDIR; + goto out; + } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { + error = ENOTDIR; + goto out; + } + } + if (error) { + VOP_ABORTOP(ndp); + goto out1; + } + if (fvp->v_mount != tdvp->v_mount) { + error = EXDEV; + goto out; + } + if (fvp == tdvp || fvp == tvp) + error = EINVAL; +out: + if (error) { + VOP_ABORTOP(&tond); + VOP_ABORTOP(ndp); } else { - ip->i_flag |= IACC|IUPD|ICHG; - iupdat(ip, &tv[0], &tv[1], 0); + error = VOP_RENAME(ndp, &tond); } - iput(ip); +out1: + ndrele(&tond); + RETURN (error); +} + +/* + * Mkdir system call + */ +mkdir(scp) + register struct syscontext *scp; +{ + struct a { + char *name; + int dmode; + } *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); + } + 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(scp) + register struct syscontext *scp; +{ + struct a { + char *name; + } *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; + } + /* + * No rmdir "." please. + */ + if (ndp->ni_dvp == vp) { + error = EINVAL; + goto out; + } + /* + * Don't unlink a mounted file. + */ + if (vp->v_flag & VROOT) + error = EBUSY; +out: + if (error) + VOP_ABORTOP(ndp); + else + error = VOP_RMDIR(ndp); + RETURN (error); +} + +/* + * Read a block of directory entries in a file system independent format + */ +getdirentries(scp) + register struct syscontext *scp; +{ + register struct a { + int fd; + char *buf; + unsigned count; + long *basep; + } *uap = (struct a *)scp->sc_ap; + struct file *fp; + struct uio auio; + struct iovec aiov; + off_t off; + int error; + + if (error = getvnode(scp->sc_ofile, 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)); + scp->sc_retval1 = uap->count - auio.uio_resid; + RETURN (error); +} + +/* + * mode mask for creation of files + */ +umask(scp) + register struct syscontext *scp; +{ + register struct a { + int mask; + } *uap = (struct a *)scp->sc_ap; + + scp->sc_retval1 = scp->sc_cmask; + scp->sc_cmask = uap->mask & 07777; + RETURN (0); } -sync() +getvnode(ofile, fdes, fpp) + struct file *ofile[]; + struct file **fpp; + int fdes; { + struct file *fp; - update(0); + if ((unsigned)fdes >= NOFILE || (fp = ofile[fdes]) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (EINVAL); + *fpp = fp; + return (0); }