1*464d645aSapb /* $NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 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
32f1ca1ce2Sapb #if HAVE_NBTOOL_CONFIG_H
33f1ca1ce2Sapb #include "nbtool_config.h"
34f1ca1ce2Sapb #endif
35f1ca1ce2Sapb
369255b46fSuch #include <sys/cdefs.h>
37*464d645aSapb __KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 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);
66*464d645aSapb static int v7fs_loop1(struct v7fs_self *, v7fs_daddr_t, size_t *,
679255b46fSuch int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
68*464d645aSapb static int v7fs_loop2(struct v7fs_self *, v7fs_daddr_t, size_t *,
699255b46fSuch int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
70*464d645aSapb static v7fs_daddr_t v7fs_link(struct v7fs_self *, v7fs_daddr_t, int);
71*464d645aSapb static v7fs_daddr_t v7fs_add_leaf(struct v7fs_self *, v7fs_daddr_t, int);
72*464d645aSapb static v7fs_daddr_t v7fs_unlink(struct v7fs_self *, v7fs_daddr_t, int);
73*464d645aSapb static v7fs_daddr_t v7fs_remove_leaf(struct v7fs_self *, v7fs_daddr_t, int);
74*464d645aSapb static v7fs_daddr_t v7fs_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
datablock_number_sanity(const struct v7fs_self * fs,v7fs_daddr_t blk)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
v7fs_datablock_allocate(struct v7fs_self * fs,v7fs_daddr_t * block_number)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
v7fs_datablock_deallocate(struct v7fs_self * fs,v7fs_daddr_t blk)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
v7fs_datablock_addr(size_t sz,struct v7fs_daddr_map * map)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
v7fs_datablock_foreach(struct v7fs_self * fs,struct v7fs_inode * p,int (* func)(struct v7fs_self *,void *,v7fs_daddr_t,size_t),void * ctx)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
289*464d645aSapb if ((ret = v7fs_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
297*464d645aSapb if ((ret = v7fs_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++) {
306*464d645aSapb blk2 = v7fs_link(fs, blk, i);
3079255b46fSuch if (!datablock_number_sanity(fs, blk))
3089255b46fSuch return EIO;
3099255b46fSuch
310*464d645aSapb if ((ret = v7fs_loop2(fs, blk2, &filesize, func, ctx)))
3119255b46fSuch return ret;
3129255b46fSuch }
3139255b46fSuch
3149255b46fSuch return EFBIG;
3159255b46fSuch }
3169255b46fSuch
3179255b46fSuch static int
v7fs_loop2(struct v7fs_self * fs,v7fs_daddr_t listblk,size_t * filesize,int (* func)(struct v7fs_self *,void *,v7fs_daddr_t,size_t),void * ctx)318*464d645aSapb v7fs_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++) {
326*464d645aSapb blk = v7fs_link(fs, listblk, j);
3279255b46fSuch if (!datablock_number_sanity(fs, blk))
3289255b46fSuch return EIO;
329*464d645aSapb if ((ret = v7fs_loop1(fs, blk, filesize, func, ctx)))
3309255b46fSuch return ret;
3319255b46fSuch }
3329255b46fSuch
3339255b46fSuch return 0;
3349255b46fSuch }
3359255b46fSuch
3369255b46fSuch static int
v7fs_loop1(struct v7fs_self * fs,v7fs_daddr_t listblk,size_t * filesize,int (* func)(struct v7fs_self *,void *,v7fs_daddr_t,size_t),void * ctx)337*464d645aSapb v7fs_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) {
346*464d645aSapb blk = v7fs_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
v7fs_datablock_last(struct v7fs_self * fs,struct v7fs_inode * inode,v7fs_off_t ofs)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 */
377*464d645aSapb blk = v7fs_link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]);
3789255b46fSuch break;
3799255b46fSuch case 2: /*Index2 */
380*464d645aSapb blk = v7fs_link(fs, v7fs_link(fs,
381*464d645aSapb addr[V7FS_NADDR_INDEX2], map.index[0]), map.index[1]);
3829255b46fSuch break;
3839255b46fSuch case 3: /*Index3 */
384*464d645aSapb blk = v7fs_link(fs, v7fs_link(fs, v7fs_link(fs,
385*464d645aSapb addr[V7FS_NADDR_INDEX3], map.index[0]), map.index[1]),
386*464d645aSapb map.index[2]);
3879255b46fSuch break;
3889255b46fSuch }
3899255b46fSuch
3909255b46fSuch return blk;
3919255b46fSuch }
3929255b46fSuch
3939255b46fSuch int
v7fs_datablock_expand(struct v7fs_self * fs,struct v7fs_inode * inode,size_t sz)3949255b46fSuch v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz)
3959255b46fSuch {
3969255b46fSuch size_t old_filesize = inode->filesize;
3979255b46fSuch size_t new_filesize = old_filesize + sz;
3989255b46fSuch struct v7fs_daddr_map oldmap, newmap;
3999255b46fSuch v7fs_daddr_t blk, idxblk;
4009255b46fSuch int error;
4019255b46fSuch v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
4029255b46fSuch v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
4039255b46fSuch
4049255b46fSuch if (old_nblk == new_nblk) {
4059255b46fSuch inode->filesize += sz;
4069255b46fSuch v7fs_inode_writeback(fs, inode);
4079255b46fSuch return 0; /* no need to expand. */
4089255b46fSuch }
4099255b46fSuch struct v7fs_inode backup = *inode;
4109255b46fSuch v7fs_daddr_t required_blk = new_nblk - old_nblk;
4119255b46fSuch
4129255b46fSuch DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize,
4139255b46fSuch required_blk);
4149255b46fSuch
4159255b46fSuch v7fs_datablock_addr(old_filesize, &oldmap);
4169255b46fSuch v7fs_daddr_t i;
4179255b46fSuch for (i = 0; i < required_blk; i++) {
4189255b46fSuch v7fs_datablock_addr(old_filesize + (i+1) * V7FS_BSIZE, &newmap);
4199255b46fSuch daddr_map_dump(&oldmap);
4209255b46fSuch daddr_map_dump(&newmap);
4219255b46fSuch
4229255b46fSuch if (oldmap.level != newmap.level) {
4239255b46fSuch /* Allocate index area */
4249255b46fSuch if ((error = v7fs_datablock_allocate(fs, &idxblk)))
4259255b46fSuch return error;
4269255b46fSuch
4279255b46fSuch switch (newmap.level) {
4289255b46fSuch case 1:
4299255b46fSuch DPRINTF("0->1\n");
4309255b46fSuch inode->addr[V7FS_NADDR_INDEX1] = idxblk;
431*464d645aSapb blk = v7fs_add_leaf(fs, idxblk, 0);
4329255b46fSuch break;
4339255b46fSuch case 2:
4349255b46fSuch DPRINTF("1->2\n");
4359255b46fSuch inode->addr[V7FS_NADDR_INDEX2] = idxblk;
436*464d645aSapb blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs,
437*464d645aSapb idxblk, 0), 0);
4389255b46fSuch break;
4399255b46fSuch case 3:
4409255b46fSuch DPRINTF("2->3\n");
4419255b46fSuch inode->addr[V7FS_NADDR_INDEX3] = idxblk;
442*464d645aSapb blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs,
443*464d645aSapb v7fs_add_leaf(fs, idxblk, 0), 0), 0);
4449255b46fSuch break;
4459255b46fSuch }
4469255b46fSuch } else {
4479255b46fSuch switch (newmap.level) {
4489255b46fSuch case 0:
4499255b46fSuch if ((error = v7fs_datablock_allocate(fs, &blk)))
4509255b46fSuch return error;
4519255b46fSuch inode->addr[newmap.index[0]] = blk;
4529255b46fSuch DPRINTF("direct index %d = blk%d\n",
4539255b46fSuch newmap.index[0], blk);
4549255b46fSuch break;
4559255b46fSuch case 1:
4569255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX1];
457*464d645aSapb blk = v7fs_add_leaf(fs, idxblk,
458*464d645aSapb newmap.index[0]);
4599255b46fSuch break;
4609255b46fSuch case 2:
4619255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX2];
462*464d645aSapb if (oldmap.index[0] != newmap.index[0]) {
463*464d645aSapb v7fs_add_leaf(fs, idxblk,
464*464d645aSapb newmap.index[0]);
465*464d645aSapb }
466*464d645aSapb blk = v7fs_add_leaf(fs, v7fs_link(fs,idxblk,
4679255b46fSuch newmap.index[0]), newmap.index[1]);
4689255b46fSuch break;
4699255b46fSuch case 3:
4709255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX3];
4719255b46fSuch
472*464d645aSapb if (oldmap.index[0] != newmap.index[0]) {
473*464d645aSapb v7fs_add_leaf(fs, idxblk,
474*464d645aSapb newmap.index[0]);
475*464d645aSapb }
4769255b46fSuch
477*464d645aSapb if (oldmap.index[1] != newmap.index[1]) {
478*464d645aSapb v7fs_add_leaf(fs, v7fs_link(fs, idxblk,
4799255b46fSuch newmap.index[0]), newmap.index[1]);
480*464d645aSapb }
481*464d645aSapb blk = v7fs_add_leaf(fs, v7fs_link(fs,
482*464d645aSapb v7fs_link(fs, idxblk, newmap.index[0]),
483*464d645aSapb newmap.index[1]), newmap.index[2]);
4849255b46fSuch break;
4859255b46fSuch }
4869255b46fSuch }
4879255b46fSuch if (!blk) {
4889255b46fSuch *inode = backup; /* structure copy; */
4899255b46fSuch return ENOSPC;
4909255b46fSuch }
4919255b46fSuch oldmap = newmap;
4929255b46fSuch }
4939255b46fSuch inode->filesize += sz;
4949255b46fSuch v7fs_inode_writeback(fs, inode);
4959255b46fSuch
4969255b46fSuch return 0;
4979255b46fSuch }
4989255b46fSuch
4999255b46fSuch static v7fs_daddr_t
v7fs_link(struct v7fs_self * fs,v7fs_daddr_t listblk,int n)500*464d645aSapb v7fs_link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n)
5019255b46fSuch {
5029255b46fSuch v7fs_daddr_t *list;
5039255b46fSuch v7fs_daddr_t blk;
5049255b46fSuch void *buf;
5059255b46fSuch
5069255b46fSuch if (!datablock_number_sanity(fs, listblk))
5079255b46fSuch return 0;
5089255b46fSuch if (!(buf = scratch_read(fs, listblk)))
5099255b46fSuch return 0;
5109255b46fSuch list = (v7fs_daddr_t *)buf;
5119255b46fSuch blk = V7FS_VAL32(fs, list[n]);
5129255b46fSuch scratch_free(fs, buf);
5139255b46fSuch
5149255b46fSuch if (!datablock_number_sanity(fs, blk))
5159255b46fSuch return 0;
5169255b46fSuch
5179255b46fSuch return blk;
5189255b46fSuch }
5199255b46fSuch
5209255b46fSuch static v7fs_daddr_t
v7fs_add_leaf(struct v7fs_self * fs,v7fs_daddr_t up,int idx)521*464d645aSapb v7fs_add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx)
5229255b46fSuch {
5239255b46fSuch v7fs_daddr_t newblk;
5249255b46fSuch v7fs_daddr_t *daddr_list;
5259255b46fSuch int error = 0;
5269255b46fSuch void *buf;
5279255b46fSuch
5289255b46fSuch if (!up)
5299255b46fSuch return 0;
5309255b46fSuch if (!datablock_number_sanity(fs, up))
5319255b46fSuch return 0;
5329255b46fSuch
5339255b46fSuch if ((error = v7fs_datablock_allocate(fs, &newblk)))
5349255b46fSuch return 0;
5359255b46fSuch if (!(buf = scratch_read(fs, up)))
5369255b46fSuch return 0;
5379255b46fSuch daddr_list = (v7fs_daddr_t *)buf;
5389255b46fSuch daddr_list[idx] = V7FS_VAL32(fs, newblk);
5399255b46fSuch if (!fs->io.write(fs->io.cookie, buf, up))
5409255b46fSuch newblk = 0;
5419255b46fSuch scratch_free(fs, buf);
5429255b46fSuch
5439255b46fSuch return newblk;
5449255b46fSuch }
5459255b46fSuch
5469255b46fSuch int
v7fs_datablock_contract(struct v7fs_self * fs,struct v7fs_inode * inode,size_t sz)5479255b46fSuch v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode,
5489255b46fSuch size_t sz)
5499255b46fSuch {
5509255b46fSuch size_t old_filesize = inode->filesize;
5519255b46fSuch size_t new_filesize = old_filesize - sz;
5529255b46fSuch struct v7fs_daddr_map oldmap, newmap;
5539255b46fSuch v7fs_daddr_t blk, idxblk;
5549255b46fSuch int error = 0;
5559255b46fSuch v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
5569255b46fSuch v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
5579255b46fSuch
5589255b46fSuch if (old_nblk == new_nblk) {
5599255b46fSuch inode->filesize -= sz;
5609255b46fSuch v7fs_inode_writeback(fs, inode);
5619255b46fSuch return 0; /* no need to contract; */
5629255b46fSuch }
5639255b46fSuch v7fs_daddr_t erase_blk = old_nblk - new_nblk;
5649255b46fSuch
5659255b46fSuch DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize,
5669255b46fSuch erase_blk);
5679255b46fSuch
5689255b46fSuch v7fs_datablock_addr(old_filesize, &oldmap);
5699255b46fSuch v7fs_daddr_t i;
5709255b46fSuch for (i = 0; i < erase_blk; i++) {
5719255b46fSuch v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap);
5729255b46fSuch
5739255b46fSuch if (oldmap.level != newmap.level) {
5749255b46fSuch switch (newmap.level) {
5759255b46fSuch case 0: /*1->0 */
5769255b46fSuch DPRINTF("1->0\n");
5779255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX1];
5789255b46fSuch inode->addr[V7FS_NADDR_INDEX1] = 0;
5799255b46fSuch error = v7fs_datablock_deallocate(fs,
580*464d645aSapb v7fs_remove_self(fs, idxblk));
5819255b46fSuch break;
5829255b46fSuch case 1: /*2->1 */
5839255b46fSuch DPRINTF("2->1\n");
5849255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX2];
5859255b46fSuch inode->addr[V7FS_NADDR_INDEX2] = 0;
5869255b46fSuch error = v7fs_datablock_deallocate(fs,
587*464d645aSapb v7fs_remove_self(fs, v7fs_remove_self(fs,
588*464d645aSapb idxblk)));
5899255b46fSuch break;
5909255b46fSuch case 2:/*3->2 */
5919255b46fSuch DPRINTF("3->2\n");
5929255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX3];
5939255b46fSuch inode->addr[V7FS_NADDR_INDEX3] = 0;
5949255b46fSuch error = v7fs_datablock_deallocate(fs,
595*464d645aSapb v7fs_remove_self(fs, v7fs_remove_self(fs,
596*464d645aSapb v7fs_remove_self(fs, idxblk))));
5979255b46fSuch break;
5989255b46fSuch }
5999255b46fSuch } else {
6009255b46fSuch switch (newmap.level) {
6019255b46fSuch case 0:
6029255b46fSuch DPRINTF("[0] %d\n", oldmap.index[0]);
6039255b46fSuch blk = inode->addr[oldmap.index[0]];
6049255b46fSuch error = v7fs_datablock_deallocate(fs, blk);
6059255b46fSuch break;
6069255b46fSuch case 1:
6079255b46fSuch DPRINTF("[1] %d\n", oldmap.index[0]);
6089255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX1];
609*464d645aSapb v7fs_remove_leaf(fs, idxblk, oldmap.index[0]);
6109255b46fSuch
6119255b46fSuch break;
6129255b46fSuch case 2:
6139255b46fSuch DPRINTF("[2] %d %d\n", oldmap.index[0],
6149255b46fSuch oldmap.index[1]);
6159255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX2];
616*464d645aSapb v7fs_remove_leaf(fs, v7fs_link(fs, idxblk,
6179255b46fSuch oldmap.index[0]), oldmap.index[1]);
6189255b46fSuch if (oldmap.index[0] != newmap.index[0]) {
619*464d645aSapb v7fs_remove_leaf(fs, idxblk,
6209255b46fSuch oldmap.index[0]);
6219255b46fSuch }
6229255b46fSuch break;
6239255b46fSuch case 3:
6249255b46fSuch DPRINTF("[2] %d %d %d\n", oldmap.index[0],
6259255b46fSuch oldmap.index[1], oldmap.index[2]);
6269255b46fSuch idxblk = inode->addr[V7FS_NADDR_INDEX3];
627*464d645aSapb v7fs_remove_leaf(fs, v7fs_link(fs,
628*464d645aSapb v7fs_link(fs, idxblk, oldmap.index[0]),
629*464d645aSapb oldmap.index[1]), oldmap.index[2]);
6309255b46fSuch
6319255b46fSuch if (oldmap.index[1] != newmap.index[1]) {
632*464d645aSapb v7fs_remove_leaf(fs, v7fs_link(fs,
633*464d645aSapb idxblk, oldmap.index[0]),
634*464d645aSapb oldmap.index[1]);
6359255b46fSuch }
6369255b46fSuch if (oldmap.index[0] != newmap.index[0]) {
637*464d645aSapb v7fs_remove_leaf(fs, idxblk,
6389255b46fSuch oldmap.index[0]);
6399255b46fSuch }
6409255b46fSuch break;
6419255b46fSuch }
6429255b46fSuch }
6439255b46fSuch oldmap = newmap;
6449255b46fSuch }
6459255b46fSuch inode->filesize -= sz;
6469255b46fSuch v7fs_inode_writeback(fs, inode);
6479255b46fSuch
6489255b46fSuch return error;
6499255b46fSuch }
6509255b46fSuch
6519255b46fSuch static v7fs_daddr_t
v7fs_unlink(struct v7fs_self * fs,v7fs_daddr_t idxblk,int n)652*464d645aSapb v7fs_unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n)
6539255b46fSuch {
6549255b46fSuch v7fs_daddr_t *daddr_list;
6559255b46fSuch v7fs_daddr_t blk;
6569255b46fSuch void *buf;
6579255b46fSuch
6589255b46fSuch if (!(buf = scratch_read(fs, idxblk)))
6599255b46fSuch return 0;
6609255b46fSuch daddr_list = (v7fs_daddr_t *)buf;
6619255b46fSuch blk = V7FS_VAL32(fs, daddr_list[n]);
6629255b46fSuch daddr_list[n] = 0;
6639255b46fSuch fs->io.write(fs->io.cookie, buf, idxblk);
6649255b46fSuch scratch_free(fs, buf);
6659255b46fSuch
6669255b46fSuch return blk; /* unlinked block. */
6679255b46fSuch }
6689255b46fSuch
6699255b46fSuch static v7fs_daddr_t
v7fs_remove_self(struct v7fs_self * fs,v7fs_daddr_t up)670*464d645aSapb v7fs_remove_self(struct v7fs_self *fs, v7fs_daddr_t up)
6719255b46fSuch {
6729255b46fSuch v7fs_daddr_t down;
6739255b46fSuch
6749255b46fSuch if (!datablock_number_sanity(fs, up))
6759255b46fSuch return 0;
6769255b46fSuch
6779255b46fSuch /* At 1st, remove from datablock list. */
678*464d645aSapb down = v7fs_unlink(fs, up, 0);
6799255b46fSuch
6809255b46fSuch /* link self to freelist. */
6819255b46fSuch v7fs_datablock_deallocate(fs, up);
6829255b46fSuch
6839255b46fSuch return down;
6849255b46fSuch }
6859255b46fSuch
6869255b46fSuch static v7fs_daddr_t
v7fs_remove_leaf(struct v7fs_self * fs,v7fs_daddr_t up,int n)687*464d645aSapb v7fs_remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n)
6889255b46fSuch {
6899255b46fSuch v7fs_daddr_t down;
6909255b46fSuch
6919255b46fSuch if (!datablock_number_sanity(fs, up))
6929255b46fSuch return 0;
6939255b46fSuch
6949255b46fSuch /* At 1st, remove from datablock list. */
695*464d645aSapb down = v7fs_unlink(fs, up, n);
6969255b46fSuch
6979255b46fSuch /* link leaf to freelist. */
6989255b46fSuch v7fs_datablock_deallocate(fs, down);
6999255b46fSuch
7009255b46fSuch return down;
7019255b46fSuch }
7029255b46fSuch
7039255b46fSuch int
v7fs_datablock_size_change(struct v7fs_self * fs,size_t newsz,struct v7fs_inode * inode)7049255b46fSuch v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz,
7059255b46fSuch struct v7fs_inode *inode)
7069255b46fSuch {
7079255b46fSuch ssize_t diff = newsz - v7fs_inode_filesize(inode);
7089255b46fSuch int error = 0;
7099255b46fSuch
7109255b46fSuch if (diff > 0)
7119255b46fSuch error = v7fs_datablock_expand(fs, inode, diff);
7129255b46fSuch else if (diff < 0)
7139255b46fSuch error = v7fs_datablock_contract(fs, inode, -diff);
7149255b46fSuch
7159255b46fSuch return error;
7169255b46fSuch }
7179255b46fSuch
7189255b46fSuch #ifdef V7FS_DATABLOCK_DEBUG
7199255b46fSuch void
daddr_map_dump(const struct v7fs_daddr_map * map)7209255b46fSuch daddr_map_dump(const struct v7fs_daddr_map *map)
7219255b46fSuch {
7229255b46fSuch
7239255b46fSuch DPRINTF("level %d ", map->level);
7249255b46fSuch int m, n = !map->level ? 1 : map->level;
7259255b46fSuch for (m = 0; m < n; m++)
7269255b46fSuch printf("[%d]", map->index[m]);
7279255b46fSuch printf("\n");
7289255b46fSuch }
7299255b46fSuch #endif
730