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