reset directory link counts on I/O failure
[unix-history] / usr / src / sys / ufs / ffs / ufs_vnops.c
index 3641dc8..8716f21 100644 (file)
@@ -2,19 +2,9 @@
  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
  * All rights reserved.
  *
  * 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%
  *
  *
- *     @(#)ufs_vnops.c 7.43 (Berkeley) %G%
+ *     @(#)ufs_vnops.c 7.56 (Berkeley) %G%
  */
 
 #include "param.h"
  */
 
 #include "param.h"
@@ -31,6 +21,9 @@
 #include "mount.h"
 #include "vnode.h"
 #include "specdev.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"
 #include "../ufs/inode.h"
 #include "../ufs/fs.h"
@@ -70,7 +63,8 @@ int   ufs_lookup(),
        ufs_bmap(),
        ufs_strategy(),
        ufs_print(),
        ufs_bmap(),
        ufs_strategy(),
        ufs_print(),
-       ufs_islocked();
+       ufs_islocked(),
+       ufs_advlock();
 
 struct vnodeops ufs_vnodeops = {
        ufs_lookup,             /* lookup */
 
 struct vnodeops ufs_vnodeops = {
        ufs_lookup,             /* lookup */
@@ -105,6 +99,7 @@ struct vnodeops ufs_vnodeops = {
        ufs_strategy,           /* strategy */
        ufs_print,              /* print */
        ufs_islocked,           /* islocked */
        ufs_strategy,           /* strategy */
        ufs_print,              /* print */
        ufs_islocked,           /* islocked */
+       ufs_advlock,            /* advlock */
 };
 
 int    spec_lookup(),
 };
 
 int    spec_lookup(),
@@ -116,8 +111,9 @@ int spec_lookup(),
        spec_ioctl(),
        spec_select(),
        ufsspec_close(),
        spec_ioctl(),
        spec_select(),
        ufsspec_close(),
+       spec_advlock(),
        spec_badop(),
        spec_badop(),
-       spec_nullop();
+       nullop();
 
 struct vnodeops spec_inodeops = {
        spec_lookup,            /* lookup */
 
 struct vnodeops spec_inodeops = {
        spec_lookup,            /* lookup */
@@ -133,7 +129,7 @@ struct vnodeops spec_inodeops = {
        spec_ioctl,             /* ioctl */
        spec_select,            /* select */
        spec_badop,             /* mmap */
        spec_ioctl,             /* ioctl */
        spec_select,            /* select */
        spec_badop,             /* mmap */
-       spec_nullop,            /* fsync */
+       nullop,                 /* fsync */
        spec_badop,             /* seek */
        spec_badop,             /* remove */
        spec_badop,             /* link */
        spec_badop,             /* seek */
        spec_badop,             /* remove */
        spec_badop,             /* link */
@@ -152,6 +148,7 @@ struct vnodeops spec_inodeops = {
        spec_strategy,          /* strategy */
        ufs_print,              /* print */
        ufs_islocked,           /* islocked */
        spec_strategy,          /* strategy */
        ufs_print,              /* print */
        ufs_islocked,           /* islocked */
+       spec_advlock,           /* advlock */
 };
 
 #ifdef FIFO
 };
 
 #ifdef FIFO
@@ -164,8 +161,8 @@ int fifo_lookup(),
        fifo_select(),
        ufsfifo_close(),
        fifo_print(),
        fifo_select(),
        ufsfifo_close(),
        fifo_print(),
-       fifo_badop(),
-       fifo_nullop();
+       fifo_advlock(),
+       fifo_badop();
 
 struct vnodeops fifo_inodeops = {
        fifo_lookup,            /* lookup */
 
 struct vnodeops fifo_inodeops = {
        fifo_lookup,            /* lookup */
@@ -181,7 +178,7 @@ struct vnodeops fifo_inodeops = {
        fifo_ioctl,             /* ioctl */
        fifo_select,            /* select */
        fifo_badop,             /* mmap */
        fifo_ioctl,             /* ioctl */
        fifo_select,            /* select */
        fifo_badop,             /* mmap */
-       fifo_nullop,            /* fsync */
+       nullop,                 /* fsync */
        fifo_badop,             /* seek */
        fifo_badop,             /* remove */
        fifo_badop,             /* link */
        fifo_badop,             /* seek */
        fifo_badop,             /* remove */
        fifo_badop,             /* link */
@@ -200,6 +197,7 @@ struct vnodeops fifo_inodeops = {
        fifo_badop,             /* strategy */
        ufs_print,              /* print */
        ufs_islocked,           /* islocked */
        fifo_badop,             /* strategy */
        ufs_print,              /* print */
        ufs_islocked,           /* islocked */
+       fifo_advlock,           /* advlock */
 };
 #endif /* FIFO */
 
 };
 #endif /* FIFO */
 
@@ -471,17 +469,17 @@ chmod1(vp, mode, cred)
        if (cred->cr_uid != ip->i_uid &&
            (error = suser(cred, &u.u_acflag)))
                return (error);
        if (cred->cr_uid != ip->i_uid &&
            (error = suser(cred, &u.u_acflag)))
                return (error);
-       ip->i_mode &= ~07777;
        if (cred->cr_uid) {
        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)
        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);
 }
 
        return (0);
 }
 
@@ -663,8 +661,8 @@ ufs_write(vp, uio, ioflag, cred)
        struct buf *bp;
        daddr_t lbn, bn;
        u_long osize;
        struct buf *bp;
        daddr_t lbn, bn;
        u_long osize;
-       int i, n, on, flags;
-       int count, size, resid, error = 0;
+       int n, on, flags;
+       int size, resid, error = 0;
 
        if (uio->uio_rw != UIO_WRITE)
                panic("ufs_write mode");
 
        if (uio->uio_rw != UIO_WRITE)
                panic("ufs_write mode");
@@ -715,12 +713,12 @@ ufs_write(vp, uio, ioflag, cred)
                if (error = balloc(ip, lbn, (int)(on + n), &bp, flags))
                        break;
                bn = bp->b_blkno;
                if (error = balloc(ip, lbn, (int)(on + n), &bp, flags))
                        break;
                bn = bp->b_blkno;
-               if (uio->uio_offset + n > ip->i_size)
+               if (uio->uio_offset + n > ip->i_size) {
                        ip->i_size = uio->uio_offset + n;
                        ip->i_size = uio->uio_offset + n;
+                       vnode_pager_setsize(vp, ip->i_size);
+               }
                size = blksize(fs, ip, lbn);
                size = blksize(fs, ip, lbn);
-               count = howmany(size, CLBYTES);
-               for (i = 0; i < count; i++)
-                       munhash(ip->i_devvp, bn + i * CLBYTES / DEV_BSIZE);
+               (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)
                n = MIN(n, size - bp->b_resid);
                error = uiomove(bp->b_un.b_addr + on, n, uio);
                if (ioflag & IO_SYNC)
@@ -850,20 +848,18 @@ ufs_link(vp, ndp)
        register struct inode *ip = VTOI(vp);
        int error;
 
        register struct inode *ip = VTOI(vp);
        int error;
 
+       if ((unsigned short)ip->i_nlink >= LINK_MAX)
+               return (EMLINK);
        if (ndp->ni_dvp != vp)
                ILOCK(ip);
        if (ndp->ni_dvp != vp)
                ILOCK(ip);
-       if (ip->i_nlink == LINK_MAX - 1) {
-               error = EMLINK;
-               goto out;
-       }
        ip->i_nlink++;
        ip->i_flag |= ICHG;
        error = iupdat(ip, &time, &time, 1);
        if (!error)
                error = direnter(ip, ndp);
        ip->i_nlink++;
        ip->i_flag |= ICHG;
        error = iupdat(ip, &time, &time, 1);
        if (!error)
                error = direnter(ip, ndp);
-out:
        if (ndp->ni_dvp != vp)
                IUNLOCK(ip);
        if (ndp->ni_dvp != vp)
                IUNLOCK(ip);
+       vput(ndp->ni_dvp);
        if (error) {
                ip->i_nlink--;
                ip->i_flag |= ICHG;
        if (error) {
                ip->i_nlink--;
                ip->i_flag |= ICHG;
@@ -966,7 +962,8 @@ ufs_rename(fndp, tndp)
                VOP_UNLOCK(fndp->ni_vp);
                if (error)
                        goto bad;
                VOP_UNLOCK(fndp->ni_vp);
                if (error)
                        goto bad;
-               tndp->ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE;
+               tndp->ni_nameiop &= ~(MODMASK | OPMASK);
+               tndp->ni_nameiop |= RENAME | LOCKPARENT | LOCKLEAF | NOCACHE;
                do {
                        dp = VTOI(tndp->ni_dvp);
                        if (xp != NULL)
                do {
                        dp = VTOI(tndp->ni_dvp);
                        if (xp != NULL)
@@ -996,12 +993,23 @@ ufs_rename(fndp, tndp)
                 * parent we don't fool with the link count.
                 */
                if (doingdirectory && newparent) {
                 * parent we don't fool with the link count.
                 */
                if (doingdirectory && newparent) {
+                       if ((unsigned short)dp->i_nlink >= LINK_MAX) {
+                               error = EMLINK;
+                               goto bad;
+                       }
                        dp->i_nlink++;
                        dp->i_flag |= ICHG;
                        dp->i_nlink++;
                        dp->i_flag |= ICHG;
-                       error = iupdat(dp, &time, &time, 1);
+                       if (error = iupdat(dp, &time, &time, 1))
+                               goto bad;
+               }
+               if (error = direnter(ip, tndp)) {
+                       if (doingdirectory && newparent) {
+                               dp->i_nlink--;
+                               dp->i_flag |= ICHG;
+                               (void) iupdat(dp, &time, &time, 1);
+                       }
+                       goto bad;
                }
                }
-               if (error = direnter(ip, tndp))
-                       goto out;
        } else {
                if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
                        panic("rename: EXDEV");
        } else {
                if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
                        panic("rename: EXDEV");
@@ -1046,6 +1054,16 @@ ufs_rename(fndp, tndp)
                }
                if (error = dirrewrite(dp, ip, tndp))
                        goto bad;
                }
                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
                vput(ITOV(dp));
                /*
                 * Adjust the link count of the target to
@@ -1071,16 +1089,20 @@ ufs_rename(fndp, tndp)
        /*
         * 3) Unlink the source.
         */
        /*
         * 3) Unlink the source.
         */
-       fndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF;
+       fndp->ni_nameiop &= ~(MODMASK | OPMASK);
+       fndp->ni_nameiop |= DELETE | LOCKPARENT | LOCKLEAF;
        (void)namei(fndp);
        if (fndp->ni_vp != NULL) {
                xp = VTOI(fndp->ni_vp);
                dp = VTOI(fndp->ni_dvp);
        } else {
        (void)namei(fndp);
        if (fndp->ni_vp != NULL) {
                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
        }
        /*
         * Ensure that the directory entry still exists and has not
@@ -1175,6 +1197,10 @@ ufs_mkdir(ndp, vap)
 
        dvp = ndp->ni_dvp;
        dp = VTOI(dvp);
 
        dvp = ndp->ni_dvp;
        dp = VTOI(dvp);
+       if ((unsigned short)dp->i_nlink >= LINK_MAX) {
+               iput(dp);
+               return (EMLINK);
+       }
        dmode = vap->va_mode&0777;
        dmode |= IFDIR;
        /*
        dmode = vap->va_mode&0777;
        dmode |= IFDIR;
        /*
@@ -1214,7 +1240,8 @@ ufs_mkdir(ndp, vap)
         */
        dp->i_nlink++;
        dp->i_flag |= ICHG;
         */
        dp->i_nlink++;
        dp->i_flag |= ICHG;
-       error = iupdat(dp, &time, &time, 1);
+       if (error = iupdat(dp, &time, &time, 1))
+               goto bad;
 
        /*
         * Initialize directory with "."
 
        /*
         * Initialize directory with "."
@@ -1231,21 +1258,23 @@ ufs_mkdir(ndp, vap)
                dp->i_flag |= ICHG;
                goto bad;
        }
                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() */
                panic("mkdir: blksize");     /* XXX - should grow w/balloc() */
-       else
+       } else {
                ip->i_size = DIRBLKSIZ;
                ip->i_size = DIRBLKSIZ;
+               ip->i_flag |= ICHG;
+       }
        /*
         * Directory all set up, now
         * install the entry for it in
         * the parent directory.
         */
        /*
         * Directory all set up, now
         * install the entry for it in
         * the parent directory.
         */
-       error = direnter(ip, ndp);
-       dp = NULL;
-       if (error) {
-               ndp->ni_nameiop = LOOKUP | NOCACHE;
+       if (error = direnter(ip, ndp)) {
+               ndp->ni_nameiop &= ~(MODMASK | OPMASK);
+               ndp->ni_nameiop |= LOOKUP | LOCKLEAF | NOCACHE;
                error = namei(ndp);
                if (!error) {
                error = namei(ndp);
                if (!error) {
+                       iput(dp);
                        dp = VTOI(ndp->ni_vp);
                        dp->i_nlink--;
                        dp->i_flag |= ICHG;
                        dp = VTOI(ndp->ni_vp);
                        dp->i_nlink--;
                        dp->i_flag |= ICHG;
@@ -1263,8 +1292,7 @@ bad:
                iput(ip);
        } else
                ndp->ni_vp = ITOV(ip);
                iput(ip);
        } else
                ndp->ni_vp = ITOV(ip);
-       if (dp)
-               iput(dp);
+       iput(dp);
        return (error);
 }
 
        return (error);
 }
 
@@ -1695,10 +1723,9 @@ maknode(mode, ndp, ipp)
         */
        if (error = iupdat(ip, &time, &time, 1))
                goto bad;
         */
        if (error = iupdat(ip, &time, &time, 1))
                goto bad;
-       if (error = direnter(ip, ndp)) {
-               pdir = NULL;
+       if (error = direnter(ip, ndp))
                goto bad;
                goto bad;
-       }
+       iput(pdir);
        *ipp = ip;
        return (0);
 
        *ipp = ip;
        return (0);
 
@@ -1707,10 +1734,96 @@ bad:
         * Write error occurred trying to update the inode
         * or the directory so must deallocate the inode.
         */
         * Write error occurred trying to update the inode
         * or the directory so must deallocate the inode.
         */
-       if (pdir)
-               iput(pdir);
+       iput(pdir);
        ip->i_nlink = 0;
        ip->i_flag |= ICHG;
        iput(ip);
        return (error);
 }
        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:
+               /*
+                * Caller is responsible for adding any necessary offset
+                * when SEEK_CUR is used.
+                */
+               start = fl->l_start;
+               break;
+
+       case SEEK_END:
+               start = ip->i_size + fl->l_start;
+               break;
+
+       default:
+               return (EINVAL);
+       }
+       if (start < 0)
+               return (EINVAL);
+       if (fl->l_len == 0)
+               end = -1;
+       else
+               end = start + fl->l_len - 1;
+       /*
+        * 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 (lf_setlock(lock));
+
+       case F_UNLCK:
+               error = lf_clearlock(lock);
+               FREE(lock, M_LOCKF);
+               return (error);
+
+       case F_GETLK:
+               error = lf_getlock(lock, fl);
+               FREE(lock, M_LOCKF);
+               return (error);
+       
+       default:
+               free(lock, M_LOCKF);
+               return (EINVAL);
+       }
+       /* NOTREACHED */
+}