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