block.c revision 94ded6c16bdcc77b43caaa151bd7afa55c5d3cf1
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
21struct 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
59static 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
137static 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
216static 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
295errcode_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
491abort_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	}
499errout:
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
510struct 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
521static 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
531errcode_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