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