1 /*        $NetBSD: ext2fs_inode.c,v 1.91 2023/08/26 05:22:50 riastradh Exp $    */
2 
3 /*
4  * Copyright (c) 1982, 1986, 1989, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *        @(#)ffs_inode.c     8.8 (Berkeley) 10/19/94
32  * Modified for ext2fs by Manuel Bouyer.
33  */
34 
35 /*
36  * Copyright (c) 1997 Manuel Bouyer.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57  *
58  *        @(#)ffs_inode.c     8.8 (Berkeley) 10/19/94
59  * Modified for ext2fs by Manuel Bouyer.
60  */
61 
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: ext2fs_inode.c,v 1.91 2023/08/26 05:22:50 riastradh Exp $");
64 
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/mount.h>
68 #include <sys/proc.h>
69 #include <sys/file.h>
70 #include <sys/buf.h>
71 #include <sys/vnode.h>
72 #include <sys/kernel.h>
73 #include <sys/kmem.h>
74 #include <sys/trace.h>
75 #include <sys/resourcevar.h>
76 #include <sys/kauth.h>
77 
78 #include <ufs/ufs/inode.h>
79 #include <ufs/ufs/ufsmount.h>
80 #include <ufs/ufs/ufs_extern.h>
81 
82 #include <ufs/ext2fs/ext2fs.h>
83 #include <ufs/ext2fs/ext2fs_extern.h>
84 
85 static int ext2fs_indirtrunc(struct inode *, daddr_t, daddr_t,
86                                           daddr_t, int, long *);
87 
88 /*
89  * These are fortunately the same values; it is likely that there is
90  * code that assumes they're equal. In any event, neither ought to
91  * ever change because it's a property of the on-disk formats.
92  */
93 CTASSERT(EXT2FS_NDADDR == UFS_NDADDR);
94 CTASSERT(EXT2FS_NIADDR == UFS_NIADDR);
95 
96 /*
97  * Get the size of an inode.
98  */
99 uint64_t
ext2fs_size(struct inode * ip)100 ext2fs_size(struct inode *ip)
101 {
102           uint64_t size = ip->i_e2fs_size;
103 
104           if ((ip->i_e2fs_mode & IFMT) == IFREG)
105                     size |= (uint64_t)ip->i_din.e2fs_din->e2di_size_high << 32;
106           return size;
107 }
108 
109 int
ext2fs_setsize(struct inode * ip,uint64_t size)110 ext2fs_setsize(struct inode *ip, uint64_t size)
111 {
112           if ((ip->i_e2fs_mode & IFMT) == IFREG ||
113               ip->i_e2fs_mode == 0) {
114                     ip->i_din.e2fs_din->e2di_size_high = size >> 32;
115                     if (size >= 0x80000000U) {
116                               struct m_ext2fs *fs = ip->i_e2fs;
117 
118                               if (fs->e2fs.e2fs_rev <= E2FS_REV0) {
119                                         /* Linux automagically upgrades to REV1 here! */
120                                         return EFBIG;
121                               }
122                               if (!EXT2F_HAS_ROCOMPAT_FEATURE(fs,
123                                   EXT2F_ROCOMPAT_LARGEFILE)) {
124                                         fs->e2fs.e2fs_features_rocompat |=
125                                             EXT2F_ROCOMPAT_LARGEFILE;
126                                         fs->e2fs_fmod = 1;
127                               }
128                     }
129           } else if (size >= 0x80000000U)
130                     return EFBIG;
131 
132           ip->i_e2fs_size = size;
133 
134           return 0;
135 }
136 
137 uint64_t
ext2fs_nblock(struct inode * ip)138 ext2fs_nblock(struct inode *ip)
139 {
140           uint64_t nblock = ip->i_e2fs_nblock;
141           struct m_ext2fs * const fs = ip->i_e2fs;
142 
143           if (EXT2F_HAS_ROCOMPAT_FEATURE(fs, EXT2F_ROCOMPAT_HUGE_FILE)) {
144                     nblock |= (uint64_t)ip->i_e2fs_nblock_high << 32;
145 
146                     if ((ip->i_e2fs_flags & EXT2_HUGE_FILE)) {
147                               nblock = EXT2_FSBTODB(fs, nblock);
148                     }
149           }
150 
151           return nblock;
152 }
153 
154 int
ext2fs_setnblock(struct inode * ip,uint64_t nblock)155 ext2fs_setnblock(struct inode *ip, uint64_t nblock)
156 {
157           struct m_ext2fs * const fs = ip->i_e2fs;
158 
159           if (nblock <= 0xffffffffULL) {
160                     CLR(ip->i_e2fs_flags, EXT2_HUGE_FILE);
161                     ip->i_e2fs_nblock = nblock;
162                     return 0;
163           }
164 
165           if (!EXT2F_HAS_ROCOMPAT_FEATURE(fs, EXT2F_ROCOMPAT_HUGE_FILE))
166                     return EFBIG;
167 
168           if (nblock <= 0xffffffffffffULL) {
169                     CLR(ip->i_e2fs_flags, EXT2_HUGE_FILE);
170                     ip->i_e2fs_nblock = nblock & 0xffffffff;
171                     ip->i_e2fs_nblock_high = (nblock >> 32) & 0xffff;
172                     return 0;
173           }
174 
175           if (EXT2_DBTOFSB(fs, nblock) <= 0xffffffffffffULL) {
176                     SET(ip->i_e2fs_flags, EXT2_HUGE_FILE);
177                     ip->i_e2fs_nblock = EXT2_DBTOFSB(fs, nblock) & 0xffffffff;
178                     ip->i_e2fs_nblock_high = (EXT2_DBTOFSB(fs, nblock) >> 32) & 0xffff;
179                     return 0;
180           }
181 
182           return EFBIG;
183 }
184 
185 /*
186  * Last reference to an inode.  If necessary, write or delete it.
187  */
188 int
ext2fs_inactive(void * v)189 ext2fs_inactive(void *v)
190 {
191           struct vop_inactive_v2_args /* {
192                     struct vnode *a_vp;
193                     bool *a_recycle;
194           } */ *ap = v;
195           struct vnode *vp = ap->a_vp;
196           struct inode *ip = VTOI(vp);
197           int error = 0;
198 
199           /* Get rid of inodes related to stale file handles. */
200           if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0)
201                     goto out;
202 
203           error = 0;
204           if (ip->i_e2fs_nlink == 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
205                     /* Defer final inode free and update to reclaim.*/
206                     if (ext2fs_size(ip) != 0) {
207                               error = ext2fs_truncate(vp, (off_t)0, 0, NOCRED);
208                     }
209                     ip->i_e2fs_dtime = time_second;
210                     ip->i_flag |= IN_CHANGE | IN_UPDATE;
211                     ip->i_omode = 1;
212           }
213           if (ip->i_flag & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) {
214                     ext2fs_update(vp, NULL, NULL, 0);
215           }
216 out:
217           /*
218            * If we are done with the inode, reclaim it
219            * so that it can be reused immediately.
220            */
221           *ap->a_recycle = (ip->i_e2fs_dtime != 0);
222 
223           return error;
224 }
225 
226 
227 /*
228  * Update the access, modified, and inode change times as specified by the
229  * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
230  * used to specify that the inode needs to be updated but that the times have
231  * already been set. The access and modified times are taken from the second
232  * and third parameters; the inode change time is always taken from the current
233  * time. If UPDATE_WAIT or UPDATE_DIROP is set, then wait for the disk
234  * write of the inode to complete.
235  */
236 int
ext2fs_update(struct vnode * vp,const struct timespec * acc,const struct timespec * mod,int updflags)237 ext2fs_update(struct vnode *vp, const struct timespec *acc,
238     const struct timespec *mod, int updflags)
239 {
240           struct m_ext2fs *fs;
241           struct buf *bp;
242           struct inode *ip;
243           int error;
244           void *cp;
245           int flags;
246 
247           if (vp->v_mount->mnt_flag & MNT_RDONLY)
248                     return 0;
249           ip = VTOI(vp);
250           EXT2FS_ITIMES(ip, acc, mod, NULL);
251           if (updflags & UPDATE_CLOSE)
252                     flags = ip->i_flag & (IN_MODIFIED | IN_ACCESSED);
253           else
254                     flags = ip->i_flag & IN_MODIFIED;
255           if (flags == 0)
256                     return 0;
257           fs = ip->i_e2fs;
258 
259           error = bread(ip->i_devvp,
260                                 EXT2_FSBTODB(fs, ino_to_fsba(fs, ip->i_number)),
261                                 (int)fs->e2fs_bsize, B_MODIFY, &bp);
262           if (error) {
263                     return error;
264           }
265           ip->i_flag &= ~(IN_MODIFIED | IN_ACCESSED);
266           cp = (char *)bp->b_data +
267               (ino_to_fsbo(fs, ip->i_number) * EXT2_DINODE_SIZE(fs));
268           e2fs_isave(ip->i_din.e2fs_din, (struct ext2fs_dinode *)cp, EXT2_DINODE_SIZE(fs));
269           if ((updflags & (UPDATE_WAIT|UPDATE_DIROP)) != 0 &&
270               (flags & IN_MODIFIED) != 0 &&
271               (vp->v_mount->mnt_flag & MNT_ASYNC) == 0)
272                     return bwrite(bp);
273           else {
274                     bdwrite(bp);
275                     return 0;
276           }
277 }
278 
279 #define   SINGLE    0         /* index of single indirect block */
280 #define   DOUBLE    1         /* index of double indirect block */
281 #define   TRIPLE    2         /* index of triple indirect block */
282 /*
283  * Truncate the inode oip to at most length size, freeing the
284  * disk blocks.
285  */
286 int
ext2fs_truncate(struct vnode * ovp,off_t length,int ioflag,kauth_cred_t cred)287 ext2fs_truncate(struct vnode *ovp, off_t length, int ioflag,
288     kauth_cred_t cred)
289 {
290           daddr_t lastblock;
291           struct inode *oip = VTOI(ovp);
292           daddr_t bn, lastiblock[EXT2FS_NIADDR], indir_lbn[EXT2FS_NIADDR];
293           /* XXX ondisk32 */
294           int32_t oldblks[EXT2FS_NDADDR + EXT2FS_NIADDR], newblks[EXT2FS_NDADDR + EXT2FS_NIADDR];
295           struct m_ext2fs *fs;
296           int offset, size, level;
297           long count, blocksreleased = 0;
298           int i, nblocks;
299           int error, allerror = 0;
300           off_t osize;
301           int sync;
302           struct ufsmount *ump = oip->i_ump;
303 
304           if (ovp->v_type == VCHR || ovp->v_type == VBLK ||
305               ovp->v_type == VFIFO || ovp->v_type == VSOCK) {
306                     return 0;
307           }
308 
309           if (length < 0)
310                     return EINVAL;
311 
312           if (ovp->v_type == VLNK &&
313               (ext2fs_size(oip) < ump->um_maxsymlinklen ||
314                (ump->um_maxsymlinklen == 0 && ext2fs_nblock(oip) == 0))) {
315                     KDASSERT(length == 0);
316                     memset((char *)&oip->i_din.e2fs_din->e2di_shortlink, 0,
317                               (u_int)ext2fs_size(oip));
318                     (void)ext2fs_setsize(oip, 0);
319                     goto update;
320           }
321           if (ext2fs_size(oip) == length) {
322                     /* still do a uvm_vnp_setsize() as writesize may be larger */
323                     uvm_vnp_setsize(ovp, length);
324                     goto update;
325           }
326           fs = oip->i_e2fs;
327           if (length > ump->um_maxfilesize)
328                     return EFBIG;
329 
330           osize = ext2fs_size(oip);
331 
332           /*
333            * Lengthen the size of the file. We must ensure that the
334            * last byte of the file is allocated. Since the smallest
335            * value of osize is 0, length will be at least 1.
336            */
337           if (osize < length) {
338                     uvm_vnp_setwritesize(ovp, length);
339                     error = ufs_balloc_range(ovp, length - 1, 1, cred,
340                         ioflag & IO_SYNC ? B_SYNC : 0);
341                     if (error) {
342                               (void) ext2fs_truncate(ovp, osize, ioflag & IO_SYNC,
343                                   cred);
344                               return error;
345                     }
346                     uvm_vnp_setsize(ovp, length);
347                     KASSERT(error  || ovp->v_size == ext2fs_size(oip));
348                     goto update;
349           }
350           /*
351            * Shorten the size of the file. If the file is not being
352            * truncated to a block boundary, the contents of the
353            * partial block following the end of the file must be
354            * zero'ed in case it ever become accessible again because
355            * of subsequent file growth.
356            */
357           offset = ext2_blkoff(fs, length);
358           if (offset != 0) {
359                     size = fs->e2fs_bsize;
360 
361                     /* XXXUBC we should handle more than just VREG */
362                     ubc_zerorange(&ovp->v_uobj, length, size - offset,
363                         UBC_VNODE_FLAGS(ovp));
364           }
365           (void)ext2fs_setsize(oip, length);
366           uvm_vnp_setsize(ovp, length);
367           /*
368            * Calculate index into inode's block list of
369            * last direct and indirect blocks (if any)
370            * which we want to keep.  Lastblock is -1 when
371            * the file is truncated to 0.
372            */
373           lastblock = ext2_lblkno(fs, length + fs->e2fs_bsize - 1) - 1;
374           lastiblock[SINGLE] = lastblock - EXT2FS_NDADDR;
375           lastiblock[DOUBLE] = lastiblock[SINGLE] - EXT2_NINDIR(fs);
376           lastiblock[TRIPLE] = lastiblock[DOUBLE] - EXT2_NINDIR(fs) * EXT2_NINDIR(fs);
377           nblocks = btodb(fs->e2fs_bsize);
378           /*
379            * Update file and block pointers on disk before we start freeing
380            * blocks.  If we crash before free'ing blocks below, the blocks
381            * will be returned to the free list.  lastiblock values are also
382            * normalized to -1 for calls to ext2fs_indirtrunc below.
383            */
384           memcpy((void *)oldblks, (void *)&oip->i_e2fs_blocks[0], sizeof oldblks);
385           sync = 0;
386           for (level = TRIPLE; level >= SINGLE; level--) {
387                     if (lastiblock[level] < 0 && oldblks[EXT2FS_NDADDR + level] != 0) {
388                               sync = 1;
389                               oip->i_e2fs_blocks[EXT2FS_NDADDR + level] = 0;
390                               lastiblock[level] = -1;
391                     }
392           }
393           for (i = 0; i < EXT2FS_NDADDR; i++) {
394                     if (i > lastblock && oldblks[i] != 0) {
395                               sync = 1;
396                               oip->i_e2fs_blocks[i] = 0;
397                     }
398           }
399           oip->i_flag |= IN_CHANGE | IN_UPDATE;
400           if (sync) {
401                     error = ext2fs_update(ovp, NULL, NULL, UPDATE_WAIT);
402                     if (error && !allerror)
403                               allerror = error;
404           }
405 
406           /*
407            * Having written the new inode to disk, save its new configuration
408            * and put back the old block pointers long enough to process them.
409            * Note that we save the new block configuration so we can check it
410            * when we are done.
411            */
412           memcpy((void *)newblks, (void *)&oip->i_e2fs_blocks[0], sizeof newblks);
413           memcpy((void *)&oip->i_e2fs_blocks[0], (void *)oldblks, sizeof oldblks);
414 
415           (void)ext2fs_setsize(oip, osize);
416           error = vtruncbuf(ovp, lastblock + 1, 0, 0);
417           if (error && !allerror)
418                     allerror = error;
419 
420           /*
421            * Indirect blocks first.
422            */
423           indir_lbn[SINGLE] = -EXT2FS_NDADDR;
424           indir_lbn[DOUBLE] = indir_lbn[SINGLE] - EXT2_NINDIR(fs) -1;
425           indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - EXT2_NINDIR(fs) * EXT2_NINDIR(fs) - 1;
426           for (level = TRIPLE; level >= SINGLE; level--) {
427                     /* XXX ondisk32 */
428                     bn = fs2h32(oip->i_e2fs_blocks[EXT2FS_NDADDR + level]);
429                     if (bn != 0) {
430                               error = ext2fs_indirtrunc(oip, indir_lbn[level],
431                                   EXT2_FSBTODB(fs, bn), lastiblock[level], level, &count);
432                               if (error)
433                                         allerror = error;
434                               blocksreleased += count;
435                               if (lastiblock[level] < 0) {
436                                         oip->i_e2fs_blocks[EXT2FS_NDADDR + level] = 0;
437                                         ext2fs_blkfree(oip, bn);
438                                         blocksreleased += nblocks;
439                               }
440                     }
441                     if (lastiblock[level] >= 0)
442                               goto done;
443           }
444 
445           /*
446            * All whole direct blocks or frags.
447            */
448           for (i = EXT2FS_NDADDR - 1; i > lastblock; i--) {
449                     /* XXX ondisk32 */
450                     bn = fs2h32(oip->i_e2fs_blocks[i]);
451                     if (bn == 0)
452                               continue;
453                     oip->i_e2fs_blocks[i] = 0;
454                     ext2fs_blkfree(oip, bn);
455                     blocksreleased += btodb(fs->e2fs_bsize);
456           }
457 
458 done:
459 #ifdef DIAGNOSTIC
460           for (level = SINGLE; level <= TRIPLE; level++)
461                     if (newblks[EXT2FS_NDADDR + level] !=
462                         oip->i_e2fs_blocks[EXT2FS_NDADDR + level])
463                               panic("ext2fs_truncate1");
464           for (i = 0; i < EXT2FS_NDADDR; i++)
465                     if (newblks[i] != oip->i_e2fs_blocks[i])
466                               panic("ext2fs_truncate2");
467           if (length == 0 &&
468               (!LIST_EMPTY(&ovp->v_cleanblkhd) ||
469                !LIST_EMPTY(&ovp->v_dirtyblkhd)))
470                     panic("ext2fs_truncate3");
471 #endif /* DIAGNOSTIC */
472           /*
473            * Put back the real size.
474            */
475           (void)ext2fs_setsize(oip, length);
476           error = ext2fs_setnblock(oip, ext2fs_nblock(oip) - blocksreleased);
477           if (error != 0)
478                     allerror = error;
479           oip->i_flag |= IN_CHANGE;
480           KASSERT(ovp->v_type != VREG || ovp->v_size == ext2fs_size(oip));
481           return allerror;
482 update:
483           oip->i_flag |= IN_CHANGE | IN_UPDATE;
484           return ext2fs_update(ovp, NULL, NULL, 0);
485 }
486 
487 /*
488  * Release blocks associated with the inode ip and stored in the indirect
489  * block bn.  Blocks are free'd in LIFO order up to (but not including)
490  * lastbn.  If level is greater than SINGLE, the block is an indirect block
491  * and recursive calls to indirtrunc must be used to cleanse other indirect
492  * blocks.
493  *
494  * NB: triple indirect blocks are untested.
495  */
496 static int
ext2fs_indirtrunc(struct inode * ip,daddr_t lbn,daddr_t dbn,daddr_t lastbn,int level,long * countp)497 ext2fs_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn, daddr_t lastbn,
498                     int level, long *countp)
499 {
500           int i;
501           struct buf *bp;
502           struct m_ext2fs *fs = ip->i_e2fs;
503           int32_t *bap;       /* XXX ondisk32 */
504           struct vnode *vp;
505           daddr_t nb, nlbn, last;
506           int32_t *copy = NULL;         /* XXX ondisk32 */
507           long blkcount, factor;
508           int nblocks, blocksreleased = 0;
509           int error = 0, allerror = 0;
510 
511           /*
512            * Calculate index in current block of last
513            * block to be kept.  -1 indicates the entire
514            * block so we need not calculate the index.
515            */
516           factor = 1;
517           for (i = SINGLE; i < level; i++)
518                     factor *= EXT2_NINDIR(fs);
519           last = lastbn;
520           if (lastbn > 0)
521                     last /= factor;
522           nblocks = btodb(fs->e2fs_bsize);
523           /*
524            * Get buffer of block pointers, zero those entries corresponding
525            * to blocks to be free'd, and update on disk copy first.  Since
526            * double(triple) indirect before single(double) indirect, calls
527            * to bmap on these blocks will fail.  However, we already have
528            * the on disk address, so we have to set the b_blkno field
529            * explicitly instead of letting bread do everything for us.
530            */
531           vp = ITOV(ip);
532           bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0);
533           if (bp->b_oflags & (BO_DONE | BO_DELWRI)) {
534                     /* Braces must be here in case trace evaluates to nothing. */
535                     trace(TR_BREADHIT, pack(vp, fs->e2fs_bsize), lbn);
536           } else {
537                     trace(TR_BREADMISS, pack(vp, fs->e2fs_bsize), lbn);
538                     curlwp->l_ru.ru_inblock++;    /* pay for read */
539                     bp->b_flags |= B_READ;
540                     if (bp->b_bcount > bp->b_bufsize)
541                               panic("ext2fs_indirtrunc: bad buffer size");
542                     bp->b_blkno = dbn;
543                     VOP_STRATEGY(vp, bp);
544                     error = biowait(bp);
545           }
546           if (error) {
547                     brelse(bp, 0);
548                     *countp = 0;
549                     return error;
550           }
551 
552           bap = (int32_t *)bp->b_data;  /* XXX ondisk32 */
553           if (lastbn >= 0) {
554                     /* XXX ondisk32 */
555                     copy = kmem_alloc(fs->e2fs_bsize, KM_SLEEP);
556                     memcpy((void *)copy, (void *)bap, (u_int)fs->e2fs_bsize);
557                     memset((void *)&bap[last + 1], 0,
558                               (u_int)(EXT2_NINDIR(fs) - (last + 1)) * sizeof (uint32_t));
559                     error = bwrite(bp);
560                     if (error)
561                               allerror = error;
562                     bap = copy;
563           }
564 
565           /*
566            * Recursively free totally unused blocks.
567            */
568           for (i = EXT2_NINDIR(fs) - 1,
569                     nlbn = lbn + 1 - i * factor; i > last;
570                     i--, nlbn += factor) {
571                     /* XXX ondisk32 */
572                     nb = fs2h32(bap[i]);
573                     if (nb == 0)
574                               continue;
575                     if (level > SINGLE) {
576                               error = ext2fs_indirtrunc(ip, nlbn, EXT2_FSBTODB(fs, nb),
577                                                                (daddr_t)-1, level - 1,
578                                                                &blkcount);
579                               if (error)
580                                         allerror = error;
581                               blocksreleased += blkcount;
582                     }
583                     ext2fs_blkfree(ip, nb);
584                     blocksreleased += nblocks;
585           }
586 
587           /*
588            * Recursively free last partial block.
589            */
590           if (level > SINGLE && lastbn >= 0) {
591                     last = lastbn % factor;
592                     /* XXX ondisk32 */
593                     nb = fs2h32(bap[i]);
594                     if (nb != 0) {
595                               error = ext2fs_indirtrunc(ip, nlbn, EXT2_FSBTODB(fs, nb),
596                                                                last, level - 1, &blkcount);
597                               if (error)
598                                         allerror = error;
599                               blocksreleased += blkcount;
600                     }
601           }
602 
603           if (copy != NULL) {
604                     kmem_free(copy, fs->e2fs_bsize);
605           } else {
606                     brelse(bp, BC_INVAL);
607           }
608 
609           *countp = blocksreleased;
610           return allerror;
611 }
612