1/* vi: set sw=4 ts=4: */ 2/* 3 * block.c --- iterate over all blocks in an inode 4 * 5 * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. 6 * 7 * %Begin-Header% 8 * This file may be redistributed under the terms of the GNU Public 9 * License. 10 * %End-Header% 11 */ 12 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 blk_t *blocknr, 26 e2_blkcnt_t bcount, 27 blk_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 40static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, 41 int ref_offset, struct block_context *ctx) 42{ 43 int ret = 0, changed = 0; 44 int i, flags, limit, offset; 45 blk_t *block_nr; 46 47 limit = ctx->fs->blocksize >> 2; 48 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 49 !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) 50 ret = (*ctx->func)(ctx->fs, ind_block, 51 BLOCK_COUNT_IND, ref_block, 52 ref_offset, ctx->priv_data); 53 if (!*ind_block || (ret & BLOCK_ABORT)) { 54 ctx->bcount += limit; 55 return ret; 56 } 57 if (*ind_block >= ctx->fs->super->s_blocks_count || 58 *ind_block < ctx->fs->super->s_first_data_block) { 59 ctx->errcode = EXT2_ET_BAD_IND_BLOCK; 60 ret |= BLOCK_ERROR; 61 return ret; 62 } 63 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, 64 ctx->ind_buf); 65 if (ctx->errcode) { 66 ret |= BLOCK_ERROR; 67 return ret; 68 } 69 70 block_nr = (blk_t *) ctx->ind_buf; 71 offset = 0; 72 if (ctx->flags & BLOCK_FLAG_APPEND) { 73 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { 74 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, 75 *ind_block, offset, 76 ctx->priv_data); 77 changed |= flags; 78 if (flags & BLOCK_ABORT) { 79 ret |= BLOCK_ABORT; 80 break; 81 } 82 offset += sizeof(blk_t); 83 } 84 } else { 85 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { 86 if (*block_nr == 0) 87 continue; 88 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, 89 *ind_block, offset, 90 ctx->priv_data); 91 changed |= flags; 92 if (flags & BLOCK_ABORT) { 93 ret |= BLOCK_ABORT; 94 break; 95 } 96 offset += sizeof(blk_t); 97 } 98 } 99 if (changed & BLOCK_CHANGED) { 100 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, 101 ctx->ind_buf); 102 if (ctx->errcode) 103 ret |= BLOCK_ERROR | BLOCK_ABORT; 104 } 105 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 106 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && 107 !(ret & BLOCK_ABORT)) 108 ret |= (*ctx->func)(ctx->fs, ind_block, 109 BLOCK_COUNT_IND, ref_block, 110 ref_offset, ctx->priv_data); 111 return ret; 112} 113 114static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, 115 int ref_offset, struct block_context *ctx) 116{ 117 int ret = 0, changed = 0; 118 int i, flags, limit, offset; 119 blk_t *block_nr; 120 121 limit = ctx->fs->blocksize >> 2; 122 if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | 123 BLOCK_FLAG_DATA_ONLY))) 124 ret = (*ctx->func)(ctx->fs, dind_block, 125 BLOCK_COUNT_DIND, ref_block, 126 ref_offset, ctx->priv_data); 127 if (!*dind_block || (ret & BLOCK_ABORT)) { 128 ctx->bcount += limit*limit; 129 return ret; 130 } 131 if (*dind_block >= ctx->fs->super->s_blocks_count || 132 *dind_block < ctx->fs->super->s_first_data_block) { 133 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; 134 ret |= BLOCK_ERROR; 135 return ret; 136 } 137 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, 138 ctx->dind_buf); 139 if (ctx->errcode) { 140 ret |= BLOCK_ERROR; 141 return ret; 142 } 143 144 block_nr = (blk_t *) ctx->dind_buf; 145 offset = 0; 146 if (ctx->flags & BLOCK_FLAG_APPEND) { 147 for (i = 0; i < limit; i++, block_nr++) { 148 flags = block_iterate_ind(block_nr, 149 *dind_block, offset, 150 ctx); 151 changed |= flags; 152 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 153 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 154 break; 155 } 156 offset += sizeof(blk_t); 157 } 158 } else { 159 for (i = 0; i < limit; i++, block_nr++) { 160 if (*block_nr == 0) { 161 ctx->bcount += limit; 162 continue; 163 } 164 flags = block_iterate_ind(block_nr, 165 *dind_block, offset, 166 ctx); 167 changed |= flags; 168 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 169 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 170 break; 171 } 172 offset += sizeof(blk_t); 173 } 174 } 175 if (changed & BLOCK_CHANGED) { 176 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, 177 ctx->dind_buf); 178 if (ctx->errcode) 179 ret |= BLOCK_ERROR | BLOCK_ABORT; 180 } 181 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 182 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && 183 !(ret & BLOCK_ABORT)) 184 ret |= (*ctx->func)(ctx->fs, dind_block, 185 BLOCK_COUNT_DIND, ref_block, 186 ref_offset, ctx->priv_data); 187 return ret; 188} 189 190static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, 191 int ref_offset, struct block_context *ctx) 192{ 193 int ret = 0, changed = 0; 194 int i, flags, limit, offset; 195 blk_t *block_nr; 196 197 limit = ctx->fs->blocksize >> 2; 198 if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | 199 BLOCK_FLAG_DATA_ONLY))) 200 ret = (*ctx->func)(ctx->fs, tind_block, 201 BLOCK_COUNT_TIND, ref_block, 202 ref_offset, ctx->priv_data); 203 if (!*tind_block || (ret & BLOCK_ABORT)) { 204 ctx->bcount += limit*limit*limit; 205 return ret; 206 } 207 if (*tind_block >= ctx->fs->super->s_blocks_count || 208 *tind_block < ctx->fs->super->s_first_data_block) { 209 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; 210 ret |= BLOCK_ERROR; 211 return ret; 212 } 213 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, 214 ctx->tind_buf); 215 if (ctx->errcode) { 216 ret |= BLOCK_ERROR; 217 return ret; 218 } 219 220 block_nr = (blk_t *) ctx->tind_buf; 221 offset = 0; 222 if (ctx->flags & BLOCK_FLAG_APPEND) { 223 for (i = 0; i < limit; i++, block_nr++) { 224 flags = block_iterate_dind(block_nr, 225 *tind_block, 226 offset, ctx); 227 changed |= flags; 228 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 229 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 230 break; 231 } 232 offset += sizeof(blk_t); 233 } 234 } else { 235 for (i = 0; i < limit; i++, block_nr++) { 236 if (*block_nr == 0) { 237 ctx->bcount += limit*limit; 238 continue; 239 } 240 flags = block_iterate_dind(block_nr, 241 *tind_block, 242 offset, ctx); 243 changed |= flags; 244 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { 245 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); 246 break; 247 } 248 offset += sizeof(blk_t); 249 } 250 } 251 if (changed & BLOCK_CHANGED) { 252 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, 253 ctx->tind_buf); 254 if (ctx->errcode) 255 ret |= BLOCK_ERROR | BLOCK_ABORT; 256 } 257 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && 258 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && 259 !(ret & BLOCK_ABORT)) 260 ret |= (*ctx->func)(ctx->fs, tind_block, 261 BLOCK_COUNT_TIND, ref_block, 262 ref_offset, ctx->priv_data); 263 264 return ret; 265} 266 267errcode_t ext2fs_block_iterate2(ext2_filsys fs, 268 ext2_ino_t ino, 269 int flags, 270 char *block_buf, 271 int (*func)(ext2_filsys fs, 272 blk_t *blocknr, 273 e2_blkcnt_t blockcnt, 274 blk_t ref_blk, 275 int ref_offset, 276 void *priv_data), 277 void *priv_data) 278{ 279 int i; 280 int got_inode = 0; 281 int ret = 0; 282 blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */ 283 struct ext2_inode inode; 284 errcode_t retval; 285 struct block_context ctx; 286 int limit; 287 288 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 289 290 /* 291 * Check to see if we need to limit large files 292 */ 293 if (flags & BLOCK_FLAG_NO_LARGE) { 294 ctx.errcode = ext2fs_read_inode(fs, ino, &inode); 295 if (ctx.errcode) 296 return ctx.errcode; 297 got_inode = 1; 298 if (!LINUX_S_ISDIR(inode.i_mode) && 299 (inode.i_size_high != 0)) 300 return EXT2_ET_FILE_TOO_BIG; 301 } 302 303 retval = ext2fs_get_blocks(fs, ino, blocks); 304 if (retval) 305 return retval; 306 307 limit = fs->blocksize >> 2; 308 309 ctx.fs = fs; 310 ctx.func = func; 311 ctx.priv_data = priv_data; 312 ctx.flags = flags; 313 ctx.bcount = 0; 314 if (block_buf) { 315 ctx.ind_buf = block_buf; 316 } else { 317 retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf); 318 if (retval) 319 return retval; 320 } 321 ctx.dind_buf = ctx.ind_buf + fs->blocksize; 322 ctx.tind_buf = ctx.dind_buf + fs->blocksize; 323 324 /* 325 * Iterate over the HURD translator block (if present) 326 */ 327 if ((fs->super->s_creator_os == EXT2_OS_HURD) && 328 !(flags & BLOCK_FLAG_DATA_ONLY)) { 329 ctx.errcode = ext2fs_read_inode(fs, ino, &inode); 330 if (ctx.errcode) 331 goto abort_exit; 332 got_inode = 1; 333 if (inode.osd1.hurd1.h_i_translator) { 334 ret |= (*ctx.func)(fs, 335 &inode.osd1.hurd1.h_i_translator, 336 BLOCK_COUNT_TRANSLATOR, 337 0, 0, priv_data); 338 if (ret & BLOCK_ABORT) 339 goto abort_exit; 340 } 341 } 342 343 /* 344 * Iterate over normal data blocks 345 */ 346 for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) { 347 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) { 348 ret |= (*ctx.func)(fs, &blocks[i], 349 ctx.bcount, 0, i, priv_data); 350 if (ret & BLOCK_ABORT) 351 goto abort_exit; 352 } 353 } 354 if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { 355 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, 356 0, EXT2_IND_BLOCK, &ctx); 357 if (ret & BLOCK_ABORT) 358 goto abort_exit; 359 } else 360 ctx.bcount += limit; 361 if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { 362 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, 363 0, EXT2_DIND_BLOCK, &ctx); 364 if (ret & BLOCK_ABORT) 365 goto abort_exit; 366 } else 367 ctx.bcount += limit * limit; 368 if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { 369 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, 370 0, EXT2_TIND_BLOCK, &ctx); 371 if (ret & BLOCK_ABORT) 372 goto abort_exit; 373 } 374 375abort_exit: 376 if (ret & BLOCK_CHANGED) { 377 if (!got_inode) { 378 retval = ext2fs_read_inode(fs, ino, &inode); 379 if (retval) 380 return retval; 381 } 382 for (i=0; i < EXT2_N_BLOCKS; i++) 383 inode.i_block[i] = blocks[i]; 384 retval = ext2fs_write_inode(fs, ino, &inode); 385 if (retval) 386 return retval; 387 } 388 389 if (!block_buf) 390 ext2fs_free_mem(&ctx.ind_buf); 391 392 return (ret & BLOCK_ERROR) ? ctx.errcode : 0; 393} 394 395/* 396 * Emulate the old ext2fs_block_iterate function! 397 */ 398 399struct xlate { 400 int (*func)(ext2_filsys fs, 401 blk_t *blocknr, 402 int bcount, 403 void *priv_data); 404 void *real_private; 405}; 406 407#ifdef __TURBOC__ 408# pragma argsused 409#endif 410static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, 411 blk_t ref_block EXT2FS_ATTR((unused)), 412 int ref_offset EXT2FS_ATTR((unused)), 413 void *priv_data) 414{ 415 struct xlate *xl = (struct xlate *) priv_data; 416 417 return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); 418} 419 420errcode_t ext2fs_block_iterate(ext2_filsys fs, 421 ext2_ino_t ino, 422 int flags, 423 char *block_buf, 424 int (*func)(ext2_filsys fs, 425 blk_t *blocknr, 426 int blockcnt, 427 void *priv_data), 428 void *priv_data) 429{ 430 struct xlate xl; 431 432 xl.real_private = priv_data; 433 xl.func = func; 434 435 return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, 436 block_buf, xlate_func, &xl); 437} 438