BSD 4_4_Lite1 release
[unix-history] / usr / src / sys / ufs / lfs / lfs_inode.c
index 9cc659b..1a06aa2 100644 (file)
@@ -1,10 +1,36 @@
 /*
 /*
- * Copyright (c) 1986, 1989, 1991 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1986, 1989, 1991, 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.
  *
  *
- *     @(#)lfs_inode.c 7.63 (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.
+ *
+ *     @(#)lfs_inode.c 8.5 (Berkeley) 12/30/93
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
 #include <ufs/lfs/lfs.h>
 #include <ufs/lfs/lfs_extern.h>
 
 #include <ufs/lfs/lfs.h>
 #include <ufs/lfs/lfs_extern.h>
 
-static struct dinode *lfs_ifind __P((struct lfs *, ino_t, struct dinode *));
-
 int
 lfs_init()
 {
 int
 lfs_init()
 {
-#ifdef VERBOSE
-       printf("lfs_init\n");
-#endif
        return (ufs_init());
 }
 
        return (ufs_init());
 }
 
-/*
- * 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.
- * Detection and handling of mount points must be done by the calling routine.
- */
-int
-lfs_vget(mntp, ino, vpp)
-       struct mount *mntp;
-       ino_t ino;
-       struct vnode **vpp;
-{
-       register struct lfs *fs;
-       register struct inode *ip;
-       struct buf *bp;
-       struct ifile *ifp;
-       struct vnode *vp;
-       struct ufsmount *ump;
-       daddr_t daddr;
-       dev_t dev;
-       int error;
-
-#ifdef VERBOSE
-       printf("lfs_vget\n");
-#endif
-       ump = VFSTOUFS(mntp);
-       dev = ump->um_dev;
-       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);
-       }
-
-       /*
-        * 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
-        * for old data structures to be purged or for the contents of the
-        * disk portion of this inode to be read.
-        */
-       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. */
-       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.
-                * Iput() will return it to the free list.
-                */
-               remque(ip);
-               ip->i_forw = ip;
-               ip->i_back = ip;
-
-               /* Unlock and discard unneeded inode. */
-               ufs_iput(ip);
-               brelse(bp);
-               *vpp = NULL;
-               return (error);
-       }
-       ip->i_din = *lfs_ifind(fs, ino, bp->b_un.b_dino);
-       brelse(bp);
-
-       /*
-        * Initialize the vnode from the inode, check for aliases.  In all
-        * cases re-init ip, the underlying vnode/inode may have changed.
-        */
-       if (error = ufs_vinit(mntp, &lfs_specops, LFS_FIFOOPS, &vp)) {
-               ufs_iput(ip);
-               *vpp = NULL;
-               return (error);
-       }
-       /*
-        * Finish inode initialization now that aliasing has been resolved.
-        */
-       ip->i_devvp = ump->um_devvp;
-       VREF(ip->i_devvp);
-       *vpp = vp;
-       return (0);
-}
-
 /* Search a block for a specific dinode. */
 /* Search a block for a specific dinode. */
-static struct dinode *
+struct dinode *
 lfs_ifind(fs, ino, dip)
        struct lfs *fs;
        ino_t ino;
        register struct dinode *dip;
 {
        register int cnt;
 lfs_ifind(fs, ino, dip)
        struct lfs *fs;
        ino_t ino;
        register struct dinode *dip;
 {
        register int cnt;
+       register struct dinode *ldip;
 
 
-#ifdef VERBOSE
-       printf("lfs_ifind: inode %d\n", ino);
-#endif
-       for (cnt = INOPB(fs); cnt--; ++dip)
-               if (dip->di_inum == ino)
-                       return (dip);
+       for (cnt = INOPB(fs), ldip = dip + (cnt - 1); cnt--; --ldip)
+               if (ldip->di_inumber == ino)
+                       return (ldip);
 
        panic("lfs_ifind: dinode %u not found", ino);
        /* NOTREACHED */
 }
 
 int
 
        panic("lfs_ifind: dinode %u not found", ino);
        /* NOTREACHED */
 }
 
 int
-lfs_update(vp, ta, tm, waitfor)
-       register struct vnode *vp;
-       struct timeval *ta, *tm;
-        int waitfor;
+lfs_update(ap)
+       struct vop_update_args /* {
+               struct vnode *a_vp;
+               struct timeval *a_access;
+               struct timeval *a_modify;
+               int a_waitfor;
+       } */ *ap;
 {
 {
+       struct vnode *vp = ap->a_vp;
        struct inode *ip;
 
        struct inode *ip;
 
-#ifdef VERBOSE
-       printf("lfs_update\n");
-#endif
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                return (0);
        ip = VTOI(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                return (0);
        ip = VTOI(vp);
-       if ((ip->i_flag & (IUPD|IACC|ICHG|IMOD)) == 0)
+       if ((ip->i_flag &
+           (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0)
                return (0);
                return (0);
-       if (ip->i_flag&IACC)
-               ip->i_atime.tv_sec = ta->tv_sec;
-       if (ip->i_flag&IUPD) {
-               ip->i_mtime.tv_sec = tm->tv_sec;
-               INCRQUAD((ip)->i_modrev);
+       if (ip->i_flag & IN_ACCESS)
+               ip->i_atime.ts_sec = ap->a_access->tv_sec;
+       if (ip->i_flag & IN_UPDATE) {
+               ip->i_mtime.ts_sec = ap->a_modify->tv_sec;
+               (ip)->i_modrev++;
        }
        }
-       if (ip->i_flag&ICHG)
-               ip->i_ctime.tv_sec = time.tv_sec;
-       ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD);
+       if (ip->i_flag & IN_CHANGE)
+               ip->i_ctime.ts_sec = time.tv_sec;
+       ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
 
 
-       /* Push back the vnode and any dirty blocks it may have. */
-       return (waitfor ? lfs_vflush(vp) : 0);
+       if (!(ip->i_flag & IN_MODIFIED))
+               ++(VFSTOUFS(vp->v_mount)->um_lfs->lfs_uinodes);
+       ip->i_flag |= IN_MODIFIED;
+
+       /* If sync, push back the vnode and any dirty blocks it may have. */
+       return (ap->a_waitfor & LFS_SYNC ? 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); \
 }
 
 /* Update segment usage information when removing a block. */
 #define UPDATE_SEGUSE \
        if (lastseg != -1) { \
                LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \
+               if ((num << fs->lfs_bshift) > sup->su_nbytes) \
+                       panic("lfs_truncate: negative bytes in segment %d\n", \
+                           lastseg); \
                sup->su_nbytes -= num << fs->lfs_bshift; \
                sup->su_nbytes -= num << fs->lfs_bshift; \
-               LFS_UBWRITE(sup_bp); \
+               e1 = VOP_BWRITE(sup_bp); \
                blocksreleased += num; \
        }
 
 #define SEGDEC { \
                blocksreleased += num; \
        }
 
 #define SEGDEC { \
-       if (daddr != UNASSIGNED) { \
+       if (daddr != 0) { \
                if (lastseg != (seg = datosn(fs, daddr))) { \
                        UPDATE_SEGUSE; \
                        num = 1; \
                if (lastseg != (seg = datosn(fs, daddr))) { \
                        UPDATE_SEGUSE; \
                        num = 1; \
@@ -217,44 +142,51 @@ lfs_update(vp, ta, tm, waitfor)
  */
 /* ARGSUSED */
 int
  */
 /* ARGSUSED */
 int
-lfs_truncate(vp, length, flags, cred)
-       struct vnode *vp;
-       off_t length;
-       int flags;
-       struct ucred *cred;
+lfs_truncate(ap)
+       struct vop_truncate_args /* {
+               struct vnode *a_vp;
+               off_t a_length;
+               int a_flags;
+               struct ucred *a_cred;
+               struct proc *a_p;
+       } */ *ap;
 {
 {
-       register INDIR *ap;
+       register struct indir *inp;
        register int i;
        register daddr_t *daddrp;
        register int i;
        register daddr_t *daddrp;
+       register struct vnode *vp = ap->a_vp;
+       off_t length = ap->a_length;
        struct buf *bp, *sup_bp;
        struct buf *bp, *sup_bp;
+       struct timeval tv;
        struct ifile *ifp;
        struct inode *ip;
        struct lfs *fs;
        struct ifile *ifp;
        struct inode *ip;
        struct lfs *fs;
-       INDIR a[NIADDR + 2], a_end[NIADDR + 2];
+       struct indir a[NIADDR + 2], a_end[NIADDR + 2];
        SEGUSE *sup;
        daddr_t daddr, lastblock, lbn, olastblock;
        SEGUSE *sup;
        daddr_t daddr, lastblock, lbn, olastblock;
-       long off, blocksreleased;
-       int error, depth, lastseg, num, offset, seg, size;
+       long off, a_released, blocksreleased, i_released;
+       int e1, e2, depth, lastseg, num, offset, seg, size;
 
 
-#ifdef VERBOSE
-       printf("lfs_truncate\n");
+       ip = VTOI(vp);
+       tv = time;
+       if (vp->v_type == VLNK && vp->v_mount->mnt_maxsymlinklen > 0) {
+#ifdef DIAGNOSTIC
+               if (length != 0)
+                       panic("lfs_truncate: partial truncate of symlink");
 #endif
 #endif
+               bzero((char *)&ip->i_shortlink, (u_int)ip->i_size);
+               ip->i_size = 0;
+               ip->i_flag |= IN_CHANGE | IN_UPDATE;
+               return (VOP_UPDATE(vp, &tv, &tv, 0));
+       }
        vnode_pager_setsize(vp, (u_long)length);
 
        vnode_pager_setsize(vp, (u_long)length);
 
-       ip = VTOI(vp);
        fs = ip->i_lfs;
 
        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 (ip->i_size <= length) {
        /* If length is larger than the file, just update the times. */
        if (ip->i_size <= length) {
-               ip->i_flag |= ICHG|IUPD;
-               return (lfs_update(vp, &time, &time, 1));
+               ip->i_flag |= IN_CHANGE | IN_UPDATE;
+               return (VOP_UPDATE(vp, &tv, &tv, 0));
        }
 
        /*
        }
 
        /*
@@ -277,17 +209,18 @@ lfs_truncate(vp, length, flags, cred)
        else {
                lbn = lblkno(fs, length);
 #ifdef QUOTA
        else {
                lbn = lblkno(fs, length);
 #ifdef QUOTA
-               if (error = getinoquota(ip))
-                       return (error);
+               if (e1 = getinoquota(ip))
+                       return (e1);
 #endif 
 #endif 
-               if (error = bread(vp, lbn, fs->lfs_bsize, NOCRED, &bp))
-                       return (error);
+               if (e1 = bread(vp, lbn, fs->lfs_bsize, NOCRED, &bp))
+                       return (e1);
                ip->i_size = length;
                size = blksize(fs);
                (void)vnode_pager_uncache(vp);
                ip->i_size = length;
                size = blksize(fs);
                (void)vnode_pager_uncache(vp);
-               bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset));
+               bzero((char *)bp->b_data + offset, (u_int)(size - offset));
                allocbuf(bp, size);
                allocbuf(bp, size);
-               LFS_UBWRITE(bp);
+               if (e1 = VOP_BWRITE(bp))
+                       return (e1);
        }
        /*
         * Modify sup->su_nbyte counters for each deleted block; keep track
        }
        /*
         * Modify sup->su_nbyte counters for each deleted block; keep track
@@ -298,7 +231,8 @@ lfs_truncate(vp, length, flags, cred)
        lastseg = -1;
 
        for (lbn = olastblock; lbn >= lastblock;) {
        lastseg = -1;
 
        for (lbn = olastblock; lbn >= lastblock;) {
-               lfs_bmaparray(vp, lbn, &daddr, a, &depth);
+               /* XXX use run length from bmap array to make this faster */
+               ufs_bmaparray(vp, lbn, &daddr, a, &depth, NULL);
                if (lbn == olastblock)
                        for (i = NIADDR + 2; i--;)
                                a_end[i] = a[i];
                if (lbn == olastblock)
                        for (i = NIADDR + 2; i--;)
                                a_end[i] = a[i];
@@ -311,42 +245,37 @@ lfs_truncate(vp, length, flags, cred)
                        break;
 #ifdef DIAGNOSTIC
                case 1:                         /* An indirect block. */
                        break;
 #ifdef DIAGNOSTIC
                case 1:                         /* An indirect block. */
-                       panic("lfs_truncate: lfs_bmaparray returned depth 1");
+                       panic("lfs_truncate: ufs_bmaparray returned depth 1");
                        /* NOTREACHED */
 #endif
                default:                        /* Chain of indirect blocks. */
                        /* 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;
+                       inp = a + --depth;
+                       if (inp->in_off > 0 && lbn != lastblock) {
+                               lbn -= inp->in_off < lbn - lastblock ?
+                                   inp->in_off : lbn - lastblock;
                                break;
                        }
                                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.
-                                */
+                       for (; depth && (inp->in_off == 0 || lbn == lastblock);
+                           --inp, --depth) {
                                if (bread(vp,
                                if (bread(vp,
-                                   ap->in_lbn, fs->lfs_bsize, NOCRED, &bp))
+                                   inp->in_lbn, fs->lfs_bsize, NOCRED, &bp))
                                        panic("lfs_truncate: bread bno %d",
                                        panic("lfs_truncate: bread bno %d",
-                                           ap->in_lbn);
-                               daddrp = bp->b_un.b_daddr + ap->in_off;
-                               for (i = ap->in_off;
+                                           inp->in_lbn);
+                               daddrp = (daddr_t *)bp->b_data + inp->in_off;
+                               for (i = inp->in_off;
                                    i++ <= a_end[depth].in_off;) {
                                        daddr = *daddrp++;
                                        SEGDEC;
                                }
                                a_end[depth].in_off = NINDIR(fs) - 1;
                                    i++ <= a_end[depth].in_off;) {
                                        daddr = *daddrp++;
                                        SEGDEC;
                                }
                                a_end[depth].in_off = NINDIR(fs) - 1;
-                               if (ap->in_off == 0)
+                               if (inp->in_off == 0)
                                        brelse (bp);
                                else {
                                        brelse (bp);
                                else {
-                                       bzero(bp->b_un.b_daddr + ap->in_off,
-                                           fs->lfs_bsize - 
-                                           ap->in_off * sizeof(daddr_t));
-                                       LFS_UBWRITE(bp);
+                                       bzero((daddr_t *)bp->b_data +
+                                           inp->in_off, fs->lfs_bsize - 
+                                           inp->in_off * sizeof(daddr_t));
+                                       if (e1 = VOP_BWRITE(bp)) 
+                                               return (e1);
                                }
                        }
                        if (depth == 0 && a[1].in_off == 0) {
                                }
                        }
                        if (depth == 0 && a[1].in_off == 0) {
@@ -365,17 +294,66 @@ lfs_truncate(vp, length, flags, cred)
                }
        }
        UPDATE_SEGUSE;
                }
        }
        UPDATE_SEGUSE;
-       ip->i_blocks -= blocksreleased;
-       /* 
-        * XXX
-        * 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.
+
+       /* If truncating the file to 0, update the version number. */
+       if (length == 0) {
+               LFS_IENTRY(ifp, fs, ip->i_number, bp);
+               ++ifp->if_version;
+               (void) VOP_BWRITE(bp);
+       }
+
+#ifdef DIAGNOSTIC
+       if (ip->i_blocks < fsbtodb(fs, blocksreleased)) {
+               printf("lfs_truncate: block count < 0\n");
+               blocksreleased = ip->i_blocks;
+       }
+#endif
+       ip->i_blocks -= fsbtodb(fs, blocksreleased);
+       fs->lfs_bfree +=  fsbtodb(fs, blocksreleased);
+       ip->i_flag |= IN_CHANGE | IN_UPDATE;
+       /*
+        * Traverse dirty block list counting number of dirty buffers
+        * that are being deleted out of the cache, so that the lfs_avail
+        * field can be updated.
         */
         */
-       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);
+       a_released = 0;
+       i_released = 0;
+       for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next)
+               if (bp->b_flags & B_LOCKED) {
+                       ++a_released;
+                       /*
+                        * XXX
+                        * When buffers are created in the cache, their block
+                        * number is set equal to their logical block number.
+                        * If that is still true, we are assuming that the
+                        * blocks are new (not yet on disk) and weren't
+                        * counted above.  However, there is a slight chance
+                        * that a block's disk address is equal to its logical
+                        * block number in which case, we'll get an overcounting
+                        * here.
+                        */
+                       if (bp->b_blkno == bp->b_lblkno)
+                               ++i_released;
+               }
+       blocksreleased = fsbtodb(fs, i_released);
+#ifdef DIAGNOSTIC
+       if (blocksreleased > ip->i_blocks) {
+               printf("lfs_inode: Warning! %s\n",
+                   "more blocks released from inode than are in inode");
+               blocksreleased = ip->i_blocks;
+       }
+#endif
+       fs->lfs_bfree += blocksreleased;
+       ip->i_blocks -= blocksreleased;
+#ifdef DIAGNOSTIC
+       if (length == 0 && ip->i_blocks != 0)
+               printf("lfs_inode: Warning! %s%d%s\n",
+                   "Truncation to zero, but ", ip->i_blocks,
+                   " blocks left on inode");
+#endif
+       fs->lfs_avail += fsbtodb(fs, a_released);
+       e1 = vinvalbuf(vp, (length > 0) ? V_SAVE : 0, ap->a_cred, ap->a_p,
+           0, 0); 
+       e2 = VOP_UPDATE(vp, &tv, &tv, 0);
+       return (e1 ? e1 : e2 ? e2 : 0);
 }
 }