xref: /netbsd-src/sys/fs/v7fs/v7fs_datablock.c (revision f1ca1ce2bf561c65d8d08b5a57f1979f43f5aa7f)
1*f1ca1ce2Sapb /*	$NetBSD: v7fs_datablock.c,v 1.4 2011/07/18 21:51:49 apb Exp $	*/
29255b46fSuch 
39255b46fSuch /*-
49255b46fSuch  * Copyright (c) 2011 The NetBSD Foundation, Inc.
59255b46fSuch  * All rights reserved.
69255b46fSuch  *
79255b46fSuch  * This code is derived from software contributed to The NetBSD Foundation
89255b46fSuch  * by UCHIYAMA Yasushi.
99255b46fSuch  *
109255b46fSuch  * Redistribution and use in source and binary forms, with or without
119255b46fSuch  * modification, are permitted provided that the following conditions
129255b46fSuch  * are met:
139255b46fSuch  * 1. Redistributions of source code must retain the above copyright
149255b46fSuch  *    notice, this list of conditions and the following disclaimer.
159255b46fSuch  * 2. Redistributions in binary form must reproduce the above copyright
169255b46fSuch  *    notice, this list of conditions and the following disclaimer in the
179255b46fSuch  *    documentation and/or other materials provided with the distribution.
189255b46fSuch  *
199255b46fSuch  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
209255b46fSuch  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219255b46fSuch  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
229255b46fSuch  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
239255b46fSuch  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
249255b46fSuch  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
259255b46fSuch  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
269255b46fSuch  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279255b46fSuch  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
289255b46fSuch  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
299255b46fSuch  * POSSIBILITY OF SUCH DAMAGE.
309255b46fSuch  */
319255b46fSuch 
32*f1ca1ce2Sapb #if HAVE_NBTOOL_CONFIG_H
33*f1ca1ce2Sapb #include "nbtool_config.h"
34*f1ca1ce2Sapb #endif
35*f1ca1ce2Sapb 
369255b46fSuch #include <sys/cdefs.h>
37*f1ca1ce2Sapb __KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.4 2011/07/18 21:51:49 apb Exp $");
389255b46fSuch #if defined _KERNEL_OPT
399255b46fSuch #include "opt_v7fs.h"
409255b46fSuch #endif
419255b46fSuch 
429255b46fSuch #include <sys/types.h>
439255b46fSuch #ifdef _KERNEL
449255b46fSuch #include <sys/systm.h>
459255b46fSuch #include <sys/param.h>
469255b46fSuch #else
479255b46fSuch #include <stdio.h>
489255b46fSuch #include <string.h>
499255b46fSuch #include <errno.h>
509255b46fSuch #endif
519255b46fSuch 
529255b46fSuch #include "v7fs.h"
539255b46fSuch #include "v7fs_impl.h"
549255b46fSuch #include "v7fs_endian.h"
559255b46fSuch #include "v7fs_inode.h"
569255b46fSuch #include "v7fs_datablock.h"
579255b46fSuch #include "v7fs_superblock.h"
589255b46fSuch 
599255b46fSuch #ifdef V7FS_DATABLOCK_DEBUG
609255b46fSuch #define	DPRINTF(fmt, args...)	printf("%s: " fmt, __func__, ##args)
619255b46fSuch #else
629255b46fSuch #define	DPRINTF(fmt, args...)	((void)0)
639255b46fSuch #endif
649255b46fSuch 
659255b46fSuch static int v7fs_datablock_deallocate(struct v7fs_self *, v7fs_daddr_t);
669255b46fSuch static int loop1(struct v7fs_self *, v7fs_daddr_t, size_t *,
679255b46fSuch     int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
689255b46fSuch static int loop2(struct v7fs_self *, v7fs_daddr_t, size_t *,
699255b46fSuch     int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
709255b46fSuch static v7fs_daddr_t link(struct v7fs_self *, v7fs_daddr_t, int);
719255b46fSuch static v7fs_daddr_t add_leaf(struct v7fs_self *, v7fs_daddr_t, int);
729255b46fSuch static v7fs_daddr_t unlink(struct v7fs_self *, v7fs_daddr_t, int);
739255b46fSuch static v7fs_daddr_t remove_leaf(struct v7fs_self *, v7fs_daddr_t, int);
749255b46fSuch static v7fs_daddr_t remove_self(struct v7fs_self *, v7fs_daddr_t);
759255b46fSuch 
769255b46fSuch #ifdef V7FS_DATABLOCK_DEBUG
779255b46fSuch void daddr_map_dump(const struct v7fs_daddr_map *);
789255b46fSuch #else
799255b46fSuch #define	daddr_map_dump(x)	((void)0)
809255b46fSuch #endif
819255b46fSuch 
829255b46fSuch bool
839255b46fSuch datablock_number_sanity(const struct v7fs_self *fs, v7fs_daddr_t blk)
849255b46fSuch {
859255b46fSuch 	const struct v7fs_superblock *sb = &fs->superblock;
869255b46fSuch 	bool ok = (blk >= sb->datablock_start_sector) &&
879255b46fSuch 	    (blk < sb->volume_size);
889255b46fSuch 
899255b46fSuch #ifdef V7FS_DATABLOCK_DEBUG
909255b46fSuch 	if (!ok) {
919255b46fSuch 		DPRINTF("Bad data block #%d\n", blk);
929255b46fSuch 	}
939255b46fSuch #endif
949255b46fSuch 
959255b46fSuch 	return ok;
969255b46fSuch }
979255b46fSuch 
989255b46fSuch int
999255b46fSuch v7fs_datablock_allocate(struct v7fs_self *fs, v7fs_daddr_t *block_number)
1009255b46fSuch {
1019255b46fSuch 	struct v7fs_superblock *sb = &fs->superblock;
1029255b46fSuch 	v7fs_daddr_t blk;
1039255b46fSuch 	int error = 0;
1049255b46fSuch 
1059255b46fSuch 	*block_number = 0;
1069255b46fSuch 	SUPERB_LOCK(fs);
1079255b46fSuch 	do {
1089255b46fSuch 		if (!sb->total_freeblock) {
1099255b46fSuch 			DPRINTF("free block exhausted!!!\n");
1109255b46fSuch 			SUPERB_UNLOCK(fs);
1119255b46fSuch 			return ENOSPC;
1129255b46fSuch 		}
1139255b46fSuch 
1149255b46fSuch 		/* Get free block from superblock cache. */
1159255b46fSuch 		blk = sb->freeblock[--sb->nfreeblock];
1169255b46fSuch 		sb->total_freeblock--;
1179255b46fSuch 		sb->modified = 1;
1189255b46fSuch 
1199255b46fSuch 		/* If nfreeblock is zero, it block is next freeblock link. */
1209255b46fSuch 		if (sb->nfreeblock == 0) {
1219255b46fSuch 			if ((error = v7fs_freeblock_update(fs, blk))) {
1229255b46fSuch 				DPRINTF("no freeblock!!!\n");
1239255b46fSuch 				SUPERB_UNLOCK(fs);
1249255b46fSuch 				return error;
1259255b46fSuch 			}
1269255b46fSuch 			/* This freeblock link is no longer required. */
1279255b46fSuch 			/* use as data block. */
1289255b46fSuch 		}
1299255b46fSuch 	} while (!datablock_number_sanity(fs, blk)); /* skip bogus block. */
1309255b46fSuch 	SUPERB_UNLOCK(fs);
1319255b46fSuch 
1329255b46fSuch 	DPRINTF("Get freeblock %d\n", blk);
1339255b46fSuch 	/* Zero clear datablock. */
1349255b46fSuch 	void *buf;
1359255b46fSuch 	if (!(buf = scratch_read(fs, blk)))
1369255b46fSuch 		return EIO;
1379255b46fSuch 	memset(buf, 0, V7FS_BSIZE);
1389255b46fSuch 	if (!fs->io.write(fs->io.cookie, buf, blk))
1399255b46fSuch 		error = EIO;
1409255b46fSuch 	scratch_free(fs, buf);
1419255b46fSuch 
1429255b46fSuch 	if (error == 0)
1439255b46fSuch 		*block_number = blk;
1449255b46fSuch 
1459255b46fSuch 	return error;
1469255b46fSuch }
1479255b46fSuch 
1489255b46fSuch static int
1499255b46fSuch v7fs_datablock_deallocate(struct v7fs_self *fs, v7fs_daddr_t blk)
1509255b46fSuch {
1519255b46fSuch 	struct v7fs_superblock *sb = &fs->superblock;
1529255b46fSuch 	void *buf;
1539255b46fSuch 	int error = 0;
1549255b46fSuch 
1559255b46fSuch 	if (!datablock_number_sanity(fs, blk))
1569255b46fSuch 		return EIO;
1579255b46fSuch 
1589255b46fSuch 	/* Add to in-core freelist. */
1599255b46fSuch 	SUPERB_LOCK(fs);
1609255b46fSuch 	if (sb->nfreeblock < V7FS_MAX_FREEBLOCK) {
1619255b46fSuch 		sb->freeblock[sb->nfreeblock++] = blk;
1629255b46fSuch 		sb->total_freeblock++;
1639255b46fSuch 		sb->modified = 1;
1649255b46fSuch 		DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
1659255b46fSuch 		SUPERB_UNLOCK(fs);
1669255b46fSuch 		return 0;
1679255b46fSuch 	}
1689255b46fSuch 
1699255b46fSuch 	/* No space to push. */
1709255b46fSuch 	/* Make this block to freeblock list.and current cache moved to this. */
1719255b46fSuch 	if (!(buf = scratch_read(fs, blk))) {
1729255b46fSuch 		SUPERB_UNLOCK(fs);
1739255b46fSuch 		return EIO;	/* Fatal */
1749255b46fSuch 	}
1759255b46fSuch 
1769255b46fSuch 	struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf;
1779255b46fSuch 	fb->nfreeblock = V7FS_MAX_FREEBLOCK;
1789255b46fSuch 	int i;
1799255b46fSuch 	for (i = 0; i < V7FS_MAX_FREEBLOCK; i++)
1809255b46fSuch 		fb->freeblock[i] = V7FS_VAL32(fs, sb->freeblock[i]);
1819255b46fSuch 
1829255b46fSuch 	if (!fs->io.write(fs->io.cookie, (uint8_t *)fb, blk)) {
1839255b46fSuch 		error =  EIO;	/* Fatal */
1849255b46fSuch 	} else {
1859255b46fSuch 		/* Link. on next allocate, this block is used as datablock, */
1869255b46fSuch 		/* and swap outed freeblock list is restored. */
1879255b46fSuch 		sb->freeblock[0] = blk;
1889255b46fSuch 		sb->nfreeblock = 1;
1899255b46fSuch 		sb->total_freeblock++;
1909255b46fSuch 		sb->modified = 1;
1919255b46fSuch 		DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
1929255b46fSuch 	}
1939255b46fSuch 	SUPERB_UNLOCK(fs);
1949255b46fSuch 	scratch_free(fs, buf);
1959255b46fSuch 
1969255b46fSuch 	return error;
1979255b46fSuch }
1989255b46fSuch 
199f96feeccSuch int
2009255b46fSuch v7fs_datablock_addr(size_t sz, struct v7fs_daddr_map *map)
2019255b46fSuch {
2029255b46fSuch #define	NIDX		V7FS_DADDR_PER_BLOCK
2039255b46fSuch #define	DIRECT_SZ	(V7FS_NADDR_DIRECT * V7FS_BSIZE)
2049255b46fSuch #define	IDX1_SZ		(NIDX * V7FS_BSIZE)
2059255b46fSuch #define	IDX2_SZ		(NIDX * NIDX * V7FS_BSIZE)
2069255b46fSuch #define	ROUND(x, a)	((((x) + ((a) - 1)) & ~((a) - 1)))
2079255b46fSuch 	if (!sz) {
2089255b46fSuch 		map->level = 0;
2099255b46fSuch 		map->index[0] = 0;
2109255b46fSuch 		return 0;
2119255b46fSuch 	}
2129255b46fSuch 
2139255b46fSuch 	sz = V7FS_ROUND_BSIZE(sz);
2149255b46fSuch 
2159255b46fSuch 	/* Direct */
2169255b46fSuch 	if (sz <= DIRECT_SZ) {
2179255b46fSuch 		map->level = 0;
2189255b46fSuch 		map->index[0] = (sz >> V7FS_BSHIFT) - 1;
2199255b46fSuch 		return 0;
2209255b46fSuch 	}
2219255b46fSuch 	/* Index 1 */
2229255b46fSuch 	sz -= DIRECT_SZ;
2239255b46fSuch 
2249255b46fSuch 	if (sz <= IDX1_SZ) {
2259255b46fSuch 		map->level = 1;
2269255b46fSuch 		map->index[0] = (sz >> V7FS_BSHIFT) - 1;
2279255b46fSuch 		return 0;
2289255b46fSuch 	}
2299255b46fSuch 	sz -= IDX1_SZ;
2309255b46fSuch 
2319255b46fSuch 	/* Index 2 */
2329255b46fSuch 	if (sz <= IDX2_SZ) {
2339255b46fSuch 		map->level = 2;
2349255b46fSuch 		map->index[0] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
2359255b46fSuch 		map->index[1] = ((sz - (map->index[0] * IDX1_SZ)) >>
2369255b46fSuch 		    V7FS_BSHIFT) - 1;
2379255b46fSuch 		return 0;
2389255b46fSuch 	}
2399255b46fSuch 	sz -= IDX2_SZ;
2409255b46fSuch 
2419255b46fSuch 	/* Index 3 */
2429255b46fSuch 	map->level = 3;
2439255b46fSuch 	map->index[0] = ROUND(sz, IDX2_SZ) / IDX2_SZ - 1;
2449255b46fSuch 	sz -= map->index[0] * IDX2_SZ;
2459255b46fSuch 	map->index[1] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
2469255b46fSuch 	sz -= map->index[1] * IDX1_SZ;
2479255b46fSuch 	map->index[2] = (sz >> V7FS_BSHIFT) - 1;
2489255b46fSuch 
2499255b46fSuch 	return map->index[2] >= NIDX ? ENOSPC : 0;
2509255b46fSuch }
2519255b46fSuch 
2529255b46fSuch int
2539255b46fSuch v7fs_datablock_foreach(struct v7fs_self *fs, struct v7fs_inode *p,
2549255b46fSuch     int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
2559255b46fSuch {
2569255b46fSuch 	size_t i;
2579255b46fSuch 	v7fs_daddr_t blk, blk2;
2589255b46fSuch 	size_t filesize;
2599255b46fSuch 	bool last;
2609255b46fSuch 	int ret;
2619255b46fSuch 
2629255b46fSuch 	if (!(filesize = v7fs_inode_filesize(p)))
263709aeb1fSuch 		return V7FS_ITERATOR_END;
2649255b46fSuch #ifdef V7FS_DATABLOCK_DEBUG
2659255b46fSuch 	size_t sz = filesize;
2669255b46fSuch #endif
2679255b46fSuch 
2689255b46fSuch 	/* Direct */
2699255b46fSuch 	for (i = 0; i < V7FS_NADDR_DIRECT; i++, filesize -= V7FS_BSIZE) {
2709255b46fSuch 		blk = p->addr[i];
2719255b46fSuch 		if (!datablock_number_sanity(fs, blk)) {
2729255b46fSuch 			DPRINTF("inode#%d direct=%zu filesize=%zu\n",
2739255b46fSuch 			    p->inode_number, i, sz);
2749255b46fSuch 			return EIO;
2759255b46fSuch 		}
2769255b46fSuch 
2779255b46fSuch 		last = filesize <= V7FS_BSIZE;
2789255b46fSuch 		if ((ret = func(fs, ctx, blk, last ? filesize : V7FS_BSIZE)))
2799255b46fSuch 			return ret;
2809255b46fSuch 		if (last)
2819255b46fSuch 			return V7FS_ITERATOR_END;
2829255b46fSuch 	}
2839255b46fSuch 
2849255b46fSuch 	/* Index 1 */
2859255b46fSuch 	blk = p->addr[V7FS_NADDR_INDEX1];
2869255b46fSuch 	if (!datablock_number_sanity(fs, blk))
2879255b46fSuch 		return EIO;
2889255b46fSuch 
2899255b46fSuch 	if ((ret = loop1(fs, blk, &filesize, func, ctx)))
2909255b46fSuch 		return ret;
2919255b46fSuch 
2929255b46fSuch 	/* Index 2 */
2939255b46fSuch 	blk = p->addr[V7FS_NADDR_INDEX2];
2949255b46fSuch 	if (!datablock_number_sanity(fs, blk))
2959255b46fSuch 		return EIO;
2969255b46fSuch 
2979255b46fSuch 	if ((ret = loop2(fs, blk, &filesize, func, ctx)))
2989255b46fSuch 		return ret;
2999255b46fSuch 
3009255b46fSuch 	/* Index 3 */
3019255b46fSuch 	blk = p->addr[V7FS_NADDR_INDEX3];
3029255b46fSuch 	if (!datablock_number_sanity(fs, blk))
3039255b46fSuch 		return EIO;
3049255b46fSuch 
3059255b46fSuch 	for (i = 0; i < V7FS_DADDR_PER_BLOCK; i++) {
3069255b46fSuch 		blk2 = link(fs, blk, i);
3079255b46fSuch 		if (!datablock_number_sanity(fs, blk))
3089255b46fSuch 			return EIO;
3099255b46fSuch 
3109255b46fSuch 		if ((ret = loop2(fs, blk2, &filesize, func, ctx)))
3119255b46fSuch 			return ret;
3129255b46fSuch 	}
3139255b46fSuch 
3149255b46fSuch 	return EFBIG;
3159255b46fSuch }
3169255b46fSuch 
3179255b46fSuch static int
3189255b46fSuch loop2(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
3199255b46fSuch     int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
3209255b46fSuch {
3219255b46fSuch 	v7fs_daddr_t blk;
3229255b46fSuch 	int ret;
3239255b46fSuch 	size_t j;
3249255b46fSuch 
3259255b46fSuch 	for (j = 0; j < V7FS_DADDR_PER_BLOCK; j++) {
3269255b46fSuch 		blk = link(fs, listblk, j);
3279255b46fSuch 		if (!datablock_number_sanity(fs, blk))
3289255b46fSuch 			return EIO;
3299255b46fSuch 		if ((ret = loop1(fs, blk, filesize, func, ctx)))
3309255b46fSuch 			return ret;
3319255b46fSuch 	}
3329255b46fSuch 
3339255b46fSuch 	return 0;
3349255b46fSuch }
3359255b46fSuch 
3369255b46fSuch static int
3379255b46fSuch loop1(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
3389255b46fSuch     int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
3399255b46fSuch {
3409255b46fSuch 	v7fs_daddr_t blk;
3419255b46fSuch 	bool last;
3429255b46fSuch 	int ret;
3439255b46fSuch 	size_t k;
3449255b46fSuch 
3459255b46fSuch 	for (k = 0; k < V7FS_DADDR_PER_BLOCK; k++, *filesize -= V7FS_BSIZE) {
3469255b46fSuch 		blk = link(fs, listblk, k);
3479255b46fSuch 		if (!datablock_number_sanity(fs, blk))
3489255b46fSuch 			return EIO;
3499255b46fSuch 		last = *filesize <= V7FS_BSIZE;
3509255b46fSuch 		if ((ret = func(fs, ctx, blk, last ? *filesize : V7FS_BSIZE)))
3519255b46fSuch 			return ret;
3529255b46fSuch 		if (last)
3539255b46fSuch 			return V7FS_ITERATOR_END;
3549255b46fSuch 	}
3559255b46fSuch 
3569255b46fSuch 	return 0;
3579255b46fSuch }
3589255b46fSuch 
3599255b46fSuch v7fs_daddr_t
3609255b46fSuch v7fs_datablock_last(struct v7fs_self *fs, struct v7fs_inode *inode,
3619255b46fSuch     v7fs_off_t ofs)
3629255b46fSuch {
3639255b46fSuch 	struct v7fs_daddr_map map;
3649255b46fSuch 	v7fs_daddr_t blk = 0;
3659255b46fSuch 	v7fs_daddr_t *addr = inode->addr;
3669255b46fSuch 
3679255b46fSuch 	/* Inquire last data block location. */
3689255b46fSuch 	if (v7fs_datablock_addr(ofs, &map) != 0)
3699255b46fSuch 		return 0;
3709255b46fSuch 
3719255b46fSuch 	switch (map.level)
3729255b46fSuch 	{
3739255b46fSuch 	case 0: /*Direct */
3749255b46fSuch 		blk = inode->addr[map.index[0]];
3759255b46fSuch 		break;
3769255b46fSuch 	case 1: /*Index1 */
3779255b46fSuch 		blk = link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]);
3789255b46fSuch 		break;
3799255b46fSuch 	case 2: /*Index2 */
3809255b46fSuch 		blk = link(fs, link(fs, addr[V7FS_NADDR_INDEX2], map.index[0]),
3819255b46fSuch 		    map.index[1]);
3829255b46fSuch 		break;
3839255b46fSuch 	case 3: /*Index3 */
3849255b46fSuch 		blk = link(fs, link(fs, link(fs, addr[V7FS_NADDR_INDEX3],
3859255b46fSuch 		    map.index[0]), map.index[1]), map.index[2]);
3869255b46fSuch 		break;
3879255b46fSuch 	}
3889255b46fSuch 
3899255b46fSuch 	return blk;
3909255b46fSuch }
3919255b46fSuch 
3929255b46fSuch int
3939255b46fSuch v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz)
3949255b46fSuch {
3959255b46fSuch 	size_t old_filesize = inode->filesize;
3969255b46fSuch 	size_t new_filesize = old_filesize + sz;
3979255b46fSuch 	struct v7fs_daddr_map oldmap, newmap;
3989255b46fSuch 	v7fs_daddr_t blk, idxblk;
3999255b46fSuch 	int error;
4009255b46fSuch 	v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
4019255b46fSuch 	v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
4029255b46fSuch 
4039255b46fSuch 	if (old_nblk == new_nblk) {
4049255b46fSuch 		inode->filesize += sz;
4059255b46fSuch 		v7fs_inode_writeback(fs, inode);
4069255b46fSuch 		return 0; /* no need to expand. */
4079255b46fSuch 	}
4089255b46fSuch 	struct v7fs_inode backup = *inode;
4099255b46fSuch 	v7fs_daddr_t required_blk = new_nblk - old_nblk;
4109255b46fSuch 
4119255b46fSuch 	DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize,
4129255b46fSuch 	    required_blk);
4139255b46fSuch 
4149255b46fSuch 	v7fs_datablock_addr(old_filesize, &oldmap);
4159255b46fSuch 	v7fs_daddr_t i;
4169255b46fSuch 	for (i = 0; i < required_blk; i++) {
4179255b46fSuch 		v7fs_datablock_addr(old_filesize + (i+1) * V7FS_BSIZE, &newmap);
4189255b46fSuch 		daddr_map_dump(&oldmap);
4199255b46fSuch 		daddr_map_dump(&newmap);
4209255b46fSuch 
4219255b46fSuch 		if (oldmap.level != newmap.level) {
4229255b46fSuch 			/* Allocate index area */
4239255b46fSuch 			if ((error = v7fs_datablock_allocate(fs, &idxblk)))
4249255b46fSuch 				return error;
4259255b46fSuch 
4269255b46fSuch 			switch (newmap.level) {
4279255b46fSuch 			case 1:
4289255b46fSuch 				DPRINTF("0->1\n");
4299255b46fSuch 				inode->addr[V7FS_NADDR_INDEX1] = idxblk;
4309255b46fSuch 				blk = add_leaf(fs, idxblk, 0);
4319255b46fSuch 				break;
4329255b46fSuch 			case 2:
4339255b46fSuch 				DPRINTF("1->2\n");
4349255b46fSuch 				inode->addr[V7FS_NADDR_INDEX2] = idxblk;
4359255b46fSuch 				blk = add_leaf(fs, add_leaf(fs, idxblk, 0), 0);
4369255b46fSuch 				break;
4379255b46fSuch 			case 3:
4389255b46fSuch 				DPRINTF("2->3\n");
4399255b46fSuch 				inode->addr[V7FS_NADDR_INDEX3] = idxblk;
4409255b46fSuch 				blk = add_leaf(fs, add_leaf(fs, add_leaf(fs,
4419255b46fSuch 				    idxblk, 0), 0), 0);
4429255b46fSuch 				break;
4439255b46fSuch 			}
4449255b46fSuch 		} else {
4459255b46fSuch 			switch (newmap.level) {
4469255b46fSuch 			case 0:
4479255b46fSuch 				if ((error = v7fs_datablock_allocate(fs, &blk)))
4489255b46fSuch 					return error;
4499255b46fSuch 				inode->addr[newmap.index[0]] = blk;
4509255b46fSuch 				DPRINTF("direct index %d = blk%d\n",
4519255b46fSuch 				    newmap.index[0], blk);
4529255b46fSuch 				break;
4539255b46fSuch 			case 1:
4549255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX1];
4559255b46fSuch 				blk = add_leaf(fs, idxblk, newmap.index[0]);
4569255b46fSuch 				break;
4579255b46fSuch 			case 2:
4589255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX2];
4599255b46fSuch 				if (oldmap.index[0] != newmap.index[0])
4609255b46fSuch 					add_leaf(fs, idxblk, newmap.index[0]);
4619255b46fSuch 				blk = add_leaf(fs, link(fs,idxblk,
4629255b46fSuch 				    newmap.index[0]), newmap.index[1]);
4639255b46fSuch 				break;
4649255b46fSuch 			case 3:
4659255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX3];
4669255b46fSuch 
4679255b46fSuch 				if (oldmap.index[0] != newmap.index[0])
4689255b46fSuch 					add_leaf(fs, idxblk, newmap.index[0]);
4699255b46fSuch 
4709255b46fSuch 				if (oldmap.index[1] != newmap.index[1])
4719255b46fSuch 					add_leaf(fs, link(fs, idxblk,
4729255b46fSuch 					    newmap.index[0]), newmap.index[1]);
4739255b46fSuch 				blk = add_leaf(fs, link(fs, link(fs, idxblk,
4749255b46fSuch 				    newmap.index[0]), newmap.index[1]),
4759255b46fSuch 				    newmap.index[2]);
4769255b46fSuch 				break;
4779255b46fSuch 			}
4789255b46fSuch 		}
4799255b46fSuch 		if (!blk) {
4809255b46fSuch 			*inode = backup; /* structure copy; */
4819255b46fSuch 			return ENOSPC;
4829255b46fSuch 		}
4839255b46fSuch 		oldmap = newmap;
4849255b46fSuch 	}
4859255b46fSuch 	inode->filesize += sz;
4869255b46fSuch 	v7fs_inode_writeback(fs, inode);
4879255b46fSuch 
4889255b46fSuch 	return 0;
4899255b46fSuch }
4909255b46fSuch 
4919255b46fSuch static v7fs_daddr_t
4929255b46fSuch link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n)
4939255b46fSuch {
4949255b46fSuch 	v7fs_daddr_t *list;
4959255b46fSuch 	v7fs_daddr_t blk;
4969255b46fSuch 	void *buf;
4979255b46fSuch 
4989255b46fSuch 	if (!datablock_number_sanity(fs, listblk))
4999255b46fSuch 		return 0;
5009255b46fSuch 	if (!(buf = scratch_read(fs, listblk)))
5019255b46fSuch 		return 0;
5029255b46fSuch 	list = (v7fs_daddr_t *)buf;
5039255b46fSuch 	blk = V7FS_VAL32(fs, list[n]);
5049255b46fSuch 	scratch_free(fs, buf);
5059255b46fSuch 
5069255b46fSuch 	if (!datablock_number_sanity(fs, blk))
5079255b46fSuch 		return 0;
5089255b46fSuch 
5099255b46fSuch 	return blk;
5109255b46fSuch }
5119255b46fSuch 
5129255b46fSuch static v7fs_daddr_t
5139255b46fSuch add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx)
5149255b46fSuch {
5159255b46fSuch 	v7fs_daddr_t newblk;
5169255b46fSuch 	v7fs_daddr_t *daddr_list;
5179255b46fSuch 	int error = 0;
5189255b46fSuch 	void *buf;
5199255b46fSuch 
5209255b46fSuch 	if (!up)
5219255b46fSuch 		return 0;
5229255b46fSuch 	if (!datablock_number_sanity(fs, up))
5239255b46fSuch 		return 0;
5249255b46fSuch 
5259255b46fSuch 	if ((error = v7fs_datablock_allocate(fs, &newblk)))
5269255b46fSuch 		return 0;
5279255b46fSuch 	if (!(buf = scratch_read(fs, up)))
5289255b46fSuch 		return 0;
5299255b46fSuch 	daddr_list = (v7fs_daddr_t *)buf;
5309255b46fSuch 	daddr_list[idx] = V7FS_VAL32(fs, newblk);
5319255b46fSuch 	if (!fs->io.write(fs->io.cookie, buf, up))
5329255b46fSuch 		newblk = 0;
5339255b46fSuch 	scratch_free(fs, buf);
5349255b46fSuch 
5359255b46fSuch 	return newblk;
5369255b46fSuch }
5379255b46fSuch 
5389255b46fSuch int
5399255b46fSuch v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode,
5409255b46fSuch     size_t sz)
5419255b46fSuch {
5429255b46fSuch 	size_t old_filesize = inode->filesize;
5439255b46fSuch 	size_t new_filesize = old_filesize - sz;
5449255b46fSuch 	struct v7fs_daddr_map oldmap, newmap;
5459255b46fSuch 	v7fs_daddr_t blk, idxblk;
5469255b46fSuch 	int error = 0;
5479255b46fSuch 	v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
5489255b46fSuch 	v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
5499255b46fSuch 
5509255b46fSuch 	if (old_nblk == new_nblk) {
5519255b46fSuch 		inode->filesize -= sz;
5529255b46fSuch 		v7fs_inode_writeback(fs, inode);
5539255b46fSuch 		return 0; /* no need to contract; */
5549255b46fSuch 	}
5559255b46fSuch 	v7fs_daddr_t erase_blk = old_nblk - new_nblk;
5569255b46fSuch 
5579255b46fSuch 	DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize,
5589255b46fSuch 	    erase_blk);
5599255b46fSuch 
5609255b46fSuch 	v7fs_datablock_addr(old_filesize, &oldmap);
5619255b46fSuch 	v7fs_daddr_t i;
5629255b46fSuch 	for (i = 0; i < erase_blk; i++) {
5639255b46fSuch 		v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap);
5649255b46fSuch 
5659255b46fSuch 		if (oldmap.level != newmap.level) {
5669255b46fSuch 			switch (newmap.level) {
5679255b46fSuch 			case 0: /*1->0 */
5689255b46fSuch 				DPRINTF("1->0\n");
5699255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX1];
5709255b46fSuch 				inode->addr[V7FS_NADDR_INDEX1] = 0;
5719255b46fSuch 				error = v7fs_datablock_deallocate(fs,
5729255b46fSuch 				    remove_self(fs, idxblk));
5739255b46fSuch 				break;
5749255b46fSuch 			case 1: /*2->1 */
5759255b46fSuch 				DPRINTF("2->1\n");
5769255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX2];
5779255b46fSuch 				inode->addr[V7FS_NADDR_INDEX2] = 0;
5789255b46fSuch 				error = v7fs_datablock_deallocate(fs,
5799255b46fSuch 				    remove_self(fs, remove_self(fs, idxblk)));
5809255b46fSuch 				break;
5819255b46fSuch 			case 2:/*3->2 */
5829255b46fSuch 				DPRINTF("3->2\n");
5839255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX3];
5849255b46fSuch 				inode->addr[V7FS_NADDR_INDEX3] = 0;
5859255b46fSuch 				error = v7fs_datablock_deallocate(fs,
5869255b46fSuch 				    remove_self(fs, remove_self(fs,
5879255b46fSuch 					remove_self(fs, idxblk))));
5889255b46fSuch 				break;
5899255b46fSuch 			}
5909255b46fSuch 		} else {
5919255b46fSuch 			switch (newmap.level) {
5929255b46fSuch 			case 0:
5939255b46fSuch 				DPRINTF("[0] %d\n", oldmap.index[0]);
5949255b46fSuch 				blk = inode->addr[oldmap.index[0]];
5959255b46fSuch 				error = v7fs_datablock_deallocate(fs, blk);
5969255b46fSuch 				break;
5979255b46fSuch 			case 1:
5989255b46fSuch 				DPRINTF("[1] %d\n", oldmap.index[0]);
5999255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX1];
6009255b46fSuch 				remove_leaf(fs, idxblk, oldmap.index[0]);
6019255b46fSuch 
6029255b46fSuch 				break;
6039255b46fSuch 			case 2:
6049255b46fSuch 				DPRINTF("[2] %d %d\n", oldmap.index[0],
6059255b46fSuch 				    oldmap.index[1]);
6069255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX2];
6079255b46fSuch 				remove_leaf(fs, link(fs, idxblk,
6089255b46fSuch 				    oldmap.index[0]), oldmap.index[1]);
6099255b46fSuch 				if (oldmap.index[0] != newmap.index[0]) {
6109255b46fSuch 					remove_leaf(fs, idxblk,
6119255b46fSuch 					    oldmap.index[0]);
6129255b46fSuch 				}
6139255b46fSuch 				break;
6149255b46fSuch 			case 3:
6159255b46fSuch 				DPRINTF("[2] %d %d %d\n", oldmap.index[0],
6169255b46fSuch 				    oldmap.index[1], oldmap.index[2]);
6179255b46fSuch 				idxblk = inode->addr[V7FS_NADDR_INDEX3];
6189255b46fSuch 				remove_leaf(fs, link(fs, link(fs, idxblk,
6199255b46fSuch 				    oldmap.index[0]), oldmap.index[1]),
6209255b46fSuch 				    oldmap.index[2]);
6219255b46fSuch 
6229255b46fSuch 				if (oldmap.index[1] != newmap.index[1])	{
6239255b46fSuch 					remove_leaf(fs, link(fs, idxblk,
6249255b46fSuch 					    oldmap.index[0]), oldmap.index[1]);
6259255b46fSuch 				}
6269255b46fSuch 				if (oldmap.index[0] != newmap.index[0]) {
6279255b46fSuch 					remove_leaf(fs, idxblk,
6289255b46fSuch 					    oldmap.index[0]);
6299255b46fSuch 				}
6309255b46fSuch 				break;
6319255b46fSuch 			}
6329255b46fSuch 		}
6339255b46fSuch 		oldmap = newmap;
6349255b46fSuch 	}
6359255b46fSuch 	inode->filesize -= sz;
6369255b46fSuch 	v7fs_inode_writeback(fs, inode);
6379255b46fSuch 
6389255b46fSuch 	return error;
6399255b46fSuch }
6409255b46fSuch 
6419255b46fSuch static v7fs_daddr_t
6429255b46fSuch unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n)
6439255b46fSuch {
6449255b46fSuch 	v7fs_daddr_t *daddr_list;
6459255b46fSuch 	v7fs_daddr_t blk;
6469255b46fSuch 	void *buf;
6479255b46fSuch 
6489255b46fSuch 	if (!(buf = scratch_read(fs, idxblk)))
6499255b46fSuch 		return 0;
6509255b46fSuch 	daddr_list = (v7fs_daddr_t *)buf;
6519255b46fSuch 	blk = V7FS_VAL32(fs, daddr_list[n]);
6529255b46fSuch 	daddr_list[n] = 0;
6539255b46fSuch 	fs->io.write(fs->io.cookie, buf, idxblk);
6549255b46fSuch 	scratch_free(fs, buf);
6559255b46fSuch 
6569255b46fSuch 	return blk; /* unlinked block. */
6579255b46fSuch }
6589255b46fSuch 
6599255b46fSuch static v7fs_daddr_t
6609255b46fSuch remove_self(struct v7fs_self *fs, v7fs_daddr_t up)
6619255b46fSuch {
6629255b46fSuch 	v7fs_daddr_t down;
6639255b46fSuch 
6649255b46fSuch 	if (!datablock_number_sanity(fs, up))
6659255b46fSuch 		return 0;
6669255b46fSuch 
6679255b46fSuch 	/* At 1st, remove from datablock list. */
6689255b46fSuch 	down = unlink(fs, up, 0);
6699255b46fSuch 
6709255b46fSuch 	/* link self to freelist. */
6719255b46fSuch 	v7fs_datablock_deallocate(fs, up);
6729255b46fSuch 
6739255b46fSuch 	return down;
6749255b46fSuch }
6759255b46fSuch 
6769255b46fSuch static v7fs_daddr_t
6779255b46fSuch remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n)
6789255b46fSuch {
6799255b46fSuch 	v7fs_daddr_t down;
6809255b46fSuch 
6819255b46fSuch 	if (!datablock_number_sanity(fs, up))
6829255b46fSuch 		return 0;
6839255b46fSuch 
6849255b46fSuch 	/* At 1st, remove from datablock list. */
6859255b46fSuch 	down = unlink(fs, up, n);
6869255b46fSuch 
6879255b46fSuch 	/* link leaf to freelist. */
6889255b46fSuch 	v7fs_datablock_deallocate(fs, down);
6899255b46fSuch 
6909255b46fSuch 	return down;
6919255b46fSuch }
6929255b46fSuch 
6939255b46fSuch int
6949255b46fSuch v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz,
6959255b46fSuch     struct v7fs_inode *inode)
6969255b46fSuch {
6979255b46fSuch 	ssize_t diff = newsz - v7fs_inode_filesize(inode);
6989255b46fSuch 	int error = 0;
6999255b46fSuch 
7009255b46fSuch 	if (diff > 0)
7019255b46fSuch 		error = v7fs_datablock_expand(fs, inode, diff);
7029255b46fSuch 	else if (diff < 0)
7039255b46fSuch 		error = v7fs_datablock_contract(fs, inode, -diff);
7049255b46fSuch 
7059255b46fSuch 	return error;
7069255b46fSuch }
7079255b46fSuch 
7089255b46fSuch #ifdef V7FS_DATABLOCK_DEBUG
7099255b46fSuch void
7109255b46fSuch daddr_map_dump(const struct v7fs_daddr_map *map)
7119255b46fSuch {
7129255b46fSuch 
7139255b46fSuch 	DPRINTF("level %d ", map->level);
7149255b46fSuch 	int m, n = !map->level ? 1 : map->level;
7159255b46fSuch 	for (m = 0; m < n; m++)
7169255b46fSuch 		printf("[%d]", map->index[m]);
7179255b46fSuch 	printf("\n");
7189255b46fSuch }
7199255b46fSuch #endif
720