block.c revision 1ad54a940c499a66241f624882f1ffa03ce56d90
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
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
39static 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	if ((ctx->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
63	    (ctx->fs->io != ctx->fs->image_io)) {
64		ctx->errcode = 0;
65		memset(ctx->ind_buf, 0, ctx->fs->blocksize);
66	} else
67		ctx->errcode = io_channel_read_blk(ctx->fs->io, *ind_block,
68						   1, ctx->ind_buf);
69	if (ctx->errcode) {
70		ret |= BLOCK_ERROR;
71		return ret;
72	}
73#ifdef EXT2FS_ENABLE_SWAPFS
74	if (ctx->fs->flags & (EXT2_FLAG_SWAP_BYTES |
75			      EXT2_FLAG_SWAP_BYTES_READ)) {
76		block_nr = (blk_t *) ctx->ind_buf;
77		for (i = 0; i < limit; i++, block_nr++)
78			*block_nr = ext2fs_swab32(*block_nr);
79	}
80#endif
81	block_nr = (blk_t *) ctx->ind_buf;
82	offset = 0;
83	if (ctx->flags & BLOCK_FLAG_APPEND) {
84		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
85			flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
86					     *ind_block, offset,
87					     ctx->priv_data);
88			changed	|= flags;
89			if (flags & BLOCK_ABORT) {
90				ret |= BLOCK_ABORT;
91				break;
92			}
93			offset += sizeof(blk_t);
94		}
95	} else {
96		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
97			if (*block_nr == 0)
98				continue;
99			flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
100					     *ind_block, offset,
101					     ctx->priv_data);
102			changed	|= flags;
103			if (flags & BLOCK_ABORT) {
104				ret |= BLOCK_ABORT;
105				break;
106			}
107			offset += sizeof(blk_t);
108		}
109	}
110	if (!(ctx->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
111	    (changed & BLOCK_CHANGED)) {
112#ifdef EXT2FS_ENABLE_SWAPFS
113		if (ctx->fs->flags & (EXT2_FLAG_SWAP_BYTES |
114				      EXT2_FLAG_SWAP_BYTES_WRITE)) {
115			block_nr = (blk_t *) ctx->ind_buf;
116			for (i = 0; i < limit; i++, block_nr++)
117				*block_nr = ext2fs_swab32(*block_nr);
118		}
119#endif
120		ctx->errcode = io_channel_write_blk(ctx->fs->io, *ind_block,
121						    1, ctx->ind_buf);
122		if (ctx->errcode)
123			ret |= BLOCK_ERROR | BLOCK_ABORT;
124	}
125	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
126	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
127	    !(ret & BLOCK_ABORT))
128		ret |= (*ctx->func)(ctx->fs, ind_block,
129				    BLOCK_COUNT_IND, ref_block,
130				    ref_offset, ctx->priv_data);
131	return ret;
132}
133
134static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
135			      int ref_offset, struct block_context *ctx)
136{
137	int	ret = 0, changed = 0;
138	int	i, flags, limit, offset;
139	blk_t	*block_nr;
140
141	limit = ctx->fs->blocksize >> 2;
142	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
143			    BLOCK_FLAG_DATA_ONLY)))
144		ret = (*ctx->func)(ctx->fs, dind_block,
145				   BLOCK_COUNT_DIND, ref_block,
146				   ref_offset, ctx->priv_data);
147	if (!*dind_block || (ret & BLOCK_ABORT)) {
148		ctx->bcount += limit*limit;
149		return ret;
150	}
151	if (*dind_block >= ctx->fs->super->s_blocks_count ||
152	    *dind_block < ctx->fs->super->s_first_data_block) {
153		ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
154		ret |= BLOCK_ERROR;
155		return ret;
156	}
157	if ((ctx->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
158	    (ctx->fs->io != ctx->fs->image_io)) {
159		ctx->errcode = 0;
160		memset(ctx->dind_buf, 0, ctx->fs->blocksize);
161	} else
162		ctx->errcode = io_channel_read_blk(ctx->fs->io, *dind_block,
163						   1, ctx->dind_buf);
164	if (ctx->errcode) {
165		ret |= BLOCK_ERROR;
166		return ret;
167	}
168#ifdef EXT2FS_ENABLE_SWAPFS
169	if (ctx->fs->flags & (EXT2_FLAG_SWAP_BYTES |
170			      EXT2_FLAG_SWAP_BYTES_READ)) {
171		block_nr = (blk_t *) ctx->dind_buf;
172		for (i = 0; i < limit; i++, block_nr++)
173			*block_nr = ext2fs_swab32(*block_nr);
174	}
175#endif
176	block_nr = (blk_t *) ctx->dind_buf;
177	offset = 0;
178	if (ctx->flags & BLOCK_FLAG_APPEND) {
179		for (i = 0; i < limit; i++, block_nr++) {
180			flags = block_iterate_ind(block_nr,
181						  *dind_block, offset,
182						  ctx);
183			changed |= flags;
184			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
185				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
186				break;
187			}
188			offset += sizeof(blk_t);
189		}
190	} else {
191		for (i = 0; i < limit; i++, block_nr++) {
192			if (*block_nr == 0) {
193				ctx->bcount += limit;
194				continue;
195			}
196			flags = block_iterate_ind(block_nr,
197						  *dind_block, offset,
198						  ctx);
199			changed |= flags;
200			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
201				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
202				break;
203			}
204			offset += sizeof(blk_t);
205		}
206	}
207	if (!(ctx->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
208	    (changed & BLOCK_CHANGED)) {
209#ifdef EXT2FS_ENABLE_SWAPFS
210		if (ctx->fs->flags & (EXT2_FLAG_SWAP_BYTES |
211				      EXT2_FLAG_SWAP_BYTES_WRITE)) {
212			block_nr = (blk_t *) ctx->dind_buf;
213			for (i = 0; i < limit; i++, block_nr++)
214				*block_nr = ext2fs_swab32(*block_nr);
215		}
216#endif
217		ctx->errcode = io_channel_write_blk(ctx->fs->io, *dind_block,
218						    1, 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		ret |= (*ctx->func)(ctx->fs, dind_block,
226				    BLOCK_COUNT_DIND, ref_block,
227				    ref_offset, ctx->priv_data);
228	return ret;
229}
230
231static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
232			      int ref_offset, struct block_context *ctx)
233{
234	int	ret = 0, changed = 0;
235	int	i, flags, limit, offset;
236	blk_t	*block_nr;
237
238	limit = ctx->fs->blocksize >> 2;
239	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
240			    BLOCK_FLAG_DATA_ONLY)))
241		ret = (*ctx->func)(ctx->fs, tind_block,
242				   BLOCK_COUNT_TIND, ref_block,
243				   ref_offset, ctx->priv_data);
244	if (!*tind_block || (ret & BLOCK_ABORT)) {
245		ctx->bcount += limit*limit*limit;
246		return ret;
247	}
248	if (*tind_block >= ctx->fs->super->s_blocks_count ||
249	    *tind_block < ctx->fs->super->s_first_data_block) {
250		ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
251		ret |= BLOCK_ERROR;
252		return ret;
253	}
254	if ((ctx->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
255	    (ctx->fs->io != ctx->fs->image_io)) {
256		ctx->errcode = 0;
257		memset(ctx->tind_buf, 0, ctx->fs->blocksize);
258	} else
259		ctx->errcode = io_channel_read_blk(ctx->fs->io, *tind_block,
260						   1, ctx->tind_buf);
261	if (ctx->errcode) {
262		ret |= BLOCK_ERROR;
263		return ret;
264	}
265#ifdef EXT2FS_ENABLE_SWAPFS
266	if (ctx->fs->flags & (EXT2_FLAG_SWAP_BYTES |
267			      EXT2_FLAG_SWAP_BYTES_READ)) {
268		block_nr = (blk_t *) ctx->tind_buf;
269		for (i = 0; i < limit; i++, block_nr++)
270			*block_nr = ext2fs_swab32(*block_nr);
271	}
272#endif
273	block_nr = (blk_t *) ctx->tind_buf;
274	offset = 0;
275	if (ctx->flags & BLOCK_FLAG_APPEND) {
276		for (i = 0; i < limit; i++, block_nr++) {
277			flags = block_iterate_dind(block_nr,
278						   *tind_block,
279						   offset, ctx);
280			changed |= flags;
281			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
282				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
283				break;
284			}
285			offset += sizeof(blk_t);
286		}
287	} else {
288		for (i = 0; i < limit; i++, block_nr++) {
289			if (*block_nr == 0) {
290				ctx->bcount += limit*limit;
291				continue;
292			}
293			flags = block_iterate_dind(block_nr,
294						   *tind_block,
295						   offset, ctx);
296			changed |= flags;
297			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
298				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
299				break;
300			}
301			offset += sizeof(blk_t);
302		}
303	}
304	if (!(ctx->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
305	    (changed & BLOCK_CHANGED)) {
306#ifdef EXT2FS_ENABLE_SWAPFS
307		if (ctx->fs->flags & (EXT2_FLAG_SWAP_BYTES |
308				      EXT2_FLAG_SWAP_BYTES_WRITE)) {
309			block_nr = (blk_t *) ctx->tind_buf;
310			for (i = 0; i < limit; i++, block_nr++)
311				*block_nr = ext2fs_swab32(*block_nr);
312		}
313#endif
314		ctx->errcode = io_channel_write_blk(ctx->fs->io, *tind_block,
315						    1, ctx->tind_buf);
316		if (ctx->errcode)
317			ret |= BLOCK_ERROR | BLOCK_ABORT;
318	}
319	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
320	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
321	    !(ret & BLOCK_ABORT))
322		ret |= (*ctx->func)(ctx->fs, tind_block,
323				    BLOCK_COUNT_TIND, ref_block,
324				    ref_offset, ctx->priv_data);
325
326	return ret;
327}
328
329errcode_t ext2fs_block_iterate2(ext2_filsys fs,
330				ext2_ino_t ino,
331				int	flags,
332				char *block_buf,
333				int (*func)(ext2_filsys fs,
334					    blk_t	*blocknr,
335					    e2_blkcnt_t	blockcnt,
336					    blk_t	ref_blk,
337					    int		ref_offset,
338					    void	*priv_data),
339				void *priv_data)
340{
341	int	i;
342	int	got_inode = 0;
343	int	ret = 0;
344	blk_t	blocks[EXT2_N_BLOCKS];	/* directory data blocks */
345	struct ext2_inode inode;
346	errcode_t	retval;
347	struct block_context ctx;
348	int	limit;
349
350	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
351
352	/*
353	 * Check to see if we need to limit large files
354	 */
355	if (flags & BLOCK_FLAG_NO_LARGE) {
356		ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
357		if (ctx.errcode)
358			return ctx.errcode;
359		got_inode = 1;
360		if (!LINUX_S_ISDIR(inode.i_mode) &&
361		    (inode.i_size_high != 0))
362			return EXT2_ET_FILE_TOO_BIG;
363	}
364
365	retval = ext2fs_get_blocks(fs, ino, blocks);
366	if (retval)
367		return retval;
368
369	limit = fs->blocksize >> 2;
370
371	ctx.fs = fs;
372	ctx.func = func;
373	ctx.priv_data = priv_data;
374	ctx.flags = flags;
375	ctx.bcount = 0;
376	if (block_buf) {
377		ctx.ind_buf = block_buf;
378	} else {
379		retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
380		if (retval)
381			return retval;
382	}
383	ctx.dind_buf = ctx.ind_buf + fs->blocksize;
384	ctx.tind_buf = ctx.dind_buf + fs->blocksize;
385
386	/*
387	 * Iterate over the HURD translator block (if present)
388	 */
389	if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
390	    !(flags & BLOCK_FLAG_DATA_ONLY)) {
391		ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
392		if (ctx.errcode)
393			goto abort_exit;
394		got_inode = 1;
395		if (inode.osd1.hurd1.h_i_translator) {
396			ret |= (*ctx.func)(fs,
397					   &inode.osd1.hurd1.h_i_translator,
398					   BLOCK_COUNT_TRANSLATOR,
399					   0, 0, priv_data);
400			if (ret & BLOCK_ABORT)
401				goto abort_exit;
402		}
403	}
404
405	/*
406	 * Iterate over normal data blocks
407	 */
408	for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
409		if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
410			ret |= (*ctx.func)(fs, &blocks[i],
411					    ctx.bcount, 0, i, priv_data);
412			if (ret & BLOCK_ABORT)
413				goto abort_exit;
414		}
415	}
416	if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
417		ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
418					 0, EXT2_IND_BLOCK, &ctx);
419		if (ret & BLOCK_ABORT)
420			goto abort_exit;
421	} else
422		ctx.bcount += limit;
423	if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
424		ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
425					  0, EXT2_DIND_BLOCK, &ctx);
426		if (ret & BLOCK_ABORT)
427			goto abort_exit;
428	} else
429		ctx.bcount += limit * limit;
430	if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
431		ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
432					  0, EXT2_TIND_BLOCK, &ctx);
433		if (ret & BLOCK_ABORT)
434			goto abort_exit;
435	}
436
437abort_exit:
438	if (ret & BLOCK_CHANGED) {
439		if (!got_inode) {
440			retval = ext2fs_read_inode(fs, ino, &inode);
441			if (retval)
442				return retval;
443		}
444		for (i=0; i < EXT2_N_BLOCKS; i++)
445			inode.i_block[i] = blocks[i];
446		retval = ext2fs_write_inode(fs, ino, &inode);
447		if (retval)
448			return retval;
449	}
450
451	if (!block_buf)
452		ext2fs_free_mem(&ctx.ind_buf);
453
454	return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
455}
456
457/*
458 * Emulate the old ext2fs_block_iterate function!
459 */
460
461struct xlate {
462	int (*func)(ext2_filsys	fs,
463		    blk_t	*blocknr,
464		    int		bcount,
465		    void	*priv_data);
466	void *real_private;
467};
468
469#ifdef __TURBOC__
470 #pragma argsused
471#endif
472static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
473		      blk_t ref_block EXT2FS_ATTR((unused)),
474		      int ref_offset EXT2FS_ATTR((unused)),
475		      void *priv_data)
476{
477	struct xlate *xl = (struct xlate *) priv_data;
478
479	return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
480}
481
482errcode_t ext2fs_block_iterate(ext2_filsys fs,
483			       ext2_ino_t ino,
484			       int	flags,
485			       char *block_buf,
486			       int (*func)(ext2_filsys fs,
487					   blk_t	*blocknr,
488					   int	blockcnt,
489					   void	*priv_data),
490			       void *priv_data)
491{
492	struct xlate xl;
493
494	xl.real_private = priv_data;
495	xl.func = func;
496
497	return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
498				     block_buf, xlate_func, &xl);
499}
500
501