root/ufs/ffs/ffs_inode.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ffs_update
  2. ffs_truncate
  3. ffs_indirtrunc

    1 /*      $OpenBSD: ffs_inode.c,v 1.49 2007/06/01 18:54:27 pedro Exp $    */
    2 /*      $NetBSD: ffs_inode.c,v 1.10 1996/05/11 18:27:19 mycroft Exp $   */
    3 
    4 /*
    5  * Copyright (c) 1982, 1986, 1989, 1993
    6  *      The Regents of the University of California.  All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the University nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  *
   32  *      @(#)ffs_inode.c 8.8 (Berkeley) 10/19/94
   33  */
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/mount.h>
   38 #include <sys/proc.h>
   39 #include <sys/file.h>
   40 #include <sys/buf.h>
   41 #include <sys/vnode.h>
   42 #include <sys/kernel.h>
   43 #include <sys/malloc.h>
   44 #include <sys/resourcevar.h>
   45 
   46 #include <uvm/uvm_extern.h>
   47 
   48 #include <ufs/ufs/quota.h>
   49 #include <ufs/ufs/inode.h>
   50 #include <ufs/ufs/ufsmount.h>
   51 #include <ufs/ufs/ufs_extern.h>
   52 
   53 #include <ufs/ffs/fs.h>
   54 #include <ufs/ffs/ffs_extern.h>
   55 
   56 int ffs_indirtrunc(struct inode *, daddr_t, daddr_t, daddr_t, int, long *);
   57 
   58 /*
   59  * Update the access, modified, and inode change times as specified by the
   60  * IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. The IN_MODIFIED
   61  * flag is used to specify that the inode needs to be updated but that the
   62  * times have already been set. The access and modified times are taken from
   63  * the second and third parameters; the inode change time is always taken
   64  * from the current time. If waitfor is set, then wait for the disk write
   65  * of the inode to complete.
   66  */
   67 int
   68 ffs_update(struct inode *ip, struct timespec *atime, 
   69     struct timespec *mtime, int waitfor)
   70 {
   71         struct vnode *vp;
   72         struct fs *fs;
   73         struct buf *bp;
   74         int error;
   75         struct timespec ts;
   76 
   77         vp = ITOV(ip);
   78         if (vp->v_mount->mnt_flag & MNT_RDONLY) {
   79                 ip->i_flag &=
   80                     ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
   81                 return (0);
   82         }
   83 
   84         if ((vp->v_mount->mnt_flag & MNT_NOATIME) &&
   85             !(ip->i_flag & (IN_CHANGE | IN_UPDATE))) {
   86                 ip->i_flag &= ~IN_ACCESS;
   87         }
   88 
   89         if ((ip->i_flag &
   90             (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
   91             waitfor != MNT_WAIT)
   92                 return (0);
   93 
   94         getnanotime(&ts);
   95 
   96         if (ip->i_flag & IN_ACCESS) {
   97                 DIP_ASSIGN(ip, atime, atime ? atime->tv_sec : ts.tv_sec);
   98                 DIP_ASSIGN(ip, atimensec, atime ? atime->tv_nsec : ts.tv_nsec);
   99         }
  100 
  101         if (ip->i_flag & IN_UPDATE) {
  102                 DIP_ASSIGN(ip, mtime, mtime ? mtime->tv_sec : ts.tv_sec);
  103                 DIP_ASSIGN(ip, mtimensec, mtime ? mtime->tv_nsec : ts.tv_nsec);
  104                 ip->i_modrev++;
  105         }
  106 
  107         if (ip->i_flag & IN_CHANGE) {
  108                 DIP_ASSIGN(ip, ctime, ts.tv_sec);
  109                 DIP_ASSIGN(ip, ctimensec, ts.tv_nsec);
  110         }
  111 
  112         ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
  113         fs = ip->i_fs;
  114 
  115         /*
  116          * Ensure that uid and gid are correct. This is a temporary
  117          * fix until fsck has been changed to do the update.
  118          */
  119         if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_inodefmt < FS_44INODEFMT) {
  120                 ip->i_din1->di_ouid = ip->i_ffs1_uid;
  121                 ip->i_din1->di_ogid = ip->i_ffs1_gid;
  122         }
  123 
  124         error = bread(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
  125                 (int)fs->fs_bsize, NOCRED, &bp);
  126         if (error) {
  127                 brelse(bp);
  128                 return (error);
  129         }
  130 
  131         if (DOINGSOFTDEP(vp))
  132                 softdep_update_inodeblock(ip, bp, waitfor);
  133         else if (ip->i_effnlink != DIP(ip, nlink))
  134                 panic("ffs_update: bad link cnt");
  135 
  136 #ifdef FFS2
  137         if (ip->i_ump->um_fstype == UM_UFS2)
  138                 *((struct ufs2_dinode *)bp->b_data +
  139                     ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2;
  140         else
  141 #endif
  142                 *((struct ufs1_dinode *)bp->b_data +
  143                     ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1;
  144 
  145         if (waitfor && !DOINGASYNC(vp)) {
  146                 return (bwrite(bp));
  147         } else {
  148                 bdwrite(bp);
  149                 return (0);
  150         }
  151 }
  152 
  153 #define SINGLE  0       /* index of single indirect block */
  154 #define DOUBLE  1       /* index of double indirect block */
  155 #define TRIPLE  2       /* index of triple indirect block */
  156 
  157 /*
  158  * Truncate the inode oip to at most length size, freeing the
  159  * disk blocks.
  160  */
  161 int
  162 ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred)
  163 {
  164         struct vnode *ovp;
  165         daddr64_t lastblock;
  166         daddr64_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
  167         daddr64_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
  168         struct fs *fs;
  169         struct buf *bp;
  170         int offset, size, level;
  171         long count, nblocks, vflags, blocksreleased = 0;
  172         int i, aflags, error, allerror;
  173         off_t osize;
  174 
  175         if (length < 0)
  176                 return (EINVAL);
  177         ovp = ITOV(oip);
  178 
  179         if (ovp->v_type != VREG &&
  180             ovp->v_type != VDIR &&
  181             ovp->v_type != VLNK)
  182                 return (0);
  183 
  184         if (DIP(oip, size) == length)
  185                 return (0);
  186 
  187         if (ovp->v_type == VLNK &&
  188             (DIP(oip, size) < ovp->v_mount->mnt_maxsymlinklen ||
  189              (ovp->v_mount->mnt_maxsymlinklen == 0 &&
  190               oip->i_din1->di_blocks == 0))) {
  191 #ifdef DIAGNOSTIC
  192                 if (length != 0)
  193                         panic("ffs_truncate: partial truncate of symlink");
  194 #endif
  195                 memset(SHORTLINK(oip), 0, (size_t) DIP(oip, size));
  196                 DIP_ASSIGN(oip, size, 0);
  197                 oip->i_flag |= IN_CHANGE | IN_UPDATE;
  198                 return (UFS_UPDATE(oip, MNT_WAIT));
  199         }
  200 
  201         if ((error = getinoquota(oip)) != 0)
  202                 return (error);
  203 
  204         uvm_vnp_setsize(ovp, length);
  205         oip->i_ci.ci_lasta = oip->i_ci.ci_clen 
  206             = oip->i_ci.ci_cstart = oip->i_ci.ci_lastw = 0;
  207 
  208         if (DOINGSOFTDEP(ovp)) {
  209                 if (length > 0 || softdep_slowdown(ovp)) {
  210                         /*
  211                          * If a file is only partially truncated, then
  212                          * we have to clean up the data structures
  213                          * describing the allocation past the truncation
  214                          * point. Finding and deallocating those structures
  215                          * is a lot of work. Since partial truncation occurs
  216                          * rarely, we solve the problem by syncing the file
  217                          * so that it will have no data structures left.
  218                          */
  219                         if ((error = VOP_FSYNC(ovp, cred, MNT_WAIT,
  220                                                curproc)) != 0)
  221                                 return (error);
  222                 } else {
  223                         (void)ufs_quota_free_blocks(oip, DIP(oip, blocks),
  224                             NOCRED);
  225                         softdep_setup_freeblocks(oip, length);
  226                         (void) vinvalbuf(ovp, 0, cred, curproc, 0, 0);
  227                         oip->i_flag |= IN_CHANGE | IN_UPDATE;
  228                         return (UFS_UPDATE(oip, 0));
  229                 }
  230         }
  231 
  232         fs = oip->i_fs;
  233         osize = DIP(oip, size);
  234         /*
  235          * Lengthen the size of the file. We must ensure that the
  236          * last byte of the file is allocated. Since the smallest
  237          * value of osize is 0, length will be at least 1.
  238          */
  239         if (osize < length) {
  240                 if (length > fs->fs_maxfilesize)
  241                         return (EFBIG);
  242                 aflags = B_CLRBUF;
  243                 if (flags & IO_SYNC)
  244                         aflags |= B_SYNC;
  245                 error = UFS_BUF_ALLOC(oip, length - 1, 1, 
  246                                    cred, aflags, &bp);
  247                 if (error)
  248                         return (error);
  249                 DIP_ASSIGN(oip, size, length);
  250                 uvm_vnp_setsize(ovp, length);
  251                 (void) uvm_vnp_uncache(ovp);
  252                 if (aflags & B_SYNC)
  253                         bwrite(bp);
  254                 else
  255                         bawrite(bp);
  256                 oip->i_flag |= IN_CHANGE | IN_UPDATE;
  257                 return (UFS_UPDATE(oip, MNT_WAIT));
  258         }
  259         uvm_vnp_setsize(ovp, length);
  260 
  261         /*
  262          * Shorten the size of the file. If the file is not being
  263          * truncated to a block boundary, the contents of the
  264          * partial block following the end of the file must be
  265          * zero'ed in case it ever becomes accessible again because
  266          * of subsequent file growth. Directories however are not
  267          * zero'ed as they should grow back initialized to empty.
  268          */
  269         offset = blkoff(fs, length);
  270         if (offset == 0) {
  271                 DIP_ASSIGN(oip, size, length);
  272         } else {
  273                 lbn = lblkno(fs, length);
  274                 aflags = B_CLRBUF;
  275                 if (flags & IO_SYNC)
  276                         aflags |= B_SYNC;
  277                 error = UFS_BUF_ALLOC(oip, length - 1, 1,
  278                                    cred, aflags, &bp);
  279                 if (error)
  280                         return (error);
  281                 /*
  282                  * When we are doing soft updates and the UFS_BALLOC
  283                  * above fills in a direct block hole with a full sized
  284                  * block that will be truncated down to a fragment below,
  285                  * we must flush out the block dependency with an FSYNC
  286                  * so that we do not get a soft updates inconsistency
  287                  * when we create the fragment below.
  288                  */
  289                 if (DOINGSOFTDEP(ovp) && lbn < NDADDR &&
  290                     fragroundup(fs, blkoff(fs, length)) < fs->fs_bsize &&
  291                     (error = VOP_FSYNC(ovp, cred, MNT_WAIT, curproc)) != 0)
  292                         return (error);
  293                 DIP_ASSIGN(oip, size, length);
  294                 size = blksize(fs, oip, lbn);
  295                 (void) uvm_vnp_uncache(ovp);
  296                 if (ovp->v_type != VDIR)
  297                         bzero((char *)bp->b_data + offset,
  298                               (u_int)(size - offset));
  299                 bp->b_bcount = size;
  300                 if (aflags & B_SYNC)
  301                         bwrite(bp);
  302                 else
  303                         bawrite(bp);
  304         }
  305         /*
  306          * Calculate index into inode's block list of
  307          * last direct and indirect blocks (if any)
  308          * which we want to keep.  Lastblock is -1 when
  309          * the file is truncated to 0.
  310          */
  311         lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
  312         lastiblock[SINGLE] = lastblock - NDADDR;
  313         lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
  314         lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
  315         nblocks = btodb(fs->fs_bsize);
  316 
  317         /*
  318          * Update file and block pointers on disk before we start freeing
  319          * blocks.  If we crash before free'ing blocks below, the blocks
  320          * will be returned to the free list.  lastiblock values are also
  321          * normalized to -1 for calls to ffs_indirtrunc below.
  322          */
  323         for (level = TRIPLE; level >= SINGLE; level--) {
  324                 oldblks[NDADDR + level] = DIP(oip, ib[level]);
  325                 if (lastiblock[level] < 0) {
  326                         DIP_ASSIGN(oip, ib[level], 0);
  327                         lastiblock[level] = -1;
  328                 }
  329         }
  330 
  331         for (i = 0; i < NDADDR; i++) {
  332                 oldblks[i] = DIP(oip, db[i]);
  333                 if (i > lastblock)
  334                         DIP_ASSIGN(oip, db[i], 0);
  335         }
  336 
  337         oip->i_flag |= IN_CHANGE | IN_UPDATE;
  338         if ((error = UFS_UPDATE(oip, MNT_WAIT)) != 0)
  339                 allerror = error;
  340 
  341         /*
  342          * Having written the new inode to disk, save its new configuration
  343          * and put back the old block pointers long enough to process them.
  344          * Note that we save the new block configuration so we can check it
  345          * when we are done.
  346          */
  347         for (i = 0; i < NDADDR; i++) {
  348                 newblks[i] = DIP(oip, db[i]);
  349                 DIP_ASSIGN(oip, db[i], oldblks[i]);
  350         }
  351 
  352         for (i = 0; i < NIADDR; i++) {
  353                 newblks[NDADDR + i] = DIP(oip, ib[i]);
  354                 DIP_ASSIGN(oip, ib[i], oldblks[NDADDR + i]);
  355         }
  356 
  357         DIP_ASSIGN(oip, size, osize);
  358         vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
  359         allerror = vinvalbuf(ovp, vflags, cred, curproc, 0, 0);
  360 
  361         /*
  362          * Indirect blocks first.
  363          */
  364         indir_lbn[SINGLE] = -NDADDR;
  365         indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1;
  366         indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
  367         for (level = TRIPLE; level >= SINGLE; level--) {
  368                 bn = DIP(oip, ib[level]);
  369                 if (bn != 0) {
  370                         error = ffs_indirtrunc(oip, indir_lbn[level],
  371                             fsbtodb(fs, bn), lastiblock[level], level, &count);
  372                         if (error)
  373                                 allerror = error;
  374                         blocksreleased += count;
  375                         if (lastiblock[level] < 0) {
  376                                 DIP_ASSIGN(oip, ib[level], 0);
  377                                 ffs_blkfree(oip, bn, fs->fs_bsize);
  378                                 blocksreleased += nblocks;
  379                         }
  380                 }
  381                 if (lastiblock[level] >= 0)
  382                         goto done;
  383         }
  384 
  385         /*
  386          * All whole direct blocks or frags.
  387          */
  388         for (i = NDADDR - 1; i > lastblock; i--) {
  389                 long bsize;
  390 
  391                 bn = DIP(oip, db[i]);
  392                 if (bn == 0)
  393                         continue;
  394 
  395                 DIP_ASSIGN(oip, db[i], 0);
  396                 bsize = blksize(fs, oip, i);
  397                 ffs_blkfree(oip, bn, bsize);
  398                 blocksreleased += btodb(bsize);
  399         }
  400         if (lastblock < 0)
  401                 goto done;
  402 
  403         /*
  404          * Finally, look for a change in size of the
  405          * last direct block; release any frags.
  406          */
  407         bn = DIP(oip, db[lastblock]);
  408         if (bn != 0) {
  409                 long oldspace, newspace;
  410 
  411                 /*
  412                  * Calculate amount of space we're giving
  413                  * back as old block size minus new block size.
  414                  */
  415                 oldspace = blksize(fs, oip, lastblock);
  416                 DIP_ASSIGN(oip, size, length);
  417                 newspace = blksize(fs, oip, lastblock);
  418                 if (newspace == 0)
  419                         panic("ffs_truncate: newspace");
  420                 if (oldspace - newspace > 0) {
  421                         /*
  422                          * Block number of space to be free'd is
  423                          * the old block # plus the number of frags
  424                          * required for the storage we're keeping.
  425                          */
  426                         bn += numfrags(fs, newspace);
  427                         ffs_blkfree(oip, bn, oldspace - newspace);
  428                         blocksreleased += btodb(oldspace - newspace);
  429                 }
  430         }
  431 done:
  432 #ifdef DIAGNOSTIC
  433         for (level = SINGLE; level <= TRIPLE; level++)
  434                 if (newblks[NDADDR + level] != DIP(oip, ib[level]))
  435                         panic("ffs_truncate1");
  436         for (i = 0; i < NDADDR; i++)
  437                 if (newblks[i] != DIP(oip, db[i]))
  438                         panic("ffs_truncate2");
  439 #endif /* DIAGNOSTIC */
  440         /*
  441          * Put back the real size.
  442          */
  443         DIP_ASSIGN(oip, size, length);
  444         DIP_ADD(oip, blocks, -blocksreleased);
  445         if (DIP(oip, blocks) < 0)       /* Sanity */
  446                 DIP_ASSIGN(oip, blocks, 0);
  447         oip->i_flag |= IN_CHANGE;
  448         (void)ufs_quota_free_blocks(oip, blocksreleased, NOCRED);
  449         return (allerror);
  450 }
  451 
  452 #ifdef FFS2
  453 #define BAP(ip, i) (((ip)->i_ump->um_fstype == UM_UFS2) ? bap2[i] : bap1[i])
  454 #else
  455 #define BAP(ip, i) bap1[i]
  456 #endif /* FFS2 */
  457 
  458 /*
  459  * Release blocks associated with the inode ip and stored in the indirect
  460  * block bn.  Blocks are free'd in LIFO order up to (but not including)
  461  * lastbn.  If level is greater than SINGLE, the block is an indirect block
  462  * and recursive calls to indirtrunc must be used to cleanse other indirect
  463  * blocks.
  464  *
  465  * NB: triple indirect blocks are untested.
  466  */
  467 int
  468 ffs_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn, daddr_t lastbn,
  469     int level, long *countp)
  470 {
  471         int i;
  472         struct buf *bp;
  473         struct fs *fs = ip->i_fs;
  474         struct vnode *vp;
  475         void *copy = NULL;
  476         daddr_t nb, nlbn, last;
  477         long blkcount, factor;
  478         int nblocks, blocksreleased = 0;
  479         int error = 0, allerror = 0;
  480         int32_t *bap1 = NULL;
  481 #ifdef FFS2
  482         int64_t *bap2 = NULL;
  483 #endif
  484 
  485         /*
  486          * Calculate index in current block of last
  487          * block to be kept.  -1 indicates the entire
  488          * block so we need not calculate the index.
  489          */
  490         factor = 1;
  491         for (i = SINGLE; i < level; i++)
  492                 factor *= NINDIR(fs);
  493         last = lastbn;
  494         if (lastbn > 0)
  495                 last /= factor;
  496         nblocks = btodb(fs->fs_bsize);
  497         /*
  498          * Get buffer of block pointers, zero those entries corresponding
  499          * to blocks to be free'd, and update on disk copy first.  Since
  500          * double(triple) indirect before single(double) indirect, calls
  501          * to bmap on these blocks will fail.  However, we already have
  502          * the on disk address, so we have to set the b_blkno field
  503          * explicitly instead of letting bread do everything for us.
  504          */
  505         vp = ITOV(ip);
  506         bp = getblk(vp, lbn, (int)fs->fs_bsize, 0, 0);
  507         if (!(bp->b_flags & (B_DONE | B_DELWRI))) {
  508                 curproc->p_stats->p_ru.ru_inblock++;    /* pay for read */
  509                 bp->b_flags |= B_READ;
  510                 if (bp->b_bcount > bp->b_bufsize)
  511                         panic("ffs_indirtrunc: bad buffer size");
  512                 bp->b_blkno = dbn;
  513                 VOP_STRATEGY(bp);
  514                 error = biowait(bp);
  515         }
  516         if (error) {
  517                 brelse(bp);
  518                 *countp = 0;
  519                 return (error);
  520         }
  521 
  522 #ifdef FFS2
  523         if (ip->i_ump->um_fstype == UM_UFS2)
  524                 bap2 = (int64_t *)bp->b_data;
  525         else
  526 #endif
  527                 bap1 = (int32_t *)bp->b_data;
  528 
  529         if (lastbn != -1) {
  530                 MALLOC(copy, void *, fs->fs_bsize, M_TEMP, M_WAITOK);
  531                 bcopy(bp->b_data, copy, (u_int) fs->fs_bsize);
  532 
  533                 for (i = last + 1; i < NINDIR(fs); i++)
  534                         BAP(ip, i) = 0;
  535 
  536                 if (!DOINGASYNC(vp)) {
  537                         error = bwrite(bp);
  538                         if (error)
  539                                 allerror = error;
  540                 } else {
  541                         bawrite(bp);
  542                 }
  543 
  544 #ifdef FFS2
  545                 if (ip->i_ump->um_fstype == UM_UFS2)
  546                         bap2 = (int64_t *)copy;
  547                 else
  548 #endif
  549                         bap1 = (int32_t *)copy;
  550         }
  551 
  552         /*
  553          * Recursively free totally unused blocks.
  554          */
  555         for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
  556             i--, nlbn += factor) {
  557                 nb = BAP(ip, i);
  558                 if (nb == 0)
  559                         continue;
  560                 if (level > SINGLE) {
  561                         error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
  562                                                (daddr_t)-1, level - 1,
  563                                                &blkcount);
  564                         if (error)
  565                                 allerror = error;
  566                         blocksreleased += blkcount;
  567                 }
  568                 ffs_blkfree(ip, nb, fs->fs_bsize);
  569                 blocksreleased += nblocks;
  570         }
  571 
  572         /*
  573          * Recursively free last partial block.
  574          */
  575         if (level > SINGLE && lastbn >= 0) {
  576                 last = lastbn % factor;
  577                 nb = BAP(ip, i);
  578                 if (nb != 0) {
  579                         error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
  580                                                last, level - 1, &blkcount);
  581                         if (error)
  582                                 allerror = error;
  583                         blocksreleased += blkcount;
  584                 }
  585         }
  586         if (copy != NULL) {
  587                 FREE(copy, M_TEMP);
  588         } else {
  589                 bp->b_flags |= B_INVAL;
  590                 brelse(bp);
  591         }
  592                 
  593         *countp = blocksreleased;
  594         return (allerror);
  595 }

/* [<][>][^][v][top][bottom][index][help] */