BSD 4_4_Lite1 release
[unix-history] / usr / src / sys / ufs / ffs / ffs_balloc.c
index 934d13b..752feec 100644 (file)
@@ -1,45 +1,80 @@
-/*     ffs_balloc.c    6.2     84/08/29        */
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *     @(#)ffs_balloc.c        8.4 (Berkeley) 9/23/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+
+#include <vm/vm.h>
 
 
-#include "param.h"
-#include "systm.h"
-#include "conf.h"
-#include "inode.h"
-#include "dir.h"
-#include "user.h"
-#include "buf.h"
-#include "proc.h"
-#include "fs.h"
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ffs/ffs_extern.h>
 
 /*
 
 /*
- * Bmap defines the structure of file system storage
- * by returning the physical block number on a device given the
- * inode and the logical block number in a file.
- * When convenient, it also leaves the physical
- * block number of the next block of the file in rablock
- * for use in read-ahead.
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
  */
  */
-/*VARARGS3*/
-daddr_t
-bmap(ip, bn, rwflg, size)
+ffs_balloc(ip, bn, size, cred, bpp, flags)
        register struct inode *ip;
        register struct inode *ip;
-       daddr_t bn;
-       int rwflg;
-       int size;       /* supplied only when rwflg == B_WRITE */
+       register daddr_t bn;
+       int size;
+       struct ucred *cred;
+       struct buf **bpp;
+       int flags;
 {
 {
-       register int i;
-       int osize, nsize;
+       register struct fs *fs;
+       register daddr_t nb;
        struct buf *bp, *nbp;
        struct buf *bp, *nbp;
-       struct fs *fs;
-       int j, sh;
-       daddr_t nb, lbn, *bap, pref, blkpref();
+       struct vnode *vp = ITOV(ip);
+       struct indir indirs[NIADDR + 2];
+       daddr_t newb, lbn, *bap, pref;
+       int osize, nsize, num, i, error;
 
 
-       if (bn < 0) {
-               u.u_error = EFBIG;
-               return ((daddr_t)0);
-       }
+       *bpp = NULL;
+       if (bn < 0)
+               return (EFBIG);
        fs = ip->i_fs;
        fs = ip->i_fs;
-       rablock = 0;
-       rasize = 0;             /* conservative */
+       lbn = bn;
 
        /*
         * If the next write will extend the file into a new block,
 
        /*
         * If the next write will extend the file into a new block,
@@ -47,18 +82,22 @@ bmap(ip, bn, rwflg, size)
         * this fragment has to be extended to be a full block.
         */
        nb = lblkno(fs, ip->i_size);
         * this fragment has to be extended to be a full block.
         */
        nb = lblkno(fs, ip->i_size);
-       if (rwflg == B_WRITE && nb < NDADDR && nb < bn) {
+       if (nb < NDADDR && nb < bn) {
                osize = blksize(fs, ip, nb);
                if (osize < fs->fs_bsize && osize > 0) {
                osize = blksize(fs, ip, nb);
                if (osize < fs->fs_bsize && osize > 0) {
-                       bp = realloccg(ip, ip->i_db[nb],
-                               blkpref(ip, nb, (int)nb, &ip->i_db[0]),
-                               osize, (int)fs->fs_bsize);
-                       if (bp == NULL)
-                               return ((daddr_t)-1);
+                       error = ffs_realloccg(ip, nb,
+                               ffs_blkpref(ip, nb, (int)nb, &ip->i_db[0]),
+                               osize, (int)fs->fs_bsize, cred, &bp);
+                       if (error)
+                               return (error);
                        ip->i_size = (nb + 1) * fs->fs_bsize;
                        ip->i_size = (nb + 1) * fs->fs_bsize;
+                       vnode_pager_setsize(vp, (u_long)ip->i_size);
                        ip->i_db[nb] = dbtofsb(fs, bp->b_blkno);
                        ip->i_db[nb] = dbtofsb(fs, bp->b_blkno);
-                       ip->i_flag |= IUPD|ICHG;
-                       bdwrite(bp);
+                       ip->i_flag |= IN_CHANGE | IN_UPDATE;
+                       if (flags & B_SYNC)
+                               bwrite(bp);
+                       else
+                               bawrite(bp);
                }
        }
        /*
                }
        }
        /*
@@ -66,143 +105,178 @@ bmap(ip, bn, rwflg, size)
         */
        if (bn < NDADDR) {
                nb = ip->i_db[bn];
         */
        if (bn < NDADDR) {
                nb = ip->i_db[bn];
-               if (rwflg == B_READ) {
-                       if (nb == 0)
-                               return ((daddr_t)-1);
-                       goto gotit;
+               if (nb != 0 && ip->i_size >= (bn + 1) * fs->fs_bsize) {
+                       error = bread(vp, bn, fs->fs_bsize, NOCRED, &bp);
+                       if (error) {
+                               brelse(bp);
+                               return (error);
+                       }
+                       *bpp = bp;
+                       return (0);
                }
                }
-               if (nb == 0 || ip->i_size < (bn + 1) * fs->fs_bsize) {
-                       if (nb != 0) {
-                               /* consider need to reallocate a frag */
-                               osize = fragroundup(fs, blkoff(fs, ip->i_size));
-                               nsize = fragroundup(fs, size);
-                               if (nsize <= osize)
-                                       goto gotit;
-                               bp = realloccg(ip, nb,
-                                       blkpref(ip, bn, (int)bn, &ip->i_db[0]),
-                                       osize, nsize);
+               if (nb != 0) {
+                       /*
+                        * Consider need to reallocate a fragment.
+                        */
+                       osize = fragroundup(fs, blkoff(fs, ip->i_size));
+                       nsize = fragroundup(fs, size);
+                       if (nsize <= osize) {
+                               error = bread(vp, bn, osize, NOCRED, &bp);
+                               if (error) {
+                                       brelse(bp);
+                                       return (error);
+                               }
                        } else {
                        } else {
-                               if (ip->i_size < (bn + 1) * fs->fs_bsize)
-                                       nsize = fragroundup(fs, size);
-                               else
-                                       nsize = fs->fs_bsize;
-                               bp = alloc(ip,
-                                       blkpref(ip, bn, (int)bn, &ip->i_db[0]),
-                                       nsize);
+                               error = ffs_realloccg(ip, bn,
+                                   ffs_blkpref(ip, bn, (int)bn, &ip->i_db[0]),
+                                   osize, nsize, cred, &bp);
+                               if (error)
+                                       return (error);
                        }
                        }
-                       if (bp == NULL)
-                               return ((daddr_t)-1);
-                       nb = dbtofsb(fs, bp->b_blkno);
-                       if ((ip->i_mode&IFMT) == IFDIR)
-                               /*
-                                * Write directory blocks synchronously
-                                * so they never appear with garbage in
-                                * them on the disk.
-                                */
-                               bwrite(bp);
+               } else {
+                       if (ip->i_size < (bn + 1) * fs->fs_bsize)
+                               nsize = fragroundup(fs, size);
                        else
                        else
-                               bdwrite(bp);
-                       ip->i_db[bn] = nb;
-                       ip->i_flag |= IUPD|ICHG;
-               }
-gotit:
-               if (bn < NDADDR - 1) {
-                       rablock = fsbtodb(fs, ip->i_db[bn + 1]);
-                       rasize = blksize(fs, ip, bn + 1);
+                               nsize = fs->fs_bsize;
+                       error = ffs_alloc(ip, bn,
+                           ffs_blkpref(ip, bn, (int)bn, &ip->i_db[0]),
+                           nsize, cred, &newb);
+                       if (error)
+                               return (error);
+                       bp = getblk(vp, bn, nsize, 0, 0);
+                       bp->b_blkno = fsbtodb(fs, newb);
+                       if (flags & B_CLRBUF)
+                               clrbuf(bp);
                }
                }
-               return (nb);
+               ip->i_db[bn] = dbtofsb(fs, bp->b_blkno);
+               ip->i_flag |= IN_CHANGE | IN_UPDATE;
+               *bpp = bp;
+               return (0);
        }
        }
-
        /*
        /*
-        * Determine how many levels of indirection.
+        * Determine the number of levels of indirection.
         */
        pref = 0;
         */
        pref = 0;
-       sh = 1;
-       lbn = bn;
-       bn -= NDADDR;
-       for (j = NIADDR; j>0; j--) {
-               sh *= NINDIR(fs);
-               if (bn < sh)
-                       break;
-               bn -= sh;
-       }
-       if (j == 0) {
-               u.u_error = EFBIG;
-               return ((daddr_t)0);
-       }
-
+       if (error = ufs_getlbns(vp, bn, indirs, &num))
+               return(error);
+#ifdef DIAGNOSTIC
+       if (num < 1)
+               panic ("ffs_balloc: ufs_bmaparray returned indirect block\n");
+#endif
        /*
        /*
-        * fetch the first indirect block
+        * Fetch the first indirect block allocating if necessary.
         */
         */
-       nb = ip->i_ib[NIADDR - j];
+       --num;
+       nb = ip->i_ib[indirs[0].in_off];
        if (nb == 0) {
        if (nb == 0) {
-               if (rwflg == B_READ)
-                       return ((daddr_t)-1);
-               pref = blkpref(ip, lbn, 0, (daddr_t *)0);
-               bp = alloc(ip, pref, (int)fs->fs_bsize);
-               if (bp == NULL)
-                       return ((daddr_t)-1);
-               nb = dbtofsb(fs, bp->b_blkno);
+               pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0);
+               if (error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
+                   cred, &newb))
+                       return (error);
+               nb = newb;
+               bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
+               bp->b_blkno = fsbtodb(fs, newb);
+               clrbuf(bp);
                /*
                 * Write synchronously so that indirect blocks
                 * never point at garbage.
                 */
                /*
                 * Write synchronously so that indirect blocks
                 * never point at garbage.
                 */
-               bwrite(bp);
-               ip->i_ib[NIADDR - j] = nb;
-               ip->i_flag |= IUPD|ICHG;
+               if (error = bwrite(bp)) {
+                       ffs_blkfree(ip, nb, fs->fs_bsize);
+                       return (error);
+               }
+               ip->i_ib[indirs[0].in_off] = newb;
+               ip->i_flag |= IN_CHANGE | IN_UPDATE;
        }
        }
-
        /*
        /*
-        * fetch through the indirect blocks
+        * Fetch through the indirect blocks, allocating as necessary.
         */
         */
-       for (; j <= NIADDR; j++) {
-               bp = bread(ip->i_dev, fsbtodb(fs, nb), (int)fs->fs_bsize);
-               if (bp->b_flags & B_ERROR) {
+       for (i = 1;;) {
+               error = bread(vp,
+                   indirs[i].in_lbn, (int)fs->fs_bsize, NOCRED, &bp);
+               if (error) {
                        brelse(bp);
                        brelse(bp);
-                       return ((daddr_t)0);
+                       return (error);
                }
                }
-               bap = bp->b_un.b_daddr;
-               sh /= NINDIR(fs);
-               i = (bn / sh) % NINDIR(fs);
-               nb = bap[i];
-               if (nb == 0) {
-                       if (rwflg==B_READ) {
-                               brelse(bp);
-                               return ((daddr_t)-1);
-                       }
-                       if (pref == 0)
-                               if (j < NIADDR)
-                                       pref = blkpref(ip, lbn, 0,
-                                               (daddr_t *)0);
-                               else
-                                       pref = blkpref(ip, lbn, i, &bap[0]);
-                       nbp = alloc(ip, pref, (int)fs->fs_bsize);
-                       if (nbp == NULL) {
-                               brelse(bp);
-                               return ((daddr_t)-1);
-                       }
-                       nb = dbtofsb(fs, nbp->b_blkno);
-                       if (j < NIADDR || (ip->i_mode&IFMT) == IFDIR)
-                               /*
-                                * Write synchronously so indirect blocks
-                                * never point at garbage and blocks
-                                * in directories never contain garbage.
-                                */
-                               bwrite(nbp);
-                       else
-                               bdwrite(nbp);
-                       bap[i] = nb;
-                       bdwrite(bp);
-               } else
+               bap = (daddr_t *)bp->b_data;
+               nb = bap[indirs[i].in_off];
+               if (i == num)
+                       break;
+               i += 1;
+               if (nb != 0) {
+                       brelse(bp);
+                       continue;
+               }
+               if (pref == 0)
+                       pref = ffs_blkpref(ip, lbn, 0, (daddr_t *)0);
+               if (error =
+                   ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb)) {
+                       brelse(bp);
+                       return (error);
+               }
+               nb = newb;
+               nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
+               nbp->b_blkno = fsbtodb(fs, nb);
+               clrbuf(nbp);
+               /*
+                * Write synchronously so that indirect blocks
+                * never point at garbage.
+                */
+               if (error = bwrite(nbp)) {
+                       ffs_blkfree(ip, nb, fs->fs_bsize);
                        brelse(bp);
                        brelse(bp);
+                       return (error);
+               }
+               bap[indirs[i - 1].in_off] = nb;
+               /*
+                * If required, write synchronously, otherwise use
+                * delayed write.
+                */
+               if (flags & B_SYNC) {
+                       bwrite(bp);
+               } else {
+                       bdwrite(bp);
+               }
        }
        }
-
        /*
        /*
-        * calculate read-ahead.
+        * Get the data block, allocating if necessary.
         */
         */
-       if (i < NINDIR(fs) - 1) {
-               rablock = fsbtodb(fs, bap[i+1]);
-               rasize = fs->fs_bsize;
+       if (nb == 0) {
+               pref = ffs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]);
+               if (error = ffs_alloc(ip,
+                   lbn, pref, (int)fs->fs_bsize, cred, &newb)) {
+                       brelse(bp);
+                       return (error);
+               }
+               nb = newb;
+               nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
+               nbp->b_blkno = fsbtodb(fs, nb);
+               if (flags & B_CLRBUF)
+                       clrbuf(nbp);
+               bap[indirs[i].in_off] = nb;
+               /*
+                * If required, write synchronously, otherwise use
+                * delayed write.
+                */
+               if (flags & B_SYNC) {
+                       bwrite(bp);
+               } else {
+                       bdwrite(bp);
+               }
+               *bpp = nbp;
+               return (0);
+       }
+       brelse(bp);
+       if (flags & B_CLRBUF) {
+               error = bread(vp, lbn, (int)fs->fs_bsize, NOCRED, &nbp);
+               if (error) {
+                       brelse(nbp);
+                       return (error);
+               }
+       } else {
+               nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
+               nbp->b_blkno = fsbtodb(fs, nb);
        }
        }
-       return (nb);
+       *bpp = nbp;
+       return (0);
 }
 }