Line data Source code
1 : /* $OpenBSD: ffs_inode.c,v 1.77 2018/03/30 17:35:20 dhill 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/buf.h>
40 : #include <sys/vnode.h>
41 : #include <sys/kernel.h>
42 : #include <sys/malloc.h>
43 : #include <sys/resourcevar.h>
44 :
45 : #include <ufs/ufs/quota.h>
46 : #include <ufs/ufs/inode.h>
47 : #include <ufs/ufs/ufsmount.h>
48 : #include <ufs/ufs/ufs_extern.h>
49 :
50 : #include <ufs/ffs/fs.h>
51 : #include <ufs/ffs/ffs_extern.h>
52 :
53 : int ffs_indirtrunc(struct inode *, daddr_t, daddr_t, daddr_t, int, long *);
54 :
55 : /*
56 : * Update the access, modified, and inode change times as specified by the
57 : * IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. The IN_MODIFIED
58 : * flag is used to specify that the inode needs to be updated but that the
59 : * times have already been set. The IN_LAZYMOD flag is used to specify
60 : * that the inode needs to be updated at some point, by reclaim if not
61 : * in the course of other changes; this is used to defer writes just to
62 : * update device timestamps. If waitfor is set, then wait for the disk
63 : * write of the inode to complete.
64 : */
65 : int
66 0 : ffs_update(struct inode *ip, int waitfor)
67 : {
68 : struct vnode *vp;
69 : struct fs *fs;
70 0 : struct buf *bp;
71 : int error;
72 :
73 0 : vp = ITOV(ip);
74 0 : ufs_itimes(vp);
75 :
76 0 : if ((ip->i_flag & IN_MODIFIED) == 0 && waitfor == 0)
77 0 : return (0);
78 :
79 0 : ip->i_flag &= ~(IN_MODIFIED | IN_LAZYMOD);
80 0 : fs = ip->i_fs;
81 :
82 : /*
83 : * Ensure that uid and gid are correct. This is a temporary
84 : * fix until fsck has been changed to do the update.
85 : */
86 0 : if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_inodefmt < FS_44INODEFMT) {
87 0 : ip->i_din1->di_ouid = ip->i_ffs1_uid;
88 0 : ip->i_din1->di_ogid = ip->i_ffs1_gid;
89 0 : }
90 :
91 0 : error = bread(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
92 0 : (int)fs->fs_bsize, &bp);
93 0 : if (error) {
94 0 : brelse(bp);
95 0 : return (error);
96 : }
97 :
98 0 : if (DOINGSOFTDEP(vp))
99 0 : softdep_update_inodeblock(ip, bp, waitfor);
100 0 : else if (ip->i_effnlink != DIP(ip, nlink))
101 0 : panic("ffs_update: bad link cnt");
102 :
103 : #ifdef FFS2
104 0 : if (ip->i_ump->um_fstype == UM_UFS2)
105 0 : *((struct ufs2_dinode *)bp->b_data +
106 0 : ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2;
107 : else
108 : #endif
109 0 : *((struct ufs1_dinode *)bp->b_data +
110 0 : ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1;
111 :
112 0 : if (waitfor && !DOINGASYNC(vp)) {
113 0 : return (bwrite(bp));
114 : } else {
115 0 : bdwrite(bp);
116 0 : return (0);
117 : }
118 0 : }
119 :
120 : #define SINGLE 0 /* index of single indirect block */
121 : #define DOUBLE 1 /* index of double indirect block */
122 : #define TRIPLE 2 /* index of triple indirect block */
123 :
124 : /*
125 : * Truncate the inode oip to at most length size, freeing the
126 : * disk blocks.
127 : */
128 : int
129 0 : ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred)
130 : {
131 : struct vnode *ovp;
132 : daddr_t lastblock;
133 0 : daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
134 0 : daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
135 : struct fs *fs;
136 0 : struct buf *bp;
137 : int offset, size, level;
138 0 : long count, nblocks, vflags, blocksreleased = 0;
139 : int i, aflags, error, allerror;
140 : off_t osize;
141 :
142 0 : if (length < 0)
143 0 : return (EINVAL);
144 0 : ovp = ITOV(oip);
145 :
146 0 : if (ovp->v_type != VREG &&
147 0 : ovp->v_type != VDIR &&
148 0 : ovp->v_type != VLNK)
149 0 : return (0);
150 :
151 0 : if (DIP(oip, size) == length)
152 0 : return (0);
153 :
154 0 : if (ovp->v_type == VLNK &&
155 0 : (DIP(oip, size) < oip->i_ump->um_maxsymlinklen ||
156 0 : (oip->i_ump->um_maxsymlinklen == 0 &&
157 0 : oip->i_din1->di_blocks == 0))) {
158 : #ifdef DIAGNOSTIC
159 0 : if (length != 0)
160 0 : panic("ffs_truncate: partial truncate of symlink");
161 : #endif
162 0 : memset(SHORTLINK(oip), 0, (size_t) DIP(oip, size));
163 0 : DIP_ASSIGN(oip, size, 0);
164 0 : oip->i_flag |= IN_CHANGE | IN_UPDATE;
165 0 : return (UFS_UPDATE(oip, 1));
166 : }
167 :
168 0 : if ((error = getinoquota(oip)) != 0)
169 0 : return (error);
170 :
171 0 : uvm_vnp_setsize(ovp, length);
172 0 : oip->i_ci.ci_lasta = oip->i_ci.ci_clen
173 0 : = oip->i_ci.ci_cstart = oip->i_ci.ci_lastw = 0;
174 :
175 0 : if (DOINGSOFTDEP(ovp)) {
176 0 : if (length > 0 || softdep_slowdown(ovp)) {
177 : /*
178 : * If a file is only partially truncated, then
179 : * we have to clean up the data structures
180 : * describing the allocation past the truncation
181 : * point. Finding and deallocating those structures
182 : * is a lot of work. Since partial truncation occurs
183 : * rarely, we solve the problem by syncing the file
184 : * so that it will have no data structures left.
185 : */
186 0 : if ((error = VOP_FSYNC(ovp, cred, MNT_WAIT,
187 0 : curproc)) != 0)
188 0 : return (error);
189 : } else {
190 0 : (void)ufs_quota_free_blocks(oip, DIP(oip, blocks),
191 : NOCRED);
192 0 : softdep_setup_freeblocks(oip, length);
193 0 : (void) vinvalbuf(ovp, 0, cred, curproc, 0, 0);
194 0 : oip->i_flag |= IN_CHANGE | IN_UPDATE;
195 0 : return (UFS_UPDATE(oip, 0));
196 : }
197 : }
198 :
199 0 : fs = oip->i_fs;
200 0 : osize = DIP(oip, size);
201 : /*
202 : * Lengthen the size of the file. We must ensure that the
203 : * last byte of the file is allocated. Since the smallest
204 : * value of osize is 0, length will be at least 1.
205 : */
206 0 : if (osize < length) {
207 0 : if (length > fs->fs_maxfilesize)
208 0 : return (EFBIG);
209 : aflags = B_CLRBUF;
210 0 : if (flags & IO_SYNC)
211 0 : aflags |= B_SYNC;
212 0 : error = UFS_BUF_ALLOC(oip, length - 1, 1,
213 : cred, aflags, &bp);
214 0 : if (error)
215 0 : return (error);
216 0 : DIP_ASSIGN(oip, size, length);
217 0 : uvm_vnp_setsize(ovp, length);
218 0 : (void) uvm_vnp_uncache(ovp);
219 0 : if (aflags & B_SYNC)
220 0 : bwrite(bp);
221 : else
222 0 : bawrite(bp);
223 0 : oip->i_flag |= IN_CHANGE | IN_UPDATE;
224 0 : return (UFS_UPDATE(oip, 1));
225 : }
226 0 : uvm_vnp_setsize(ovp, length);
227 :
228 : /*
229 : * Shorten the size of the file. If the file is not being
230 : * truncated to a block boundary, the contents of the
231 : * partial block following the end of the file must be
232 : * zero'ed in case it ever becomes accessible again because
233 : * of subsequent file growth. Directories however are not
234 : * zero'ed as they should grow back initialized to empty.
235 : */
236 0 : offset = blkoff(fs, length);
237 0 : if (offset == 0) {
238 0 : DIP_ASSIGN(oip, size, length);
239 : } else {
240 0 : lbn = lblkno(fs, length);
241 : aflags = B_CLRBUF;
242 0 : if (flags & IO_SYNC)
243 0 : aflags |= B_SYNC;
244 0 : error = UFS_BUF_ALLOC(oip, length - 1, 1,
245 : cred, aflags, &bp);
246 0 : if (error)
247 0 : return (error);
248 : /*
249 : * When we are doing soft updates and the UFS_BALLOC
250 : * above fills in a direct block hole with a full sized
251 : * block that will be truncated down to a fragment below,
252 : * we must flush out the block dependency with an FSYNC
253 : * so that we do not get a soft updates inconsistency
254 : * when we create the fragment below.
255 : */
256 0 : if (DOINGSOFTDEP(ovp) && lbn < NDADDR &&
257 0 : fragroundup(fs, blkoff(fs, length)) < fs->fs_bsize &&
258 0 : (error = VOP_FSYNC(ovp, cred, MNT_WAIT, curproc)) != 0)
259 0 : return (error);
260 0 : DIP_ASSIGN(oip, size, length);
261 0 : size = blksize(fs, oip, lbn);
262 0 : (void) uvm_vnp_uncache(ovp);
263 0 : if (ovp->v_type != VDIR)
264 0 : memset(bp->b_data + offset, 0, size - offset);
265 0 : buf_adjcnt(bp, size);
266 0 : if (aflags & B_SYNC)
267 0 : bwrite(bp);
268 : else
269 0 : bawrite(bp);
270 : }
271 : /*
272 : * Calculate index into inode's block list of
273 : * last direct and indirect blocks (if any)
274 : * which we want to keep. Lastblock is -1 when
275 : * the file is truncated to 0.
276 : */
277 0 : lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
278 0 : lastiblock[SINGLE] = lastblock - NDADDR;
279 0 : lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
280 0 : lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
281 0 : nblocks = btodb(fs->fs_bsize);
282 :
283 : /*
284 : * Update file and block pointers on disk before we start freeing
285 : * blocks. If we crash before free'ing blocks below, the blocks
286 : * will be returned to the free list. lastiblock values are also
287 : * normalized to -1 for calls to ffs_indirtrunc below.
288 : */
289 0 : for (level = TRIPLE; level >= SINGLE; level--) {
290 0 : oldblks[NDADDR + level] = DIP(oip, ib[level]);
291 0 : if (lastiblock[level] < 0) {
292 0 : DIP_ASSIGN(oip, ib[level], 0);
293 0 : lastiblock[level] = -1;
294 0 : }
295 : }
296 :
297 0 : for (i = 0; i < NDADDR; i++) {
298 0 : oldblks[i] = DIP(oip, db[i]);
299 0 : if (i > lastblock)
300 0 : DIP_ASSIGN(oip, db[i], 0);
301 : }
302 :
303 0 : oip->i_flag |= IN_CHANGE | IN_UPDATE;
304 0 : if ((error = UFS_UPDATE(oip, 1)) != 0)
305 0 : allerror = error;
306 :
307 : /*
308 : * Having written the new inode to disk, save its new configuration
309 : * and put back the old block pointers long enough to process them.
310 : * Note that we save the new block configuration so we can check it
311 : * when we are done.
312 : */
313 0 : for (i = 0; i < NDADDR; i++) {
314 0 : newblks[i] = DIP(oip, db[i]);
315 0 : DIP_ASSIGN(oip, db[i], oldblks[i]);
316 : }
317 :
318 0 : for (i = 0; i < NIADDR; i++) {
319 0 : newblks[NDADDR + i] = DIP(oip, ib[i]);
320 0 : DIP_ASSIGN(oip, ib[i], oldblks[NDADDR + i]);
321 : }
322 :
323 0 : DIP_ASSIGN(oip, size, osize);
324 0 : vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
325 0 : allerror = vinvalbuf(ovp, vflags, cred, curproc, 0, 0);
326 :
327 : /*
328 : * Indirect blocks first.
329 : */
330 0 : indir_lbn[SINGLE] = -NDADDR;
331 0 : indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) - 1;
332 0 : indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
333 0 : for (level = TRIPLE; level >= SINGLE; level--) {
334 0 : bn = DIP(oip, ib[level]);
335 0 : if (bn != 0) {
336 0 : error = ffs_indirtrunc(oip, indir_lbn[level],
337 0 : fsbtodb(fs, bn), lastiblock[level], level, &count);
338 0 : if (error)
339 0 : allerror = error;
340 0 : blocksreleased += count;
341 0 : if (lastiblock[level] < 0) {
342 0 : DIP_ASSIGN(oip, ib[level], 0);
343 0 : ffs_blkfree(oip, bn, fs->fs_bsize);
344 0 : blocksreleased += nblocks;
345 0 : }
346 : }
347 0 : if (lastiblock[level] >= 0)
348 : goto done;
349 : }
350 :
351 : /*
352 : * All whole direct blocks or frags.
353 : */
354 0 : for (i = NDADDR - 1; i > lastblock; i--) {
355 : long bsize;
356 :
357 0 : bn = DIP(oip, db[i]);
358 0 : if (bn == 0)
359 0 : continue;
360 :
361 0 : DIP_ASSIGN(oip, db[i], 0);
362 0 : bsize = blksize(fs, oip, i);
363 0 : ffs_blkfree(oip, bn, bsize);
364 0 : blocksreleased += btodb(bsize);
365 0 : }
366 0 : if (lastblock < 0)
367 : goto done;
368 :
369 : /*
370 : * Finally, look for a change in size of the
371 : * last direct block; release any frags.
372 : */
373 0 : bn = DIP(oip, db[lastblock]);
374 0 : if (bn != 0) {
375 : long oldspace, newspace;
376 :
377 : /*
378 : * Calculate amount of space we're giving
379 : * back as old block size minus new block size.
380 : */
381 0 : oldspace = blksize(fs, oip, lastblock);
382 0 : DIP_ASSIGN(oip, size, length);
383 0 : newspace = blksize(fs, oip, lastblock);
384 0 : if (newspace == 0)
385 0 : panic("ffs_truncate: newspace");
386 0 : if (oldspace - newspace > 0) {
387 : /*
388 : * Block number of space to be free'd is
389 : * the old block # plus the number of frags
390 : * required for the storage we're keeping.
391 : */
392 0 : bn += numfrags(fs, newspace);
393 0 : ffs_blkfree(oip, bn, oldspace - newspace);
394 0 : blocksreleased += btodb(oldspace - newspace);
395 0 : }
396 0 : }
397 : done:
398 : #ifdef DIAGNOSTIC
399 0 : for (level = SINGLE; level <= TRIPLE; level++)
400 0 : if (newblks[NDADDR + level] != DIP(oip, ib[level]))
401 0 : panic("ffs_truncate1");
402 0 : for (i = 0; i < NDADDR; i++)
403 0 : if (newblks[i] != DIP(oip, db[i]))
404 0 : panic("ffs_truncate2");
405 : #endif /* DIAGNOSTIC */
406 : /*
407 : * Put back the real size.
408 : */
409 0 : DIP_ASSIGN(oip, size, length);
410 0 : if (DIP(oip, blocks) >= blocksreleased)
411 0 : DIP_ADD(oip, blocks, -blocksreleased);
412 : else /* sanity */
413 0 : DIP_ASSIGN(oip, blocks, 0);
414 0 : oip->i_flag |= IN_CHANGE;
415 0 : (void)ufs_quota_free_blocks(oip, blocksreleased, NOCRED);
416 0 : return (allerror);
417 0 : }
418 :
419 : #ifdef FFS2
420 : #define BAP(ip, i) (((ip)->i_ump->um_fstype == UM_UFS2) ? bap2[i] : bap1[i])
421 : #define BAP_ASSIGN(ip, i, value) \
422 : do { \
423 : if ((ip)->i_ump->um_fstype == UM_UFS2) \
424 : bap2[i] = (value); \
425 : else \
426 : bap1[i] = (value); \
427 : } while (0)
428 : #else
429 : #define BAP(ip, i) bap1[i]
430 : #define BAP_ASSIGN(ip, i, value) do { bap1[i] = (value); } while (0)
431 : #endif /* FFS2 */
432 :
433 : /*
434 : * Release blocks associated with the inode ip and stored in the indirect
435 : * block bn. Blocks are free'd in LIFO order up to (but not including)
436 : * lastbn. If level is greater than SINGLE, the block is an indirect block
437 : * and recursive calls to indirtrunc must be used to cleanse other indirect
438 : * blocks.
439 : *
440 : * NB: triple indirect blocks are untested.
441 : */
442 : int
443 0 : ffs_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn,
444 : daddr_t lastbn, int level, long *countp)
445 : {
446 : int i;
447 : struct buf *bp;
448 0 : struct fs *fs = ip->i_fs;
449 : struct vnode *vp;
450 : void *copy = NULL;
451 : daddr_t nb, nlbn, last;
452 0 : long blkcount, factor;
453 : int nblocks, blocksreleased = 0;
454 : int error = 0, allerror = 0;
455 : int32_t *bap1 = NULL;
456 : #ifdef FFS2
457 : int64_t *bap2 = NULL;
458 : #endif
459 :
460 : /*
461 : * Calculate index in current block of last
462 : * block to be kept. -1 indicates the entire
463 : * block so we need not calculate the index.
464 : */
465 : factor = 1;
466 0 : for (i = SINGLE; i < level; i++)
467 0 : factor *= NINDIR(fs);
468 : last = lastbn;
469 0 : if (lastbn > 0)
470 0 : last /= factor;
471 0 : nblocks = btodb(fs->fs_bsize);
472 : /*
473 : * Get buffer of block pointers, zero those entries corresponding
474 : * to blocks to be free'd, and update on disk copy first. Since
475 : * double(triple) indirect before single(double) indirect, calls
476 : * to bmap on these blocks will fail. However, we already have
477 : * the on disk address, so we have to set the b_blkno field
478 : * explicitly instead of letting bread do everything for us.
479 : */
480 0 : vp = ITOV(ip);
481 0 : bp = getblk(vp, lbn, (int)fs->fs_bsize, 0, 0);
482 0 : if (!(bp->b_flags & (B_DONE | B_DELWRI))) {
483 0 : curproc->p_ru.ru_inblock++; /* pay for read */
484 0 : bcstats.pendingreads++;
485 0 : bcstats.numreads++;
486 0 : bp->b_flags |= B_READ;
487 0 : if (bp->b_bcount > bp->b_bufsize)
488 0 : panic("ffs_indirtrunc: bad buffer size");
489 0 : bp->b_blkno = dbn;
490 0 : VOP_STRATEGY(bp);
491 0 : error = biowait(bp);
492 0 : }
493 0 : if (error) {
494 0 : brelse(bp);
495 0 : *countp = 0;
496 0 : return (error);
497 : }
498 :
499 : #ifdef FFS2
500 0 : if (ip->i_ump->um_fstype == UM_UFS2)
501 0 : bap2 = (int64_t *)bp->b_data;
502 : else
503 : #endif
504 0 : bap1 = (int32_t *)bp->b_data;
505 :
506 0 : if (lastbn != -1) {
507 0 : copy = malloc(fs->fs_bsize, M_TEMP, M_WAITOK);
508 0 : memcpy(copy, bp->b_data, fs->fs_bsize);
509 :
510 0 : for (i = last + 1; i < NINDIR(fs); i++)
511 0 : BAP_ASSIGN(ip, i, 0);
512 :
513 0 : if (!DOINGASYNC(vp)) {
514 0 : error = bwrite(bp);
515 0 : if (error)
516 0 : allerror = error;
517 : } else {
518 0 : bawrite(bp);
519 : }
520 :
521 : #ifdef FFS2
522 0 : if (ip->i_ump->um_fstype == UM_UFS2)
523 0 : bap2 = (int64_t *)copy;
524 : else
525 : #endif
526 0 : bap1 = (int32_t *)copy;
527 : }
528 :
529 : /*
530 : * Recursively free totally unused blocks.
531 : */
532 0 : for (i = NINDIR(fs) - 1, nlbn = lbn + 1 - i * factor; i > last;
533 0 : i--, nlbn += factor) {
534 0 : nb = BAP(ip, i);
535 0 : if (nb == 0)
536 : continue;
537 0 : if (level > SINGLE) {
538 0 : error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
539 0 : -1, level - 1, &blkcount);
540 0 : if (error)
541 0 : allerror = error;
542 0 : blocksreleased += blkcount;
543 0 : }
544 0 : ffs_blkfree(ip, nb, fs->fs_bsize);
545 0 : blocksreleased += nblocks;
546 0 : }
547 :
548 : /*
549 : * Recursively free last partial block.
550 : */
551 0 : if (level > SINGLE && lastbn >= 0) {
552 0 : last = lastbn % factor;
553 0 : nb = BAP(ip, i);
554 0 : if (nb != 0) {
555 0 : error = ffs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
556 0 : last, level - 1, &blkcount);
557 0 : if (error)
558 0 : allerror = error;
559 0 : blocksreleased += blkcount;
560 0 : }
561 : }
562 0 : if (copy != NULL) {
563 0 : free(copy, M_TEMP, fs->fs_bsize);
564 0 : } else {
565 0 : bp->b_flags |= B_INVAL;
566 0 : brelse(bp);
567 : }
568 :
569 0 : *countp = blocksreleased;
570 0 : return (allerror);
571 0 : }
|