block.c revision 18a1444b4f1e6a0948fd38fa0de382d86cfe04de
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		    blk64_t	*blocknr,
25		    e2_blkcnt_t	bcount,
26		    blk64_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	blk64_t	blk64;
66
67	limit = ctx->fs->blocksize >> 2;
68	if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
69	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) {
70		blk64 = *ind_block;
71		ret = (*ctx->func)(ctx->fs, &blk64,
72				   BLOCK_COUNT_IND, ref_block,
73				   ref_offset, ctx->priv_data);
74		*ind_block = blk64;
75	}
76	check_for_ro_violation_return(ctx, ret);
77	if (!*ind_block || (ret & BLOCK_ABORT)) {
78		ctx->bcount += limit;
79		return ret;
80	}
81	if (*ind_block >= ext2fs_blocks_count(ctx->fs->super) ||
82	    *ind_block < ctx->fs->super->s_first_data_block) {
83		ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
84		ret |= BLOCK_ERROR;
85		return ret;
86	}
87	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block,
88					     ctx->ind_buf);
89	if (ctx->errcode) {
90		ret |= BLOCK_ERROR;
91		return ret;
92	}
93
94	block_nr = (blk_t *) ctx->ind_buf;
95	offset = 0;
96	if (ctx->flags & BLOCK_FLAG_APPEND) {
97		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
98			blk64 = *block_nr;
99			flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount,
100					     *ind_block, offset,
101					     ctx->priv_data);
102			*block_nr = blk64;
103			changed	|= flags;
104			if (flags & BLOCK_ABORT) {
105				ret |= BLOCK_ABORT;
106				break;
107			}
108			offset += sizeof(blk_t);
109		}
110	} else {
111		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
112			if (*block_nr == 0)
113				goto skip_sparse;
114			blk64 = *block_nr;
115			flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount,
116					     *ind_block, offset,
117					     ctx->priv_data);
118			*block_nr = blk64;
119			changed	|= flags;
120			if (flags & BLOCK_ABORT) {
121				ret |= BLOCK_ABORT;
122				break;
123			}
124		skip_sparse:
125			offset += sizeof(blk_t);
126		}
127	}
128	check_for_ro_violation_return(ctx, changed);
129	if (changed & BLOCK_CHANGED) {
130		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
131						      ctx->ind_buf);
132		if (ctx->errcode)
133			ret |= BLOCK_ERROR | BLOCK_ABORT;
134	}
135	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
136	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
137	    !(ret & BLOCK_ABORT)) {
138		blk64 = *ind_block;
139		ret |= (*ctx->func)(ctx->fs, &blk64,
140				    BLOCK_COUNT_IND, ref_block,
141				    ref_offset, ctx->priv_data);
142		*ind_block = blk64;
143	}
144	check_for_ro_violation_return(ctx, ret);
145	return ret;
146}
147
148static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
149			      int ref_offset, struct block_context *ctx)
150{
151	int	ret = 0, changed = 0;
152	int	i, flags, limit, offset;
153	blk_t	*block_nr;
154	blk64_t	blk64;
155
156	limit = ctx->fs->blocksize >> 2;
157	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
158			    BLOCK_FLAG_DATA_ONLY))) {
159		blk64 = *dind_block;
160		ret = (*ctx->func)(ctx->fs, &blk64,
161				   BLOCK_COUNT_DIND, ref_block,
162				   ref_offset, ctx->priv_data);
163		*dind_block = blk64;
164	}
165	check_for_ro_violation_return(ctx, ret);
166	if (!*dind_block || (ret & BLOCK_ABORT)) {
167		ctx->bcount += limit*limit;
168		return ret;
169	}
170	if (*dind_block >= ext2fs_blocks_count(ctx->fs->super) ||
171	    *dind_block < ctx->fs->super->s_first_data_block) {
172		ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
173		ret |= BLOCK_ERROR;
174		return ret;
175	}
176	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block,
177					     ctx->dind_buf);
178	if (ctx->errcode) {
179		ret |= BLOCK_ERROR;
180		return ret;
181	}
182
183	block_nr = (blk_t *) ctx->dind_buf;
184	offset = 0;
185	if (ctx->flags & BLOCK_FLAG_APPEND) {
186		for (i = 0; i < limit; i++, block_nr++) {
187			flags = block_iterate_ind(block_nr,
188						  *dind_block, offset,
189						  ctx);
190			changed |= flags;
191			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
192				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
193				break;
194			}
195			offset += sizeof(blk_t);
196		}
197	} else {
198		for (i = 0; i < limit; i++, block_nr++) {
199			if (*block_nr == 0) {
200				ctx->bcount += limit;
201				continue;
202			}
203			flags = block_iterate_ind(block_nr,
204						  *dind_block, offset,
205						  ctx);
206			changed |= flags;
207			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
208				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
209				break;
210			}
211			offset += sizeof(blk_t);
212		}
213	}
214	check_for_ro_violation_return(ctx, changed);
215	if (changed & BLOCK_CHANGED) {
216		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
217						      ctx->dind_buf);
218		if (ctx->errcode)
219			ret |= BLOCK_ERROR | BLOCK_ABORT;
220	}
221	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
222	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
223	    !(ret & BLOCK_ABORT)) {
224		blk64 = *dind_block;
225		ret |= (*ctx->func)(ctx->fs, &blk64,
226				    BLOCK_COUNT_DIND, ref_block,
227				    ref_offset, ctx->priv_data);
228		*dind_block = blk64;
229	}
230	check_for_ro_violation_return(ctx, ret);
231	return ret;
232}
233
234static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
235			      int ref_offset, struct block_context *ctx)
236{
237	int	ret = 0, changed = 0;
238	int	i, flags, limit, offset;
239	blk_t	*block_nr;
240	blk64_t	blk64;
241
242	limit = ctx->fs->blocksize >> 2;
243	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
244			    BLOCK_FLAG_DATA_ONLY))) {
245		blk64 = *tind_block;
246		ret = (*ctx->func)(ctx->fs, &blk64,
247				   BLOCK_COUNT_TIND, ref_block,
248				   ref_offset, ctx->priv_data);
249		*tind_block = blk64;
250	}
251	check_for_ro_violation_return(ctx, ret);
252	if (!*tind_block || (ret & BLOCK_ABORT)) {
253		ctx->bcount += limit*limit*limit;
254		return ret;
255	}
256	if (*tind_block >= ext2fs_blocks_count(ctx->fs->super) ||
257	    *tind_block < ctx->fs->super->s_first_data_block) {
258		ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
259		ret |= BLOCK_ERROR;
260		return ret;
261	}
262	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block,
263					     ctx->tind_buf);
264	if (ctx->errcode) {
265		ret |= BLOCK_ERROR;
266		return ret;
267	}
268
269	block_nr = (blk_t *) ctx->tind_buf;
270	offset = 0;
271	if (ctx->flags & BLOCK_FLAG_APPEND) {
272		for (i = 0; i < limit; i++, block_nr++) {
273			flags = block_iterate_dind(block_nr,
274						   *tind_block,
275						   offset, ctx);
276			changed |= flags;
277			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
278				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
279				break;
280			}
281			offset += sizeof(blk_t);
282		}
283	} else {
284		for (i = 0; i < limit; i++, block_nr++) {
285			if (*block_nr == 0) {
286				ctx->bcount += limit*limit;
287				continue;
288			}
289			flags = block_iterate_dind(block_nr,
290						   *tind_block,
291						   offset, ctx);
292			changed |= flags;
293			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
294				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
295				break;
296			}
297			offset += sizeof(blk_t);
298		}
299	}
300	check_for_ro_violation_return(ctx, changed);
301	if (changed & BLOCK_CHANGED) {
302		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
303						      ctx->tind_buf);
304		if (ctx->errcode)
305			ret |= BLOCK_ERROR | BLOCK_ABORT;
306	}
307	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
308	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
309	    !(ret & BLOCK_ABORT)) {
310		blk64 = *tind_block;
311		ret |= (*ctx->func)(ctx->fs, &blk64,
312				    BLOCK_COUNT_TIND, ref_block,
313				    ref_offset, ctx->priv_data);
314		*tind_block = blk64;
315	}
316	check_for_ro_violation_return(ctx, ret);
317	return ret;
318}
319
320errcode_t ext2fs_block_iterate3(ext2_filsys fs,
321				ext2_ino_t ino,
322				int	flags,
323				char *block_buf,
324				int (*func)(ext2_filsys fs,
325					    blk64_t	*blocknr,
326					    e2_blkcnt_t	blockcnt,
327					    blk64_t	ref_blk,
328					    int		ref_offset,
329					    void	*priv_data),
330				void *priv_data)
331{
332	int	i;
333	int	r, ret = 0;
334	struct ext2_inode inode;
335	errcode_t	retval;
336	struct block_context ctx;
337	int	limit;
338	blk64_t	blk64;
339
340	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
341
342	ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
343	if (ctx.errcode)
344		return ctx.errcode;
345
346	/*
347	 * Check to see if we need to limit large files
348	 */
349	if (flags & BLOCK_FLAG_NO_LARGE) {
350		if (!LINUX_S_ISDIR(inode.i_mode) &&
351		    (inode.i_size_high != 0))
352			return EXT2_ET_FILE_TOO_BIG;
353	}
354
355	limit = fs->blocksize >> 2;
356
357	ctx.fs = fs;
358	ctx.func = func;
359	ctx.priv_data = priv_data;
360	ctx.flags = flags;
361	ctx.bcount = 0;
362	if (block_buf) {
363		ctx.ind_buf = block_buf;
364	} else {
365		retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf);
366		if (retval)
367			return retval;
368	}
369	ctx.dind_buf = ctx.ind_buf + fs->blocksize;
370	ctx.tind_buf = ctx.dind_buf + fs->blocksize;
371
372	/*
373	 * Iterate over the HURD translator block (if present)
374	 */
375	if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
376	    !(flags & BLOCK_FLAG_DATA_ONLY)) {
377		if (inode.osd1.hurd1.h_i_translator) {
378			blk64 = inode.osd1.hurd1.h_i_translator;
379			ret |= (*ctx.func)(fs, &blk64,
380					   BLOCK_COUNT_TRANSLATOR,
381					   0, 0, priv_data);
382			inode.osd1.hurd1.h_i_translator = (blk_t) blk64;
383			if (ret & BLOCK_ABORT)
384				goto abort_exit;
385			check_for_ro_violation_goto(&ctx, ret, abort_exit);
386		}
387	}
388
389	if (inode.i_flags & EXT4_EXTENTS_FL) {
390		ext2_extent_handle_t	handle;
391		struct ext2fs_extent	extent, next;
392		e2_blkcnt_t		blockcnt = 0;
393		blk64_t			blk, new_blk;
394		int			op = EXT2_EXTENT_ROOT;
395		int			uninit;
396		unsigned int		j;
397
398		ctx.errcode = ext2fs_extent_open2(fs, ino, &inode, &handle);
399		if (ctx.errcode)
400			goto abort_exit;
401
402		while (1) {
403			if (op == EXT2_EXTENT_CURRENT)
404				ctx.errcode = 0;
405			else
406				ctx.errcode = ext2fs_extent_get(handle, op,
407								&extent);
408			if (ctx.errcode) {
409				if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT)
410					break;
411				ctx.errcode = 0;
412				if (!(flags & BLOCK_FLAG_APPEND))
413					break;
414			next_block_set:
415				blk = 0;
416				r = (*ctx.func)(fs, &blk, blockcnt,
417						0, 0, priv_data);
418				ret |= r;
419				check_for_ro_violation_goto(&ctx, ret,
420							    extent_done);
421				if (r & BLOCK_CHANGED) {
422					ctx.errcode =
423						ext2fs_extent_set_bmap(handle,
424						       (blk64_t) blockcnt++,
425						       (blk64_t) blk, 0);
426					if (ctx.errcode || (ret & BLOCK_ABORT))
427						break;
428					if (blk)
429						goto next_block_set;
430				}
431				break;
432			}
433
434			op = EXT2_EXTENT_NEXT;
435			blk = extent.e_pblk;
436			if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) {
437				if (ctx.flags & BLOCK_FLAG_DATA_ONLY)
438					continue;
439				if ((!(extent.e_flags &
440				       EXT2_EXTENT_FLAGS_SECOND_VISIT) &&
441				     !(ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE)) ||
442				    ((extent.e_flags &
443				      EXT2_EXTENT_FLAGS_SECOND_VISIT) &&
444				     (ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE))) {
445					ret |= (*ctx.func)(fs, &blk,
446							   -1, 0, 0, priv_data);
447					if (ret & BLOCK_CHANGED) {
448						extent.e_pblk = blk;
449						ctx.errcode =
450				ext2fs_extent_replace(handle, 0, &extent);
451						if (ctx.errcode)
452							break;
453					}
454					if (ret & BLOCK_ABORT)
455						break;
456				}
457				continue;
458			}
459			uninit = 0;
460			if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
461				uninit = EXT2_EXTENT_SET_BMAP_UNINIT;
462
463			/*
464			 * Get the next extent before we start messing
465			 * with the current extent
466			 */
467			retval = ext2fs_extent_get(handle, op, &next);
468
469#if 0
470			printf("lblk %llu pblk %llu len %d blockcnt %llu\n",
471			       extent.e_lblk, extent.e_pblk,
472			       extent.e_len, blockcnt);
473#endif
474			if (extent.e_lblk + extent.e_len <= (blk64_t) blockcnt)
475				continue;
476			if (extent.e_lblk > (blk64_t) blockcnt)
477				blockcnt = extent.e_lblk;
478			j = blockcnt - extent.e_lblk;
479			blk += j;
480			for (blockcnt = extent.e_lblk, j = 0;
481			     j < extent.e_len;
482			     blk++, blockcnt++, j++) {
483				new_blk = blk;
484				r = (*ctx.func)(fs, &new_blk, blockcnt,
485						0, 0, priv_data);
486				ret |= r;
487				check_for_ro_violation_goto(&ctx, ret,
488							    extent_done);
489				if (r & BLOCK_CHANGED) {
490					ctx.errcode =
491						ext2fs_extent_set_bmap(handle,
492						       (blk64_t) blockcnt,
493						       new_blk, uninit);
494					if (ctx.errcode)
495						goto extent_done;
496				}
497				if (ret & BLOCK_ABORT)
498					goto extent_done;
499			}
500			if (retval == 0) {
501				extent = next;
502				op = EXT2_EXTENT_CURRENT;
503			}
504		}
505
506	extent_done:
507		ext2fs_extent_free(handle);
508		ret |= BLOCK_ERROR; /* ctx.errcode is always valid here */
509		goto errout;
510	}
511
512	/*
513	 * Iterate over normal data blocks
514	 */
515	for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
516		if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) {
517			blk64 = inode.i_block[i];
518			ret |= (*ctx.func)(fs, &blk64, ctx.bcount, 0, i,
519					   priv_data);
520			inode.i_block[i] = (blk_t) blk64;
521			if (ret & BLOCK_ABORT)
522				goto abort_exit;
523		}
524	}
525	check_for_ro_violation_goto(&ctx, ret, abort_exit);
526	if (inode.i_block[EXT2_IND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) {
527		ret |= block_iterate_ind(&inode.i_block[EXT2_IND_BLOCK],
528					 0, EXT2_IND_BLOCK, &ctx);
529		if (ret & BLOCK_ABORT)
530			goto abort_exit;
531	} else
532		ctx.bcount += limit;
533	if (inode.i_block[EXT2_DIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) {
534		ret |= block_iterate_dind(&inode.i_block[EXT2_DIND_BLOCK],
535					  0, EXT2_DIND_BLOCK, &ctx);
536		if (ret & BLOCK_ABORT)
537			goto abort_exit;
538	} else
539		ctx.bcount += limit * limit;
540	if (inode.i_block[EXT2_TIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) {
541		ret |= block_iterate_tind(&inode.i_block[EXT2_TIND_BLOCK],
542					  0, EXT2_TIND_BLOCK, &ctx);
543		if (ret & BLOCK_ABORT)
544			goto abort_exit;
545	}
546
547abort_exit:
548	if (ret & BLOCK_CHANGED) {
549		retval = ext2fs_write_inode(fs, ino, &inode);
550		if (retval) {
551			ret |= BLOCK_ERROR;
552			ctx.errcode = retval;
553		}
554	}
555errout:
556	if (!block_buf)
557		ext2fs_free_mem(&ctx.ind_buf);
558
559	return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
560}
561
562/*
563 * Emulate the old ext2fs_block_iterate function!
564 */
565
566struct xlate64 {
567	int (*func)(ext2_filsys fs,
568		    blk_t	*blocknr,
569		    e2_blkcnt_t	blockcnt,
570		    blk_t	ref_blk,
571		    int		ref_offset,
572		    void	*priv_data);
573	void *real_private;
574};
575
576static int xlate64_func(ext2_filsys fs, blk64_t	*blocknr,
577			e2_blkcnt_t blockcnt, blk64_t ref_blk,
578			int ref_offset, void *priv_data)
579{
580	struct xlate64 *xl = (struct xlate64 *) priv_data;
581	int		ret;
582	blk_t		block32 = *blocknr;
583
584	ret = (*xl->func)(fs, &block32, blockcnt, (blk_t) ref_blk, ref_offset,
585			     xl->real_private);
586	*blocknr = block32;
587	return ret;
588}
589
590errcode_t ext2fs_block_iterate2(ext2_filsys fs,
591				ext2_ino_t ino,
592				int	flags,
593				char *block_buf,
594				int (*func)(ext2_filsys fs,
595					    blk_t	*blocknr,
596					    e2_blkcnt_t	blockcnt,
597					    blk_t	ref_blk,
598					    int		ref_offset,
599					    void	*priv_data),
600				void *priv_data)
601{
602	struct xlate64 xl;
603
604	xl.real_private = priv_data;
605	xl.func = func;
606
607	return ext2fs_block_iterate3(fs, ino, flags, block_buf,
608				     xlate64_func, &xl);
609}
610
611
612struct xlate {
613	int (*func)(ext2_filsys	fs,
614		    blk_t	*blocknr,
615		    int		bcount,
616		    void	*priv_data);
617	void *real_private;
618};
619
620#ifdef __TURBOC__
621 #pragma argsused
622#endif
623static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
624		      blk_t ref_block EXT2FS_ATTR((unused)),
625		      int ref_offset EXT2FS_ATTR((unused)),
626		      void *priv_data)
627{
628	struct xlate *xl = (struct xlate *) priv_data;
629
630	return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
631}
632
633errcode_t ext2fs_block_iterate(ext2_filsys fs,
634			       ext2_ino_t ino,
635			       int	flags,
636			       char *block_buf,
637			       int (*func)(ext2_filsys fs,
638					   blk_t	*blocknr,
639					   int	blockcnt,
640					   void	*priv_data),
641			       void *priv_data)
642{
643	struct xlate xl;
644
645	xl.real_private = priv_data;
646	xl.func = func;
647
648	return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
649				     block_buf, xlate_func, &xl);
650}
651
652