• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <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 #define check_for_ro_violation_return(ctx, ret)				\
40 	do {								\
41 		if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) &&		\
42 		    ((ret) & BLOCK_CHANGED)) {				\
43 			(ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE;	\
44 			ret |= BLOCK_ABORT | BLOCK_ERROR;		\
45 			return ret;					\
46 		}							\
47 	} while (0)
48 
49 #define check_for_ro_violation_goto(ctx, ret, label)			\
50 	do {								\
51 		if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) &&		\
52 		    ((ret) & BLOCK_CHANGED)) {				\
53 			(ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE;	\
54 			ret |= BLOCK_ABORT | BLOCK_ERROR;		\
55 			goto label;					\
56 		}							\
57 	} while (0)
58 
block_iterate_ind(blk_t * ind_block,blk_t ref_block,int ref_offset,struct block_context * ctx)59 static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
60 			     int ref_offset, struct block_context *ctx)
61 {
62 	int	ret = 0, changed = 0;
63 	int	i, flags, limit, offset;
64 	blk_t	*block_nr;
65 
66 	limit = ctx->fs->blocksize >> 2;
67 	if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
68 	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
69 		ret = (*ctx->func)(ctx->fs, ind_block,
70 				   BLOCK_COUNT_IND, ref_block,
71 				   ref_offset, ctx->priv_data);
72 	check_for_ro_violation_return(ctx, ret);
73 	if (!*ind_block || (ret & BLOCK_ABORT)) {
74 		ctx->bcount += limit;
75 		return ret;
76 	}
77 	if (*ind_block >= ctx->fs->super->s_blocks_count ||
78 	    *ind_block < ctx->fs->super->s_first_data_block) {
79 		ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
80 		ret |= BLOCK_ERROR;
81 		return ret;
82 	}
83 	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block,
84 					     ctx->ind_buf);
85 	if (ctx->errcode) {
86 		ret |= BLOCK_ERROR;
87 		return ret;
88 	}
89 
90 	block_nr = (blk_t *) ctx->ind_buf;
91 	offset = 0;
92 	if (ctx->flags & BLOCK_FLAG_APPEND) {
93 		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
94 			flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
95 					     *ind_block, offset,
96 					     ctx->priv_data);
97 			changed	|= flags;
98 			if (flags & BLOCK_ABORT) {
99 				ret |= BLOCK_ABORT;
100 				break;
101 			}
102 			offset += sizeof(blk_t);
103 		}
104 	} else {
105 		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
106 			if (*block_nr == 0)
107 				goto skip_sparse;
108 			flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
109 					     *ind_block, offset,
110 					     ctx->priv_data);
111 			changed	|= flags;
112 			if (flags & BLOCK_ABORT) {
113 				ret |= BLOCK_ABORT;
114 				break;
115 			}
116 		skip_sparse:
117 			offset += sizeof(blk_t);
118 		}
119 	}
120 	check_for_ro_violation_return(ctx, changed);
121 	if (changed & BLOCK_CHANGED) {
122 		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
123 						      ctx->ind_buf);
124 		if (ctx->errcode)
125 			ret |= BLOCK_ERROR | BLOCK_ABORT;
126 	}
127 	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
128 	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
129 	    !(ret & BLOCK_ABORT))
130 		ret |= (*ctx->func)(ctx->fs, ind_block,
131 				    BLOCK_COUNT_IND, ref_block,
132 				    ref_offset, ctx->priv_data);
133 	check_for_ro_violation_return(ctx, ret);
134 	return ret;
135 }
136 
block_iterate_dind(blk_t * dind_block,blk_t ref_block,int ref_offset,struct block_context * ctx)137 static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
138 			      int ref_offset, struct block_context *ctx)
139 {
140 	int	ret = 0, changed = 0;
141 	int	i, flags, limit, offset;
142 	blk_t	*block_nr;
143 
144 	limit = ctx->fs->blocksize >> 2;
145 	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
146 			    BLOCK_FLAG_DATA_ONLY)))
147 		ret = (*ctx->func)(ctx->fs, dind_block,
148 				   BLOCK_COUNT_DIND, ref_block,
149 				   ref_offset, ctx->priv_data);
150 	check_for_ro_violation_return(ctx, ret);
151 	if (!*dind_block || (ret & BLOCK_ABORT)) {
152 		ctx->bcount += limit*limit;
153 		return ret;
154 	}
155 	if (*dind_block >= ctx->fs->super->s_blocks_count ||
156 	    *dind_block < ctx->fs->super->s_first_data_block) {
157 		ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
158 		ret |= BLOCK_ERROR;
159 		return ret;
160 	}
161 	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block,
162 					     ctx->dind_buf);
163 	if (ctx->errcode) {
164 		ret |= BLOCK_ERROR;
165 		return ret;
166 	}
167 
168 	block_nr = (blk_t *) ctx->dind_buf;
169 	offset = 0;
170 	if (ctx->flags & BLOCK_FLAG_APPEND) {
171 		for (i = 0; i < limit; i++, block_nr++) {
172 			flags = block_iterate_ind(block_nr,
173 						  *dind_block, offset,
174 						  ctx);
175 			changed |= flags;
176 			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
177 				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
178 				break;
179 			}
180 			offset += sizeof(blk_t);
181 		}
182 	} else {
183 		for (i = 0; i < limit; i++, block_nr++) {
184 			if (*block_nr == 0) {
185 				ctx->bcount += limit;
186 				continue;
187 			}
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 	}
199 	check_for_ro_violation_return(ctx, changed);
200 	if (changed & BLOCK_CHANGED) {
201 		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
202 						      ctx->dind_buf);
203 		if (ctx->errcode)
204 			ret |= BLOCK_ERROR | BLOCK_ABORT;
205 	}
206 	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
207 	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
208 	    !(ret & BLOCK_ABORT))
209 		ret |= (*ctx->func)(ctx->fs, dind_block,
210 				    BLOCK_COUNT_DIND, ref_block,
211 				    ref_offset, ctx->priv_data);
212 	check_for_ro_violation_return(ctx, ret);
213 	return ret;
214 }
215 
block_iterate_tind(blk_t * tind_block,blk_t ref_block,int ref_offset,struct block_context * ctx)216 static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
217 			      int ref_offset, struct block_context *ctx)
218 {
219 	int	ret = 0, changed = 0;
220 	int	i, flags, limit, offset;
221 	blk_t	*block_nr;
222 
223 	limit = ctx->fs->blocksize >> 2;
224 	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
225 			    BLOCK_FLAG_DATA_ONLY)))
226 		ret = (*ctx->func)(ctx->fs, tind_block,
227 				   BLOCK_COUNT_TIND, ref_block,
228 				   ref_offset, ctx->priv_data);
229 	check_for_ro_violation_return(ctx, ret);
230 	if (!*tind_block || (ret & BLOCK_ABORT)) {
231 		ctx->bcount += limit*limit*limit;
232 		return ret;
233 	}
234 	if (*tind_block >= ctx->fs->super->s_blocks_count ||
235 	    *tind_block < ctx->fs->super->s_first_data_block) {
236 		ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
237 		ret |= BLOCK_ERROR;
238 		return ret;
239 	}
240 	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block,
241 					     ctx->tind_buf);
242 	if (ctx->errcode) {
243 		ret |= BLOCK_ERROR;
244 		return ret;
245 	}
246 
247 	block_nr = (blk_t *) ctx->tind_buf;
248 	offset = 0;
249 	if (ctx->flags & BLOCK_FLAG_APPEND) {
250 		for (i = 0; i < limit; i++, block_nr++) {
251 			flags = block_iterate_dind(block_nr,
252 						   *tind_block,
253 						   offset, ctx);
254 			changed |= flags;
255 			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
256 				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
257 				break;
258 			}
259 			offset += sizeof(blk_t);
260 		}
261 	} else {
262 		for (i = 0; i < limit; i++, block_nr++) {
263 			if (*block_nr == 0) {
264 				ctx->bcount += limit*limit;
265 				continue;
266 			}
267 			flags = block_iterate_dind(block_nr,
268 						   *tind_block,
269 						   offset, ctx);
270 			changed |= flags;
271 			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
272 				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
273 				break;
274 			}
275 			offset += sizeof(blk_t);
276 		}
277 	}
278 	check_for_ro_violation_return(ctx, changed);
279 	if (changed & BLOCK_CHANGED) {
280 		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
281 						      ctx->tind_buf);
282 		if (ctx->errcode)
283 			ret |= BLOCK_ERROR | BLOCK_ABORT;
284 	}
285 	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
286 	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
287 	    !(ret & BLOCK_ABORT))
288 		ret |= (*ctx->func)(ctx->fs, tind_block,
289 				    BLOCK_COUNT_TIND, ref_block,
290 				    ref_offset, ctx->priv_data);
291 	check_for_ro_violation_return(ctx, ret);
292 	return ret;
293 }
294 
ext2fs_block_iterate2(ext2_filsys fs,ext2_ino_t ino,int flags,char * block_buf,int (* func)(ext2_filsys fs,blk_t * blocknr,e2_blkcnt_t blockcnt,blk_t ref_blk,int ref_offset,void * priv_data),void * priv_data)295 errcode_t ext2fs_block_iterate2(ext2_filsys fs,
296 				ext2_ino_t ino,
297 				int	flags,
298 				char *block_buf,
299 				int (*func)(ext2_filsys fs,
300 					    blk_t	*blocknr,
301 					    e2_blkcnt_t	blockcnt,
302 					    blk_t	ref_blk,
303 					    int		ref_offset,
304 					    void	*priv_data),
305 				void *priv_data)
306 {
307 	int	i;
308 	int	r, ret = 0;
309 	struct ext2_inode inode;
310 	errcode_t	retval;
311 	struct block_context ctx;
312 	int	limit;
313 
314 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
315 
316 	ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
317 	if (ctx.errcode)
318 		return ctx.errcode;
319 
320 	/*
321 	 * Check to see if we need to limit large files
322 	 */
323 	if (flags & BLOCK_FLAG_NO_LARGE) {
324 		if (!LINUX_S_ISDIR(inode.i_mode) &&
325 		    (inode.i_size_high != 0))
326 			return EXT2_ET_FILE_TOO_BIG;
327 	}
328 
329 	limit = fs->blocksize >> 2;
330 
331 	ctx.fs = fs;
332 	ctx.func = func;
333 	ctx.priv_data = priv_data;
334 	ctx.flags = flags;
335 	ctx.bcount = 0;
336 	if (block_buf) {
337 		ctx.ind_buf = block_buf;
338 	} else {
339 		retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf);
340 		if (retval)
341 			return retval;
342 	}
343 	ctx.dind_buf = ctx.ind_buf + fs->blocksize;
344 	ctx.tind_buf = ctx.dind_buf + fs->blocksize;
345 
346 	/*
347 	 * Iterate over the HURD translator block (if present)
348 	 */
349 	if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
350 	    !(flags & BLOCK_FLAG_DATA_ONLY)) {
351 		if (inode.osd1.hurd1.h_i_translator) {
352 			ret |= (*ctx.func)(fs,
353 					   &inode.osd1.hurd1.h_i_translator,
354 					   BLOCK_COUNT_TRANSLATOR,
355 					   0, 0, priv_data);
356 			if (ret & BLOCK_ABORT)
357 				goto abort_exit;
358 			check_for_ro_violation_goto(&ctx, ret, abort_exit);
359 		}
360 	}
361 
362 	if (inode.i_flags & EXT4_EXTENTS_FL) {
363 		ext2_extent_handle_t	handle;
364 		struct ext2fs_extent	extent;
365 		e2_blkcnt_t		blockcnt = 0;
366 		blk_t			blk, new_blk;
367 		int			op = EXT2_EXTENT_ROOT;
368 		int			uninit;
369 		unsigned int		j;
370 
371 		ctx.errcode = ext2fs_extent_open2(fs, ino, &inode, &handle);
372 		if (ctx.errcode)
373 			goto abort_exit;
374 
375 		while (1) {
376 			ctx.errcode = ext2fs_extent_get(handle, op, &extent);
377 			if (ctx.errcode) {
378 				if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT)
379 					break;
380 				ctx.errcode = 0;
381 				if (!(flags & BLOCK_FLAG_APPEND))
382 					break;
383 			next_block_set:
384 				blk = 0;
385 				r = (*ctx.func)(fs, &blk, blockcnt,
386 						0, 0, priv_data);
387 				ret |= r;
388 				check_for_ro_violation_goto(&ctx, ret,
389 							    extent_errout);
390 				if (r & BLOCK_CHANGED) {
391 					ctx.errcode =
392 						ext2fs_extent_set_bmap(handle,
393 						       (blk64_t) blockcnt++,
394 						       (blk64_t) blk, 0);
395 					if (ctx.errcode || (ret & BLOCK_ABORT))
396 						break;
397 					if (blk)
398 						goto next_block_set;
399 				}
400 				break;
401 			}
402 
403 			op = EXT2_EXTENT_NEXT;
404 			blk = extent.e_pblk;
405 			if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) {
406 				if (ctx.flags & BLOCK_FLAG_DATA_ONLY)
407 					continue;
408 				if ((!(extent.e_flags &
409 				       EXT2_EXTENT_FLAGS_SECOND_VISIT) &&
410 				     !(ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE)) ||
411 				    ((extent.e_flags &
412 				      EXT2_EXTENT_FLAGS_SECOND_VISIT) &&
413 				     (ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE))) {
414 					ret |= (*ctx.func)(fs, &blk,
415 							   -1, 0, 0, priv_data);
416 					if (ret & BLOCK_CHANGED) {
417 						extent.e_pblk = blk;
418 						ctx.errcode =
419 				ext2fs_extent_replace(handle, 0, &extent);
420 						if (ctx.errcode)
421 							break;
422 					}
423 				}
424 				continue;
425 			}
426 			uninit = 0;
427 			if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
428 				uninit = EXT2_EXTENT_SET_BMAP_UNINIT;
429 			for (blockcnt = extent.e_lblk, j = 0;
430 			     j < extent.e_len;
431 			     blk++, blockcnt++, j++) {
432 				new_blk = blk;
433 				r = (*ctx.func)(fs, &new_blk, blockcnt,
434 						0, 0, priv_data);
435 				ret |= r;
436 				check_for_ro_violation_goto(&ctx, ret,
437 							    extent_errout);
438 				if (r & BLOCK_CHANGED) {
439 					ctx.errcode =
440 						ext2fs_extent_set_bmap(handle,
441 						       (blk64_t) blockcnt,
442 						       (blk64_t) new_blk,
443 						       uninit);
444 					if (ctx.errcode)
445 						goto extent_errout;
446 				}
447 				if (ret & BLOCK_ABORT)
448 					break;
449 			}
450 		}
451 
452 	extent_errout:
453 		ext2fs_extent_free(handle);
454 		ret |= BLOCK_ERROR | BLOCK_ABORT;
455 		goto errout;
456 	}
457 
458 	/*
459 	 * Iterate over normal data blocks
460 	 */
461 	for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
462 		if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) {
463 			ret |= (*ctx.func)(fs, &inode.i_block[i],
464 					    ctx.bcount, 0, i, priv_data);
465 			if (ret & BLOCK_ABORT)
466 				goto abort_exit;
467 		}
468 	}
469 	check_for_ro_violation_goto(&ctx, ret, abort_exit);
470 	if (inode.i_block[EXT2_IND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) {
471 		ret |= block_iterate_ind(&inode.i_block[EXT2_IND_BLOCK],
472 					 0, EXT2_IND_BLOCK, &ctx);
473 		if (ret & BLOCK_ABORT)
474 			goto abort_exit;
475 	} else
476 		ctx.bcount += limit;
477 	if (inode.i_block[EXT2_DIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) {
478 		ret |= block_iterate_dind(&inode.i_block[EXT2_DIND_BLOCK],
479 					  0, EXT2_DIND_BLOCK, &ctx);
480 		if (ret & BLOCK_ABORT)
481 			goto abort_exit;
482 	} else
483 		ctx.bcount += limit * limit;
484 	if (inode.i_block[EXT2_TIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) {
485 		ret |= block_iterate_tind(&inode.i_block[EXT2_TIND_BLOCK],
486 					  0, EXT2_TIND_BLOCK, &ctx);
487 		if (ret & BLOCK_ABORT)
488 			goto abort_exit;
489 	}
490 
491 abort_exit:
492 	if (ret & BLOCK_CHANGED) {
493 		retval = ext2fs_write_inode(fs, ino, &inode);
494 		if (retval) {
495 			ret |= BLOCK_ERROR;
496 			ctx.errcode = retval;
497 		}
498 	}
499 errout:
500 	if (!block_buf)
501 		ext2fs_free_mem(&ctx.ind_buf);
502 
503 	return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
504 }
505 
506 /*
507  * Emulate the old ext2fs_block_iterate function!
508  */
509 
510 struct xlate {
511 	int (*func)(ext2_filsys	fs,
512 		    blk_t	*blocknr,
513 		    int		bcount,
514 		    void	*priv_data);
515 	void *real_private;
516 };
517 
518 #ifdef __TURBOC__
519  #pragma argsused
520 #endif
xlate_func(ext2_filsys fs,blk_t * blocknr,e2_blkcnt_t blockcnt,blk_t ref_block EXT2FS_ATTR ((unused)),int ref_offset EXT2FS_ATTR ((unused)),void * priv_data)521 static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
522 		      blk_t ref_block EXT2FS_ATTR((unused)),
523 		      int ref_offset EXT2FS_ATTR((unused)),
524 		      void *priv_data)
525 {
526 	struct xlate *xl = (struct xlate *) priv_data;
527 
528 	return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
529 }
530 
ext2fs_block_iterate(ext2_filsys fs,ext2_ino_t ino,int flags,char * block_buf,int (* func)(ext2_filsys fs,blk_t * blocknr,int blockcnt,void * priv_data),void * priv_data)531 errcode_t ext2fs_block_iterate(ext2_filsys fs,
532 			       ext2_ino_t ino,
533 			       int	flags,
534 			       char *block_buf,
535 			       int (*func)(ext2_filsys fs,
536 					   blk_t	*blocknr,
537 					   int	blockcnt,
538 					   void	*priv_data),
539 			       void *priv_data)
540 {
541 	struct xlate xl;
542 
543 	xl.real_private = priv_data;
544 	xl.func = func;
545 
546 	return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
547 				     block_buf, xlate_func, &xl);
548 }
549 
550