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