block.c revision beb388a473bc12fa874743a7b9f97ec3094bb9d1
1/* 2 * block.c --- iterate over all blocks in an inode 3 * 4 * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Library 8 * General Public License, version 2. 9 * %End-Header% 10 */ 11 12#include "config.h" 13#include <stdio.h> 14#include <string.h> 15#if HAVE_UNISTD_H 16#include <unistd.h> 17#endif 18 19#include "ext2_fs.h" 20#include "ext2fs.h" 21 22struct block_context { 23 ext2_filsys fs; 24 int (*func)(ext2_filsys fs, 25 blk64_t *blocknr, 26 e2_blkcnt_t bcount, 27 blk64_t ref_blk, 28 int ref_offset, 29 void *priv_data); 30 e2_blkcnt_t bcount; 31 int bsize; 32 int flags; 33 errcode_t errcode; 34 char *ind_buf; 35 char *dind_buf; 36 char *tind_buf; 37 void *priv_data; 38}; 39 40#define check_for_ro_violation_return(ctx, ret) \ 41 do { \ 42 if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ 43 ((ret) & BLOCK_CHANGED)) { \ 44 (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ 45 ret |= BLOCK_ABORT | BLOCK_ERROR; \ 46 return ret; \ 47 } \ 48 } while (0) 49 50#define check_for_ro_violation_goto(ctx, ret, label) \ 51 do { \ 52 if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ 53 ((ret) & BLOCK_CHANGED)) { \ 54 (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ 55 ret |= BLOCK_ABORT | BLOCK_ERROR; \ 56 goto label; \ 57 } \ 58 } while (0) 59 60static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, 61 int ref_offset, struct block_context *ctx) 62{ 63 int ret = 0, changed = 0; 64 int i, flags, limit, offset; 65 blk_t *block_nr; 66 blk64_t blk64; 67 68 limit = ctx->fs->blocksize >> 2; 69 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 70 !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) { 71 blk64 = *ind_block; 72 ret = (*ctx->func)(ctx->fs, &blk64, 73 BLOCK_COUNT_IND, ref_block, 74 ref_offset, ctx->priv_data); 75 *ind_block = blk64; 76 } 77 check_for_ro_violation_return(ctx, ret); 78 if (!*ind_block || (ret & BLOCK_ABORT)) { 79 ctx->bcount += limit; 80 return ret; 81 } 82 if (*ind_block >= ext2fs_blocks_count(ctx->fs->super) || 83 *ind_block < ctx->fs->super->s_first_data_block) { 84 ctx->errcode = EXT2_ET_BAD_IND_BLOCK; 85 ret |= BLOCK_ERROR; 86 return ret; 87 } 88 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, 89 ctx->ind_buf); 90 if (ctx->errcode) { 91 ret |= BLOCK_ERROR; 92 return ret; 93 } 94 95 block_nr = (blk_t *) ctx->ind_buf; 96 offset = 0; 97 if (ctx->flags & BLOCK_FLAG_APPEND) { 98 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { 99 blk64 = *block_nr; 100 flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount, 101 *ind_block, offset, 102 ctx->priv_data); 103 *block_nr = blk64; 104 changed |= flags; 105 if (flags & BLOCK_ABORT) { 106 ret |= BLOCK_ABORT; 107 break; 108 } 109 offset += sizeof(blk_t); 110 } 111 } else { 112 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { 113 if (*block_nr == 0) 114 goto skip_sparse; 115 blk64 = *block_nr; 116 flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount, 117 *ind_block, offset, 118 ctx->priv_data); 119 *block_nr = blk64; 120 changed |= flags; 121 if (flags & BLOCK_ABORT) { 122 ret |= BLOCK_ABORT; 123 break; 124 } 125 skip_sparse: 126 offset += sizeof(blk_t); 127 } 128 } 129 check_for_ro_violation_return(ctx, changed); 130 if (changed & BLOCK_CHANGED) { 131 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, 132 ctx->ind_buf); 133 if (ctx->errcode) 134 ret |= BLOCK_ERROR | BLOCK_ABORT; 135 } 136 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 137 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && 138 !(ret & BLOCK_ABORT)) { 139 blk64 = *ind_block; 140 ret |= (*ctx->func)(ctx->fs, &blk64, 141 BLOCK_COUNT_IND, ref_block, 142 ref_offset, ctx->priv_data); 143 *ind_block = blk64; 144 } 145 check_for_ro_violation_return(ctx, ret); 146 return ret; 147} 148 149static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, 150 int ref_offset, struct block_context *ctx) 151{ 152 int ret = 0, changed = 0; 153 int i, flags, limit, offset; 154 blk_t *block_nr; 155 blk64_t blk64; 156 157 limit = ctx->fs->blocksize >> 2; 158 if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | 159 BLOCK_FLAG_DATA_ONLY))) { 160 blk64 = *dind_block; 161 ret = (*ctx->func)(ctx->fs, &blk64, 162 BLOCK_COUNT_DIND, ref_block, 163 ref_offset, ctx->priv_data); 164 *dind_block = blk64; 165 } 166 check_for_ro_violation_return(ctx, ret); 167 if (!*dind_block || (ret & BLOCK_ABORT)) { 168 ctx->bcount += limit*limit; 169 return ret; 170 } 171 if (*dind_block >= ext2fs_blocks_count(ctx->fs->super) || 172 *dind_block < ctx->fs->super->s_first_data_block) { 173 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; 174 ret |= BLOCK_ERROR; 175 return ret; 176 } 177 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, 178 ctx->dind_buf); 179 if (ctx->errcode) { 180 ret |= BLOCK_ERROR; 181 return ret; 182 } 183 184 block_nr = (blk_t *) ctx->dind_buf; 185 offset = 0; 186 if (ctx->flags & BLOCK_FLAG_APPEND) { 187 for (i = 0; i < limit; i++, block_nr++) { 188 flags = block_iterate_ind(block_nr, 189 *dind_block, offset, 190 ctx); 191 changed |= flags; 192 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 193 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 194 break; 195 } 196 offset += sizeof(blk_t); 197 } 198 } else { 199 for (i = 0; i < limit; i++, block_nr++) { 200 if (*block_nr == 0) { 201 ctx->bcount += limit; 202 continue; 203 } 204 flags = block_iterate_ind(block_nr, 205 *dind_block, offset, 206 ctx); 207 changed |= flags; 208 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 209 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 210 break; 211 } 212 offset += sizeof(blk_t); 213 } 214 } 215 check_for_ro_violation_return(ctx, changed); 216 if (changed & BLOCK_CHANGED) { 217 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, 218 ctx->dind_buf); 219 if (ctx->errcode) 220 ret |= BLOCK_ERROR | BLOCK_ABORT; 221 } 222 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 223 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && 224 !(ret & BLOCK_ABORT)) { 225 blk64 = *dind_block; 226 ret |= (*ctx->func)(ctx->fs, &blk64, 227 BLOCK_COUNT_DIND, ref_block, 228 ref_offset, ctx->priv_data); 229 *dind_block = blk64; 230 } 231 check_for_ro_violation_return(ctx, ret); 232 return ret; 233} 234 235static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, 236 int ref_offset, struct block_context *ctx) 237{ 238 int ret = 0, changed = 0; 239 int i, flags, limit, offset; 240 blk_t *block_nr; 241 blk64_t blk64; 242 243 limit = ctx->fs->blocksize >> 2; 244 if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | 245 BLOCK_FLAG_DATA_ONLY))) { 246 blk64 = *tind_block; 247 ret = (*ctx->func)(ctx->fs, &blk64, 248 BLOCK_COUNT_TIND, ref_block, 249 ref_offset, ctx->priv_data); 250 *tind_block = blk64; 251 } 252 check_for_ro_violation_return(ctx, ret); 253 if (!*tind_block || (ret & BLOCK_ABORT)) { 254 ctx->bcount += limit*limit*limit; 255 return ret; 256 } 257 if (*tind_block >= ext2fs_blocks_count(ctx->fs->super) || 258 *tind_block < ctx->fs->super->s_first_data_block) { 259 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; 260 ret |= BLOCK_ERROR; 261 return ret; 262 } 263 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, 264 ctx->tind_buf); 265 if (ctx->errcode) { 266 ret |= BLOCK_ERROR; 267 return ret; 268 } 269 270 block_nr = (blk_t *) ctx->tind_buf; 271 offset = 0; 272 if (ctx->flags & BLOCK_FLAG_APPEND) { 273 for (i = 0; i < limit; i++, block_nr++) { 274 flags = block_iterate_dind(block_nr, 275 *tind_block, 276 offset, ctx); 277 changed |= flags; 278 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 279 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 280 break; 281 } 282 offset += sizeof(blk_t); 283 } 284 } else { 285 for (i = 0; i < limit; i++, block_nr++) { 286 if (*block_nr == 0) { 287 ctx->bcount += limit*limit; 288 continue; 289 } 290 flags = block_iterate_dind(block_nr, 291 *tind_block, 292 offset, ctx); 293 changed |= flags; 294 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 295 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 296 break; 297 } 298 offset += sizeof(blk_t); 299 } 300 } 301 check_for_ro_violation_return(ctx, changed); 302 if (changed & BLOCK_CHANGED) { 303 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, 304 ctx->tind_buf); 305 if (ctx->errcode) 306 ret |= BLOCK_ERROR | BLOCK_ABORT; 307 } 308 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 309 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && 310 !(ret & BLOCK_ABORT)) { 311 blk64 = *tind_block; 312 ret |= (*ctx->func)(ctx->fs, &blk64, 313 BLOCK_COUNT_TIND, ref_block, 314 ref_offset, ctx->priv_data); 315 *tind_block = blk64; 316 } 317 check_for_ro_violation_return(ctx, ret); 318 return ret; 319} 320 321errcode_t ext2fs_block_iterate3(ext2_filsys fs, 322 ext2_ino_t ino, 323 int flags, 324 char *block_buf, 325 int (*func)(ext2_filsys fs, 326 blk64_t *blocknr, 327 e2_blkcnt_t blockcnt, 328 blk64_t ref_blk, 329 int ref_offset, 330 void *priv_data), 331 void *priv_data) 332{ 333 int i; 334 int r, ret = 0; 335 struct ext2_inode inode; 336 errcode_t retval; 337 struct block_context ctx; 338 int limit; 339 blk64_t blk64; 340 341 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 342 343 ctx.errcode = ext2fs_read_inode(fs, ino, &inode); 344 if (ctx.errcode) 345 return ctx.errcode; 346 347 /* 348 * Check to see if we need to limit large files 349 */ 350 if (flags & BLOCK_FLAG_NO_LARGE) { 351 if (!LINUX_S_ISDIR(inode.i_mode) && 352 (inode.i_size_high != 0)) 353 return EXT2_ET_FILE_TOO_BIG; 354 } 355 356 limit = fs->blocksize >> 2; 357 358 ctx.fs = fs; 359 ctx.func = func; 360 ctx.priv_data = priv_data; 361 ctx.flags = flags; 362 ctx.bcount = 0; 363 if (block_buf) { 364 ctx.ind_buf = block_buf; 365 } else { 366 retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf); 367 if (retval) 368 return retval; 369 } 370 ctx.dind_buf = ctx.ind_buf + fs->blocksize; 371 ctx.tind_buf = ctx.dind_buf + fs->blocksize; 372 373 /* 374 * Iterate over the HURD translator block (if present) 375 */ 376 if ((fs->super->s_creator_os == EXT2_OS_HURD) && 377 !(flags & BLOCK_FLAG_DATA_ONLY)) { 378 if (inode.osd1.hurd1.h_i_translator) { 379 blk64 = inode.osd1.hurd1.h_i_translator; 380 ret |= (*ctx.func)(fs, &blk64, 381 BLOCK_COUNT_TRANSLATOR, 382 0, 0, priv_data); 383 inode.osd1.hurd1.h_i_translator = (blk_t) blk64; 384 if (ret & BLOCK_ABORT) 385 goto abort_exit; 386 check_for_ro_violation_goto(&ctx, ret, abort_exit); 387 } 388 } 389 390 if (inode.i_flags & EXT4_EXTENTS_FL) { 391 ext2_extent_handle_t handle; 392 struct ext2fs_extent extent, next; 393 e2_blkcnt_t blockcnt = 0; 394 blk64_t blk, new_blk; 395 int op = EXT2_EXTENT_ROOT; 396 int uninit; 397 unsigned int j; 398 399 ctx.errcode = ext2fs_extent_open2(fs, ino, &inode, &handle); 400 if (ctx.errcode) 401 goto abort_exit; 402 403 while (1) { 404 if (op == EXT2_EXTENT_CURRENT) 405 ctx.errcode = 0; 406 else 407 ctx.errcode = ext2fs_extent_get(handle, op, 408 &extent); 409 if (ctx.errcode) { 410 if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT) 411 break; 412 ctx.errcode = 0; 413 if (!(flags & BLOCK_FLAG_APPEND)) 414 break; 415 next_block_set: 416 blk = 0; 417 r = (*ctx.func)(fs, &blk, blockcnt, 418 0, 0, priv_data); 419 ret |= r; 420 check_for_ro_violation_goto(&ctx, ret, 421 extent_done); 422 if (r & BLOCK_CHANGED) { 423 ctx.errcode = 424 ext2fs_extent_set_bmap(handle, 425 (blk64_t) blockcnt++, 426 (blk64_t) blk, 0); 427 if (ctx.errcode || (ret & BLOCK_ABORT)) 428 break; 429 if (blk) 430 goto next_block_set; 431 } 432 break; 433 } 434 435 op = EXT2_EXTENT_NEXT; 436 blk = extent.e_pblk; 437 if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) { 438 if (ctx.flags & BLOCK_FLAG_DATA_ONLY) 439 continue; 440 if ((!(extent.e_flags & 441 EXT2_EXTENT_FLAGS_SECOND_VISIT) && 442 !(ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE)) || 443 ((extent.e_flags & 444 EXT2_EXTENT_FLAGS_SECOND_VISIT) && 445 (ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE))) { 446 ret |= (*ctx.func)(fs, &blk, 447 -1, 0, 0, priv_data); 448 if (ret & BLOCK_CHANGED) { 449 extent.e_pblk = blk; 450 ctx.errcode = 451 ext2fs_extent_replace(handle, 0, &extent); 452 if (ctx.errcode) 453 break; 454 } 455 if (ret & BLOCK_ABORT) 456 break; 457 } 458 continue; 459 } 460 uninit = 0; 461 if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) 462 uninit = EXT2_EXTENT_SET_BMAP_UNINIT; 463 464 /* 465 * Get the next extent before we start messing 466 * with the current extent 467 */ 468 retval = ext2fs_extent_get(handle, op, &next); 469 470#if 0 471 printf("lblk %llu pblk %llu len %d blockcnt %llu\n", 472 extent.e_lblk, extent.e_pblk, 473 extent.e_len, blockcnt); 474#endif 475 if (extent.e_lblk + extent.e_len <= blockcnt) 476 continue; 477 if (extent.e_lblk > blockcnt) 478 blockcnt = extent.e_lblk; 479 j = blockcnt - extent.e_lblk; 480 blk += j; 481 for (blockcnt = extent.e_lblk, j = 0; 482 j < extent.e_len; 483 blk++, blockcnt++, j++) { 484 new_blk = blk; 485 r = (*ctx.func)(fs, &new_blk, blockcnt, 486 0, 0, priv_data); 487 ret |= r; 488 check_for_ro_violation_goto(&ctx, ret, 489 extent_done); 490 if (r & BLOCK_CHANGED) { 491 ctx.errcode = 492 ext2fs_extent_set_bmap(handle, 493 (blk64_t) blockcnt, 494 new_blk, uninit); 495 if (ctx.errcode) 496 goto extent_done; 497 } 498 if (ret & BLOCK_ABORT) 499 goto extent_done; 500 } 501 if (retval == 0) { 502 extent = next; 503 op = EXT2_EXTENT_CURRENT; 504 } 505 } 506 507 extent_done: 508 ext2fs_extent_free(handle); 509 ret |= BLOCK_ERROR; /* ctx.errcode is always valid here */ 510 goto errout; 511 } 512 513 /* 514 * Iterate over normal data blocks 515 */ 516 for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { 517 if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) { 518 blk64 = inode.i_block[i]; 519 ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i, 520 priv_data); 521 inode.i_block[i] = (blk_t) blk64; 522 if (ret & BLOCK_ABORT) 523 goto abort_exit; 524 } 525 } 526 check_for_ro_violation_goto(&ctx, ret, abort_exit); 527 if (inode.i_block[EXT2_IND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { 528 ret |= block_iterate_ind(&inode.i_block[EXT2_IND_BLOCK], 529 0, EXT2_IND_BLOCK, &ctx); 530 if (ret & BLOCK_ABORT) 531 goto abort_exit; 532 } else 533 ctx.bcount += limit; 534 if (inode.i_block[EXT2_DIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { 535 ret |= block_iterate_dind(&inode.i_block[EXT2_DIND_BLOCK], 536 0, EXT2_DIND_BLOCK, &ctx); 537 if (ret & BLOCK_ABORT) 538 goto abort_exit; 539 } else 540 ctx.bcount += limit * limit; 541 if (inode.i_block[EXT2_TIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { 542 ret |= block_iterate_tind(&inode.i_block[EXT2_TIND_BLOCK], 543 0, EXT2_TIND_BLOCK, &ctx); 544 if (ret & BLOCK_ABORT) 545 goto abort_exit; 546 } 547 548abort_exit: 549 if (ret & BLOCK_CHANGED) { 550 retval = ext2fs_write_inode(fs, ino, &inode); 551 if (retval) { 552 ret |= BLOCK_ERROR; 553 ctx.errcode = retval; 554 } 555 } 556errout: 557 if (!block_buf) 558 ext2fs_free_mem(&ctx.ind_buf); 559 560 return (ret & BLOCK_ERROR) ? ctx.errcode : 0; 561} 562 563/* 564 * Emulate the old ext2fs_block_iterate function! 565 */ 566 567struct xlate64 { 568 int (*func)(ext2_filsys fs, 569 blk_t *blocknr, 570 e2_blkcnt_t blockcnt, 571 blk_t ref_blk, 572 int ref_offset, 573 void *priv_data); 574 void *real_private; 575}; 576 577static int xlate64_func(ext2_filsys fs, blk64_t *blocknr, 578 e2_blkcnt_t blockcnt, blk64_t ref_blk, 579 int ref_offset, void *priv_data) 580{ 581 struct xlate64 *xl = (struct xlate64 *) priv_data; 582 int ret; 583 blk_t block32 = *blocknr; 584 585 ret = (*xl->func)(fs, &block32, blockcnt, (blk_t) ref_blk, ref_offset, 586 xl->real_private); 587 *blocknr = block32; 588 return ret; 589} 590 591errcode_t ext2fs_block_iterate2(ext2_filsys fs, 592 ext2_ino_t ino, 593 int flags, 594 char *block_buf, 595 int (*func)(ext2_filsys fs, 596 blk_t *blocknr, 597 e2_blkcnt_t blockcnt, 598 blk_t ref_blk, 599 int ref_offset, 600 void *priv_data), 601 void *priv_data) 602{ 603 struct xlate64 xl; 604 605 xl.real_private = priv_data; 606 xl.func = func; 607 608 return ext2fs_block_iterate3(fs, ino, flags, block_buf, 609 xlate64_func, &xl); 610} 611 612 613struct xlate { 614 int (*func)(ext2_filsys fs, 615 blk_t *blocknr, 616 int bcount, 617 void *priv_data); 618 void *real_private; 619}; 620 621#ifdef __TURBOC__ 622 #pragma argsused 623#endif 624static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, 625 blk_t ref_block EXT2FS_ATTR((unused)), 626 int ref_offset EXT2FS_ATTR((unused)), 627 void *priv_data) 628{ 629 struct xlate *xl = (struct xlate *) priv_data; 630 631 return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); 632} 633 634errcode_t ext2fs_block_iterate(ext2_filsys fs, 635 ext2_ino_t ino, 636 int flags, 637 char *block_buf, 638 int (*func)(ext2_filsys fs, 639 blk_t *blocknr, 640 int blockcnt, 641 void *priv_data), 642 void *priv_data) 643{ 644 struct xlate xl; 645 646 xl.real_private = priv_data; 647 xl.func = func; 648 649 return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, 650 block_buf, xlate_func, &xl); 651} 652 653