|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23: /*
24: * Copyright (c) 1982, 1986, 1989, 1993
25: * The Regents of the University of California. All rights reserved.
26: *
27: * Redistribution and use in source and binary forms, with or without
28: * modification, are permitted provided that the following conditions
29: * are met:
30: * 1. Redistributions of source code must retain the above copyright
31: * notice, this list of conditions and the following disclaimer.
32: * 2. Redistributions in binary form must reproduce the above copyright
33: * notice, this list of conditions and the following disclaimer in the
34: * documentation and/or other materials provided with the distribution.
35: * 3. All advertising materials mentioning features or use of this software
36: * must display the following acknowledgement:
37: * This product includes software developed by the University of
38: * California, Berkeley and its contributors.
39: * 4. Neither the name of the University nor the names of its contributors
40: * may be used to endorse or promote products derived from this software
41: * without specific prior written permission.
42: *
43: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53: * SUCH DAMAGE.
54: *
55: * @(#)ffs_inode.c 8.13 (Berkeley) 4/21/95
56: */
57:
58: #include <rev_endian_fs.h>
59: #include <mach_nbc.h>
60: #include <vm/vm_pager.h>
61: #include <vm/vnode_pager.h>
62:
63: #include <sys/param.h>
64: #include <sys/systm.h>
65: #include <sys/mount.h>
66: #include <sys/proc.h>
67: #include <sys/file.h>
68: #include <sys/buf.h>
69: #include <sys/vnode.h>
70: #include <sys/kernel.h>
71: #include <sys/malloc.h>
72: #include <sys/trace.h>
73: #include <sys/resourcevar.h>
74:
75: #include <sys/vm.h>
76:
77: #include <ufs/ufs/quota.h>
78: #include <ufs/ufs/inode.h>
79: #include <ufs/ufs/ufsmount.h>
80: #include <ufs/ufs/ufs_extern.h>
81:
82: #include <ufs/ffs/fs.h>
83: #include <ufs/ffs/ffs_extern.h>
84:
85: #if REV_ENDIAN_FS
86: #include <ufs/ufs/ufs_byte_order.h>
87: #include <architecture/byte_order.h>
88: #endif /* REV_ENDIAN_FS */
89:
90: #if MACH_NBC
91: #include <kern/mapfs.h>
92: #endif /* MACH_NBC */
93:
94:
95:
96: static int ffs_indirtrunc __P((struct inode *, ufs_daddr_t, ufs_daddr_t,
97: ufs_daddr_t, int, long *));
98:
99: /*
100: * Update the access, modified, and inode change times as specified by the
101: * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
102: * used to specify that the inode needs to be updated but that the times have
103: * already been set. The access and modified times are taken from the second
104: * and third parameters; the inode change time is always taken from the current
105: * time. If waitfor is set, then wait for the disk write of the inode to
106: * complete.
107: */
108: int
109: ffs_update(ap)
110: struct vop_update_args /* {
111: struct vnode *a_vp;
112: struct timeval *a_access;
113: struct timeval *a_modify;
114: int a_waitfor;
115: } */ *ap;
116: {
117: register struct fs *fs;
118: struct buf *bp;
119: struct inode *ip;
120: int error;
121: #if REV_ENDIAN_FS
122: struct mount *mp=(ap->a_vp)->v_mount;
123: int rev_endian=(mp->mnt_flag & MNT_REVEND);
124: #endif /* REV_ENDIAN_FS */
125:
126: ip = VTOI(ap->a_vp);
127: if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
128: ip->i_flag &=
129: ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
130: return (0);
131: }
132: if ((ip->i_flag &
133: (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0)
134: return (0);
135: if (ip->i_flag & IN_ACCESS)
136: ip->i_atime = ap->a_access->tv_sec;
137: if (ip->i_flag & IN_UPDATE) {
138: ip->i_mtime = ap->a_modify->tv_sec;
139: ip->i_modrev++;
140: }
141: if (ip->i_flag & IN_CHANGE)
142: ip->i_ctime = time.tv_sec;
143: ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
144: fs = ip->i_fs;
145: /*
146: * Ensure that uid and gid are correct. This is a temporary
147: * fix until fsck has been changed to do the update.
148: */
149: if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
150: ip->i_din.di_ouid = ip->i_uid; /* XXX */
151: ip->i_din.di_ogid = ip->i_gid; /* XXX */
152: } /* XXX */
153: if (error = bread(ip->i_devvp,
154: fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
155: (int)fs->fs_bsize, NOCRED, &bp)) {
156: brelse(bp);
157: return (error);
158: }
159: #if REV_ENDIAN_FS
160: if (rev_endian)
161: byte_swap_inode_out(ip, ((struct dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)));
162: else {
163: #endif /* REV_ENDIAN_FS */
164: *((struct dinode *)bp->b_data +
165: ino_to_fsbo(fs, ip->i_number)) = ip->i_din;
166: #if REV_ENDIAN_FS
167: }
168: #endif /* REV_ENDIAN_FS */
169:
170: if (ap->a_waitfor && (ap->a_vp->v_mount->mnt_flag & MNT_ASYNC) == 0)
171: return (bwrite(bp));
172: else {
173: bdwrite(bp);
174: return (0);
175: }
176: }
177:
178: #define SINGLE 0 /* index of single indirect block */
179: #define DOUBLE 1 /* index of double indirect block */
180: #define TRIPLE 2 /* index of triple indirect block */
181: /*
182: * Truncate the inode oip to at most length size, freeing the
183: * disk blocks.
184: */
185: ffs_truncate(ap)
186: struct vop_truncate_args /* {
187: struct vnode *a_vp;
188: off_t a_length;
189: int a_flags;
190: struct ucred *a_cred;
191: struct proc *a_p;
192: } */ *ap;
193: {
194: register struct vnode *ovp = ap->a_vp;
195: ufs_daddr_t lastblock;
196: register struct inode *oip;
197: ufs_daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
198: ufs_daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
199: off_t length = ap->a_length;
200: register struct fs *fs;
201: struct buf *bp;
202: int offset, size, level;
203: long count, nblocks, vflags, blocksreleased = 0;
204: struct timeval tv;
205: register int i;
206: int aflags, error, allerror;
207: off_t osize;
208: #if NeXT
209: int devBlockSize=0;
210: #endif
211:
212: if (length < 0)
213: return (EINVAL);
214:
215: oip = VTOI(ovp);
216: fs = oip->i_fs;
217:
218: if (length > fs->fs_maxfilesize)
219: return (EFBIG);
220:
221: tv = time;
222: if (ovp->v_type == VLNK &&
223: oip->i_size < ovp->v_mount->mnt_maxsymlinklen) {
224: #if DIAGNOSTIC
225: if (length != 0)
226: panic("ffs_truncate: partial truncate of symlink");
227: #endif
228: bzero((char *)&oip->i_shortlink, (u_int)oip->i_size);
229: oip->i_size = 0;
230: oip->i_flag |= IN_CHANGE | IN_UPDATE;
231: #if MACH_NBC
232: error = mapfs_trunc(ovp, (vm_offset_t)0);
233: if (error)
234: return (error);
235: #endif /* MACH_NBC */
236: /* needs to be present till Unified buffer cache implementaiton */
237: ubc_truncate(ovp, (vm_offset_t)0);
238:
239: return (VOP_UPDATE(ovp, &tv, &tv, 1));
240: }
241:
242: #if MACH_NBC
243: error = mapfs_trunc(ovp, (vm_offset_t)length);
244: if (error)
245: return (error);
246: #endif /* MACH_NBC */
247: /* this needs to be present till Unified buffer cache implementaiton */
248: ubc_truncate(ovp, (vm_offset_t)length);
249:
250:
251: if (oip->i_size == length) {
252: oip->i_flag |= IN_CHANGE | IN_UPDATE;
253: return (VOP_UPDATE(ovp, &tv, &tv, 0));
254: }
255: #if QUOTA
256: if (error = getinoquota(oip))
257: return (error);
258: #endif
259: osize = oip->i_size;
260:
261: /*
262: * Lengthen the size of the file. We must ensure that the
263: * last byte of the file is allocated. Since the smallest
264: * value of osize is 0, length will be at least 1.
265: */
266: if (osize < length) {
267: offset = blkoff(fs, length - 1);
268: lbn = lblkno(fs, length - 1);
269: aflags = B_CLRBUF;
270: if (ap->a_flags & IO_SYNC)
271: aflags |= B_SYNC;
272: if (error = ffs_balloc(oip, lbn, offset + 1, ap->a_cred, &bp,
273: aflags))
274: return (error);
275: oip->i_size = length;
276: #if MACH_NBC
277: if ((ovp->v_type == VREG) && (ovp->v_vm_info && !(ovp->v_vm_info->mapped))) {
278: #endif /* MACH_NBC */
279: vnode_pager_setsize(ovp, (u_long)length);
280: vnode_uncache(ovp);
281: #if MACH_NBC
282: }
283: #endif /* !MACH_NBC */
284: if (aflags & B_SYNC)
285: bwrite(bp);
286: else
287: bawrite(bp);
288: oip->i_flag |= IN_CHANGE | IN_UPDATE;
289: return (VOP_UPDATE(ovp, &tv, &tv, 1));
290: }
291: /*
292: * Shorten the size of the file. If the file is not being
293: * truncated to a block boundry, the contents of the
294: * partial block following the end of the file must be
295: * zero'ed in case it ever become accessable again because
296: * of subsequent file growth.
297: */
298: offset = blkoff(fs, length);
299: if (offset == 0) {
300: oip->i_size = length;
301: } else {
302: lbn = lblkno(fs, length);
303: aflags = B_CLRBUF;
304: if (ap->a_flags & IO_SYNC)
305: aflags |= B_SYNC;
306: if (error = ffs_balloc(oip, lbn, offset, ap->a_cred, &bp,
307: aflags))
308: return (error);
309: oip->i_size = length;
310: size = blksize(fs, oip, lbn);
311: (void) vnode_uncache(ovp); /* Ignore errors! */
312: bzero((char *)bp->b_data + offset, (u_int)(size - offset));
313: allocbuf(bp, size);
314: if (aflags & B_SYNC)
315: bwrite(bp);
316: else
317: bawrite(bp);
318: }
319: #if MACH_NBC
320: if ((ovp->v_type == VREG) && (ovp->v_vm_info && !(ovp->v_vm_info->mapped))) {
321: #endif /* MACH_NBC */
322: vnode_pager_setsize(ovp, (u_long)length);
323: #if MACH_NBC
324: }
325: #endif /* MACH_NBC */
326:
327: /*
328: * Calculate index into inode's block list of
329: * last direct and indirect blocks (if any)
330: * which we want to keep. Lastblock is -1 when
331: * the file is truncated to 0.
332: */
333: lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
334: lastiblock[SINGLE] = lastblock - NDADDR;
335: lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
336: lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
337: #ifdef NeXT
338: VOP_DEVBLOCKSIZE(oip->i_devvp,&devBlockSize);
339: nblocks = btodb(fs->fs_bsize, devBlockSize);
340: #else
341: nblocks = btodb(fs->fs_bsize);
342: #endif /* NeXT */
343: /*
344: * Update file and block pointers on disk before we start freeing
345: * blocks. If we crash before free'ing blocks below, the blocks
346: * will be returned to the free list. lastiblock values are also
347: * normalized to -1 for calls to ffs_indirtrunc below.
348: */
349: bcopy((caddr_t)&oip->i_db[0], (caddr_t)oldblks, sizeof oldblks);
350: for (level = TRIPLE; level >= SINGLE; level--)
351: if (lastiblock[level] < 0) {
352: oip->i_ib[level] = 0;
353: lastiblock[level] = -1;
354: }
355: for (i = NDADDR - 1; i > lastblock; i--)
356: oip->i_db[i] = 0;
357: oip->i_flag |= IN_CHANGE | IN_UPDATE;
358: if (error = VOP_UPDATE(ovp, &tv, &tv, MNT_WAIT))
359: allerror = error;
360: /*
361: * Having written the new inode to disk, save its new configuration
362: * and put back the old block pointers long enough to process them.
363: * Note that we save the new block configuration so we can check it
364: * when we are done.
365: */
366: bcopy((caddr_t)&oip->i_db[0], (caddr_t)newblks, sizeof newblks);
367: bcopy((caddr_t)oldblks, (caddr_t)&oip->i_db[0], sizeof oldblks);
368: oip->i_size = osize;
369: vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
370: allerror = vinvalbuf(ovp, vflags, ap->a_cred, ap->a_p, 0, 0);
371:
372: /*
373: * Indirect blocks first.
374: */
375: indir_lbn[SINGLE] = -NDADDR;
376: indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1;
377: indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
378: for (level = TRIPLE; level >= SINGLE; level--) {
379: bn = oip->i_ib[level];
380: if (bn != 0) {
381: error = ffs_indirtrunc(oip, indir_lbn[level],
382: fsbtodb(fs, bn), lastiblock[level], level, &count);
383: if (error)
384: allerror = error;
385: blocksreleased += count;
386: if (lastiblock[level] < 0) {
387: oip->i_ib[level] = 0;
388: ffs_blkfree(oip, bn, fs->fs_bsize);
389: blocksreleased += nblocks;
390: }
391: }
392: if (lastiblock[level] >= 0)
393: goto done;
394: }
395:
396: /*
397: * All whole direct blocks or frags.
398: */
399: for (i = NDADDR - 1; i > lastblock; i--) {
400: register long bsize;
401:
402: bn = oip->i_db[i];
403: if (bn == 0)
404: continue;
405: oip->i_db[i] = 0;
406: bsize = blksize(fs, oip, i);
407: ffs_blkfree(oip, bn, bsize);
408: #ifdef NeXT
409: blocksreleased += btodb(bsize, devBlockSize);
410: #else
411: blocksreleased += btodb(bsize);
412: #endif /* NeXT */
413: }
414: if (lastblock < 0)
415: goto done;
416:
417: /*
418: * Finally, look for a change in size of the
419: * last direct block; release any frags.
420: */
421: bn = oip->i_db[lastblock];
422: if (bn != 0) {
423: long oldspace, newspace;
424:
425: /*
426: * Calculate amount of space we're giving
427: * back as old block size minus new block size.
428: */
429: oldspace = blksize(fs, oip, lastblock);
430: oip->i_size = length;
431: newspace = blksize(fs, oip, lastblock);
432: if (newspace == 0)
433: panic("itrunc: newspace");
434: if (oldspace - newspace > 0) {
435: /*
436: * Block number of space to be free'd is
437: * the old block # plus the number of frags
438: * required for the storage we're keeping.
439: */
440: bn += numfrags(fs, newspace);
441: ffs_blkfree(oip, bn, oldspace - newspace);
442: #ifdef NeXT
443: blocksreleased += btodb(oldspace - newspace, devBlockSize);
444: #else
445: blocksreleased += btodb(oldspace - newspace);
446: #endif /* NeXT */
447: }
448: }
449: done:
450: #if DIAGNOSTIC
451: for (level = SINGLE; level <= TRIPLE; level++)
452: if (newblks[NDADDR + level] != oip->i_ib[level])
453: panic("itrunc1");
454: for (i = 0; i < NDADDR; i++)
455: if (newblks[i] != oip->i_db[i])
456: panic("itrunc2");
457: if (length == 0 &&
458: (ovp->v_dirtyblkhd.lh_first || ovp->v_cleanblkhd.lh_first))
459: panic("itrunc3");
460: #endif /* DIAGNOSTIC */
461: /*
462: * Put back the real size.
463: */
464: oip->i_size = length;
465: oip->i_blocks -= blocksreleased;
466: if (oip->i_blocks < 0) /* sanity */
467: oip->i_blocks = 0;
468: oip->i_flag |= IN_CHANGE;
469: #if QUOTA
470: (void) chkdq(oip, -blocksreleased, NOCRED, 0);
471: #endif
472: return (allerror);
473: }
474:
475: /*
476: * Release blocks associated with the inode ip and stored in the indirect
477: * block bn. Blocks are free'd in LIFO order up to (but not including)
478: * lastbn. If level is greater than SINGLE, the block is an indirect block
479: * and recursive calls to indirtrunc must be used to cleanse other indirect
480: * blocks.
481: *
482: * NB: triple indirect blocks are untested.
483: */
484: static int
485: ffs_indirtrunc(ip, lbn, dbn, lastbn, level, countp)
486: register struct inode *ip;
487: ufs_daddr_t lbn, lastbn;
488: ufs_daddr_t dbn;
489: int level;
490: long *countp;
491: {
492: register int i;
493: struct buf *bp;
494: struct buf *tbp;
495: register struct fs *fs = ip->i_fs;
496: register ufs_daddr_t *bap;
497: struct vnode *vp=ITOV(ip);
498: ufs_daddr_t *copy, nb, nlbn, last;
499: long blkcount, factor;
500: int nblocks, blocksreleased = 0;
501: int error = 0, allerror = 0;
502: #if NeXT
503: int devBlockSize=0;
504: #endif /* NeXT */
505: #if REV_ENDIAN_FS
506: struct mount *mp=vp->v_mount;
507: int rev_endian=(mp->mnt_flag & MNT_REVEND);
508: #endif /* REV_ENDIAN_FS */
509:
510: /*
511: * Calculate index in current block of last
512: * block to be kept. -1 indicates the entire
513: * block so we need not calculate the index.
514: */
515: factor = 1;
516: for (i = SINGLE; i < level; i++)
517: factor *= NINDIR(fs);
518: last = lastbn;
519: if (lastbn > 0)
520: last /= factor;
521: #if NeXT
522: VOP_DEVBLOCKSIZE(ip->i_devvp,&devBlockSize);
523: nblocks = btodb(fs->fs_bsize, devBlockSize);
524: #else
525: nblocks = btodb(fs->fs_bsize);
526: #endif /* NeXT */
527:
528: /* Doing a MALLOC here is asking for trouble. We can still
529: * deadlock on pagerfile lock, in case we are running
530: * low on memory and block in MALLOC
531: */
532:
533: tbp = geteblk(fs->fs_bsize);
534: copy = (ufs_daddr_t *)tbp->b_data;
535:
536: /*
537: * Get buffer of block pointers, zero those entries corresponding
538: * to blocks to be free'd, and update on disk copy first. Since
539: * double(triple) indirect before single(double) indirect, calls
540: * to bmap on these blocks will fail. However, we already have
541: * the on disk address, so we have to set the b_blkno field
542: * explicitly instead of letting bread do everything for us.
543: */
544:
545: vp = ITOV(ip);
546: bp = getblk(vp, lbn, (int)fs->fs_bsize, 0, 0);
547: if (bp->b_flags & (B_DONE | B_DELWRI)) {
548: /* Braces must be here in case trace evaluates to nothing. */
549: trace(TR_BREADHIT, pack(vp, fs->fs_bsize), lbn);
550: } else {
551: trace(TR_BREADMISS, pack(vp, fs->fs_bsize), lbn);
552: current_proc()->p_stats->p_ru.ru_inblock++; /* pay for read */
553: bp->b_flags |= B_READ;
554: if (bp->b_bcount > bp->b_bufsize)
555: panic("ffs_indirtrunc: bad buffer size");
556: bp->b_blkno = dbn;
557: VOP_STRATEGY(bp);
558: error = biowait(bp);
559: }
560: if (error) {
561: brelse(bp);
562: *countp = 0;
563: brelse(tbp);
564: return (error);
565: }
566:
567: bap = (ufs_daddr_t *)bp->b_data;
568: bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize);
569: bzero((caddr_t)&bap[last + 1],
570: (u_int)(NINDIR(fs) - (last + 1)) * sizeof (ufs_daddr_t));
571: if (last == -1)
572: bp->b_flags |= B_INVAL;
573: error = bwrite(bp);
574: if (error)
575: allerror = error;
576: bap = copy;
577:
578: /*
579: * Recursively free totally unused blocks.
580: */
581: for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
582: i--, nlbn += factor) {
583: #if REV_ENDIAN_FS
584: if (rev_endian)
585: nb = NXSwapLong(bap[i]);
586: else {
587: #endif /* REV_ENDIAN_FS */
588: nb = bap[i];
589: #if REV_ENDIAN_FS
590: }
591: #endif /* REV_ENDIAN_FS */
592: if (nb == 0)
593: continue;
594: if (level > SINGLE) {
595: if (error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
596: (ufs_daddr_t)-1, level - 1, &blkcount))
597: allerror = error;
598: blocksreleased += blkcount;
599: }
600: ffs_blkfree(ip, nb, fs->fs_bsize);
601: blocksreleased += nblocks;
602: }
603:
604: /*
605: * Recursively free last partial block.
606: */
607: if (level > SINGLE && lastbn >= 0) {
608: last = lastbn % factor;
609: #if REV_ENDIAN_FS
610: if (rev_endian)
611: nb = NXSwapLong(bap[i]);
612: else {
613: #endif /* REV_ENDIAN_FS */
614: nb = bap[i];
615: #if REV_ENDIAN_FS
616: }
617: #endif /* REV_ENDIAN_FS */
618: if (nb != 0) {
619: if (error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
620: last, level - 1, &blkcount))
621: allerror = error;
622: blocksreleased += blkcount;
623: }
624: }
625: brelse(tbp);
626: *countp = blocksreleased;
627: return (allerror);
628: }
629:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.