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