Home | History | Annotate | Line # | Download | only in v7fs
v7fs_datablock.c revision 1.1
      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