X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/5e03b55dcb52583818730cc2f1aa8a6e352e4c1f..ad7871609881e73855d0b04da49b486cd93efca7:/usr/src/sys/ufs/ufs/ufs_vnops.c diff --git a/usr/src/sys/ufs/ufs/ufs_vnops.c b/usr/src/sys/ufs/ufs/ufs_vnops.c index 7318807802..71a395e843 100644 --- a/usr/src/sys/ufs/ufs/ufs_vnops.c +++ b/usr/src/sys/ufs/ufs/ufs_vnops.c @@ -1,48 +1,103 @@ /* - * Copyright (c) 1982, 1986, 1989 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. * - * %sccs.include.redist.c% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * - * @(#)ufs_vnops.c 7.64.1.1 (Berkeley) %G% + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ufs_vnops.c 8.1 (Berkeley) 6/11/93 */ -#include "param.h" -#include "systm.h" -#include "namei.h" -#include "resourcevar.h" -#include "kernel.h" -#include "file.h" -#include "stat.h" -#include "buf.h" -#include "proc.h" -#include "conf.h" -#include "mount.h" -#include "vnode.h" -#include "specdev.h" -#include "fifo.h" -#include "malloc.h" - -#include "lockf.h" -#include "quota.h" -#include "inode.h" -#include "dir.h" -#include "fs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +static int ufs_chmod __P((struct vnode *, int, struct ucred *, struct proc *)); +static int ufs_chown + __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *)); + +union _qcvt { + quad_t qcvt; + long val[2]; +}; +#define SETHIGH(q, h) { \ + union _qcvt tmp; \ + tmp.qcvt = (q); \ + tmp.val[_QUAD_HIGHWORD] = (h); \ + (q) = tmp.qcvt; \ +} +#define SETLOW(q, l) { \ + union _qcvt tmp; \ + tmp.qcvt = (q); \ + tmp.val[_QUAD_LOWWORD] = (l); \ + (q) = tmp.qcvt; \ +} /* * Create a regular file */ -ufs_create(ndp, vap, p) - struct nameidata *ndp; - struct vattr *vap; - struct proc *p; +int +ufs_create(ap) + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; { - struct inode *ip; int error; - if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) + if (error = + ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode), + ap->a_dvp, ap->a_vpp, ap->a_cnp)) return (error); - ndp->ni_vp = ITOV(ip); return (0); } @@ -50,18 +105,25 @@ ufs_create(ndp, vap, p) * Mknod vnode call */ /* ARGSUSED */ -ufs_mknod(ndp, vap, cred, p) - struct nameidata *ndp; - struct ucred *cred; - struct vattr *vap; - struct proc *p; +int +ufs_mknod(ap) + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; { - register struct vnode *vp; - struct inode *ip; + register struct vattr *vap = ap->a_vap; + register struct vnode **vpp = ap->a_vpp; + register struct inode *ip; int error; - if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) + if (error = + ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode), + ap->a_dvp, vpp, ap->a_cnp)) return (error); + ip = VTOI(*vpp); ip->i_flag |= IACC|IUPD|ICHG; if (vap->va_rdev != VNOVAL) { /* @@ -75,10 +137,10 @@ ufs_mknod(ndp, vap, cred, p) * checked to see if it is an alias of an existing entry * in the inode cache. */ - vp = ITOV(ip); - vput(vp); - vp->v_type = VNON; - vgone(vp); + vput(*vpp); + (*vpp)->v_type = VNON; + vgone(*vpp); + *vpp = 0; return (0); } @@ -88,13 +150,22 @@ ufs_mknod(ndp, vap, cred, p) * Nothing to do. */ /* ARGSUSED */ -ufs_open(vp, mode, cred, p) - struct vnode *vp; - int mode; - struct ucred *cred; - struct proc *p; +int +ufs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { + /* + * Files marked append-only must be opened for appending. + */ + if ((VTOI(ap->a_vp)->i_flags & APPEND) && + (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) + return (EPERM); return (0); } @@ -104,12 +175,16 @@ ufs_open(vp, mode, cred, p) * Update the times on the inode. */ /* ARGSUSED */ -ufs_close(vp, fflag, cred, p) - struct vnode *vp; - int fflag; - struct ucred *cred; - struct proc *p; +int +ufs_close(ap) + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { + register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) @@ -122,13 +197,19 @@ ufs_close(vp, fflag, cred, p) * The mode is shifted to select the owner/group/other fields. The * super user is granted all permissions. */ -ufs_access(vp, mode, cred, p) - struct vnode *vp; - register int mode; - struct ucred *cred; - struct proc *p; +int +ufs_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { + register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); + register struct ucred *cred = ap->a_cred; + mode_t mode = ap->a_mode; register gid_t *gp; int i, error; @@ -147,6 +228,8 @@ ufs_access(vp, mode, cred, p) } } #endif /* QUOTA */ + if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) + return (EPERM); /* * If you're the super-user, you always get access. */ @@ -167,19 +250,22 @@ ufs_access(vp, mode, cred, p) found: ; } - if ((ip->i_mode & mode) != 0) - return (0); - return (EACCES); + return ((ip->i_mode & mode) == mode ? 0 : EACCES); } /* ARGSUSED */ -ufs_getattr(vp, vap, cred, p) - struct vnode *vp; - register struct vattr *vap; - struct ucred *cred; - struct proc *p; +int +ufs_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { + register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); + register struct vattr *vap = ap->a_vap; ITIMES(ip, &time, &time); /* @@ -192,18 +278,10 @@ ufs_getattr(vp, vap, cred, p) vap->va_uid = ip->i_uid; vap->va_gid = ip->i_gid; vap->va_rdev = (dev_t)ip->i_rdev; -#ifdef tahoe - vap->va_size = ip->i_size; - vap->va_size_rsv = 0; -#else - vap->va_qsize = ip->i_din.di_qsize; -#endif - vap->va_atime.tv_sec = ip->i_atime; - vap->va_atime.tv_usec = 0; - vap->va_mtime.tv_sec = ip->i_mtime; - vap->va_mtime.tv_usec = 0; - vap->va_ctime.tv_sec = ip->i_ctime; - vap->va_ctime.tv_usec = 0; + vap->va_size = ip->i_din.di_size; + vap->va_atime = ip->i_atime; + vap->va_mtime = ip->i_mtime; + vap->va_ctime = ip->i_ctime; vap->va_flags = ip->i_flags; vap->va_gen = ip->i_gen; /* this doesn't belong here */ @@ -212,27 +290,35 @@ ufs_getattr(vp, vap, cred, p) else if (vp->v_type == VCHR) vap->va_blocksize = MAXBSIZE; else - vap->va_blocksize = ip->i_fs->fs_bsize; + vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; vap->va_bytes = dbtob(ip->i_blocks); - vap->va_bytes_rsv = 0; vap->va_type = vp->v_type; + vap->va_filerev = ip->i_modrev; return (0); } /* * Set attribute vnode op. called from several syscalls */ -ufs_setattr(vp, vap, cred, p) - register struct vnode *vp; - register struct vattr *vap; - register struct ucred *cred; - struct proc *p; +int +ufs_setattr(ap) + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { + register struct vattr *vap = ap->a_vap; + register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); - int error = 0; + register struct ucred *cred = ap->a_cred; + register struct proc *p = ap->a_p; + struct timeval atimeval, mtimeval; + int error; /* - * Check for unsetable attributes. + * Check for unsettable attributes. */ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || @@ -240,44 +326,60 @@ ufs_setattr(vp, vap, cred, p) ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { return (EINVAL); } + if (vap->va_flags != VNOVAL) { + if (cred->cr_uid != ip->i_uid && + (error = suser(cred, &p->p_acflag))) + return (error); + if (cred->cr_uid == 0) { + if ((ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) && + securelevel > 0) + return (EPERM); + ip->i_flags = vap->va_flags; + } else { + if (ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) + return (EPERM); + ip->i_flags &= SF_SETTABLE; + ip->i_flags |= (vap->va_flags & UF_SETTABLE); + } + ip->i_flag |= ICHG; + if (vap->va_flags & (IMMUTABLE | APPEND)) + return (0); + } + if (ip->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); /* * Go through the fields and update iff not VNOVAL. */ - if (vap->va_uid != (u_short)VNOVAL || vap->va_gid != (u_short)VNOVAL) - if (error = chown1(vp, vap->va_uid, vap->va_gid, p)) + if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) + if (error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred, p)) return (error); if (vap->va_size != VNOVAL) { if (vp->v_type == VDIR) return (EISDIR); - if (error = itrunc(ip, vap->va_size, 0)) /* XXX IO_SYNC? */ + if (error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p)) return (error); } - if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { + ip = VTOI(vp); + if (vap->va_atime.ts_sec != VNOVAL || vap->va_mtime.ts_sec != VNOVAL) { if (cred->cr_uid != ip->i_uid && - (error = suser(cred, &p->p_acflag))) + (error = suser(cred, &p->p_acflag)) && + ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || + (error = VOP_ACCESS(vp, VWRITE, cred, p)))) return (error); - if (vap->va_atime.tv_sec != VNOVAL) + if (vap->va_atime.ts_sec != VNOVAL) ip->i_flag |= IACC; - if (vap->va_mtime.tv_sec != VNOVAL) - ip->i_flag |= IUPD; - ip->i_flag |= ICHG; - if (error = iupdat(ip, &vap->va_atime, &vap->va_mtime, 1)) + if (vap->va_mtime.ts_sec != VNOVAL) + ip->i_flag |= IUPD | ICHG; + atimeval.tv_sec = vap->va_atime.ts_sec; + atimeval.tv_usec = vap->va_atime.ts_nsec / 1000; + mtimeval.tv_sec = vap->va_mtime.ts_sec; + mtimeval.tv_usec = vap->va_mtime.ts_nsec / 1000; + if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1)) return (error); } - if (vap->va_mode != (u_short)VNOVAL) - error = chmod1(vp, (int)vap->va_mode, p); - if (vap->va_flags != VNOVAL) { - if (cred->cr_uid != ip->i_uid && - (error = suser(cred, &p->p_acflag))) - return (error); - if (cred->cr_uid == 0) { - ip->i_flags = vap->va_flags; - } else { - ip->i_flags &= 0xffff0000; - ip->i_flags |= (vap->va_flags & 0xffff); - } - ip->i_flag |= ICHG; - } + error = 0; + if (vap->va_mode != (mode_t)VNOVAL) + error = ufs_chmod(vp, (int)vap->va_mode, cred, p); return (error); } @@ -285,12 +387,13 @@ ufs_setattr(vp, vap, cred, p) * Change the mode on a file. * Inode must be locked before calling. */ -chmod1(vp, mode, p) +static int +ufs_chmod(vp, mode, cred, p) register struct vnode *vp; register int mode; + register struct ucred *cred; struct proc *p; { - register struct ucred *cred = p->p_ucred; register struct inode *ip = VTOI(vp); int error; @@ -315,14 +418,15 @@ chmod1(vp, mode, p) * Perform chown operation on inode ip; * inode must be locked prior to call. */ -chown1(vp, uid, gid, p) +static int +ufs_chown(vp, uid, gid, cred, p) register struct vnode *vp; uid_t uid; gid_t gid; + struct ucred *cred; struct proc *p; { register struct inode *ip = VTOI(vp); - register struct ucred *cred = p->p_ucred; uid_t ouid; gid_t ogid; int error = 0; @@ -331,9 +435,9 @@ chown1(vp, uid, gid, p) long change; #endif - if (uid == (u_short)VNOVAL) + if (uid == (uid_t)VNOVAL) uid = ip->i_uid; - if (gid == (u_short)VNOVAL) + if (gid == (gid_t)VNOVAL) gid = ip->i_gid; /* * If we don't own the file, are trying to change the owner @@ -417,184 +521,32 @@ good: return (0); } -/* - * Vnode op for reading. - */ -/* ARGSUSED */ -ufs_read(vp, uio, ioflag, cred) - struct vnode *vp; - register struct uio *uio; - int ioflag; - struct ucred *cred; -{ - register struct inode *ip = VTOI(vp); - register struct fs *fs; - struct buf *bp; - daddr_t lbn, bn, rablock; - int size, diff, error = 0; - long n, on, type; - -#ifdef DIAGNOSTIC - if (uio->uio_rw != UIO_READ) - panic("ufs_read mode"); - type = ip->i_mode & IFMT; - if (type != IFDIR && type != IFREG && type != IFLNK) - panic("ufs_read type"); -#endif - if (uio->uio_resid == 0) - return (0); - if (uio->uio_offset < 0) - return (EINVAL); - ip->i_flag |= IACC; - fs = ip->i_fs; - do { - lbn = lblkno(fs, uio->uio_offset); - on = blkoff(fs, uio->uio_offset); - n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); - diff = ip->i_size - uio->uio_offset; - if (diff <= 0) - return (0); - if (diff < n) - n = diff; - size = blksize(fs, ip, lbn); - rablock = lbn + 1; - if (vp->v_lastr + 1 == lbn && - lblktosize(fs, rablock) < ip->i_size) - error = breada(ITOV(ip), lbn, size, rablock, - blksize(fs, ip, rablock), NOCRED, &bp); - else - error = bread(ITOV(ip), lbn, size, NOCRED, &bp); - vp->v_lastr = lbn; - n = MIN(n, size - bp->b_resid); - if (error) { - brelse(bp); - return (error); - } - error = uiomove(bp->b_un.b_addr + on, (int)n, uio); - if (n + on == fs->fs_bsize || uio->uio_offset == ip->i_size) - bp->b_flags |= B_AGE; - brelse(bp); - } while (error == 0 && uio->uio_resid > 0 && n != 0); - return (error); -} - -/* - * Vnode op for writing. - */ -ufs_write(vp, uio, ioflag, cred) - register struct vnode *vp; - struct uio *uio; - int ioflag; - struct ucred *cred; -{ - struct proc *p = uio->uio_procp; - register struct inode *ip = VTOI(vp); - register struct fs *fs; - struct buf *bp; - daddr_t lbn, bn; - u_long osize; - int n, on, flags; - int size, resid, error = 0; - -#ifdef DIAGNOSTIC - if (uio->uio_rw != UIO_WRITE) - panic("ufs_write mode"); -#endif - switch (vp->v_type) { - case VREG: - if (ioflag & IO_APPEND) - uio->uio_offset = ip->i_size; - /* fall through */ - case VLNK: - break; - - case VDIR: - if ((ioflag & IO_SYNC) == 0) - panic("ufs_write nonsync dir write"); - break; - - default: - panic("ufs_write type"); - } - if (uio->uio_offset < 0) - return (EINVAL); - if (uio->uio_resid == 0) - return (0); - /* - * Maybe this should be above the vnode op call, but so long as - * file servers have no limits, i don't think it matters - */ - if (vp->v_type == VREG && p && - uio->uio_offset + uio->uio_resid > - p->p_rlimit[RLIMIT_FSIZE].rlim_cur) { - psignal(p, SIGXFSZ); - return (EFBIG); - } - resid = uio->uio_resid; - osize = ip->i_size; - fs = ip->i_fs; - flags = 0; - if (ioflag & IO_SYNC) - flags = B_SYNC; - do { - lbn = lblkno(fs, uio->uio_offset); - on = blkoff(fs, uio->uio_offset); - n = MIN((unsigned)(fs->fs_bsize - on), uio->uio_resid); - if (n < fs->fs_bsize) - flags |= B_CLRBUF; - else - flags &= ~B_CLRBUF; - if (error = balloc(ip, lbn, (int)(on + n), &bp, flags)) - break; - bn = bp->b_blkno; - if (uio->uio_offset + n > ip->i_size) { - ip->i_size = uio->uio_offset + n; - vnode_pager_setsize(vp, ip->i_size); - } - size = blksize(fs, ip, lbn); - (void) vnode_pager_uncache(vp); - n = MIN(n, size - bp->b_resid); - error = uiomove(bp->b_un.b_addr + on, n, uio); - if (ioflag & IO_SYNC) - (void) bwrite(bp); - else if (n + on == fs->fs_bsize) { - bp->b_flags |= B_AGE; - bawrite(bp); - } else - bdwrite(bp); - ip->i_flag |= IUPD|ICHG; - if (cred->cr_uid != 0) - ip->i_mode &= ~(ISUID|ISGID); - } while (error == 0 && uio->uio_resid > 0 && n != 0); - if (error && (ioflag & IO_UNIT)) { - (void) itrunc(ip, osize, ioflag & IO_SYNC); - uio->uio_offset -= resid - uio->uio_resid; - uio->uio_resid = resid; - } - if (!error && (ioflag & IO_SYNC)) - error = iupdat(ip, &time, &time, 1); - return (error); -} - /* ARGSUSED */ -ufs_ioctl(vp, com, data, fflag, cred, p) - struct vnode *vp; - int com; - caddr_t data; - int fflag; - struct ucred *cred; - struct proc *p; +int +ufs_ioctl(ap) + struct vop_ioctl_args /* { + struct vnode *a_vp; + int a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { return (ENOTTY); } /* ARGSUSED */ -ufs_select(vp, which, fflags, cred, p) - struct vnode *vp; - int which, fflags; - struct ucred *cred; - struct proc *p; +int +ufs_select(ap) + struct vop_select_args /* { + struct vnode *a_vp; + int a_which; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { /* @@ -609,45 +561,33 @@ ufs_select(vp, which, fflags, cred, p) * NB Currently unsupported. */ /* ARGSUSED */ -ufs_mmap(vp, fflags, cred, p) - struct vnode *vp; - int fflags; - struct ucred *cred; - struct proc *p; +int +ufs_mmap(ap) + struct vop_mmap_args /* { + struct vnode *a_vp; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { return (EINVAL); } -/* - * Synch an open file. - */ -/* ARGSUSED */ -ufs_fsync(vp, fflags, cred, waitfor, p) - struct vnode *vp; - int fflags; - struct ucred *cred; - int waitfor; - struct proc *p; -{ - struct inode *ip = VTOI(vp); - - if (fflags & FWRITE) - ip->i_flag |= ICHG; - vflushbuf(vp, waitfor == MNT_WAIT ? B_SYNC : 0); - return (iupdat(ip, &time, &time, waitfor == MNT_WAIT)); -} - /* * Seek on a file * * Nothing to do, so just return. */ /* ARGSUSED */ -ufs_seek(vp, oldoff, newoff, cred) - struct vnode *vp; - off_t oldoff, newoff; - struct ucred *cred; +int +ufs_seek(ap) + struct vop_seek_args /* { + struct vnode *a_vp; + off_t a_oldoff; + off_t a_newoff; + struct ucred *a_cred; + } */ *ap; { return (0); @@ -658,65 +598,255 @@ ufs_seek(vp, oldoff, newoff, cred) * Hard to avoid races here, especially * in unlinking directories. */ -ufs_remove(ndp, p) - struct nameidata *ndp; - struct proc *p; +int +ufs_remove(ap) + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; { - register struct inode *ip, *dp; + register struct inode *ip; + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; int error; - ip = VTOI(ndp->ni_vp); - dp = VTOI(ndp->ni_dvp); - error = dirremove(ndp); - if (!error) { + ip = VTOI(vp); + if ((ip->i_flags & (IMMUTABLE | APPEND)) || + (VTOI(dvp)->i_flags & APPEND)) { + error = EPERM; + goto out; + } + if ((error = ufs_dirremove(dvp, ap->a_cnp)) == 0) { ip->i_nlink--; ip->i_flag |= ICHG; } - if (dp == ip) - vrele(ITOV(ip)); +out: + if (dvp == vp) + vrele(vp); else - iput(ip); - iput(dp); + vput(vp); + vput(dvp); return (error); } /* * link vnode call */ -ufs_link(vp, ndp, p) - register struct vnode *vp; - register struct nameidata *ndp; - struct proc *p; +int +ufs_link(ap) + struct vop_link_args /* { + struct vnode *a_vp; + struct vnode *a_tdvp; + struct componentname *a_cnp; + } */ *ap; { - register struct inode *ip = VTOI(vp); + register struct vnode *vp = ap->a_vp; + register struct vnode *tdvp = ap->a_tdvp; + register struct componentname *cnp = ap->a_cnp; + register struct inode *ip; + struct timeval tv; int error; -#ifdef DIANOSTIC - if ((ndp->ni_nameiop & HASBUF) == 0) +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_link: no name"); #endif - if ((unsigned short)ip->i_nlink >= LINK_MAX) { - free(ndp->ni_pnbuf, M_NAMEI); - return (EMLINK); + if (vp->v_mount != tdvp->v_mount) { + VOP_ABORTOP(vp, cnp); + error = EXDEV; + goto out2; + } + if (vp != tdvp && (error = VOP_LOCK(tdvp))) { + VOP_ABORTOP(vp, cnp); + goto out2; + } + ip = VTOI(tdvp); + if ((nlink_t)ip->i_nlink >= LINK_MAX) { + VOP_ABORTOP(vp, cnp); + error = EMLINK; + goto out1; + } + if (ip->i_flags & (IMMUTABLE | APPEND)) { + VOP_ABORTOP(vp, cnp); + error = EPERM; + goto out1; } - if (ndp->ni_dvp != vp) - ILOCK(ip); ip->i_nlink++; ip->i_flag |= ICHG; - error = iupdat(ip, &time, &time, 1); + tv = time; + error = VOP_UPDATE(tdvp, &tv, &tv, 1); if (!error) - error = direnter(ip, ndp); - if (ndp->ni_dvp != vp) - IUNLOCK(ip); - FREE(ndp->ni_pnbuf, M_NAMEI); - vput(ndp->ni_dvp); + error = ufs_direnter(ip, vp, cnp); if (error) { ip->i_nlink--; ip->i_flag |= ICHG; } + FREE(cnp->cn_pnbuf, M_NAMEI); +out1: + if (vp != tdvp) + VOP_UNLOCK(tdvp); +out2: + vput(vp); + return (error); +} + + + +/* + * relookup - lookup a path name component + * Used by lookup to re-aquire things. + */ +int +relookup(dvp, vpp, cnp) + struct vnode *dvp, **vpp; + struct componentname *cnp; +{ + register struct vnode *dp = 0; /* the directory we are searching */ + struct vnode *tdp; /* saved dp */ + struct mount *mp; /* mount table entry */ + int docache; /* == 0 do not cache last component */ + int wantparent; /* 1 => wantparent or lockparent flag */ + int rdonly; /* lookup read-only flag bit */ + char *cp; /* DEBUG: check name ptr/len */ + int newhash; /* DEBUG: check name hash */ + int error = 0; + + /* + * Setup: break out flag bits into variables. + */ + wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT); + docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE; + if (cnp->cn_nameiop == DELETE || + (wantparent && cnp->cn_nameiop != CREATE)) + docache = 0; + rdonly = cnp->cn_flags & RDONLY; + cnp->cn_flags &= ~ISSYMLINK; + dp = dvp; + VOP_LOCK(dp); + +/* dirloop: */ + /* + * Search a new directory. + * + * The cn_hash value is for use by vfs_cache. + * The last component of the filename is left accessible via + * cnp->cn_nameptr for callers that need the name. Callers needing + * the name set the SAVENAME flag. When done, they assume + * responsibility for freeing the pathname buffer. + */ +#ifdef NAMEI_DIAGNOSTIC + for (newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++) + newhash += (unsigned char)*cp; + if (newhash != cnp->cn_hash) + panic("relookup: bad hash"); + if (cnp->cn_namelen != cp - cnp->cn_nameptr) + panic ("relookup: bad len"); + if (*cp != 0) + panic("relookup: not last component"); + printf("{%s}: ", cnp->cn_nameptr); +#endif + + /* + * Check for degenerate name (e.g. / or "") + * which is a way of talking about a directory, + * e.g. like "/." or ".". + */ + if (cnp->cn_nameptr[0] == '\0') { + if (cnp->cn_nameiop != LOOKUP || wantparent) { + error = EISDIR; + goto bad; + } + if (dp->v_type != VDIR) { + error = ENOTDIR; + goto bad; + } + if (!(cnp->cn_flags & LOCKLEAF)) + VOP_UNLOCK(dp); + *vpp = dp; + if (cnp->cn_flags & SAVESTART) + panic("lookup: SAVESTART"); + return (0); + } + + if (cnp->cn_flags & ISDOTDOT) + panic ("relookup: lookup on dot-dot"); + + /* + * We now have a segment name to search for, and a directory to search. + */ + if (error = VOP_LOOKUP(dp, vpp, cnp)) { +#ifdef DIAGNOSTIC + if (*vpp != NULL) + panic("leaf should be empty"); +#endif + if (error != EJUSTRETURN) + goto bad; + /* + * If creating and at end of pathname, then can consider + * allowing file to be created. + */ + if (rdonly || (dvp->v_mount->mnt_flag & MNT_RDONLY)) { + error = EROFS; + goto bad; + } + /* ASSERT(dvp == ndp->ni_startdir) */ + if (cnp->cn_flags & SAVESTART) + VREF(dvp); + /* + * We return with ni_vp NULL to indicate that the entry + * doesn't currently exist, leaving a pointer to the + * (possibly locked) directory inode in ndp->ni_dvp. + */ + return (0); + } + dp = *vpp; + +#ifdef DIAGNOSTIC + /* + * Check for symbolic link + */ + if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW)) + panic ("relookup: symlink found.\n"); +#endif + +nextname: + /* + * Check for read-only file systems. + */ + if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) { + /* + * Disallow directory write attempts on read-only + * file systems. + */ + if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) || + (wantparent && + (dvp->v_mount->mnt_flag & MNT_RDONLY))) { + error = EROFS; + goto bad2; + } + } + /* ASSERT(dvp == ndp->ni_startdir) */ + if (cnp->cn_flags & SAVESTART) + VREF(dvp); + + if (!wantparent) + vrele(dvp); + if ((cnp->cn_flags & LOCKLEAF) == 0) + VOP_UNLOCK(dp); + return (0); + +bad2: + if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN)) + VOP_UNLOCK(dvp); + vrele(dvp); +bad: + vput(dp); + *vpp = NULL; return (error); } + /* * Rename system call. * rename("foo", "bar"); @@ -741,59 +871,116 @@ ufs_link(vp, ndp, p) * is different from the source, patch the ".." entry in the * directory. */ -ufs_rename(fndp, tndp, p) - register struct nameidata *fndp, *tndp; - struct proc *p; +int +ufs_rename(ap) + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap; { + struct vnode *tvp = ap->a_tvp; + register struct vnode *tdvp = ap->a_tdvp; + struct vnode *fvp = ap->a_fvp; + register struct vnode *fdvp = ap->a_fdvp; + register struct componentname *tcnp = ap->a_tcnp; + register struct componentname *fcnp = ap->a_fcnp; register struct inode *ip, *xp, *dp; struct dirtemplate dirbuf; + struct timeval tv; int doingdirectory = 0, oldparent = 0, newparent = 0; int error = 0; + int fdvpneedsrele = 1, tdvpneedsrele = 1; + u_char namlen; -#ifdef DIANOSTIC - if ((tndp->ni_nameiop & HASBUF) == 0 || - (fndp->ni_nameiop & HASBUF) == 0) +#ifdef DIAGNOSTIC + if ((tcnp->cn_flags & HASBUF) == 0 || + (fcnp->cn_flags & HASBUF) == 0) panic("ufs_rename: no name"); #endif - dp = VTOI(fndp->ni_dvp); - ip = VTOI(fndp->ni_vp); + /* + * Check for cross-device rename. + */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; +abortit: + VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */ + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ + vrele(fdvp); + vrele(fvp); + return (error); + } + /* * Check if just deleting a link name. */ - if (fndp->ni_vp == tndp->ni_vp) { - VOP_ABORTOP(tndp); - vput(tndp->ni_dvp); - vput(tndp->ni_vp); - vrele(fndp->ni_dvp); - if ((ip->i_mode&IFMT) == IFDIR) { - VOP_ABORTOP(fndp); - vrele(fndp->ni_vp); - return (EINVAL); + if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) || + (VTOI(tdvp)->i_flags & APPEND))) { + error = EPERM; + goto abortit; + } + if (fvp == tvp) { + if (fvp->v_type == VDIR) { + error = EINVAL; + goto abortit; } - doingdirectory = 0; - goto unlinkit; + VOP_ABORTOP(fdvp, fcnp); + vrele(fdvp); + vrele(fvp); + vput(tdvp); + vput(tvp); + tcnp->cn_flags &= ~MODMASK; + tcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + if ((tcnp->cn_flags & SAVESTART) == 0) + panic("ufs_rename: lost from startdir"); + tcnp->cn_nameiop = DELETE; + (void) relookup(tdvp, &tvp, tcnp); + return (VOP_REMOVE(tdvp, tvp, tcnp)); } - ILOCK(ip); - if ((ip->i_mode&IFMT) == IFDIR) { + if (error = VOP_LOCK(fvp)) + goto abortit; + dp = VTOI(fdvp); + ip = VTOI(fvp); + if ((ip->i_flags & (IMMUTABLE | APPEND)) || (dp->i_flags & APPEND)) { + VOP_UNLOCK(fvp); + error = EPERM; + goto abortit; + } + if ((ip->i_mode & IFMT) == IFDIR) { /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ - if ((fndp->ni_namelen == 1 && fndp->ni_ptr[0] == '.') || - dp == ip || fndp->ni_isdotdot || (ip->i_flag & IRENAME)) { - VOP_ABORTOP(tndp); - vput(tndp->ni_dvp); - if (tndp->ni_vp) - vput(tndp->ni_vp); - VOP_ABORTOP(fndp); - vrele(fndp->ni_dvp); - vput(fndp->ni_vp); - return (EINVAL); + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || + dp == ip || (fcnp->cn_flags&ISDOTDOT) || + (ip->i_flag & IRENAME)) { + VOP_UNLOCK(fvp); + error = EINVAL; + goto abortit; } ip->i_flag |= IRENAME; oldparent = dp->i_number; doingdirectory++; } - vrele(fndp->ni_dvp); + vrele(fdvp); + + /* + * When the target exists, both the directory + * and target vnodes are returned locked. + */ + dp = VTOI(tdvp); + xp = NULL; + if (tvp) + xp = VTOI(tvp); /* * 1) Bump link count while we're moving stuff @@ -803,17 +990,12 @@ ufs_rename(fndp, tndp, p) */ ip->i_nlink++; ip->i_flag |= ICHG; - error = iupdat(ip, &time, &time, 1); - IUNLOCK(ip); + tv = time; + if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) { + VOP_UNLOCK(fvp); + goto bad; + } - /* - * When the target exists, both the directory - * and target vnodes are returned locked. - */ - dp = VTOI(tndp->ni_dvp); - xp = NULL; - if (tndp->ni_vp) - xp = VTOI(tndp->ni_vp); /* * If ".." must be changed (ie the directory gets a new * parent) then the source directory must not be in the @@ -824,27 +1006,25 @@ ufs_rename(fndp, tndp, p) * to namei, as the parent directory is unlocked by the * call to checkpath(). */ + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + VOP_UNLOCK(fvp); if (oldparent != dp->i_number) newparent = dp->i_number; if (doingdirectory && newparent) { - VOP_LOCK(fndp->ni_vp); - error = ufs_access(fndp->ni_vp, VWRITE, tndp->ni_cred, p); - VOP_UNLOCK(fndp->ni_vp); - if (error) + if (error) /* write access check above */ goto bad; if (xp != NULL) - iput(xp); - if (error = checkpath(ip, dp, tndp->ni_cred)) + vput(tvp); + if (error = ufs_checkpath(ip, dp, tcnp->cn_cred)) goto out; - if ((tndp->ni_nameiop & SAVESTART) == 0) + if ((tcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost to startdir"); - p->p_spare[1]--; - if (error = lookup(tndp, p)) + if (error = relookup(tdvp, &tvp, tcnp)) goto out; - dp = VTOI(tndp->ni_dvp); + dp = VTOI(tdvp); xp = NULL; - if (tndp->ni_vp) - xp = VTOI(tndp->ni_vp); + if (tvp) + xp = VTOI(tvp); } /* * 2) If target doesn't exist, link the target @@ -862,24 +1042,24 @@ ufs_rename(fndp, tndp, p) * parent we don't fool with the link count. */ if (doingdirectory && newparent) { - if ((unsigned short)dp->i_nlink >= LINK_MAX) { + if ((nlink_t)dp->i_nlink >= LINK_MAX) { error = EMLINK; goto bad; } dp->i_nlink++; dp->i_flag |= ICHG; - if (error = iupdat(dp, &time, &time, 1)) + if (error = VOP_UPDATE(tdvp, &tv, &tv, 1)) goto bad; } - if (error = direnter(ip, tndp)) { + if (error = ufs_direnter(ip, tdvp, tcnp)) { if (doingdirectory && newparent) { dp->i_nlink--; dp->i_flag |= ICHG; - (void) iupdat(dp, &time, &time, 1); + (void)VOP_UPDATE(tdvp, &tv, &tv, 1); } goto bad; } - iput(dp); + vput(tdvp); } else { if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) panic("rename: EXDEV"); @@ -894,9 +1074,9 @@ ufs_rename(fndp, tndp, p) * otherwise the destination may not be changed (except by * root). This implements append-only directories. */ - if ((dp->i_mode & ISVTX) && tndp->ni_cred->cr_uid != 0 && - tndp->ni_cred->cr_uid != dp->i_uid && - xp->i_uid != tndp->ni_cred->cr_uid) { + if ((dp->i_mode & ISVTX) && tcnp->cn_cred->cr_uid != 0 && + tcnp->cn_cred->cr_uid != dp->i_uid && + xp->i_uid != tcnp->cn_cred->cr_uid) { error = EPERM; goto bad; } @@ -906,7 +1086,7 @@ ufs_rename(fndp, tndp, p) * (both directories, or both not directories). */ if ((xp->i_mode&IFMT) == IFDIR) { - if (!dirempty(xp, dp->i_number, tndp->ni_cred) || + if (!ufs_dirempty(xp, dp->i_number, tcnp->cn_cred) || xp->i_nlink > 2) { error = ENOTEMPTY; goto bad; @@ -915,12 +1095,12 @@ ufs_rename(fndp, tndp, p) error = ENOTDIR; goto bad; } - cache_purge(ITOV(dp)); + cache_purge(tdvp); } else if (doingdirectory) { error = EISDIR; goto bad; } - if (error = dirrewrite(dp, ip, tndp)) + if (error = ufs_dirrewrite(dp, ip, tcnp)) goto bad; /* * If the target directory is in the same @@ -932,7 +1112,7 @@ ufs_rename(fndp, tndp, p) dp->i_nlink--; dp->i_flag |= ICHG; } - vput(ITOV(dp)); + vput(tdvp); /* * Adjust the link count of the target to * reflect the dirrewrite above. If this is @@ -947,33 +1127,32 @@ ufs_rename(fndp, tndp, p) if (doingdirectory) { if (--xp->i_nlink != 0) panic("rename: linked directory"); - error = itrunc(xp, (u_long)0, IO_SYNC); + error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC, + tcnp->cn_cred, tcnp->cn_proc); } xp->i_flag |= ICHG; - iput(xp); + vput(tvp); xp = NULL; } /* * 3) Unlink the source. */ -unlinkit: - fndp->ni_nameiop &= ~MODMASK; - fndp->ni_nameiop |= LOCKPARENT | LOCKLEAF; - if ((fndp->ni_nameiop & SAVESTART) == 0) + fcnp->cn_flags &= ~MODMASK; + fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + if ((fcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost from startdir"); - p->p_spare[1]--; - (void) lookup(fndp, p); - if (fndp->ni_vp != NULL) { - xp = VTOI(fndp->ni_vp); - dp = VTOI(fndp->ni_dvp); + (void) relookup(fdvp, &fvp, fcnp); + if (fvp != NULL) { + xp = VTOI(fvp); + dp = VTOI(fdvp); } else { /* * From name has disappeared. */ if (doingdirectory) panic("rename: lost dir entry"); - vrele(ITOV(ip)); + vrele(ap->a_fvp); return (0); } /* @@ -999,29 +1178,38 @@ unlinkit: if (doingdirectory && newparent) { dp->i_nlink--; dp->i_flag |= ICHG; - error = vn_rdwr(UIO_READ, ITOV(xp), (caddr_t)&dirbuf, + error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, - tndp->ni_cred, (int *)0, (struct proc *)0); + tcnp->cn_cred, (int *)0, (struct proc *)0); if (error == 0) { - if (dirbuf.dotdot_namlen != 2 || +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (fvp->v_mount->mnt_maxsymlinklen <= 0) + namlen = dirbuf.dotdot_type; + else + namlen = dirbuf.dotdot_namlen; +# else + namlen = dirbuf.dotdot_namlen; +# endif + if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { - dirbad(xp, 12, "rename: mangled dir"); + ufs_dirbad(xp, (doff_t)12, + "rename: mangled dir"); } else { dirbuf.dotdot_ino = newparent; - (void) vn_rdwr(UIO_WRITE, ITOV(xp), + (void) vn_rdwr(UIO_WRITE, fvp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_SYNC, - tndp->ni_cred, (int *)0, + tcnp->cn_cred, (int *)0, (struct proc *)0); - cache_purge(ITOV(dp)); + cache_purge(fdvp); } } } - error = dirremove(fndp); + error = ufs_dirremove(fdvp, fcnp); if (!error) { xp->i_nlink--; xp->i_flag |= ICHG; @@ -1029,10 +1217,10 @@ unlinkit: xp->i_flag &= ~IRENAME; } if (dp) - vput(ITOV(dp)); + vput(fdvp); if (xp) - vput(ITOV(xp)); - vrele(ITOV(ip)); + vput(fvp); + vrele(ap->a_fvp); return (error); bad: @@ -1040,16 +1228,23 @@ bad: vput(ITOV(xp)); vput(ITOV(dp)); out: - ip->i_nlink--; - ip->i_flag |= ICHG; - vrele(ITOV(ip)); + if (VOP_LOCK(fvp) == 0) { + ip->i_nlink--; + ip->i_flag |= ICHG; + vput(fvp); + } else + vrele(fvp); return (error); } /* * A virgin directory (no blushing please). */ -struct dirtemplate mastertemplate = { +static struct dirtemplate mastertemplate = { + 0, 12, DT_DIR, 1, ".", + 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." +}; +static struct odirtemplate omastertemplate = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." }; @@ -1057,59 +1252,61 @@ struct dirtemplate mastertemplate = { /* * Mkdir system call */ -ufs_mkdir(ndp, vap, p) - struct nameidata *ndp; - struct vattr *vap; - struct proc *p; +int +ufs_mkdir(ap) + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; { + register struct vnode *dvp = ap->a_dvp; + register struct vattr *vap = ap->a_vap; + register struct componentname *cnp = ap->a_cnp; register struct inode *ip, *dp; - struct inode *tip; - struct vnode *dvp; - struct dirtemplate dirtemplate; - int error; - int dmode; + struct vnode *tvp; + struct dirtemplate dirtemplate, *dtp; + struct timeval tv; + int error, dmode; -#ifdef DIANOSTIC - if ((ndp->ni_nameiop & HASBUF) == 0) +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_mkdir: no name"); #endif - dvp = ndp->ni_dvp; dp = VTOI(dvp); - if ((unsigned short)dp->i_nlink >= LINK_MAX) { - free(ndp->ni_pnbuf, M_NAMEI); - iput(dp); - return (EMLINK); + if ((nlink_t)dp->i_nlink >= LINK_MAX) { + error = EMLINK; + goto out; } - dmode = vap->va_mode&0777; + dmode = vap->va_mode & 0777; dmode |= IFDIR; /* - * Must simulate part of maknode here to acquire the inode, but - * not have it entered in the parent directory. The entry is made - * later after writing "." and ".." entries. + * Must simulate part of ufs_makeinode here to acquire the inode, + * but not have it entered in the parent directory. The entry is + * made later after writing "." and ".." entries. */ - if (error = ialloc(dp, dirpref(dp->i_fs), dmode, ndp->ni_cred, &tip)) { - free(ndp->ni_pnbuf, M_NAMEI); - iput(dp); - return (error); - } - ip = tip; - ip->i_uid = ndp->ni_cred->cr_uid; + if (error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) + goto out; + ip = VTOI(tvp); + ip->i_uid = cnp->cn_cred->cr_uid; ip->i_gid = dp->i_gid; #ifdef QUOTA if ((error = getinoquota(ip)) || - (error = chkiq(ip, 1, ndp->ni_cred, 0))) { - free(ndp->ni_pnbuf, M_NAMEI); - ifree(ip, ip->i_number, dmode); - iput(ip); - iput(dp); + (error = chkiq(ip, 1, cnp->cn_cred, 0))) { + free(cnp->cn_pnbuf, M_NAMEI); + VOP_VFREE(tvp, ip->i_number, dmode); + vput(tvp); + vput(dvp); return (error); } #endif ip->i_flag |= IACC|IUPD|ICHG; ip->i_mode = dmode; - ITOV(ip)->v_type = VDIR; /* Rest init'd in iget() */ + tvp->v_type = VDIR; /* Rest init'd in iget() */ ip->i_nlink = 2; - error = iupdat(ip, &time, &time, 1); + tv = time; + error = VOP_UPDATE(tvp, &tv, &tv, 1); /* * Bump link count in parent directory @@ -1119,74 +1316,79 @@ ufs_mkdir(ndp, vap, p) */ dp->i_nlink++; dp->i_flag |= ICHG; - if (error = iupdat(dp, &time, &time, 1)) + if (error = VOP_UPDATE(dvp, &tv, &tv, 1)) goto bad; - /* - * Initialize directory with "." - * and ".." from static template. - */ - dirtemplate = mastertemplate; + /* Initialize directory with "." and ".." from static template. */ + if (dvp->v_mount->mnt_maxsymlinklen > 0) + dtp = &mastertemplate; + else + dtp = (struct dirtemplate *)&omastertemplate; + dirtemplate = *dtp; dirtemplate.dot_ino = ip->i_number; dirtemplate.dotdot_ino = dp->i_number; - error = vn_rdwr(UIO_WRITE, ITOV(ip), (caddr_t)&dirtemplate, + error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate, sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, - IO_NODELOCKED|IO_SYNC, ndp->ni_cred, (int *)0, (struct proc *)0); + IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0); if (error) { dp->i_nlink--; dp->i_flag |= ICHG; goto bad; } - if (DIRBLKSIZ > dp->i_fs->fs_fsize) { - panic("mkdir: blksize"); /* XXX - should grow w/balloc() */ - } else { + if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) + panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */ + else { ip->i_size = DIRBLKSIZ; ip->i_flag |= ICHG; } - /* - * Directory all set up, now - * install the entry for it in - * the parent directory. - */ - if (error = direnter(ip, ndp)) { + + /* Directory set up, now install it's entry in the parent directory. */ + if (error = ufs_direnter(ip, dvp, cnp)) { dp->i_nlink--; dp->i_flag |= ICHG; } bad: /* - * No need to do an explicit itrunc here, - * vrele will do this for us because we set - * the link count to 0. + * No need to do an explicit VOP_TRUNCATE here, vrele will do this + * for us because we set the link count to 0. */ if (error) { ip->i_nlink = 0; ip->i_flag |= ICHG; - iput(ip); + vput(tvp); } else - ndp->ni_vp = ITOV(ip); - FREE(ndp->ni_pnbuf, M_NAMEI); - iput(dp); + *ap->a_vpp = tvp; +out: + FREE(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); return (error); } /* * Rmdir system call. */ -ufs_rmdir(ndp, p) - register struct nameidata *ndp; - struct proc *p; +int +ufs_rmdir(ap) + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; { + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; + register struct componentname *cnp = ap->a_cnp; register struct inode *ip, *dp; - int error = 0; + int error; - ip = VTOI(ndp->ni_vp); - dp = VTOI(ndp->ni_dvp); + ip = VTOI(vp); + dp = VTOI(dvp); /* * No rmdir "." please. */ if (dp == ip) { - vrele(ITOV(dp)); - iput(ip); + vrele(dvp); + vput(vp); return (EINVAL); } /* @@ -1196,22 +1398,28 @@ ufs_rmdir(ndp, p) * the current directory and thus be * non-empty.) */ - if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number, ndp->ni_cred)) { + error = 0; + if (ip->i_nlink != 2 || + !ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) { error = ENOTEMPTY; goto out; } + if ((dp->i_flags & APPEND) || (ip->i_flags & (IMMUTABLE | APPEND))) { + error = EPERM; + goto out; + } /* * Delete reference to directory before purging * inode. If we crash in between, the directory * will be reattached to lost+found, */ - if (error = dirremove(ndp)) + if (error = ufs_dirremove(dvp, cnp)) goto out; dp->i_nlink--; dp->i_flag |= ICHG; - cache_purge(ITOV(dp)); - iput(dp); - ndp->ni_dvp = NULL; + cache_purge(dvp); + vput(dvp); + dvp = NULL; /* * Truncate inode. The only stuff left * in the directory is "." and "..". The @@ -1224,46 +1432,69 @@ ufs_rmdir(ndp, p) * worry about them later. */ ip->i_nlink -= 2; - error = itrunc(ip, (u_long)0, IO_SYNC); + error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, + cnp->cn_proc); cache_purge(ITOV(ip)); out: - if (ndp->ni_dvp) - iput(dp); - iput(ip); + if (dvp) + vput(dvp); + vput(vp); return (error); } /* * symlink -- make a symbolic link */ -ufs_symlink(ndp, vap, target, p) - struct nameidata *ndp; - struct vattr *vap; - char *target; - struct proc *p; +int +ufs_symlink(ap) + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap; { - struct inode *ip; - int error; + register struct vnode *vp, **vpp = ap->a_vpp; + register struct inode *ip; + int len, error; - error = maknode(IFLNK | vap->va_mode, ndp, &ip); - if (error) + if (error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, + vpp, ap->a_cnp)) return (error); - error = vn_rdwr(UIO_WRITE, ITOV(ip), target, strlen(target), (off_t)0, - UIO_SYSSPACE, IO_NODELOCKED, ndp->ni_cred, (int *)0, - (struct proc *)0); - iput(ip); + vp = *vpp; + len = strlen(ap->a_target); + if (len < vp->v_mount->mnt_maxsymlinklen) { + ip = VTOI(vp); + bcopy(ap->a_target, (char *)ip->i_shortlink, len); + ip->i_size = len; + ip->i_flag |= IUPD|ICHG; + } else + error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, + UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0, + (struct proc *)0); + vput(vp); return (error); } /* - * Vnode op for read and write + * Vnode op for reading directories. + * + * The routine below assumes that the on-disk format of a directory + * is the same as that defined by . If the on-disk + * format changes, then it will be necessary to do a conversion + * from the on-disk format that read returns to the format defined + * by . */ -ufs_readdir(vp, uio, cred, eofflagp) - struct vnode *vp; - register struct uio *uio; - struct ucred *cred; - int *eofflagp; +int +ufs_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap; { + register struct uio *uio = ap->a_uio; int count, lost, error; count = uio->uio_resid; @@ -1273,25 +1504,73 @@ ufs_readdir(vp, uio, cred, eofflagp) return (EINVAL); uio->uio_resid = count; uio->uio_iov->iov_len = count; - error = ufs_read(vp, uio, 0, cred); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) { + error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); + } else { + struct dirent *dp, *edp; + struct uio auio; + struct iovec aiov; + caddr_t dirbuf; + int readcnt; + u_char tmp; + + auio = *uio; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + aiov.iov_len = count; + MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); + aiov.iov_base = dirbuf; + error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); + if (error == 0) { + readcnt = count - auio.uio_resid; + edp = (struct dirent *)&dirbuf[readcnt]; + for (dp = (struct dirent *)dirbuf; dp < edp; ) { + tmp = dp->d_namlen; + dp->d_namlen = dp->d_type; + dp->d_type = tmp; + if (dp->d_reclen > 0) { + dp = (struct dirent *) + ((char *)dp + dp->d_reclen); + } else { + error = EIO; + break; + } + } + if (dp >= edp) + error = uiomove(dirbuf, readcnt, uio); + } + FREE(dirbuf, M_TEMP); + } +# else + error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); +# endif uio->uio_resid += lost; - if ((VTOI(vp)->i_size - uio->uio_offset) <= 0) - *eofflagp = 1; - else - *eofflagp = 0; return (error); } /* * Return target name of a symbolic link */ -ufs_readlink(vp, uiop, cred) - struct vnode *vp; - struct uio *uiop; - struct ucred *cred; +int +ufs_readlink(ap) + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap; { + register struct vnode *vp = ap->a_vp; + register struct inode *ip = VTOI(vp); + int isize; - return (ufs_read(vp, uiop, 0, cred)); + isize = ip->i_size; + if (isize < vp->v_mount->mnt_maxsymlinklen) { + uiomove((char *)ip->i_shortlink, isize, ap->a_uio); + return (0); + } + return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); } /* @@ -1299,89 +1578,140 @@ ufs_readlink(vp, uiop, cred) * done. If a buffer has been saved in anticipation of a CREATE, delete it. */ /* ARGSUSED */ -ufs_abortop(ndp) - struct nameidata *ndp; +int +ufs_abortop(ap) + struct vop_abortop_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + } */ *ap; { - - if ((ndp->ni_nameiop & (HASBUF | SAVESTART)) == HASBUF) - FREE(ndp->ni_pnbuf, M_NAMEI); + if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) + FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return (0); } /* - * Lock an inode. + * Lock an inode. If its already locked, set the WANT bit and sleep. */ -ufs_lock(vp) - struct vnode *vp; +int +ufs_lock(ap) + struct vop_lock_args /* { + struct vnode *a_vp; + } */ *ap; { - register struct inode *ip = VTOI(vp); + register struct vnode *vp = ap->a_vp; + register struct inode *ip; + struct proc *p = curproc; /* XXX */ - ILOCK(ip); +start: + while (vp->v_flag & VXLOCK) { + vp->v_flag |= VXWANT; + sleep((caddr_t)vp, PINOD); + } + if (vp->v_tag == VT_NON) + return (ENOENT); + ip = VTOI(vp); + if (ip->i_flag & ILOCKED) { + ip->i_flag |= IWANT; +#ifdef DIAGNOSTIC + if (p) { + if (p->p_pid == ip->i_lockholder) + panic("locking against myself"); + ip->i_lockwaiter = p->p_pid; + } else + ip->i_lockwaiter = -1; +#endif + (void) sleep((caddr_t)ip, PINOD); + goto start; + } +#ifdef DIAGNOSTIC + ip->i_lockwaiter = 0; + if (ip->i_lockholder != 0) + panic("lockholder (%d) != 0", ip->i_lockholder); + if (p && p->p_pid == 0) + printf("locking by process 0\n"); + if (p) + ip->i_lockholder = p->p_pid; + else + ip->i_lockholder = -1; +#endif + ip->i_flag |= ILOCKED; return (0); } /* - * Unlock an inode. + * Unlock an inode. If WANT bit is on, wakeup. */ -ufs_unlock(vp) - struct vnode *vp; +int lockcount = 90; +int +ufs_unlock(ap) + struct vop_unlock_args /* { + struct vnode *a_vp; + } */ *ap; { - register struct inode *ip = VTOI(vp); + register struct inode *ip = VTOI(ap->a_vp); + struct proc *p = curproc; /* XXX */ - if (!(ip->i_flag & ILOCKED)) +#ifdef DIAGNOSTIC + if ((ip->i_flag & ILOCKED) == 0) { + vprint("ufs_unlock: unlocked inode", ap->a_vp); panic("ufs_unlock NOT LOCKED"); - IUNLOCK(ip); + } + if (p && p->p_pid != ip->i_lockholder && p->p_pid > -1 && + ip->i_lockholder > -1 && lockcount++ < 100) + panic("unlocker (%d) != lock holder (%d)", + p->p_pid, ip->i_lockholder); + ip->i_lockholder = 0; +#endif + ip->i_flag &= ~ILOCKED; + if (ip->i_flag & IWANT) { + ip->i_flag &= ~IWANT; + wakeup((caddr_t)ip); + } return (0); } /* * Check for a locked inode. */ -ufs_islocked(vp) - struct vnode *vp; +int +ufs_islocked(ap) + struct vop_islocked_args /* { + struct vnode *a_vp; + } */ *ap; { - if (VTOI(vp)->i_flag & ILOCKED) + if (VTOI(ap->a_vp)->i_flag & ILOCKED) return (1); return (0); } -/* - * Get access to bmap - */ -ufs_bmap(vp, bn, vpp, bnp) - struct vnode *vp; - daddr_t bn; - struct vnode **vpp; - daddr_t *bnp; -{ - struct inode *ip = VTOI(vp); - - if (vpp != NULL) - *vpp = ip->i_devvp; - if (bnp == NULL) - return (0); - return (bmap(ip, bn, bnp)); -} - /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ -int checkoverlap = 0; - -ufs_strategy(bp) - register struct buf *bp; +int +ufs_strategy(ap) + struct vop_strategy_args /* { + struct buf *a_bp; + } */ *ap; { - register struct inode *ip = VTOI(bp->b_vp); - struct vnode *vp; + register struct buf *bp = ap->a_bp; + register struct vnode *vp = bp->b_vp; + register struct inode *ip; int error; - if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) + ip = VTOI(vp); + if (vp->v_type == VBLK || vp->v_type == VCHR) panic("ufs_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { - if (error = bmap(ip, bp->b_lblkno, &bp->b_blkno)) + if (error = + VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL)) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + biodone(bp); return (error); + } if ((long)bp->b_blkno == -1) clrbuf(bp); } @@ -1389,47 +1719,22 @@ ufs_strategy(bp) biodone(bp); return (0); } -#ifdef DIAGNOSTIC - if (checkoverlap) { - register struct buf *ep; - struct buf *ebp; - daddr_t start, last; - - ebp = &buf[nbuf]; - start = bp->b_blkno; - last = start + btodb(bp->b_bcount) - 1; - for (ep = buf; ep < ebp; ep++) { - if (ep == bp || (ep->b_flags & B_INVAL) || - ep->b_vp == NULLVP) - continue; - if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0)) - continue; - if (vp != ip->i_devvp) - continue; - /* look for overlap */ - if (ep->b_bcount == 0 || ep->b_blkno > last || - ep->b_blkno + btodb(ep->b_bcount) <= start) - continue; - vprint("Disk overlap", vp); - printf("\tstart %d, end %d overlap start %d, end %d\n", - start, last, ep->b_blkno, - ep->b_blkno + btodb(ep->b_bcount) - 1); - panic("Disk buffer overlap"); - } - } -#endif /* DIAGNOSTIC */ vp = ip->i_devvp; bp->b_dev = vp->v_rdev; - (*(vp->v_op->vop_strategy))(bp); + VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); return (0); } /* * Print out the contents of an inode. */ -ufs_print(vp) - struct vnode *vp; +int +ufs_print(ap) + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; { + register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); printf("tag VT_UFS, ino %d, on dev %d, %d", ip->i_number, @@ -1439,46 +1744,53 @@ ufs_print(vp) fifo_printinfo(vp); #endif /* FIFO */ printf("%s\n", (ip->i_flag & ILOCKED) ? " (LOCKED)" : ""); - if (ip->i_spare0 == 0) - return; - printf("\towner pid %d", ip->i_spare0); - if (ip->i_spare1) - printf(" waiting pid %d", ip->i_spare1); + if (ip->i_lockholder == 0) + return (0); + printf("\towner pid %d", ip->i_lockholder); + if (ip->i_lockwaiter) + printf(" waiting pid %d", ip->i_lockwaiter); printf("\n"); + return (0); } /* * Read wrapper for special devices. */ -ufsspec_read(vp, uio, ioflag, cred) - struct vnode *vp; - struct uio *uio; - int ioflag; - struct ucred *cred; +int +ufsspec_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { /* * Set access flag. */ - VTOI(vp)->i_flag |= IACC; - return (spec_read(vp, uio, ioflag, cred)); + VTOI(ap->a_vp)->i_flag |= IACC; + return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for special devices. */ -ufsspec_write(vp, uio, ioflag, cred) - struct vnode *vp; - struct uio *uio; - int ioflag; - struct ucred *cred; +int +ufsspec_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { /* * Set update and change flags. */ - VTOI(vp)->i_flag |= IUPD|ICHG; - return (spec_write(vp, uio, ioflag, cred)); + VTOI(ap->a_vp)->i_flag |= IUPD|ICHG; + return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap)); } /* @@ -1486,52 +1798,63 @@ ufsspec_write(vp, uio, ioflag, cred) * * Update the times on the inode then do device close. */ -ufsspec_close(vp, fflag, cred, p) - struct vnode *vp; - int fflag; - struct ucred *cred; - struct proc *p; +int +ufsspec_close(ap) + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { - register struct inode *ip = VTOI(vp); + register struct inode *ip = VTOI(ap->a_vp); - if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) + if (ap->a_vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) ITIMES(ip, &time, &time); - return (spec_close(vp, fflag, cred, p)); + return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap)); } #ifdef FIFO /* * Read wrapper for fifo's */ -ufsfifo_read(vp, uio, ioflag, cred) - struct vnode *vp; - struct uio *uio; - int ioflag; - struct ucred *cred; +int +ufsfifo_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { + extern int (**fifo_vnodeop_p)(); /* * Set access flag. */ - VTOI(vp)->i_flag |= IACC; - return (fifo_read(vp, uio, ioflag, cred)); + VTOI(ap->a_vp)->i_flag |= IACC; + return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for fifo's. */ -ufsfifo_write(vp, uio, ioflag, cred) - struct vnode *vp; - struct uio *uio; - int ioflag; - struct ucred *cred; +int +ufsfifo_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { + extern int (**fifo_vnodeop_p)(); /* * Set update and change flags. */ - VTOI(vp)->i_flag |= IUPD|ICHG; - return (fifo_write(vp, uio, ioflag, cred)); + VTOI(ap->a_vp)->i_flag |= IUPD|ICHG; + return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap)); } /* @@ -1539,108 +1862,74 @@ ufsfifo_write(vp, uio, ioflag, cred) * * Update the times on the inode then do device close. */ -ufsfifo_close(vp, fflag, cred, p) - struct vnode *vp; - int fflag; - struct ucred *cred; - struct proc *p; +ufsfifo_close(ap) + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { - register struct inode *ip = VTOI(vp); + extern int (**fifo_vnodeop_p)(); + register struct inode *ip = VTOI(ap->a_vp); - if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) + if (ap->a_vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) ITIMES(ip, &time, &time); - return (fifo_close(vp, fflag, cred, p)); + return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap)); } #endif /* FIFO */ /* - * Allocate a new inode. + * Return POSIX pathconf information applicable to ufs filesystems. */ -maknode(mode, ndp, ipp) - int mode; - register struct nameidata *ndp; - struct inode **ipp; +ufs_pathconf(ap) + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + int *a_retval; + } */ *ap; { - register struct inode *ip; - struct inode *tip; - register struct inode *pdir = VTOI(ndp->ni_dvp); - ino_t ipref; - int error; -#ifdef DIANOSTIC - if ((ndp->ni_nameiop & HASBUF) == 0) - panic("maknode: no name"); -#endif - *ipp = 0; - if ((mode & IFMT) == 0) - mode |= IFREG; - if ((mode & IFMT) == IFDIR) - ipref = dirpref(pdir->i_fs); - else - ipref = pdir->i_number; - if (error = ialloc(pdir, ipref, mode, ndp->ni_cred, &tip)) { - free(ndp->ni_pnbuf, M_NAMEI); - iput(pdir); - return (error); - } - ip = tip; - ip->i_uid = ndp->ni_cred->cr_uid; - ip->i_gid = pdir->i_gid; -#ifdef QUOTA - if ((error = getinoquota(ip)) || - (error = chkiq(ip, 1, ndp->ni_cred, 0))) { - free(ndp->ni_pnbuf, M_NAMEI); - ifree(ip, ip->i_number, mode); - iput(ip); - iput(pdir); - return (error); + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = LINK_MAX; + return (0); + case _PC_NAME_MAX: + *ap->a_retval = NAME_MAX; + return (0); + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; + return (0); + case _PC_PIPE_BUF: + *ap->a_retval = PIPE_BUF; + return (0); + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return (0); + case _PC_NO_TRUNC: + *ap->a_retval = 1; + return (0); + default: + return (EINVAL); } -#endif - ip->i_flag |= IACC|IUPD|ICHG; - ip->i_mode = mode; - ITOV(ip)->v_type = IFTOVT(mode); /* Rest init'd in iget() */ - ip->i_nlink = 1; - if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, ndp->ni_cred) && - suser(ndp->ni_cred, NULL)) - ip->i_mode &= ~ISGID; - - /* - * Make sure inode goes to disk before directory entry. - */ - if (error = iupdat(ip, &time, &time, 1)) - goto bad; - if (error = direnter(ip, ndp)) - goto bad; - if ((ndp->ni_nameiop & SAVESTART) == 0) - FREE(ndp->ni_pnbuf, M_NAMEI); - iput(pdir); - *ipp = ip; - return (0); - -bad: - /* - * Write error occurred trying to update the inode - * or the directory so must deallocate the inode. - */ - free(ndp->ni_pnbuf, M_NAMEI); - iput(pdir); - ip->i_nlink = 0; - ip->i_flag |= ICHG; - iput(ip); - return (error); + /* NOTREACHED */ } /* * Advisory record locking support */ -ufs_advlock(vp, id, op, fl, flags) - struct vnode *vp; - caddr_t id; - int op; - register struct flock *fl; - int flags; +int +ufs_advlock(ap) + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap; { - register struct inode *ip = VTOI(vp); + register struct inode *ip = VTOI(ap->a_vp); + register struct flock *fl = ap->a_fl; register struct lockf *lock; off_t start, end; int error; @@ -1649,7 +1938,7 @@ ufs_advlock(vp, id, op, fl, flags) * Avoid the common case of unlocking when inode has no locks. */ if (ip->i_lockf == (struct lockf *)0) { - if (op != F_SETLK) { + if (ap->a_op != F_SETLK) { fl->l_type = F_UNLCK; return (0); } @@ -1687,16 +1976,16 @@ ufs_advlock(vp, id, op, fl, flags) MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK); lock->lf_start = start; lock->lf_end = end; - lock->lf_id = id; + lock->lf_id = ap->a_id; lock->lf_inode = ip; lock->lf_type = fl->l_type; lock->lf_next = (struct lockf *)0; lock->lf_block = (struct lockf *)0; - lock->lf_flags = flags; + lock->lf_flags = ap->a_flags; /* * Do the requested operation. */ - switch(op) { + switch(ap->a_op) { case F_SETLK: return (lf_setlock(lock)); @@ -1718,122 +2007,139 @@ ufs_advlock(vp, id, op, fl, flags) } /* - * Global vfs data structures for ufs + * Initialize the vnode associated with a new inode, handle aliased + * vnodes. */ -struct vnodeops ufs_vnodeops = { - ufs_lookup, /* lookup */ - ufs_create, /* create */ - ufs_mknod, /* mknod */ - ufs_open, /* open */ - ufs_close, /* close */ - ufs_access, /* access */ - ufs_getattr, /* getattr */ - ufs_setattr, /* setattr */ - ufs_read, /* read */ - ufs_write, /* write */ - ufs_ioctl, /* ioctl */ - ufs_select, /* select */ - ufs_mmap, /* mmap */ - ufs_fsync, /* fsync */ - ufs_seek, /* seek */ - ufs_remove, /* remove */ - ufs_link, /* link */ - ufs_rename, /* rename */ - ufs_mkdir, /* mkdir */ - ufs_rmdir, /* rmdir */ - ufs_symlink, /* symlink */ - ufs_readdir, /* readdir */ - ufs_readlink, /* readlink */ - ufs_abortop, /* abortop */ - ufs_inactive, /* inactive */ - ufs_reclaim, /* reclaim */ - ufs_lock, /* lock */ - ufs_unlock, /* unlock */ - ufs_bmap, /* bmap */ - ufs_strategy, /* strategy */ - ufs_print, /* print */ - ufs_islocked, /* islocked */ - ufs_advlock, /* advlock */ -}; +int +ufs_vinit(mntp, specops, fifoops, vpp) + struct mount *mntp; + int (**specops)(); + int (**fifoops)(); + struct vnode **vpp; +{ + struct inode *ip; + struct vnode *vp, *nvp; + + vp = *vpp; + ip = VTOI(vp); + switch(vp->v_type = IFTOVT(ip->i_mode)) { + case VCHR: + case VBLK: + vp->v_op = specops; + if (nvp = checkalias(vp, ip->i_rdev, mntp)) { + /* + * Discard unneeded vnode, but save its inode. + */ + ufs_ihashrem(ip); + VOP_UNLOCK(vp); + nvp->v_data = vp->v_data; + vp->v_data = NULL; + vp->v_op = spec_vnodeop_p; + vrele(vp); + vgone(vp); + /* + * Reinitialize aliased inode. + */ + vp = nvp; + ip->i_vnode = vp; + ufs_ihashins(ip); + } + break; + case VFIFO: +#ifdef FIFO + vp->v_op = fifoops; + break; +#else + return (EOPNOTSUPP); +#endif + } + if (ip->i_number == ROOTINO) + vp->v_flag |= VROOT; + /* + * Initialize modrev times + */ + SETHIGH(ip->i_modrev, mono_time.tv_sec); + SETLOW(ip->i_modrev, mono_time.tv_usec * 4294); + *vpp = vp; + return (0); +} -struct vnodeops spec_inodeops = { - spec_lookup, /* lookup */ - spec_create, /* create */ - spec_mknod, /* mknod */ - spec_open, /* open */ - ufsspec_close, /* close */ - ufs_access, /* access */ - ufs_getattr, /* getattr */ - ufs_setattr, /* setattr */ - ufsspec_read, /* read */ - ufsspec_write, /* write */ - spec_ioctl, /* ioctl */ - spec_select, /* select */ - spec_mmap, /* mmap */ - spec_fsync, /* fsync */ - spec_seek, /* seek */ - spec_remove, /* remove */ - spec_link, /* link */ - spec_rename, /* rename */ - spec_mkdir, /* mkdir */ - spec_rmdir, /* rmdir */ - spec_symlink, /* symlink */ - spec_readdir, /* readdir */ - spec_readlink, /* readlink */ - spec_abortop, /* abortop */ - ufs_inactive, /* inactive */ - ufs_reclaim, /* reclaim */ - ufs_lock, /* lock */ - ufs_unlock, /* unlock */ - spec_bmap, /* bmap */ - spec_strategy, /* strategy */ - ufs_print, /* print */ - ufs_islocked, /* islocked */ - spec_advlock, /* advlock */ -}; +/* + * Allocate a new inode. + */ +int +ufs_makeinode(mode, dvp, vpp, cnp) + int mode; + struct vnode *dvp; + struct vnode **vpp; + struct componentname *cnp; +{ + register struct inode *ip, *pdir; + struct timeval tv; + struct vnode *tvp; + int error; -#ifdef FIFO -struct vnodeops fifo_inodeops = { - fifo_lookup, /* lookup */ - fifo_create, /* create */ - fifo_mknod, /* mknod */ - fifo_open, /* open */ - ufsfifo_close, /* close */ - ufs_access, /* access */ - ufs_getattr, /* getattr */ - ufs_setattr, /* setattr */ - ufsfifo_read, /* read */ - ufsfifo_write, /* write */ - fifo_ioctl, /* ioctl */ - fifo_select, /* select */ - fifo_mmap, /* mmap */ - fifo_fsync, /* fsync */ - fifo_seek, /* seek */ - fifo_remove, /* remove */ - fifo_link, /* link */ - fifo_rename, /* rename */ - fifo_mkdir, /* mkdir */ - fifo_rmdir, /* rmdir */ - fifo_symlink, /* symlink */ - fifo_readdir, /* readdir */ - fifo_readlink, /* readlink */ - fifo_abortop, /* abortop */ - ufs_inactive, /* inactive */ - ufs_reclaim, /* reclaim */ - ufs_lock, /* lock */ - ufs_unlock, /* unlock */ - fifo_bmap, /* bmap */ - fifo_strategy, /* strategy */ - ufs_print, /* print */ - ufs_islocked, /* islocked */ - fifo_advlock, /* advlock */ -}; -#endif /* FIFO */ + pdir = VTOI(dvp); +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) + panic("ufs_makeinode: no name"); +#endif + *vpp = NULL; + if ((mode & IFMT) == 0) + mode |= IFREG; -enum vtype iftovt_tab[16] = { - VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, - VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD, -}; -int vttoif_tab[9] = { - 0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFIFO, IFMT, -}; + if (error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) { + free(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); + return (error); + } + ip = VTOI(tvp); + ip->i_gid = pdir->i_gid; + if ((mode & IFMT) == IFLNK) + ip->i_uid = pdir->i_uid; + else + ip->i_uid = cnp->cn_cred->cr_uid; +#ifdef QUOTA + if ((error = getinoquota(ip)) || + (error = chkiq(ip, 1, cnp->cn_cred, 0))) { + free(cnp->cn_pnbuf, M_NAMEI); + VOP_VFREE(tvp, ip->i_number, mode); + vput(tvp); + vput(dvp); + return (error); + } +#endif + ip->i_flag |= IACC|IUPD|ICHG; + ip->i_mode = mode; + tvp->v_type = IFTOVT(mode); /* Rest init'd in iget() */ + ip->i_nlink = 1; + if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) && + suser(cnp->cn_cred, NULL)) + ip->i_mode &= ~ISGID; + + /* + * Make sure inode goes to disk before directory entry. + */ + tv = time; + if (error = VOP_UPDATE(tvp, &tv, &tv, 1)) + goto bad; + if (error = ufs_direnter(ip, dvp, cnp)) + goto bad; + if ((cnp->cn_flags & SAVESTART) == 0) + FREE(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); + *vpp = tvp; + return (0); + +bad: + /* + * Write error occurred trying to update the inode + * or the directory so must deallocate the inode. + */ + free(cnp->cn_pnbuf, M_NAMEI); + vput(dvp); + ip->i_nlink = 0; + ip->i_flag |= ICHG; + vput(tvp); + return (error); +}