1/*	$NetBSD: v7fs_datablock.c,v 1.4 2011/07/18 21:51:49 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.4 2011/07/18 21:51:49 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
65static int v7fs_datablock_deallocate(struct v7fs_self *, v7fs_daddr_t);
66static int v7fs_loop1(struct v7fs_self *, v7fs_daddr_t, size_t *,
67    int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
68static int v7fs_loop2(struct v7fs_self *, v7fs_daddr_t, size_t *,
69    int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
70static v7fs_daddr_t v7fs_link(struct v7fs_self *, v7fs_daddr_t, int);
71static v7fs_daddr_t v7fs_add_leaf(struct v7fs_self *, v7fs_daddr_t, int);
72static v7fs_daddr_t v7fs_unlink(struct v7fs_self *, v7fs_daddr_t, int);
73static v7fs_daddr_t v7fs_remove_leaf(struct v7fs_self *, v7fs_daddr_t, int);
74static v7fs_daddr_t v7fs_remove_self(struct v7fs_self *, v7fs_daddr_t);
75
76#ifdef V7FS_DATABLOCK_DEBUG
77void daddr_map_dump(const struct v7fs_daddr_map *);
78#else
79#define	daddr_map_dump(x)	((void)0)
80#endif
81
82bool
83datablock_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
98int
99v7fs_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
148static int
149v7fs_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
199int
200v7fs_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
252int
253v7fs_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
317static int
318v7fs_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
336static int
337v7fs_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
359v7fs_daddr_t
360v7fs_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
393int
394v7fs_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
499static v7fs_daddr_t
500v7fs_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
520static v7fs_daddr_t
521v7fs_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
546int
547v7fs_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
651static v7fs_daddr_t
652v7fs_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
669static v7fs_daddr_t
670v7fs_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
686static v7fs_daddr_t
687v7fs_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
703int
704v7fs_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
719void
720daddr_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