v7fs_datablock.c revision 1.5 1 1.5 apb /* $NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $ */
2 1.1 uch
3 1.1 uch /*-
4 1.1 uch * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 1.1 uch * All rights reserved.
6 1.1 uch *
7 1.1 uch * This code is derived from software contributed to The NetBSD Foundation
8 1.1 uch * by UCHIYAMA Yasushi.
9 1.1 uch *
10 1.1 uch * Redistribution and use in source and binary forms, with or without
11 1.1 uch * modification, are permitted provided that the following conditions
12 1.1 uch * are met:
13 1.1 uch * 1. Redistributions of source code must retain the above copyright
14 1.1 uch * notice, this list of conditions and the following disclaimer.
15 1.1 uch * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 uch * notice, this list of conditions and the following disclaimer in the
17 1.1 uch * documentation and/or other materials provided with the distribution.
18 1.1 uch *
19 1.1 uch * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 uch * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 uch * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 uch * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 uch * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 uch * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 uch * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 uch * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 uch * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 uch * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 uch * POSSIBILITY OF SUCH DAMAGE.
30 1.1 uch */
31 1.1 uch
32 1.4 apb #if HAVE_NBTOOL_CONFIG_H
33 1.4 apb #include "nbtool_config.h"
34 1.4 apb #endif
35 1.4 apb
36 1.1 uch #include <sys/cdefs.h>
37 1.5 apb __KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $");
38 1.1 uch #if defined _KERNEL_OPT
39 1.1 uch #include "opt_v7fs.h"
40 1.1 uch #endif
41 1.1 uch
42 1.1 uch #include <sys/types.h>
43 1.1 uch #ifdef _KERNEL
44 1.1 uch #include <sys/systm.h>
45 1.1 uch #include <sys/param.h>
46 1.1 uch #else
47 1.1 uch #include <stdio.h>
48 1.1 uch #include <string.h>
49 1.1 uch #include <errno.h>
50 1.1 uch #endif
51 1.1 uch
52 1.1 uch #include "v7fs.h"
53 1.1 uch #include "v7fs_impl.h"
54 1.1 uch #include "v7fs_endian.h"
55 1.1 uch #include "v7fs_inode.h"
56 1.1 uch #include "v7fs_datablock.h"
57 1.1 uch #include "v7fs_superblock.h"
58 1.1 uch
59 1.1 uch #ifdef V7FS_DATABLOCK_DEBUG
60 1.1 uch #define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args)
61 1.1 uch #else
62 1.1 uch #define DPRINTF(fmt, args...) ((void)0)
63 1.1 uch #endif
64 1.1 uch
65 1.1 uch static int v7fs_datablock_deallocate(struct v7fs_self *, v7fs_daddr_t);
66 1.5 apb static int v7fs_loop1(struct v7fs_self *, v7fs_daddr_t, size_t *,
67 1.1 uch int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
68 1.5 apb static int v7fs_loop2(struct v7fs_self *, v7fs_daddr_t, size_t *,
69 1.1 uch int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *);
70 1.5 apb static v7fs_daddr_t v7fs_link(struct v7fs_self *, v7fs_daddr_t, int);
71 1.5 apb static v7fs_daddr_t v7fs_add_leaf(struct v7fs_self *, v7fs_daddr_t, int);
72 1.5 apb static v7fs_daddr_t v7fs_unlink(struct v7fs_self *, v7fs_daddr_t, int);
73 1.5 apb static v7fs_daddr_t v7fs_remove_leaf(struct v7fs_self *, v7fs_daddr_t, int);
74 1.5 apb static v7fs_daddr_t v7fs_remove_self(struct v7fs_self *, v7fs_daddr_t);
75 1.1 uch
76 1.1 uch #ifdef V7FS_DATABLOCK_DEBUG
77 1.1 uch void daddr_map_dump(const struct v7fs_daddr_map *);
78 1.1 uch #else
79 1.1 uch #define daddr_map_dump(x) ((void)0)
80 1.1 uch #endif
81 1.1 uch
82 1.1 uch bool
83 1.1 uch datablock_number_sanity(const struct v7fs_self *fs, v7fs_daddr_t blk)
84 1.1 uch {
85 1.1 uch const struct v7fs_superblock *sb = &fs->superblock;
86 1.1 uch bool ok = (blk >= sb->datablock_start_sector) &&
87 1.1 uch (blk < sb->volume_size);
88 1.1 uch
89 1.1 uch #ifdef V7FS_DATABLOCK_DEBUG
90 1.1 uch if (!ok) {
91 1.1 uch DPRINTF("Bad data block #%d\n", blk);
92 1.1 uch }
93 1.1 uch #endif
94 1.1 uch
95 1.1 uch return ok;
96 1.1 uch }
97 1.1 uch
98 1.1 uch int
99 1.1 uch v7fs_datablock_allocate(struct v7fs_self *fs, v7fs_daddr_t *block_number)
100 1.1 uch {
101 1.1 uch struct v7fs_superblock *sb = &fs->superblock;
102 1.1 uch v7fs_daddr_t blk;
103 1.1 uch int error = 0;
104 1.1 uch
105 1.1 uch *block_number = 0;
106 1.1 uch SUPERB_LOCK(fs);
107 1.1 uch do {
108 1.1 uch if (!sb->total_freeblock) {
109 1.1 uch DPRINTF("free block exhausted!!!\n");
110 1.1 uch SUPERB_UNLOCK(fs);
111 1.1 uch return ENOSPC;
112 1.1 uch }
113 1.1 uch
114 1.1 uch /* Get free block from superblock cache. */
115 1.1 uch blk = sb->freeblock[--sb->nfreeblock];
116 1.1 uch sb->total_freeblock--;
117 1.1 uch sb->modified = 1;
118 1.1 uch
119 1.1 uch /* If nfreeblock is zero, it block is next freeblock link. */
120 1.1 uch if (sb->nfreeblock == 0) {
121 1.1 uch if ((error = v7fs_freeblock_update(fs, blk))) {
122 1.1 uch DPRINTF("no freeblock!!!\n");
123 1.1 uch SUPERB_UNLOCK(fs);
124 1.1 uch return error;
125 1.1 uch }
126 1.1 uch /* This freeblock link is no longer required. */
127 1.1 uch /* use as data block. */
128 1.1 uch }
129 1.1 uch } while (!datablock_number_sanity(fs, blk)); /* skip bogus block. */
130 1.1 uch SUPERB_UNLOCK(fs);
131 1.1 uch
132 1.1 uch DPRINTF("Get freeblock %d\n", blk);
133 1.1 uch /* Zero clear datablock. */
134 1.1 uch void *buf;
135 1.1 uch if (!(buf = scratch_read(fs, blk)))
136 1.1 uch return EIO;
137 1.1 uch memset(buf, 0, V7FS_BSIZE);
138 1.1 uch if (!fs->io.write(fs->io.cookie, buf, blk))
139 1.1 uch error = EIO;
140 1.1 uch scratch_free(fs, buf);
141 1.1 uch
142 1.1 uch if (error == 0)
143 1.1 uch *block_number = blk;
144 1.1 uch
145 1.1 uch return error;
146 1.1 uch }
147 1.1 uch
148 1.1 uch static int
149 1.1 uch v7fs_datablock_deallocate(struct v7fs_self *fs, v7fs_daddr_t blk)
150 1.1 uch {
151 1.1 uch struct v7fs_superblock *sb = &fs->superblock;
152 1.1 uch void *buf;
153 1.1 uch int error = 0;
154 1.1 uch
155 1.1 uch if (!datablock_number_sanity(fs, blk))
156 1.1 uch return EIO;
157 1.1 uch
158 1.1 uch /* Add to in-core freelist. */
159 1.1 uch SUPERB_LOCK(fs);
160 1.1 uch if (sb->nfreeblock < V7FS_MAX_FREEBLOCK) {
161 1.1 uch sb->freeblock[sb->nfreeblock++] = blk;
162 1.1 uch sb->total_freeblock++;
163 1.1 uch sb->modified = 1;
164 1.1 uch DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
165 1.1 uch SUPERB_UNLOCK(fs);
166 1.1 uch return 0;
167 1.1 uch }
168 1.1 uch
169 1.1 uch /* No space to push. */
170 1.1 uch /* Make this block to freeblock list.and current cache moved to this. */
171 1.1 uch if (!(buf = scratch_read(fs, blk))) {
172 1.1 uch SUPERB_UNLOCK(fs);
173 1.1 uch return EIO; /* Fatal */
174 1.1 uch }
175 1.1 uch
176 1.1 uch struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf;
177 1.1 uch fb->nfreeblock = V7FS_MAX_FREEBLOCK;
178 1.1 uch int i;
179 1.1 uch for (i = 0; i < V7FS_MAX_FREEBLOCK; i++)
180 1.1 uch fb->freeblock[i] = V7FS_VAL32(fs, sb->freeblock[i]);
181 1.1 uch
182 1.1 uch if (!fs->io.write(fs->io.cookie, (uint8_t *)fb, blk)) {
183 1.1 uch error = EIO; /* Fatal */
184 1.1 uch } else {
185 1.1 uch /* Link. on next allocate, this block is used as datablock, */
186 1.1 uch /* and swap outed freeblock list is restored. */
187 1.1 uch sb->freeblock[0] = blk;
188 1.1 uch sb->nfreeblock = 1;
189 1.1 uch sb->total_freeblock++;
190 1.1 uch sb->modified = 1;
191 1.1 uch DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
192 1.1 uch }
193 1.1 uch SUPERB_UNLOCK(fs);
194 1.1 uch scratch_free(fs, buf);
195 1.1 uch
196 1.1 uch return error;
197 1.1 uch }
198 1.1 uch
199 1.3 uch int
200 1.1 uch v7fs_datablock_addr(size_t sz, struct v7fs_daddr_map *map)
201 1.1 uch {
202 1.1 uch #define NIDX V7FS_DADDR_PER_BLOCK
203 1.1 uch #define DIRECT_SZ (V7FS_NADDR_DIRECT * V7FS_BSIZE)
204 1.1 uch #define IDX1_SZ (NIDX * V7FS_BSIZE)
205 1.1 uch #define IDX2_SZ (NIDX * NIDX * V7FS_BSIZE)
206 1.1 uch #define ROUND(x, a) ((((x) + ((a) - 1)) & ~((a) - 1)))
207 1.1 uch if (!sz) {
208 1.1 uch map->level = 0;
209 1.1 uch map->index[0] = 0;
210 1.1 uch return 0;
211 1.1 uch }
212 1.1 uch
213 1.1 uch sz = V7FS_ROUND_BSIZE(sz);
214 1.1 uch
215 1.1 uch /* Direct */
216 1.1 uch if (sz <= DIRECT_SZ) {
217 1.1 uch map->level = 0;
218 1.1 uch map->index[0] = (sz >> V7FS_BSHIFT) - 1;
219 1.1 uch return 0;
220 1.1 uch }
221 1.1 uch /* Index 1 */
222 1.1 uch sz -= DIRECT_SZ;
223 1.1 uch
224 1.1 uch if (sz <= IDX1_SZ) {
225 1.1 uch map->level = 1;
226 1.1 uch map->index[0] = (sz >> V7FS_BSHIFT) - 1;
227 1.1 uch return 0;
228 1.1 uch }
229 1.1 uch sz -= IDX1_SZ;
230 1.1 uch
231 1.1 uch /* Index 2 */
232 1.1 uch if (sz <= IDX2_SZ) {
233 1.1 uch map->level = 2;
234 1.1 uch map->index[0] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
235 1.1 uch map->index[1] = ((sz - (map->index[0] * IDX1_SZ)) >>
236 1.1 uch V7FS_BSHIFT) - 1;
237 1.1 uch return 0;
238 1.1 uch }
239 1.1 uch sz -= IDX2_SZ;
240 1.1 uch
241 1.1 uch /* Index 3 */
242 1.1 uch map->level = 3;
243 1.1 uch map->index[0] = ROUND(sz, IDX2_SZ) / IDX2_SZ - 1;
244 1.1 uch sz -= map->index[0] * IDX2_SZ;
245 1.1 uch map->index[1] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
246 1.1 uch sz -= map->index[1] * IDX1_SZ;
247 1.1 uch map->index[2] = (sz >> V7FS_BSHIFT) - 1;
248 1.1 uch
249 1.1 uch return map->index[2] >= NIDX ? ENOSPC : 0;
250 1.1 uch }
251 1.1 uch
252 1.1 uch int
253 1.1 uch v7fs_datablock_foreach(struct v7fs_self *fs, struct v7fs_inode *p,
254 1.1 uch int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
255 1.1 uch {
256 1.1 uch size_t i;
257 1.1 uch v7fs_daddr_t blk, blk2;
258 1.1 uch size_t filesize;
259 1.1 uch bool last;
260 1.1 uch int ret;
261 1.1 uch
262 1.1 uch if (!(filesize = v7fs_inode_filesize(p)))
263 1.2 uch return V7FS_ITERATOR_END;
264 1.1 uch #ifdef V7FS_DATABLOCK_DEBUG
265 1.1 uch size_t sz = filesize;
266 1.1 uch #endif
267 1.1 uch
268 1.1 uch /* Direct */
269 1.1 uch for (i = 0; i < V7FS_NADDR_DIRECT; i++, filesize -= V7FS_BSIZE) {
270 1.1 uch blk = p->addr[i];
271 1.1 uch if (!datablock_number_sanity(fs, blk)) {
272 1.1 uch DPRINTF("inode#%d direct=%zu filesize=%zu\n",
273 1.1 uch p->inode_number, i, sz);
274 1.1 uch return EIO;
275 1.1 uch }
276 1.1 uch
277 1.1 uch last = filesize <= V7FS_BSIZE;
278 1.1 uch if ((ret = func(fs, ctx, blk, last ? filesize : V7FS_BSIZE)))
279 1.1 uch return ret;
280 1.1 uch if (last)
281 1.1 uch return V7FS_ITERATOR_END;
282 1.1 uch }
283 1.1 uch
284 1.1 uch /* Index 1 */
285 1.1 uch blk = p->addr[V7FS_NADDR_INDEX1];
286 1.1 uch if (!datablock_number_sanity(fs, blk))
287 1.1 uch return EIO;
288 1.1 uch
289 1.5 apb if ((ret = v7fs_loop1(fs, blk, &filesize, func, ctx)))
290 1.1 uch return ret;
291 1.1 uch
292 1.1 uch /* Index 2 */
293 1.1 uch blk = p->addr[V7FS_NADDR_INDEX2];
294 1.1 uch if (!datablock_number_sanity(fs, blk))
295 1.1 uch return EIO;
296 1.1 uch
297 1.5 apb if ((ret = v7fs_loop2(fs, blk, &filesize, func, ctx)))
298 1.1 uch return ret;
299 1.1 uch
300 1.1 uch /* Index 3 */
301 1.1 uch blk = p->addr[V7FS_NADDR_INDEX3];
302 1.1 uch if (!datablock_number_sanity(fs, blk))
303 1.1 uch return EIO;
304 1.1 uch
305 1.1 uch for (i = 0; i < V7FS_DADDR_PER_BLOCK; i++) {
306 1.5 apb blk2 = v7fs_link(fs, blk, i);
307 1.1 uch if (!datablock_number_sanity(fs, blk))
308 1.1 uch return EIO;
309 1.1 uch
310 1.5 apb if ((ret = v7fs_loop2(fs, blk2, &filesize, func, ctx)))
311 1.1 uch return ret;
312 1.1 uch }
313 1.1 uch
314 1.1 uch return EFBIG;
315 1.1 uch }
316 1.1 uch
317 1.1 uch static int
318 1.5 apb v7fs_loop2(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
319 1.1 uch int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
320 1.1 uch {
321 1.1 uch v7fs_daddr_t blk;
322 1.1 uch int ret;
323 1.1 uch size_t j;
324 1.1 uch
325 1.1 uch for (j = 0; j < V7FS_DADDR_PER_BLOCK; j++) {
326 1.5 apb blk = v7fs_link(fs, listblk, j);
327 1.1 uch if (!datablock_number_sanity(fs, blk))
328 1.1 uch return EIO;
329 1.5 apb if ((ret = v7fs_loop1(fs, blk, filesize, func, ctx)))
330 1.1 uch return ret;
331 1.1 uch }
332 1.1 uch
333 1.1 uch return 0;
334 1.1 uch }
335 1.1 uch
336 1.1 uch static int
337 1.5 apb v7fs_loop1(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize,
338 1.1 uch int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx)
339 1.1 uch {
340 1.1 uch v7fs_daddr_t blk;
341 1.1 uch bool last;
342 1.1 uch int ret;
343 1.1 uch size_t k;
344 1.1 uch
345 1.1 uch for (k = 0; k < V7FS_DADDR_PER_BLOCK; k++, *filesize -= V7FS_BSIZE) {
346 1.5 apb blk = v7fs_link(fs, listblk, k);
347 1.1 uch if (!datablock_number_sanity(fs, blk))
348 1.1 uch return EIO;
349 1.1 uch last = *filesize <= V7FS_BSIZE;
350 1.1 uch if ((ret = func(fs, ctx, blk, last ? *filesize : V7FS_BSIZE)))
351 1.1 uch return ret;
352 1.1 uch if (last)
353 1.1 uch return V7FS_ITERATOR_END;
354 1.1 uch }
355 1.1 uch
356 1.1 uch return 0;
357 1.1 uch }
358 1.1 uch
359 1.1 uch v7fs_daddr_t
360 1.1 uch v7fs_datablock_last(struct v7fs_self *fs, struct v7fs_inode *inode,
361 1.1 uch v7fs_off_t ofs)
362 1.1 uch {
363 1.1 uch struct v7fs_daddr_map map;
364 1.1 uch v7fs_daddr_t blk = 0;
365 1.1 uch v7fs_daddr_t *addr = inode->addr;
366 1.1 uch
367 1.1 uch /* Inquire last data block location. */
368 1.1 uch if (v7fs_datablock_addr(ofs, &map) != 0)
369 1.1 uch return 0;
370 1.1 uch
371 1.1 uch switch (map.level)
372 1.1 uch {
373 1.1 uch case 0: /*Direct */
374 1.1 uch blk = inode->addr[map.index[0]];
375 1.1 uch break;
376 1.1 uch case 1: /*Index1 */
377 1.5 apb blk = v7fs_link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]);
378 1.1 uch break;
379 1.1 uch case 2: /*Index2 */
380 1.5 apb blk = v7fs_link(fs, v7fs_link(fs,
381 1.5 apb addr[V7FS_NADDR_INDEX2], map.index[0]), map.index[1]);
382 1.1 uch break;
383 1.1 uch case 3: /*Index3 */
384 1.5 apb blk = v7fs_link(fs, v7fs_link(fs, v7fs_link(fs,
385 1.5 apb addr[V7FS_NADDR_INDEX3], map.index[0]), map.index[1]),
386 1.5 apb map.index[2]);
387 1.1 uch break;
388 1.1 uch }
389 1.1 uch
390 1.1 uch return blk;
391 1.1 uch }
392 1.1 uch
393 1.1 uch int
394 1.1 uch v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz)
395 1.1 uch {
396 1.1 uch size_t old_filesize = inode->filesize;
397 1.1 uch size_t new_filesize = old_filesize + sz;
398 1.1 uch struct v7fs_daddr_map oldmap, newmap;
399 1.1 uch v7fs_daddr_t blk, idxblk;
400 1.1 uch int error;
401 1.1 uch v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
402 1.1 uch v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
403 1.1 uch
404 1.1 uch if (old_nblk == new_nblk) {
405 1.1 uch inode->filesize += sz;
406 1.1 uch v7fs_inode_writeback(fs, inode);
407 1.1 uch return 0; /* no need to expand. */
408 1.1 uch }
409 1.1 uch struct v7fs_inode backup = *inode;
410 1.1 uch v7fs_daddr_t required_blk = new_nblk - old_nblk;
411 1.1 uch
412 1.1 uch DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize,
413 1.1 uch required_blk);
414 1.1 uch
415 1.1 uch v7fs_datablock_addr(old_filesize, &oldmap);
416 1.1 uch v7fs_daddr_t i;
417 1.1 uch for (i = 0; i < required_blk; i++) {
418 1.1 uch v7fs_datablock_addr(old_filesize + (i+1) * V7FS_BSIZE, &newmap);
419 1.1 uch daddr_map_dump(&oldmap);
420 1.1 uch daddr_map_dump(&newmap);
421 1.1 uch
422 1.1 uch if (oldmap.level != newmap.level) {
423 1.1 uch /* Allocate index area */
424 1.1 uch if ((error = v7fs_datablock_allocate(fs, &idxblk)))
425 1.1 uch return error;
426 1.1 uch
427 1.1 uch switch (newmap.level) {
428 1.1 uch case 1:
429 1.1 uch DPRINTF("0->1\n");
430 1.1 uch inode->addr[V7FS_NADDR_INDEX1] = idxblk;
431 1.5 apb blk = v7fs_add_leaf(fs, idxblk, 0);
432 1.1 uch break;
433 1.1 uch case 2:
434 1.1 uch DPRINTF("1->2\n");
435 1.1 uch inode->addr[V7FS_NADDR_INDEX2] = idxblk;
436 1.5 apb blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs,
437 1.5 apb idxblk, 0), 0);
438 1.1 uch break;
439 1.1 uch case 3:
440 1.1 uch DPRINTF("2->3\n");
441 1.1 uch inode->addr[V7FS_NADDR_INDEX3] = idxblk;
442 1.5 apb blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs,
443 1.5 apb v7fs_add_leaf(fs, idxblk, 0), 0), 0);
444 1.1 uch break;
445 1.1 uch }
446 1.1 uch } else {
447 1.1 uch switch (newmap.level) {
448 1.1 uch case 0:
449 1.1 uch if ((error = v7fs_datablock_allocate(fs, &blk)))
450 1.1 uch return error;
451 1.1 uch inode->addr[newmap.index[0]] = blk;
452 1.1 uch DPRINTF("direct index %d = blk%d\n",
453 1.1 uch newmap.index[0], blk);
454 1.1 uch break;
455 1.1 uch case 1:
456 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX1];
457 1.5 apb blk = v7fs_add_leaf(fs, idxblk,
458 1.5 apb newmap.index[0]);
459 1.1 uch break;
460 1.1 uch case 2:
461 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX2];
462 1.5 apb if (oldmap.index[0] != newmap.index[0]) {
463 1.5 apb v7fs_add_leaf(fs, idxblk,
464 1.5 apb newmap.index[0]);
465 1.5 apb }
466 1.5 apb blk = v7fs_add_leaf(fs, v7fs_link(fs,idxblk,
467 1.1 uch newmap.index[0]), newmap.index[1]);
468 1.1 uch break;
469 1.1 uch case 3:
470 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX3];
471 1.1 uch
472 1.5 apb if (oldmap.index[0] != newmap.index[0]) {
473 1.5 apb v7fs_add_leaf(fs, idxblk,
474 1.5 apb newmap.index[0]);
475 1.5 apb }
476 1.1 uch
477 1.5 apb if (oldmap.index[1] != newmap.index[1]) {
478 1.5 apb v7fs_add_leaf(fs, v7fs_link(fs, idxblk,
479 1.1 uch newmap.index[0]), newmap.index[1]);
480 1.5 apb }
481 1.5 apb blk = v7fs_add_leaf(fs, v7fs_link(fs,
482 1.5 apb v7fs_link(fs, idxblk, newmap.index[0]),
483 1.5 apb newmap.index[1]), newmap.index[2]);
484 1.1 uch break;
485 1.1 uch }
486 1.1 uch }
487 1.1 uch if (!blk) {
488 1.1 uch *inode = backup; /* structure copy; */
489 1.1 uch return ENOSPC;
490 1.1 uch }
491 1.1 uch oldmap = newmap;
492 1.1 uch }
493 1.1 uch inode->filesize += sz;
494 1.1 uch v7fs_inode_writeback(fs, inode);
495 1.1 uch
496 1.1 uch return 0;
497 1.1 uch }
498 1.1 uch
499 1.1 uch static v7fs_daddr_t
500 1.5 apb v7fs_link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n)
501 1.1 uch {
502 1.1 uch v7fs_daddr_t *list;
503 1.1 uch v7fs_daddr_t blk;
504 1.1 uch void *buf;
505 1.1 uch
506 1.1 uch if (!datablock_number_sanity(fs, listblk))
507 1.1 uch return 0;
508 1.1 uch if (!(buf = scratch_read(fs, listblk)))
509 1.1 uch return 0;
510 1.1 uch list = (v7fs_daddr_t *)buf;
511 1.1 uch blk = V7FS_VAL32(fs, list[n]);
512 1.1 uch scratch_free(fs, buf);
513 1.1 uch
514 1.1 uch if (!datablock_number_sanity(fs, blk))
515 1.1 uch return 0;
516 1.1 uch
517 1.1 uch return blk;
518 1.1 uch }
519 1.1 uch
520 1.1 uch static v7fs_daddr_t
521 1.5 apb v7fs_add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx)
522 1.1 uch {
523 1.1 uch v7fs_daddr_t newblk;
524 1.1 uch v7fs_daddr_t *daddr_list;
525 1.1 uch int error = 0;
526 1.1 uch void *buf;
527 1.1 uch
528 1.1 uch if (!up)
529 1.1 uch return 0;
530 1.1 uch if (!datablock_number_sanity(fs, up))
531 1.1 uch return 0;
532 1.1 uch
533 1.1 uch if ((error = v7fs_datablock_allocate(fs, &newblk)))
534 1.1 uch return 0;
535 1.1 uch if (!(buf = scratch_read(fs, up)))
536 1.1 uch return 0;
537 1.1 uch daddr_list = (v7fs_daddr_t *)buf;
538 1.1 uch daddr_list[idx] = V7FS_VAL32(fs, newblk);
539 1.1 uch if (!fs->io.write(fs->io.cookie, buf, up))
540 1.1 uch newblk = 0;
541 1.1 uch scratch_free(fs, buf);
542 1.1 uch
543 1.1 uch return newblk;
544 1.1 uch }
545 1.1 uch
546 1.1 uch int
547 1.1 uch v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode,
548 1.1 uch size_t sz)
549 1.1 uch {
550 1.1 uch size_t old_filesize = inode->filesize;
551 1.1 uch size_t new_filesize = old_filesize - sz;
552 1.1 uch struct v7fs_daddr_map oldmap, newmap;
553 1.1 uch v7fs_daddr_t blk, idxblk;
554 1.1 uch int error = 0;
555 1.1 uch v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT;
556 1.1 uch v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT;
557 1.1 uch
558 1.1 uch if (old_nblk == new_nblk) {
559 1.1 uch inode->filesize -= sz;
560 1.1 uch v7fs_inode_writeback(fs, inode);
561 1.1 uch return 0; /* no need to contract; */
562 1.1 uch }
563 1.1 uch v7fs_daddr_t erase_blk = old_nblk - new_nblk;
564 1.1 uch
565 1.1 uch DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize,
566 1.1 uch erase_blk);
567 1.1 uch
568 1.1 uch v7fs_datablock_addr(old_filesize, &oldmap);
569 1.1 uch v7fs_daddr_t i;
570 1.1 uch for (i = 0; i < erase_blk; i++) {
571 1.1 uch v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap);
572 1.1 uch
573 1.1 uch if (oldmap.level != newmap.level) {
574 1.1 uch switch (newmap.level) {
575 1.1 uch case 0: /*1->0 */
576 1.1 uch DPRINTF("1->0\n");
577 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX1];
578 1.1 uch inode->addr[V7FS_NADDR_INDEX1] = 0;
579 1.1 uch error = v7fs_datablock_deallocate(fs,
580 1.5 apb v7fs_remove_self(fs, idxblk));
581 1.1 uch break;
582 1.1 uch case 1: /*2->1 */
583 1.1 uch DPRINTF("2->1\n");
584 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX2];
585 1.1 uch inode->addr[V7FS_NADDR_INDEX2] = 0;
586 1.1 uch error = v7fs_datablock_deallocate(fs,
587 1.5 apb v7fs_remove_self(fs, v7fs_remove_self(fs,
588 1.5 apb idxblk)));
589 1.1 uch break;
590 1.1 uch case 2:/*3->2 */
591 1.1 uch DPRINTF("3->2\n");
592 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX3];
593 1.1 uch inode->addr[V7FS_NADDR_INDEX3] = 0;
594 1.1 uch error = v7fs_datablock_deallocate(fs,
595 1.5 apb v7fs_remove_self(fs, v7fs_remove_self(fs,
596 1.5 apb v7fs_remove_self(fs, idxblk))));
597 1.1 uch break;
598 1.1 uch }
599 1.1 uch } else {
600 1.1 uch switch (newmap.level) {
601 1.1 uch case 0:
602 1.1 uch DPRINTF("[0] %d\n", oldmap.index[0]);
603 1.1 uch blk = inode->addr[oldmap.index[0]];
604 1.1 uch error = v7fs_datablock_deallocate(fs, blk);
605 1.1 uch break;
606 1.1 uch case 1:
607 1.1 uch DPRINTF("[1] %d\n", oldmap.index[0]);
608 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX1];
609 1.5 apb v7fs_remove_leaf(fs, idxblk, oldmap.index[0]);
610 1.1 uch
611 1.1 uch break;
612 1.1 uch case 2:
613 1.1 uch DPRINTF("[2] %d %d\n", oldmap.index[0],
614 1.1 uch oldmap.index[1]);
615 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX2];
616 1.5 apb v7fs_remove_leaf(fs, v7fs_link(fs, idxblk,
617 1.1 uch oldmap.index[0]), oldmap.index[1]);
618 1.1 uch if (oldmap.index[0] != newmap.index[0]) {
619 1.5 apb v7fs_remove_leaf(fs, idxblk,
620 1.1 uch oldmap.index[0]);
621 1.1 uch }
622 1.1 uch break;
623 1.1 uch case 3:
624 1.1 uch DPRINTF("[2] %d %d %d\n", oldmap.index[0],
625 1.1 uch oldmap.index[1], oldmap.index[2]);
626 1.1 uch idxblk = inode->addr[V7FS_NADDR_INDEX3];
627 1.5 apb v7fs_remove_leaf(fs, v7fs_link(fs,
628 1.5 apb v7fs_link(fs, idxblk, oldmap.index[0]),
629 1.5 apb oldmap.index[1]), oldmap.index[2]);
630 1.1 uch
631 1.1 uch if (oldmap.index[1] != newmap.index[1]) {
632 1.5 apb v7fs_remove_leaf(fs, v7fs_link(fs,
633 1.5 apb idxblk, oldmap.index[0]),
634 1.5 apb oldmap.index[1]);
635 1.1 uch }
636 1.1 uch if (oldmap.index[0] != newmap.index[0]) {
637 1.5 apb v7fs_remove_leaf(fs, idxblk,
638 1.1 uch oldmap.index[0]);
639 1.1 uch }
640 1.1 uch break;
641 1.1 uch }
642 1.1 uch }
643 1.1 uch oldmap = newmap;
644 1.1 uch }
645 1.1 uch inode->filesize -= sz;
646 1.1 uch v7fs_inode_writeback(fs, inode);
647 1.1 uch
648 1.1 uch return error;
649 1.1 uch }
650 1.1 uch
651 1.1 uch static v7fs_daddr_t
652 1.5 apb v7fs_unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n)
653 1.1 uch {
654 1.1 uch v7fs_daddr_t *daddr_list;
655 1.1 uch v7fs_daddr_t blk;
656 1.1 uch void *buf;
657 1.1 uch
658 1.1 uch if (!(buf = scratch_read(fs, idxblk)))
659 1.1 uch return 0;
660 1.1 uch daddr_list = (v7fs_daddr_t *)buf;
661 1.1 uch blk = V7FS_VAL32(fs, daddr_list[n]);
662 1.1 uch daddr_list[n] = 0;
663 1.1 uch fs->io.write(fs->io.cookie, buf, idxblk);
664 1.1 uch scratch_free(fs, buf);
665 1.1 uch
666 1.1 uch return blk; /* unlinked block. */
667 1.1 uch }
668 1.1 uch
669 1.1 uch static v7fs_daddr_t
670 1.5 apb v7fs_remove_self(struct v7fs_self *fs, v7fs_daddr_t up)
671 1.1 uch {
672 1.1 uch v7fs_daddr_t down;
673 1.1 uch
674 1.1 uch if (!datablock_number_sanity(fs, up))
675 1.1 uch return 0;
676 1.1 uch
677 1.1 uch /* At 1st, remove from datablock list. */
678 1.5 apb down = v7fs_unlink(fs, up, 0);
679 1.1 uch
680 1.1 uch /* link self to freelist. */
681 1.1 uch v7fs_datablock_deallocate(fs, up);
682 1.1 uch
683 1.1 uch return down;
684 1.1 uch }
685 1.1 uch
686 1.1 uch static v7fs_daddr_t
687 1.5 apb v7fs_remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n)
688 1.1 uch {
689 1.1 uch v7fs_daddr_t down;
690 1.1 uch
691 1.1 uch if (!datablock_number_sanity(fs, up))
692 1.1 uch return 0;
693 1.1 uch
694 1.1 uch /* At 1st, remove from datablock list. */
695 1.5 apb down = v7fs_unlink(fs, up, n);
696 1.1 uch
697 1.1 uch /* link leaf to freelist. */
698 1.1 uch v7fs_datablock_deallocate(fs, down);
699 1.1 uch
700 1.1 uch return down;
701 1.1 uch }
702 1.1 uch
703 1.1 uch int
704 1.1 uch v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz,
705 1.1 uch struct v7fs_inode *inode)
706 1.1 uch {
707 1.1 uch ssize_t diff = newsz - v7fs_inode_filesize(inode);
708 1.1 uch int error = 0;
709 1.1 uch
710 1.1 uch if (diff > 0)
711 1.1 uch error = v7fs_datablock_expand(fs, inode, diff);
712 1.1 uch else if (diff < 0)
713 1.1 uch error = v7fs_datablock_contract(fs, inode, -diff);
714 1.1 uch
715 1.1 uch return error;
716 1.1 uch }
717 1.1 uch
718 1.1 uch #ifdef V7FS_DATABLOCK_DEBUG
719 1.1 uch void
720 1.1 uch daddr_map_dump(const struct v7fs_daddr_map *map)
721 1.1 uch {
722 1.1 uch
723 1.1 uch DPRINTF("level %d ", map->level);
724 1.1 uch int m, n = !map->level ? 1 : map->level;
725 1.1 uch for (m = 0; m < n; m++)
726 1.1 uch printf("[%d]", map->index[m]);
727 1.1 uch printf("\n");
728 1.1 uch }
729 1.1 uch #endif
730