X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/86cdabf6f2d7da31eac857bc4799229c6254a582..d9d75b8f74f20e0f43170b709e78e766c99adaf7:/usr/src/sys/ufs/ffs/ffs_vnops.c diff --git a/usr/src/sys/ufs/ffs/ffs_vnops.c b/usr/src/sys/ufs/ffs/ffs_vnops.c index 089a00b480..0d3244914a 100644 --- a/usr/src/sys/ufs/ffs/ffs_vnops.c +++ b/usr/src/sys/ufs/ffs/ffs_vnops.c @@ -2,19 +2,9 @@ * Copyright (c) 1982, 1986, 1989 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. + * %sccs.include.redist.c% * - * @(#)ffs_vnops.c 7.21 (Berkeley) %G% + * @(#)ffs_vnops.c 7.51 (Berkeley) %G% */ #include "param.h" @@ -25,15 +15,18 @@ #include "stat.h" #include "buf.h" #include "proc.h" -#include "uio.h" #include "socket.h" #include "socketvar.h" #include "conf.h" #include "mount.h" #include "vnode.h" +#include "specdev.h" +#include "fcntl.h" +#include "malloc.h" +#include "../ufs/lockf.h" +#include "../ufs/quota.h" #include "../ufs/inode.h" #include "../ufs/fs.h" -#include "../ufs/quota.h" /* * Global vfs data structures for ufs @@ -68,49 +61,57 @@ int ufs_lookup(), ufs_lock(), ufs_unlock(), ufs_bmap(), - ufs_strategy(); + ufs_strategy(), + ufs_print(), + ufs_islocked(), + ufs_advlock(); struct vnodeops ufs_vnodeops = { - ufs_lookup, - ufs_create, - ufs_mknod, - ufs_open, - ufs_close, - ufs_access, - ufs_getattr, - ufs_setattr, - ufs_read, - ufs_write, - ufs_ioctl, - ufs_select, - ufs_mmap, - ufs_fsync, - ufs_seek, - ufs_remove, - ufs_link, - ufs_rename, - ufs_mkdir, - ufs_rmdir, - ufs_symlink, - ufs_readdir, - ufs_readlink, - ufs_abortop, - ufs_inactive, - ufs_reclaim, - ufs_lock, - ufs_unlock, - ufs_bmap, - ufs_strategy, + 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 spec_lookup(), spec_open(), - spec_read(), - spec_write(), + ufsspec_read(), + ufsspec_write(), spec_strategy(), + spec_bmap(), spec_ioctl(), spec_select(), - spec_close(), + ufsspec_close(), + spec_advlock(), spec_badop(), spec_nullop(); @@ -119,12 +120,12 @@ struct vnodeops spec_inodeops = { spec_badop, /* create */ spec_badop, /* mknod */ spec_open, /* open */ - spec_close, /* close */ + ufsspec_close, /* close */ ufs_access, /* access */ ufs_getattr, /* getattr */ ufs_setattr, /* setattr */ - spec_read, /* read */ - spec_write, /* write */ + ufsspec_read, /* read */ + ufsspec_write, /* write */ spec_ioctl, /* ioctl */ spec_select, /* select */ spec_badop, /* mmap */ @@ -143,15 +144,70 @@ struct vnodeops spec_inodeops = { ufs_reclaim, /* reclaim */ ufs_lock, /* lock */ ufs_unlock, /* unlock */ - spec_badop, /* bmap */ + spec_bmap, /* bmap */ spec_strategy, /* strategy */ + ufs_print, /* print */ + ufs_islocked, /* islocked */ + spec_advlock, /* advlock */ +}; + +#ifdef FIFO +int fifo_lookup(), + fifo_open(), + ufsfifo_read(), + ufsfifo_write(), + fifo_bmap(), + fifo_ioctl(), + fifo_select(), + ufsfifo_close(), + fifo_print(), + fifo_advlock(), + fifo_badop(), + fifo_nullop(); + +struct vnodeops fifo_inodeops = { + fifo_lookup, /* lookup */ + fifo_badop, /* create */ + fifo_badop, /* 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_badop, /* mmap */ + fifo_nullop, /* fsync */ + fifo_badop, /* seek */ + fifo_badop, /* remove */ + fifo_badop, /* link */ + fifo_badop, /* rename */ + fifo_badop, /* mkdir */ + fifo_badop, /* rmdir */ + fifo_badop, /* symlink */ + fifo_badop, /* readdir */ + fifo_badop, /* readlink */ + fifo_badop, /* abortop */ + ufs_inactive, /* inactive */ + ufs_reclaim, /* reclaim */ + ufs_lock, /* lock */ + ufs_unlock, /* unlock */ + fifo_bmap, /* bmap */ + fifo_badop, /* strategy */ + ufs_print, /* print */ + ufs_islocked, /* islocked */ + fifo_advlock, /* advlock */ }; +#endif /* FIFO */ -enum vtype iftovt_tab[8] = { - VNON, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VBAD, +enum vtype iftovt_tab[16] = { + VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, + VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD, }; -int vttoif_tab[8] = { - 0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFMT, +int vttoif_tab[9] = { + 0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFIFO, IFMT, }; /* @@ -185,21 +241,21 @@ ufs_mknod(ndp, vap, cred) if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip)) return (error); - vp = ITOV(ip); - if (vap->va_rdev) { + ip->i_flag |= IACC|IUPD|ICHG; + if (vap->va_rdev != VNOVAL) { /* * Want to be able to use this to make badblock * inodes, so don't truncate the dev number. */ - vp->v_rdev = ip->i_rdev = vap->va_rdev; - ip->i_flag |= IACC|IUPD|ICHG; + ip->i_rdev = vap->va_rdev; } /* * Remove inode so that it will be reloaded by iget and * checked to see if it is an alias of an existing entry * in the inode cache. */ - iput(ip); + vp = ITOV(ip); + vput(vp); vp->v_type = VNON; vgone(vp); return (0); @@ -233,18 +289,63 @@ ufs_close(vp, fflag, cred) { register struct inode *ip = VTOI(vp); - if (vp->v_count > 1 && !(ip->i_flag & ILOCKED)) + if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) ITIMES(ip, &time, &time); return (0); } +/* + * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC. + * The mode is shifted to select the owner/group/other fields. The + * super user is granted all permissions. + */ ufs_access(vp, mode, cred) struct vnode *vp; - int mode; + register int mode; struct ucred *cred; { + register struct inode *ip = VTOI(vp); + register gid_t *gp; + int i, error; - return (iaccess(VTOI(vp), mode, cred)); +#ifdef DIAGNOSTIC + if (!VOP_ISLOCKED(vp)) { + vprint("ufs_access: not locked", vp); + panic("ufs_access: not locked"); + } +#endif +#ifdef QUOTA + if (mode & VWRITE) { + switch (vp->v_type) { + case VREG: case VDIR: case VLNK: + if (error = getinoquota(ip)) + return (error); + } + } +#endif /* QUOTA */ + /* + * If you're the super-user, you always get access. + */ + if (cred->cr_uid == 0) + return (0); + /* + * Access check is based on only one of owner, group, public. + * If not owner, then check group. If not a member of the + * group, then check public access. + */ + if (cred->cr_uid != ip->i_uid) { + mode >>= 3; + gp = cred->cr_groups; + for (i = 0; i < cred->cr_ngroups; i++, gp++) + if (ip->i_gid == *gp) + goto found; + mode >>= 3; +found: + ; + } + if ((ip->i_mode & mode) != 0) + return (0); + return (EACCES); } /* ARGSUSED */ @@ -266,8 +367,12 @@ ufs_getattr(vp, vap, cred) vap->va_uid = ip->i_uid; vap->va_gid = ip->i_gid; vap->va_rdev = (dev_t)ip->i_rdev; - vap->va_size = ip->i_din.di_qsize.val[0]; - vap->va_size1 = ip->i_din.di_qsize.val[1]; +#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; @@ -284,7 +389,7 @@ ufs_getattr(vp, vap, cred) else vap->va_blocksize = ip->i_fs->fs_bsize; vap->va_bytes = dbtob(ip->i_blocks); - vap->va_bytes1 = -1; + vap->va_bytes_rsv = 0; vap->va_type = vp->v_type; return (0); } @@ -318,7 +423,7 @@ ufs_setattr(vp, vap, cred) if (vap->va_size != VNOVAL) { if (vp->v_type == VDIR) return (EISDIR); - if (error = itrunc(ip, vap->va_size)) + if (error = itrunc(ip, vap->va_size, 0)) /* XXX IO_SYNC? */ return (error); } if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { @@ -365,17 +470,17 @@ chmod1(vp, mode, cred) if (cred->cr_uid != ip->i_uid && (error = suser(cred, &u.u_acflag))) return (error); - ip->i_mode &= ~07777; if (cred->cr_uid) { - if (vp->v_type != VDIR) - mode &= ~ISVTX; - if (!groupmember(ip->i_gid, cred)) - mode &= ~ISGID; + if (vp->v_type != VDIR && (mode & ISVTX)) + return (EFTYPE); + if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) + return (EPERM); } + ip->i_mode &= ~07777; ip->i_mode |= mode & 07777; ip->i_flag |= ICHG; if ((vp->v_flag & VTEXT) && (ip->i_mode & ISVTX) == 0) - xrele(vp); + (void) vnode_pager_uncache(vp); return (0); } @@ -390,10 +495,13 @@ chown1(vp, uid, gid, cred) struct ucred *cred; { register struct inode *ip = VTOI(vp); + uid_t ouid; + gid_t ogid; + int error = 0; #ifdef QUOTA - register long change; + register int i; + long change; #endif - int error; if (uid == (u_short)VNOVAL) uid = ip->i_uid; @@ -408,30 +516,231 @@ chown1(vp, uid, gid, cred) !groupmember((gid_t)gid, cred)) && (error = suser(cred, &u.u_acflag))) return (error); + ouid = ip->i_uid; + ogid = ip->i_gid; #ifdef QUOTA - if (ip->i_uid == uid) /* this just speeds things a little */ - change = 0; - else - change = ip->i_blocks; - (void) chkdq(ip, -change, 1); - (void) chkiq(ip->i_dev, ip, ip->i_uid, 1); - dqrele(ip->i_dquot); + if (error = getinoquota(ip)) + return (error); + if (ouid == uid) { + dqrele(vp, ip->i_dquot[USRQUOTA]); + ip->i_dquot[USRQUOTA] = NODQUOT; + } + if (ogid == gid) { + dqrele(vp, ip->i_dquot[GRPQUOTA]); + ip->i_dquot[GRPQUOTA] = NODQUOT; + } + change = ip->i_blocks; + (void) chkdq(ip, -change, cred, CHOWN); + (void) chkiq(ip, -1, cred, CHOWN); + for (i = 0; i < MAXQUOTAS; i++) { + dqrele(vp, ip->i_dquot[i]); + ip->i_dquot[i] = NODQUOT; + } #endif - if (ip->i_uid != uid && cred->cr_uid != 0) - ip->i_mode &= ~ISUID; - if (ip->i_gid != gid && cred->cr_uid != 0) - ip->i_mode &= ~ISGID; ip->i_uid = uid; ip->i_gid = gid; - ip->i_flag |= ICHG; #ifdef QUOTA - ip->i_dquot = inoquota(ip); - (void) chkdq(ip, change, 1); - (void) chkiq(ip->i_dev, (struct inode *)NULL, (uid_t)uid, 1); - return (u.u_error); /* should == 0 ALWAYS !! */ -#else + if ((error = getinoquota(ip)) == 0) { + if (ouid == uid) { + dqrele(vp, ip->i_dquot[USRQUOTA]); + ip->i_dquot[USRQUOTA] = NODQUOT; + } + if (ogid == gid) { + dqrele(vp, ip->i_dquot[GRPQUOTA]); + ip->i_dquot[GRPQUOTA] = NODQUOT; + } + if ((error = chkdq(ip, change, cred, CHOWN)) == 0) { + if ((error = chkiq(ip, 1, cred, CHOWN)) == 0) + goto good; + else + (void) chkdq(ip, -change, cred, CHOWN|FORCE); + } + for (i = 0; i < MAXQUOTAS; i++) { + dqrele(vp, ip->i_dquot[i]); + ip->i_dquot[i] = NODQUOT; + } + } + ip->i_uid = ouid; + ip->i_gid = ogid; + if (getinoquota(ip) == 0) { + if (ouid == uid) { + dqrele(vp, ip->i_dquot[USRQUOTA]); + ip->i_dquot[USRQUOTA] = NODQUOT; + } + if (ogid == gid) { + dqrele(vp, ip->i_dquot[GRPQUOTA]); + ip->i_dquot[GRPQUOTA] = NODQUOT; + } + (void) chkdq(ip, change, cred, FORCE|CHOWN); + (void) chkiq(ip, 1, cred, FORCE|CHOWN); + (void) getinoquota(ip); + } + return (error); +good: + if (getinoquota(ip)) + panic("chown: lost quota"); +#endif /* QUOTA */ + if (ouid != uid || ogid != gid) + ip->i_flag |= ICHG; + if (ouid != uid && cred->cr_uid != 0) + ip->i_mode &= ~ISUID; + if (ogid != gid && cred->cr_uid != 0) + ip->i_mode &= ~ISGID; return (0); -#endif +} + +/* + * 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; + + 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"); + 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; +{ + 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; + + if (uio->uio_rw != UIO_WRITE) + panic("ufs_write mode"); + 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 && + uio->uio_offset + uio->uio_resid > + u.u_rlimit[RLIMIT_FSIZE].rlim_cur) { + psignal(u.u_procp, 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 */ @@ -443,18 +752,16 @@ ufs_ioctl(vp, com, data, fflag, cred) struct ucred *cred; { - printf("ufs_ioctl called with type %d\n", vp->v_type); return (ENOTTY); } /* ARGSUSED */ -ufs_select(vp, which, cred) +ufs_select(vp, which, fflags, cred) struct vnode *vp; - int which; + int which, fflags; struct ucred *cred; { - printf("ufs_select called with type %d\n", vp->v_type); return (1); /* XXX */ } @@ -487,7 +794,8 @@ ufs_fsync(vp, fflags, cred, waitfor) if (fflags&FWRITE) ip->i_flag |= ICHG; - return (syncip(ip, waitfor)); + vflushbuf(vp, waitfor == MNT_WAIT ? B_SYNC : 0); + return (iupdat(ip, &time, &time, waitfor == MNT_WAIT)); } /* @@ -605,9 +913,13 @@ ufs_rename(fndp, tndp) */ if ((d->d_namlen == 1 && d->d_name[0] == '.') || dp == ip || fndp->ni_isdotdot || (ip->i_flag & IRENAME)) { - IUNLOCK(ip); - ufs_abortop(fndp); - ufs_abortop(tndp); + 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); } ip->i_flag |= IRENAME; @@ -648,7 +960,10 @@ ufs_rename(fndp, tndp) if (oldparent != dp->i_number) newparent = dp->i_number; if (doingdirectory && newparent) { - if (error = iaccess(ip, IWRITE, tndp->ni_cred)) + VOP_LOCK(fndp->ni_vp); + error = ufs_access(fndp->ni_vp, VWRITE, tndp->ni_cred); + VOP_UNLOCK(fndp->ni_vp); + if (error) goto bad; tndp->ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE; do { @@ -730,6 +1045,16 @@ ufs_rename(fndp, tndp) } if (error = dirrewrite(dp, ip, tndp)) goto bad; + /* + * If the target directory is in the same + * directory as the source directory, + * decrement the link count on the parent + * of the target directory. + */ + if (doingdirectory && !newparent) { + dp->i_nlink--; + dp->i_flag |= ICHG; + } vput(ITOV(dp)); /* * Adjust the link count of the target to @@ -745,7 +1070,7 @@ ufs_rename(fndp, tndp) if (doingdirectory) { if (--xp->i_nlink != 0) panic("rename: linked directory"); - error = itrunc(xp, (u_long)0); + error = itrunc(xp, (u_long)0, IO_SYNC); } xp->i_flag |= ICHG; iput(xp); @@ -761,10 +1086,13 @@ ufs_rename(fndp, tndp) xp = VTOI(fndp->ni_vp); dp = VTOI(fndp->ni_dvp); } else { - if (fndp->ni_dvp != NULL) - vput(fndp->ni_dvp); - xp = NULL; - dp = NULL; + /* + * From name has disappeared. + */ + if (doingdirectory) + panic("rename: lost dir entry"); + vrele(ITOV(ip)); + return (0); } /* * Ensure that the directory entry still exists and has not @@ -797,7 +1125,7 @@ ufs_rename(fndp, tndp) if (dirbuf.dotdot_namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { - printf("rename: mangled dir\n"); + dirbad(xp, 12, "rename: mangled dir"); } else { dirbuf.dotdot_ino = newparent; (void) vn_rdwr(UIO_WRITE, ITOV(xp), @@ -868,25 +1196,26 @@ ufs_mkdir(ndp, vap) * directory. The entry is made later * after writing "." and ".." entries out. */ - error = ialloc(dp, dirpref(dp->i_fs), dmode, &tip); - if (error) { + if (error = ialloc(dp, dirpref(dp->i_fs), dmode, ndp->ni_cred, &tip)) { iput(dp); return (error); } ip = tip; + ip->i_uid = ndp->ni_cred->cr_uid; + ip->i_gid = dp->i_gid; #ifdef QUOTA - if (ip->i_dquot != NODQUOT) - panic("mkdir: dquot"); + if ((error = getinoquota(ip)) || + (error = chkiq(ip, 1, ndp->ni_cred, 0))) { + ifree(ip, ip->i_number, dmode); + iput(ip); + iput(dp); + return (error); + } #endif ip->i_flag |= IACC|IUPD|ICHG; ip->i_mode = dmode; ITOV(ip)->v_type = VDIR; /* Rest init'd in iget() */ ip->i_nlink = 2; - ip->i_uid = ndp->ni_cred->cr_uid; - ip->i_gid = dp->i_gid; -#ifdef QUOTA - ip->i_dquot = inoquota(ip); -#endif error = iupdat(ip, &time, &time, 1); /* @@ -914,10 +1243,12 @@ ufs_mkdir(ndp, vap) dp->i_flag |= ICHG; goto bad; } - if (DIRBLKSIZ > dp->i_fs->fs_fsize) + if (DIRBLKSIZ > dp->i_fs->fs_fsize) { panic("mkdir: blksize"); /* XXX - should grow w/balloc() */ - else + } else { ip->i_size = DIRBLKSIZ; + ip->i_flag |= ICHG; + } /* * Directory all set up, now * install the entry for it in @@ -1005,7 +1336,7 @@ ufs_rmdir(ndp) * worry about them later. */ ip->i_nlink -= 2; - error = itrunc(ip, (u_long)0); + error = itrunc(ip, (u_long)0, IO_SYNC); cache_purge(ITOV(ip)); out: if (ndp->ni_dvp) @@ -1037,10 +1368,11 @@ ufs_symlink(ndp, vap, target) /* * Vnode op for read and write */ -ufs_readdir(vp, uio, cred) +ufs_readdir(vp, uio, cred, eofflagp) struct vnode *vp; register struct uio *uio; struct ucred *cred; + int *eofflagp; { int count, lost, error; @@ -1053,6 +1385,10 @@ ufs_readdir(vp, uio, cred) uio->uio_iov->iov_len = count; error = ufs_read(vp, uio, 0, cred); uio->uio_resid += lost; + if ((VTOI(vp)->i_size - uio->uio_offset) <= 0) + *eofflagp = 1; + else + *eofflagp = 0; return (error); } @@ -1070,28 +1406,19 @@ ufs_readlink(vp, uiop, cred) /* * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually - * done. Iff ni_vp/ni_dvp not null and locked, unlock. + * done. Nothing to do at the moment. */ +/* ARGSUSED */ ufs_abortop(ndp) - register struct nameidata *ndp; + struct nameidata *ndp; { - register struct inode *ip; - if (ndp->ni_vp) { - ip = VTOI(ndp->ni_vp); - if (ip->i_flag & ILOCKED) - IUNLOCK(ip); - vrele(ndp->ni_vp); - } - if (ndp->ni_dvp) { - ip = VTOI(ndp->ni_dvp); - if (ip->i_flag & ILOCKED) - IUNLOCK(ip); - vrele(ndp->ni_dvp); - } - return; + return (0); } +/* + * Lock an inode. + */ ufs_lock(vp) struct vnode *vp; { @@ -1101,6 +1428,9 @@ ufs_lock(vp) return (0); } +/* + * Unlock an inode. + */ ufs_unlock(vp) struct vnode *vp; { @@ -1112,6 +1442,18 @@ ufs_unlock(vp) return (0); } +/* + * Check for a locked inode. + */ +ufs_islocked(vp) + struct vnode *vp; +{ + + if (VTOI(vp)->i_flag & ILOCKED) + return (1); + return (0); +} + /* * Get access to bmap */ @@ -1127,19 +1469,196 @@ ufs_bmap(vp, bn, vpp, bnp) *vpp = ip->i_devvp; if (bnp == NULL) return (0); - return (bmap(ip, bn, bnp, (daddr_t *)0, (int *)0)); + return (bmap(ip, bn, bnp)); } /* - * Just call the device strategy routine + * 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; { - (*bdevsw[major(bp->b_dev)].d_strategy)(bp); + register struct inode *ip = VTOI(bp->b_vp); + struct vnode *vp; + int error; + + if (bp->b_vp->v_type == VBLK || bp->b_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)) + return (error); + if ((long)bp->b_blkno == -1) + clrbuf(bp); + } + if ((long)bp->b_blkno == -1) { + 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->vn_strategy))(bp); return (0); } +/* + * Print out the contents of an inode. + */ +ufs_print(vp) + struct vnode *vp; +{ + register struct inode *ip = VTOI(vp); + + printf("tag VT_UFS, ino %d, on dev %d, %d", ip->i_number, + major(ip->i_dev), minor(ip->i_dev)); +#ifdef FIFO + if (vp->v_type == VFIFO) + 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); + printf("\n"); +} + +/* + * Read wrapper for special devices. + */ +ufsspec_read(vp, uio, ioflag, cred) + struct vnode *vp; + struct uio *uio; + int ioflag; + struct ucred *cred; +{ + + /* + * Set access flag. + */ + VTOI(vp)->i_flag |= IACC; + return (spec_read(vp, uio, ioflag, cred)); +} + +/* + * Write wrapper for special devices. + */ +ufsspec_write(vp, uio, ioflag, cred) + struct vnode *vp; + struct uio *uio; + int ioflag; + struct ucred *cred; +{ + + /* + * Set update and change flags. + */ + VTOI(vp)->i_flag |= IUPD|ICHG; + return (spec_write(vp, uio, ioflag, cred)); +} + +/* + * Close wrapper for special devices. + * + * Update the times on the inode then do device close. + */ +ufsspec_close(vp, fflag, cred) + struct vnode *vp; + int fflag; + struct ucred *cred; +{ + register struct inode *ip = VTOI(vp); + + if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) + ITIMES(ip, &time, &time); + return (spec_close(vp, fflag, cred)); +} + +#ifdef FIFO +/* + * Read wrapper for fifo's + */ +ufsfifo_read(vp, uio, ioflag, cred) + struct vnode *vp; + struct uio *uio; + int ioflag; + struct ucred *cred; +{ + + /* + * Set access flag. + */ + VTOI(vp)->i_flag |= IACC; + return (fifo_read(vp, uio, ioflag, cred)); +} + +/* + * Write wrapper for fifo's. + */ +ufsfifo_write(vp, uio, ioflag, cred) + struct vnode *vp; + struct uio *uio; + int ioflag; + struct ucred *cred; +{ + + /* + * Set update and change flags. + */ + VTOI(vp)->i_flag |= IUPD|ICHG; + return (fifo_write(vp, uio, ioflag, cred)); +} + +/* + * Close wrapper for fifo's. + * + * Update the times on the inode then do device close. + */ +ufsfifo_close(vp, fflag, cred) + struct vnode *vp; + int fflag; + struct ucred *cred; +{ + register struct inode *ip = VTOI(vp); + + if (vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) + ITIMES(ip, &time, &time); + return (fifo_close(vp, fflag, cred)); +} +#endif /* FIFO */ + /* * Make a new file. */ @@ -1155,49 +1674,300 @@ maknode(mode, ndp, ipp) int error; *ipp = 0; + if ((mode & IFMT) == 0) + mode |= IFREG; if ((mode & IFMT) == IFDIR) ipref = dirpref(pdir->i_fs); else ipref = pdir->i_number; - error = ialloc(pdir, ipref, mode, &tip); - if (error) { + if (error = ialloc(pdir, ipref, mode, ndp->ni_cred, &tip)) { iput(pdir); return (error); } ip = tip; + ip->i_uid = ndp->ni_cred->cr_uid; + ip->i_gid = pdir->i_gid; #ifdef QUOTA - if (ip->i_dquot != NODQUOT) - panic("maknode: dquot"); + if ((error = getinoquota(ip)) || + (error = chkiq(ip, 1, ndp->ni_cred, 0))) { + ifree(ip, ip->i_number, mode); + iput(ip); + iput(pdir); + return (error); + } #endif ip->i_flag |= IACC|IUPD|ICHG; - if ((mode & IFMT) == 0) - mode |= IFREG; ip->i_mode = mode; ITOV(ip)->v_type = IFTOVT(mode); /* Rest init'd in iget() */ ip->i_nlink = 1; - ip->i_uid = ndp->ni_cred->cr_uid; - ip->i_gid = pdir->i_gid; if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, ndp->ni_cred) && suser(ndp->ni_cred, NULL)) ip->i_mode &= ~ISGID; -#ifdef QUOTA - ip->i_dquot = inoquota(ip); -#endif /* * Make sure inode goes to disk before directory entry. */ - if ((error = iupdat(ip, &time, &time, 1)) || - (error = direnter(ip, ndp))) { + if (error = iupdat(ip, &time, &time, 1)) + goto bad; + if (error = direnter(ip, ndp)) { + pdir = NULL; + goto bad; + } + *ipp = ip; + return (0); + +bad: + /* + * Write error occurred trying to update the inode + * or the directory so must deallocate the inode. + */ + if (pdir) + iput(pdir); + ip->i_nlink = 0; + ip->i_flag |= ICHG; + iput(ip); + return (error); +} + +/* + * 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; +{ + register struct inode *ip = VTOI(vp); + register struct lockf *lock; + off_t start, end; + int error; + + /* + * Avoid the common case of unlocking when inode has no locks. + */ + if (ip->i_lockf == (struct lockf *)0) { + if (op != F_SETLK) { + fl->l_type = F_UNLCK; + return (0); + } + } + /* + * Convert the flock structure into a start and end. + */ + switch (fl->l_whence) { + + case SEEK_SET: + case SEEK_CUR: /* - * Write error occurred trying to update the inode - * or the directory so must deallocate the inode. + * Caller is responsible for adding any necessary offset + * when SEEK_CUR is used. */ - ip->i_nlink = 0; - ip->i_flag |= ICHG; - iput(ip); - return (error); + start = fl->l_start; + break; + + case SEEK_END: + start = ip->i_size + fl->l_start; + break; + + default: + return (EINVAL); } - *ipp = ip; + if (start < 0) + return (EINVAL); + if (fl->l_len == 0) + end = -1; + else + end = start + fl->l_len; + /* + * Create the lockf structure + */ + MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK); + lock->lf_start = start; + lock->lf_end = end; + lock->lf_id = 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; + /* + * Do the requested operation. + */ + switch(op) { + case F_SETLK: + return (ufs_setlock(lock)); + + case F_UNLCK: + return (ufs_advunlock(lock)); + + case F_GETLK: + return (ufs_advgetlock(lock, fl)); + + default: + free(lock, M_LOCKF); + return (EINVAL); + } + /* NOTREACHED */ +} + +/* + * This variable controls the maximum number of processes that will + * be checked in doing deadlock detection. + */ +int maxlockdepth = MAXDEPTH; + +/* + * Set a byte-range lock. + */ +ufs_setlock(lock) + register struct lockf *lock; +{ + register struct inode *ip = lock->lf_inode; + register struct lockf *block; + static char lockstr[] = "lockf"; + int priority, error; + +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + lf_print("ufs_setlock", lock); +#endif /* LOCKF_DEBUG */ + + /* + * Set the priority + */ + priority = PLOCK; + if ((lock->lf_type & F_WRLCK) == 0) + priority += 4; + priority |= PCATCH; + /* + * Scan lock list for this file looking for locks that would block us. + */ + while (block = lf_getblock(lock)) { + /* + * Free the structure and return if nonblocking. + */ + if ((lock->lf_flags & F_WAIT) == 0) { + free(lock, M_LOCKF); + return (EAGAIN); + } + /* + * We are blocked. Since flock style locks cover + * the whole file, there is no chance for deadlock. + * For byte-range locks we must check for deadlock. + * + * Deadlock detection is done by looking through the + * wait channels to see if there are any cycles that + * involve us. MAXDEPTH is set just to make sure we + * do not go off into neverland. + */ + if ((lock->lf_flags & F_POSIX) && + (block->lf_flags & F_POSIX)) { + register struct proc *wproc; + register struct lockf *waitblock; + int i = 0; + + /* The block is waiting on something */ + wproc = (struct proc *)block->lf_id; + while (wproc->p_wchan && + (wproc->p_wmesg == lockstr) && + (i++ < maxlockdepth)) { + waitblock = (struct lockf *)wproc->p_wchan; + /* Get the owner of the blocking lock */ + waitblock = waitblock->lf_next; + if ((waitblock->lf_flags & F_POSIX) == 0) + break; + wproc = (struct proc *)waitblock->lf_id; + if (wproc == (struct proc *)lock->lf_id) { + free(lock, M_LOCKF); + return (EDEADLK); + } + } + } + /* + * Add our lock to the blocked + * list and sleep until we're free. + */ +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + lf_print("ufs_advlock: blocking on", block); +#endif /* LOCKF_DEBUG */ + /* + * Remember who blocked us (for deadlock detection) + */ + lock->lf_next = block; + lf_addblock(block, lock); + if (error = tsleep((caddr_t *)lock, priority, lockstr, 0)) { + free(lock, M_LOCKF); + return (error); + } + } + /* + * No blocks!! Add the lock. Note that addlock will + * downgrade or upgrade any overlapping locks this + * process already owns. + */ +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + lf_print("ufs_advlock: got the lock", lock); +#endif /* LOCKF_DEBUG */ + lf_addlock(lock); + return (0); +} + +/* + * Remove a byte-range lock on an inode. + */ +ufs_advunlock(lock) + struct lockf *lock; +{ + struct lockf *blocklist; + + if (lock->lf_inode->i_lockf == (struct lockf *)0) + return (0); +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + lf_print("ufs_advunlock", lock); +#endif /* LOCKF_DEBUG */ + /* + * Generally, find the lock (or an overlap to that lock) + * and remove it (or shrink it), then wakeup anyone we can. + */ + blocklist = lf_remove(lock); + FREE(lock, M_LOCKF); + lf_wakelock(blocklist); + return (0); +} + +/* + * Return the blocking pid + */ +ufs_advgetlock(lock, fl) + register struct lockf *lock; + register struct flock *fl; +{ + register struct lockf *block; + off_t start, end; + +#ifdef LOCKF_DEBUG + if (lockf_debug & 4) + lf_print("ufs_advgetlock", lock); +#endif /* LOCKF_DEBUG */ + + if (block = lf_getblock(lock)) { + fl->l_type = block->lf_type; + fl->l_whence = SEEK_SET; + fl->l_start = block->lf_start; + if (block->lf_end == -1) + fl->l_len = 0; + else + fl->l_len = block->lf_end - block->lf_start; + if (block->lf_flags & F_POSIX) + fl->l_pid = ((struct proc *)(block->lf_id))->p_pid; + else + fl->l_pid = -1; + } + FREE(lock, M_LOCKF); return (0); }