xref: /netbsd-src/sys/fs/v7fs/v7fs_datablock.c (revision 88fcb00c0357f2d7c1774f86a352637bfda96184)
1 /*	$NetBSD: v7fs_datablock.c,v 1.1 2011/06/27 11:52:24 uch Exp $	*/
2 
3 /*-
4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.1 2011/06/27 11:52:24 uch Exp $");
34 #if defined _KERNEL_OPT
35 #include "opt_v7fs.h"
36 #endif
37 
38 #include <sys/types.h>
39 #ifdef _KERNEL
40 #include <sys/systm.h>
41 #include <sys/param.h>
42 #else
43 #include <stdio.h>
44 #include <string.h>
45 #include <errno.h>
46 #endif
47 
48 #include "v7fs.h"
49 #include "v7fs_impl.h"
50 #include "v7fs_endian.h"
51 #include "v7fs_inode.h"
52 #include "v7fs_datablock.h"
53 #include "v7fs_superblock.h"
54 
55 #ifdef V7FS_DATABLOCK_DEBUG
56 #define	DPRINTF(fmt, args...)	printf("%s: " fmt, __func__, ##args)
57 #else
58 #define	DPRINTF(fmt, args...)	((void)0)
59 #endif
60 
61 struct v7fs_daddr_map {
62 	int level; /* direct, index1, index2, index3 */
63 	v7fs_daddr_t index[3];
64 };
65 
66 static int v7fs_datablock_addr(size_t, struct v7fs_daddr_map *);
67 static int v7fs_datablock_deallocate(struct v7fs_self *, v7fs_daddr_t);
68 static int loop1(struct v7fs_self *, v7fs_daddr_t, size_t *,
69     int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
70 static int loop2(struct v7fs_self *, v7fs_daddr_t, size_t *,
71     int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
72 static v7fs_daddr_t link(struct v7fs_self *, v7fs_daddr_t, int);
73 static v7fs_daddr_t add_leaf(struct v7fs_self *, v7fs_daddr_t, int);
74 static v7fs_daddr_t unlink(struct v7fs_self *, v7fs_daddr_t, int);
75 static v7fs_daddr_t remove_leaf(struct v7fs_self *, v7fs_daddr_t, int);
76 static v7fs_daddr_t remove_self(struct v7fs_self *, v7fs_daddr_t);
77 
78 #ifdef V7FS_DATABLOCK_DEBUG
79 void daddr_map_dump(const struct v7fs_daddr_map *);
80 #else
81 #define	daddr_map_dump(x)	((void)0)
82 #endif
83 
84 bool
85 datablock_number_sanity(const struct v7fs_self *fs, v7fs_daddr_t blk)
86 {
87 	const struct v7fs_superblock *sb = &fs->superblock;
88 	bool ok = (blk >= sb->datablock_start_sector) &&
89 	    (blk < sb->volume_size);
90 
91 #ifdef V7FS_DATABLOCK_DEBUG
92 	if (!ok) {
93 		DPRINTF("Bad data block #%d\n", blk);
94 	}
95 #endif
96 
97 	return ok;
98 }
99 
100 int
101 v7fs_datablock_allocate(struct v7fs_self *fs, v7fs_daddr_t *block_number)
102 {
103 	struct v7fs_superblock *sb = &fs->superblock;
104 	v7fs_daddr_t blk;
105 	int error = 0;
106 
107 	*block_number = 0;
108 	SUPERB_LOCK(fs);
109 	do {
110 		if (!sb->total_freeblock) {
111 			DPRINTF("free block exhausted!!!\n");
112 			SUPERB_UNLOCK(fs);
113 			return ENOSPC;
114 		}
115 
116 		/* Get free block from superblock cache. */
117 		blk = sb->freeblock[--sb->nfreeblock];
118 		sb->total_freeblock--;
119 		sb->modified = 1;
120 
121 		/* If nfreeblock is zero, it block is next freeblock link. */
122 		if (sb->nfreeblock == 0) {
123 			if ((error = v7fs_freeblock_update(fs, blk))) {
124 				DPRINTF("no freeblock!!!\n");
125 				SUPERB_UNLOCK(fs);
126 				return error;
127 			}
128 			/* This freeblock link is no longer required. */
129 			/* use as data block. */
130 		}
131 	} while (!datablock_number_sanity(fs, blk)); /* skip bogus block. */
132 	SUPERB_UNLOCK(fs);
133 
134 	DPRINTF("Get freeblock %d\n", blk);
135 	/* Zero clear datablock. */
136 	void *buf;
137 	if (!(buf = scratch_read(fs, blk)))
138 		return EIO;
139 	memset(buf, 0, V7FS_BSIZE);
140 	if (!fs->io.write(fs->io.cookie, buf, blk))
141 		error = EIO;
142 	scratch_free(fs, buf);
143 
144 	if (error == 0)
145 		*block_number = blk;
146 
147 	return error;
148 }
149 
150 static int
151 v7fs_datablock_deallocate(struct v7fs_self *fs, v7fs_daddr_t blk)
152 {
153 	struct v7fs_superblock *sb = &fs->superblock;
154 	void *buf;
155 	int error = 0;
156 
157 	if (!datablock_number_sanity(fs, blk))
158 		return EIO;
159 
160 	/* Add to in-core freelist. */
161 	SUPERB_LOCK(fs);
162 	if (sb->nfreeblock < V7FS_MAX_FREEBLOCK) {
163 		sb->freeblock[sb->nfreeblock++] = blk;
164 		sb->total_freeblock++;
165 		sb->modified = 1;
166 		DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
167 		SUPERB_UNLOCK(fs);
168 		return 0;
169 	}
170 
171 	/* No space to push. */
172 	/* Make this block to freeblock list.and current cache moved to this. */
173 	if (!(buf = scratch_read(fs, blk))) {
174 		SUPERB_UNLOCK(fs);
175 		return EIO;	/* Fatal */
176 	}
177 
178 	struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf;
179 	fb->nfreeblock = V7FS_MAX_FREEBLOCK;
180 	int i;
181 	for (i = 0; i < V7FS_MAX_FREEBLOCK; i++)
182 		fb->freeblock[i] = V7FS_VAL32(fs, sb->freeblock[i]);
183 
184 	if (!fs->io.write(fs->io.cookie, (uint8_t *)fb, blk)) {
185 		error =  EIO;	/* Fatal */
186 	} else {
187 		/* Link. on next allocate, this block is used as datablock, */
188 		/* and swap outed freeblock list is restored. */
189 		sb->freeblock[0] = blk;
190 		sb->nfreeblock = 1;
191 		sb->total_freeblock++;
192 		sb->modified = 1;
193 		DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
194 	}
195 	SUPERB_UNLOCK(fs);
196 	scratch_free(fs, buf);
197 
198 	return error;
199 }
200 
201 static int
202 v7fs_datablock_addr(size_t sz, struct v7fs_daddr_map *map)
203 {
204 #define	NIDX		V7FS_DADDR_PER_BLOCK
205 #define	DIRECT_SZ	(V7FS_NADDR_DIRECT * V7FS_BSIZE)
206 #define	IDX1_SZ		(NIDX * V7FS_BSIZE)
207 #define	IDX2_SZ		(NIDX * NIDX * V7FS_BSIZE)
208 #define	ROUND(x, a)	((((x) + ((a) - 1)) & ~((a) - 1)))
209 	if (!sz) {
210 		map->level = 0;
211 		map->index[0] = 0;
212 		return 0;
213 	}
214 
215 	sz = V7FS_ROUND_BSIZE(sz);
216 
217 	/* Direct */
218 	if (sz <= DIRECT_SZ) {
219 		map->level = 0;
220 		map->index[0] = (sz >> V7FS_BSHIFT) - 1;
221 		return 0;
222 	}
223 	/* Index 1 */
224 	sz -= DIRECT_SZ;
225 
226 	if (sz <= IDX1_SZ) {
227 		map->level = 1;
228 		map->index[0] = (sz >> V7FS_BSHIFT) - 1;
229 		return 0;
230 	}
231 	sz -= IDX1_SZ;
232 
233 	/* Index 2 */
234 	if (sz <= IDX2_SZ) {
235 		map->level = 2;
236 		map->index[0] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
237 		map->index[1] = ((sz - (map->index[0] * IDX1_SZ)) >>
238 		    V7FS_BSHIFT) - 1;
239 		return 0;
240 	}
241 	sz -= IDX2_SZ;
242 
243 	/* Index 3 */
244 	map->level = 3;
245 	map->index[0] = ROUND(sz, IDX2_SZ) / IDX2_SZ - 1;
246 	sz -= map->index[0] * IDX2_SZ;
247 	map->index[1] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
248 	sz -= map->index[1] * IDX1_SZ;
249 	map->index[2] = (sz >> V7FS_BSHIFT) - 1;
250 
251 	return map->index[2] >= NIDX ? ENOSPC : 0;
252 }
253 
254 int
255 v7fs_datablock_foreach(struct v7fs_self *fs, struct v7fs_inode *p,
256     int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
257 {
258 	size_t i;
259 	v7fs_daddr_t blk, blk2;
260 	size_t filesize;
261 	bool last;
262 	int ret;
263 
264 	if (!(filesize = v7fs_inode_filesize(p)))
265 		return 0;
266 #ifdef V7FS_DATABLOCK_DEBUG
267 	size_t sz = filesize;
268 #endif
269 
270 	/* Direct */
271 	for (i = 0; i < V7FS_NADDR_DIRECT; i++, filesize -= V7FS_BSIZE) {
272 		blk = p->addr[i];
273 		if (!datablock_number_sanity(fs, blk)) {
274 			DPRINTF("inode#%d direct=%zu filesize=%zu\n",
275 			    p->inode_number, i, sz);
276 			return EIO;
277 		}
278 
279 		last = filesize <= V7FS_BSIZE;
280 		if ((ret = func(fs, ctx, blk, last ? filesize : V7FS_BSIZE)))
281 			return ret;
282 		if (last)
283 			return V7FS_ITERATOR_END;
284 	}
285 
286 	/* Index 1 */
287 	blk = p->addr[V7FS_NADDR_INDEX1];
288 	if (!datablock_number_sanity(fs, blk))
289 		return EIO;
290 
291 	if ((ret = loop1(fs, blk, &filesize, func, ctx)))
292 		return ret;
293 
294 	/* Index 2 */
295 	blk = p->addr[V7FS_NADDR_INDEX2];
296 	if (!datablock_number_sanity(fs, blk))
297 		return EIO;
298 
299 	if ((ret = loop2(fs, blk, &filesize, func, ctx)))
300 		return ret;
301 
302 	/* Index 3 */
303 	blk = p->addr[V7FS_NADDR_INDEX3];
304 	if (!datablock_number_sanity(fs, blk))
305 		return EIO;
306 
307 	for (i = 0; i < V7FS_DADDR_PER_BLOCK; i++) {
308 		blk2 = link(fs, blk, i);
309 		if (!datablock_number_sanity(fs, blk))
310 			return EIO;
311 
312 		if ((ret = loop2(fs, blk2, &filesize, func, ctx)))
313 			return ret;
314 	}
315 
316 	return EFBIG;
317 }
318 
319 static int
320 loop2(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
321     int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
322 {
323 	v7fs_daddr_t blk;
324 	int ret;
325 	size_t j;
326 
327 	for (j = 0; j < V7FS_DADDR_PER_BLOCK; j++) {
328 		blk = link(fs, listblk, j);
329 		if (!datablock_number_sanity(fs, blk))
330 			return EIO;
331 		if ((ret = loop1(fs, blk, filesize, func, ctx)))
332 			return ret;
333 	}
334 
335 	return 0;
336 }
337 
338 static int
339 loop1(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
340     int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
341 {
342 	v7fs_daddr_t blk;
343 	bool last;
344 	int ret;
345 	size_t k;
346 
347 	for (k = 0; k < V7FS_DADDR_PER_BLOCK; k++, *filesize -= V7FS_BSIZE) {
348 		blk = link(fs, listblk, k);
349 		if (!datablock_number_sanity(fs, blk))
350 			return EIO;
351 		last = *filesize <= V7FS_BSIZE;
352 		if ((ret = func(fs, ctx, blk, last ? *filesize : V7FS_BSIZE)))
353 			return ret;
354 		if (last)
355 			return V7FS_ITERATOR_END;
356 	}
357 
358 	return 0;
359 }
360 
361 v7fs_daddr_t
362 v7fs_datablock_last(struct v7fs_self *fs, struct v7fs_inode *inode,
363     v7fs_off_t ofs)
364 {
365 	struct v7fs_daddr_map map;
366 	v7fs_daddr_t blk = 0;
367 	v7fs_daddr_t *addr = inode->addr;
368 
369 	/* Inquire last data block location. */
370 	if (v7fs_datablock_addr(ofs, &map) != 0)
371 		return 0;
372 
373 	switch (map.level)
374 	{
375 	case 0: /*Direct */
376 		blk = inode->addr[map.index[0]];
377 		break;
378 	case 1: /*Index1 */
379 		blk = link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]);
380 		break;
381 	case 2: /*Index2 */
382 		blk = link(fs, link(fs, addr[V7FS_NADDR_INDEX2], map.index[0]),
383 		    map.index[1]);
384 		break;
385 	case 3: /*Index3 */
386 		blk = link(fs, link(fs, link(fs, addr[V7FS_NADDR_INDEX3],
387 		    map.index[0]), map.index[1]), map.index[2]);
388 		break;
389 	}
390 
391 	return blk;
392 }
393 
394 int
395 v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz)
396 {
397 	size_t old_filesize = inode->filesize;
398 	size_t new_filesize = old_filesize + sz;
399 	struct v7fs_daddr_map oldmap, newmap;
400 	v7fs_daddr_t blk, idxblk;
401 	int error;
402 	v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
403 	v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
404 
405 	if (old_nblk == new_nblk) {
406 		inode->filesize += sz;
407 		v7fs_inode_writeback(fs, inode);
408 		return 0; /* no need to expand. */
409 	}
410 	struct v7fs_inode backup = *inode;
411 	v7fs_daddr_t required_blk = new_nblk - old_nblk;
412 
413 	DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize,
414 	    required_blk);
415 
416 	v7fs_datablock_addr(old_filesize, &oldmap);
417 	v7fs_daddr_t i;
418 	for (i = 0; i < required_blk; i++) {
419 		v7fs_datablock_addr(old_filesize + (i+1) * V7FS_BSIZE, &newmap);
420 		daddr_map_dump(&oldmap);
421 		daddr_map_dump(&newmap);
422 
423 		if (oldmap.level != newmap.level) {
424 			/* Allocate index area */
425 			if ((error = v7fs_datablock_allocate(fs, &idxblk)))
426 				return error;
427 
428 			switch (newmap.level) {
429 			case 1:
430 				DPRINTF("0->1\n");
431 				inode->addr[V7FS_NADDR_INDEX1] = idxblk;
432 				blk = add_leaf(fs, idxblk, 0);
433 				break;
434 			case 2:
435 				DPRINTF("1->2\n");
436 				inode->addr[V7FS_NADDR_INDEX2] = idxblk;
437 				blk = add_leaf(fs, add_leaf(fs, idxblk, 0), 0);
438 				break;
439 			case 3:
440 				DPRINTF("2->3\n");
441 				inode->addr[V7FS_NADDR_INDEX3] = idxblk;
442 				blk = add_leaf(fs, add_leaf(fs, add_leaf(fs,
443 				    idxblk, 0), 0), 0);
444 				break;
445 			}
446 		} else {
447 			switch (newmap.level) {
448 			case 0:
449 				if ((error = v7fs_datablock_allocate(fs, &blk)))
450 					return error;
451 				inode->addr[newmap.index[0]] = blk;
452 				DPRINTF("direct index %d = blk%d\n",
453 				    newmap.index[0], blk);
454 				break;
455 			case 1:
456 				idxblk = inode->addr[V7FS_NADDR_INDEX1];
457 				blk = add_leaf(fs, idxblk, newmap.index[0]);
458 				break;
459 			case 2:
460 				idxblk = inode->addr[V7FS_NADDR_INDEX2];
461 				if (oldmap.index[0] != newmap.index[0])
462 					add_leaf(fs, idxblk, newmap.index[0]);
463 				blk = add_leaf(fs, link(fs,idxblk,
464 				    newmap.index[0]), newmap.index[1]);
465 				break;
466 			case 3:
467 				idxblk = inode->addr[V7FS_NADDR_INDEX3];
468 
469 				if (oldmap.index[0] != newmap.index[0])
470 					add_leaf(fs, idxblk, newmap.index[0]);
471 
472 				if (oldmap.index[1] != newmap.index[1])
473 					add_leaf(fs, link(fs, idxblk,
474 					    newmap.index[0]), newmap.index[1]);
475 				blk = add_leaf(fs, link(fs, link(fs, idxblk,
476 				    newmap.index[0]), newmap.index[1]),
477 				    newmap.index[2]);
478 				break;
479 			}
480 		}
481 		if (!blk) {
482 			*inode = backup; /* structure copy; */
483 			return ENOSPC;
484 		}
485 		oldmap = newmap;
486 	}
487 	inode->filesize += sz;
488 	v7fs_inode_writeback(fs, inode);
489 
490 	return 0;
491 }
492 
493 static v7fs_daddr_t
494 link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n)
495 {
496 	v7fs_daddr_t *list;
497 	v7fs_daddr_t blk;
498 	void *buf;
499 
500 	if (!datablock_number_sanity(fs, listblk))
501 		return 0;
502 	if (!(buf = scratch_read(fs, listblk)))
503 		return 0;
504 	list = (v7fs_daddr_t *)buf;
505 	blk = V7FS_VAL32(fs, list[n]);
506 	scratch_free(fs, buf);
507 
508 	if (!datablock_number_sanity(fs, blk))
509 		return 0;
510 
511 	return blk;
512 }
513 
514 static v7fs_daddr_t
515 add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx)
516 {
517 	v7fs_daddr_t newblk;
518 	v7fs_daddr_t *daddr_list;
519 	int error = 0;
520 	void *buf;
521 
522 	if (!up)
523 		return 0;
524 	if (!datablock_number_sanity(fs, up))
525 		return 0;
526 
527 	if ((error = v7fs_datablock_allocate(fs, &newblk)))
528 		return 0;
529 	if (!(buf = scratch_read(fs, up)))
530 		return 0;
531 	daddr_list = (v7fs_daddr_t *)buf;
532 	daddr_list[idx] = V7FS_VAL32(fs, newblk);
533 	if (!fs->io.write(fs->io.cookie, buf, up))
534 		newblk = 0;
535 	scratch_free(fs, buf);
536 
537 	return newblk;
538 }
539 
540 int
541 v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode,
542     size_t sz)
543 {
544 	size_t old_filesize = inode->filesize;
545 	size_t new_filesize = old_filesize - sz;
546 	struct v7fs_daddr_map oldmap, newmap;
547 	v7fs_daddr_t blk, idxblk;
548 	int error = 0;
549 	v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
550 	v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
551 
552 	if (old_nblk == new_nblk) {
553 		inode->filesize -= sz;
554 		v7fs_inode_writeback(fs, inode);
555 		return 0; /* no need to contract; */
556 	}
557 	v7fs_daddr_t erase_blk = old_nblk - new_nblk;
558 
559 	DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize,
560 	    erase_blk);
561 
562 	v7fs_datablock_addr(old_filesize, &oldmap);
563 	v7fs_daddr_t i;
564 	for (i = 0; i < erase_blk; i++) {
565 		v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap);
566 
567 		if (oldmap.level != newmap.level) {
568 			switch (newmap.level) {
569 			case 0: /*1->0 */
570 				DPRINTF("1->0\n");
571 				idxblk = inode->addr[V7FS_NADDR_INDEX1];
572 				inode->addr[V7FS_NADDR_INDEX1] = 0;
573 				error = v7fs_datablock_deallocate(fs,
574 				    remove_self(fs, idxblk));
575 				break;
576 			case 1: /*2->1 */
577 				DPRINTF("2->1\n");
578 				idxblk = inode->addr[V7FS_NADDR_INDEX2];
579 				inode->addr[V7FS_NADDR_INDEX2] = 0;
580 				error = v7fs_datablock_deallocate(fs,
581 				    remove_self(fs, remove_self(fs, idxblk)));
582 				break;
583 			case 2:/*3->2 */
584 				DPRINTF("3->2\n");
585 				idxblk = inode->addr[V7FS_NADDR_INDEX3];
586 				inode->addr[V7FS_NADDR_INDEX3] = 0;
587 				error = v7fs_datablock_deallocate(fs,
588 				    remove_self(fs, remove_self(fs,
589 					remove_self(fs, idxblk))));
590 				break;
591 			}
592 		} else {
593 			switch (newmap.level) {
594 			case 0:
595 				DPRINTF("[0] %d\n", oldmap.index[0]);
596 				blk = inode->addr[oldmap.index[0]];
597 				error = v7fs_datablock_deallocate(fs, blk);
598 				break;
599 			case 1:
600 				DPRINTF("[1] %d\n", oldmap.index[0]);
601 				idxblk = inode->addr[V7FS_NADDR_INDEX1];
602 				remove_leaf(fs, idxblk, oldmap.index[0]);
603 
604 				break;
605 			case 2:
606 				DPRINTF("[2] %d %d\n", oldmap.index[0],
607 				    oldmap.index[1]);
608 				idxblk = inode->addr[V7FS_NADDR_INDEX2];
609 				remove_leaf(fs, link(fs, idxblk,
610 				    oldmap.index[0]), oldmap.index[1]);
611 				if (oldmap.index[0] != newmap.index[0]) {
612 					remove_leaf(fs, idxblk,
613 					    oldmap.index[0]);
614 				}
615 				break;
616 			case 3:
617 				DPRINTF("[2] %d %d %d\n", oldmap.index[0],
618 				    oldmap.index[1], oldmap.index[2]);
619 				idxblk = inode->addr[V7FS_NADDR_INDEX3];
620 				remove_leaf(fs, link(fs, link(fs, idxblk,
621 				    oldmap.index[0]), oldmap.index[1]),
622 				    oldmap.index[2]);
623 
624 				if (oldmap.index[1] != newmap.index[1])	{
625 					remove_leaf(fs, link(fs, idxblk,
626 					    oldmap.index[0]), oldmap.index[1]);
627 				}
628 				if (oldmap.index[0] != newmap.index[0]) {
629 					remove_leaf(fs, idxblk,
630 					    oldmap.index[0]);
631 				}
632 				break;
633 			}
634 		}
635 		oldmap = newmap;
636 	}
637 	inode->filesize -= sz;
638 	v7fs_inode_writeback(fs, inode);
639 
640 	return error;
641 }
642 
643 static v7fs_daddr_t
644 unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n)
645 {
646 	v7fs_daddr_t *daddr_list;
647 	v7fs_daddr_t blk;
648 	void *buf;
649 
650 	if (!(buf = scratch_read(fs, idxblk)))
651 		return 0;
652 	daddr_list = (v7fs_daddr_t *)buf;
653 	blk = V7FS_VAL32(fs, daddr_list[n]);
654 	daddr_list[n] = 0;
655 	fs->io.write(fs->io.cookie, buf, idxblk);
656 	scratch_free(fs, buf);
657 
658 	return blk; /* unlinked block. */
659 }
660 
661 static v7fs_daddr_t
662 remove_self(struct v7fs_self *fs, v7fs_daddr_t up)
663 {
664 	v7fs_daddr_t down;
665 
666 	if (!datablock_number_sanity(fs, up))
667 		return 0;
668 
669 	/* At 1st, remove from datablock list. */
670 	down = unlink(fs, up, 0);
671 
672 	/* link self to freelist. */
673 	v7fs_datablock_deallocate(fs, up);
674 
675 	return down;
676 }
677 
678 static v7fs_daddr_t
679 remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n)
680 {
681 	v7fs_daddr_t down;
682 
683 	if (!datablock_number_sanity(fs, up))
684 		return 0;
685 
686 	/* At 1st, remove from datablock list. */
687 	down = unlink(fs, up, n);
688 
689 	/* link leaf to freelist. */
690 	v7fs_datablock_deallocate(fs, down);
691 
692 	return down;
693 }
694 
695 int
696 v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz,
697     struct v7fs_inode *inode)
698 {
699 	ssize_t diff = newsz - v7fs_inode_filesize(inode);
700 	int error = 0;
701 
702 	if (diff > 0)
703 		error = v7fs_datablock_expand(fs, inode, diff);
704 	else if (diff < 0)
705 		error = v7fs_datablock_contract(fs, inode, -diff);
706 
707 	return error;
708 }
709 
710 #ifdef V7FS_DATABLOCK_DEBUG
711 void
712 daddr_map_dump(const struct v7fs_daddr_map *map)
713 {
714 
715 	DPRINTF("level %d ", map->level);
716 	int m, n = !map->level ? 1 : map->level;
717 	for (m = 0; m < n; m++)
718 		printf("[%d]", map->index[m]);
719 	printf("\n");
720 }
721 #endif
722