only delete space used by inode, on inode deletion; required
[unix-history] / usr / src / sys / ufs / lfs / lfs_inode.c
index d93f316..e3e28c9 100644 (file)
@@ -4,7 +4,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)lfs_inode.c 7.52 (Berkeley) %G%
+ *     @(#)lfs_inode.c 7.56 (Berkeley) %G%
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
@@ -34,25 +34,6 @@ lfs_init()
        return (ufs_init());
 }
 
        return (ufs_init());
 }
 
-static daddr_t
-lfs_itod(fs, ino)
-       struct lfs *fs;
-       ino_t ino;
-{
-       BUF *bp;
-       IFILE *ifp;
-       daddr_t iaddr;
-
-       /* Translate an inode number to a disk address. */
-       if (ino == LFS_IFILE_INUM)
-               return (fs->lfs_idaddr);
-
-       LFS_IENTRY(ifp, fs, ino, bp);
-       iaddr = ifp->if_daddr;
-       brelse(bp);
-       return (iaddr);
-}
-
 /*
  * Look up an LFS dinode number to find its incore vnode.  If not already
  * in core, read it in from the specified device.  Return the inode locked.
 /*
  * Look up an LFS dinode number to find its incore vnode.  If not already
  * in core, read it in from the specified device.  Return the inode locked.
@@ -67,8 +48,10 @@ lfs_vget(mntp, ino, vpp)
        register struct lfs *fs;
        register struct inode *ip;
        struct buf *bp;
        register struct lfs *fs;
        register struct inode *ip;
        struct buf *bp;
+       struct ifile *ifp;
        struct vnode *vp;
        struct ufsmount *ump;
        struct vnode *vp;
        struct ufsmount *ump;
+       daddr_t daddr;
        dev_t dev;
        int error;
 
        dev_t dev;
        int error;
 
@@ -80,11 +63,24 @@ lfs_vget(mntp, ino, vpp)
        if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
                return (0);
 
        if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
                return (0);
 
+       /* Translate the inode number to a disk address. */
+       fs = ump->um_lfs;
+       if (ino == LFS_IFILE_INUM)
+               daddr = fs->lfs_idaddr;
+       else {
+               LFS_IENTRY(ifp, fs, ino, bp);
+               daddr = ifp->if_daddr;
+               brelse(bp);
+               if (daddr == LFS_UNUSED_DADDR)
+                       return (ENOENT);
+       }
+
        /* Allocate new vnode/inode. */
        if (error = lfs_vcreate(mntp, ino, &vp)) {
                *vpp = NULL;
                return (error);
        }
        /* Allocate new vnode/inode. */
        if (error = lfs_vcreate(mntp, ino, &vp)) {
                *vpp = NULL;
                return (error);
        }
+
        /*
         * Put it onto its hash chain and lock it so that other requests for
         * this inode will block if they arrive while we are sleeping waiting
        /*
         * Put it onto its hash chain and lock it so that other requests for
         * this inode will block if they arrive while we are sleeping waiting
@@ -94,10 +90,17 @@ lfs_vget(mntp, ino, vpp)
        ip = VTOI(vp);
        ufs_ihashins(ip);
 
        ip = VTOI(vp);
        ufs_ihashins(ip);
 
+       /*
+        * XXX
+        * This may not need to be here, logically it should go down with
+        * the i_devvp initialization.
+        * Ask Kirk.
+        */
+       ip->i_lfs = ump->um_lfs;
+
        /* Read in the disk contents for the inode, copy into the inode. */
        /* Read in the disk contents for the inode, copy into the inode. */
-       ip->i_lfs = fs = ump->um_lfs;
-       if (error = bread(ump->um_devvp, lfs_itod(fs, ino),
-           (int)fs->lfs_bsize, NOCRED, &bp)) {
+       if (error =
+           bread(ump->um_devvp, daddr, (int)fs->lfs_bsize, NOCRED, &bp)) {
                /*
                 * The inode does not contain anything useful, so it
                 * would be misleading to leave it on its hash chain.
                /*
                 * The inode does not contain anything useful, so it
                 * would be misleading to leave it on its hash chain.
@@ -160,80 +163,194 @@ lfs_update(vp, ta, tm, waitfor)
                ip->i_ctime = time.tv_sec;
        ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD);
 
                ip->i_ctime = time.tv_sec;
        ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD);
 
-       /*
-        * XXX
-        * I'm not real sure what to do here; once we have fsync and partial
-        * segments working in the LFS context, this must be fixed to be
-        * correct.  The contents of the inode have to be pushed back to
-        * stable storage.
-        */
-       return (0);
+       /* Push back the vnode and any dirty blocks it may have. */
+       return (waitfor ? lfs_vflush(vp) : 0);
+}
+
+/* Update segment usage information when removing a block. */
+#define UPDATE_SEGUSE \
+       if (lastseg != -1) { \
+               LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \
+               sup->su_nbytes -= fs->lfs_bsize * num; \
+               LFS_UBWRITE(sup_bp); \
+               blocksreleased += num; \
+       }
+
+#define SEGDEC { \
+       if (daddr != UNASSIGNED) { \
+               if (lastseg != (seg = datosn(fs, daddr))) { \
+                       UPDATE_SEGUSE; \
+                       num = 1; \
+                       lastseg = seg; \
+               } else \
+                       ++num; \
+       } \
 }
 
 /*
 }
 
 /*
- * Truncate the inode ip to at most length size.
- *
- * NB: triple indirect blocks are untested.
+ * Truncate the inode ip to at most length size.  Update segment usage
+ * table information.
  */
 /* ARGSUSED */
 int
  */
 /* ARGSUSED */
 int
-lfs_truncate(ovp, length, flags)
-       struct vnode *ovp;
+lfs_truncate(vp, length, flags)
+       struct vnode *vp;
        u_long length;
        int flags;
 {
        u_long length;
        int flags;
 {
-       register struct lfs *fs;
-       register struct inode *oip;
-       struct buf *bp;
-       daddr_t lbn;
-       int error, offset, size;
+       register INDIR *ap;
+       register int i;
+       register daddr_t *daddrp;
+       struct buf *bp, *sup_bp;
+       struct ifile *ifp;
+       struct inode *ip;
+       struct lfs *fs;
+       INDIR a[NIADDR + 2], a_end[NIADDR + 2];
+       SEGUSE *sup;
+       daddr_t daddr, lastblock, lbn, olastblock;
+       off_t off;
+       long blocksreleased;
+       int error, depth, lastseg, num, offset, seg, size;
 
 #ifdef VERBOSE
        printf("lfs_truncate\n");
 #endif
 
 #ifdef VERBOSE
        printf("lfs_truncate\n");
 #endif
-       vnode_pager_setsize(ovp, length);
-       oip = VTOI(ovp);
+       vnode_pager_setsize(vp, length);
+
+       ip = VTOI(vp);
+       fs = ip->i_lfs;
+
+       /* If truncating the file to 0, update the version number. */
+       if (length == 0) {
+               LFS_IENTRY(ifp, fs, ip->i_number, bp);
+               ++ifp->if_version;
+               LFS_UBWRITE(bp);
+       }
 
        /* If length is larger than the file, just update the times. */
 
        /* If length is larger than the file, just update the times. */
-       if (oip->i_size <= length) {
-               oip->i_flag |= ICHG|IUPD;
-               ITIMES(oip, &time, &time);
-               return (0);
+       if (ip->i_size <= length) {
+               ip->i_flag |= ICHG|IUPD;
+               return (lfs_update(vp, &time, &time, 1));
        }
 
        }
 
+       /*
+        * Calculate index into inode's block list of last direct and indirect
+        * blocks (if any) which we want to keep.  Lastblock is 0 when the
+        * file is truncated to 0.
+        */
+       lastblock = lblkno(fs, length + fs->lfs_bsize - 1);
+       olastblock = lblkno(fs, ip->i_size + fs->lfs_bsize - 1) - 1;
+
        /*
         * Update the size of the file. If the file is not being truncated to
         * a block boundry, the contents of the partial block following the end
         * of the file must be zero'ed in case it ever become accessable again
         * because of subsequent file growth.
         */
        /*
         * Update the size of the file. If the file is not being truncated to
         * a block boundry, the contents of the partial block following the end
         * of the file must be zero'ed in case it ever become accessable again
         * because of subsequent file growth.
         */
-       fs = oip->i_lfs;
        offset = blkoff(fs, length);
        if (offset == 0)
        offset = blkoff(fs, length);
        if (offset == 0)
-               oip->i_size = length;
+               ip->i_size = length;
        else {
                lbn = lblkno(fs, length);
 #ifdef QUOTA
        else {
                lbn = lblkno(fs, length);
 #ifdef QUOTA
-               if (error = getinoquota(oip))
+               if (error = getinoquota(ip))
                        return (error);
 #endif 
                        return (error);
 #endif 
-               if (error = bread(ovp, lbn, fs->lfs_bsize, NOCRED, &bp))
+               if (error = bread(vp, lbn, fs->lfs_bsize, NOCRED, &bp))
                        return (error);
                        return (error);
-               oip->i_size = length;
+               ip->i_size = length;
                size = blksize(fs);
                size = blksize(fs);
-               (void)vnode_pager_uncache(ovp);
+               (void)vnode_pager_uncache(vp);
                bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset));
                allocbuf(bp, size);
                LFS_UBWRITE(bp);
        }
        /*
                bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset));
                allocbuf(bp, size);
                LFS_UBWRITE(bp);
        }
        /*
+        * Modify sup->su_nbyte counters for each deleted block; keep track
+        * of number of blocks removed for ip->i_blocks.
+        */
+       blocksreleased = 0;
+       num = 0;
+       lastseg = -1;
+
+       for (lbn = olastblock; lbn >= lastblock;) {
+               lfs_bmaparray(vp, lbn, &daddr, a, &depth);
+               if (lbn == olastblock)
+                       for (i = NIADDR + 2; i--;)
+                               a_end[i] = a[i];
+               switch (depth) {
+               case 0:                         /* Direct block. */
+                       daddr = ip->i_db[lbn];
+                       SEGDEC;
+                       ip->i_db[lbn] = 0;
+                       --lbn;
+                       break;
+#ifdef DIAGNOSTIC
+               case 1:                         /* An indirect block. */
+                       panic("lfs_truncate: lfs_bmaparray returned depth 1");
+                       /* NOTREACHED */
+#endif
+               default:                        /* Chain of indirect blocks. */
+                       ap = a + --depth;
+                       if (ap->in_off > 0 && lbn != lastblock) {
+                               lbn -= ap->in_off < lbn - lastblock ?
+                                   ap->in_off : lbn - lastblock;
+                               break;
+                       }
+                       for (; depth && (ap->in_off == 0 || lbn == lastblock);
+                           --ap, --depth) {
+                               /*
+                                * XXX
+                                * The indirect block may not yet exist, so
+                                * bread will create one just so we can free
+                                * it.
+                                */
+                               if (bread(vp,
+                                   ap->in_lbn, fs->lfs_bsize, NOCRED, &bp))
+                                       panic("lfs_truncate: bread bno %d",
+                                           ap->in_lbn);
+                               daddrp = bp->b_un.b_daddr + ap->in_off;
+                               for (i = ap->in_off;
+                                   i++ <= a_end[depth].in_off;) {
+                                       daddr = *daddrp++;
+                                       SEGDEC;
+                               }
+                               a_end[depth].in_off=NINDIR(fs)-1;
+                               if (ap->in_off > 0 && lbn == lastblock) {
+                                       bzero(bp->b_un.b_daddr + ap->in_off,
+                                           fs->lfs_bsize - 
+                                           ap->in_off * sizeof(daddr_t));
+                                       LFS_UBWRITE(bp);
+                               } else 
+                                       brelse (bp);
+                       }
+                       if (a[1].in_off == 0) {
+                               off = a[0].in_off;
+                               daddr = ip->i_ib[off];
+                               SEGDEC;
+                               ip->i_ib[off] = 0;
+                       }
+                       if (lbn == lastblock)
+                               --lbn;
+                       else {
+                               lbn -= NINDIR(fs);
+                               if (lbn < lastblock)
+                                       lbn = lastblock;
+                       }
+               }
+       }
+       UPDATE_SEGUSE;
+       ip->i_blocks -= blocksreleased;
+       /* 
         * XXX
         * XXX
-        * Bzero inode block pointers here, for consistency with ffs.
-        * Segment usage information has to be updated when the blocks
-        * are free.
-        * Block count in the inode has to be fixed when blocks are
-        * free.
+        * Currently, we don't know when we allocate an indirect block, so
+        * ip->i_blocks isn't getting incremented appropriately.  As a result,
+        * when we delete any indirect blocks, we get a bad number here.
         */
         */
-       (void)vinvalbuf(ovp, length > 0);
+       if (ip->i_blocks < 0)
+               ip->i_blocks = 0;
+       ip->i_flag |= ICHG|IUPD;
+       (void)vinvalbuf(vp, length > 0); 
+       error = lfs_update(vp, &time, &time, MNT_WAIT);
        return (0);
 }
        return (0);
 }