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