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