libext2: Add BLOCK_FLAG_READ_ONLY flag to ext2fs_block_iterate2()

This flag allows the caller to promise that it will not try to modify
the block numbers returned by the iterator.

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c
index 07a6415..381d31c 100644
--- a/lib/ext2fs/block.c
+++ b/lib/ext2fs/block.c
@@ -36,6 +36,16 @@
 	void	*priv_data;
 };
 
+#define check_for_ro_violation(ctx, ret)				\
+	do {								\
+		if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) &&		\
+		    ((ret) & BLOCK_CHANGED)) {				\
+			(ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE;	\
+			return BLOCK_ABORT;				\
+		}							\
+	} while (0)
+		
+
 static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
 			     int ref_offset, struct block_context *ctx)
 {
@@ -49,6 +59,7 @@
 		ret = (*ctx->func)(ctx->fs, ind_block,
 				   BLOCK_COUNT_IND, ref_block,
 				   ref_offset, ctx->priv_data);
+	check_for_ro_violation(ctx, ret);
 	if (!*ind_block || (ret & BLOCK_ABORT)) {
 		ctx->bcount += limit;
 		return ret;
@@ -95,6 +106,7 @@
 			offset += sizeof(blk_t);
 		}
 	}
+	check_for_ro_violation(ctx, changed);
 	if (changed & BLOCK_CHANGED) {
 		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
 						      ctx->ind_buf);
@@ -107,6 +119,7 @@
 		ret |= (*ctx->func)(ctx->fs, ind_block,
 				    BLOCK_COUNT_IND, ref_block,
 				    ref_offset, ctx->priv_data);
+	check_for_ro_violation(ctx, ret);
 	return ret;
 }
 	
@@ -123,6 +136,7 @@
 		ret = (*ctx->func)(ctx->fs, dind_block,
 				   BLOCK_COUNT_DIND, ref_block,
 				   ref_offset, ctx->priv_data);
+	check_for_ro_violation(ctx, ret);
 	if (!*dind_block || (ret & BLOCK_ABORT)) {
 		ctx->bcount += limit*limit;
 		return ret;
@@ -171,6 +185,7 @@
 			offset += sizeof(blk_t);
 		}
 	}
+	check_for_ro_violation(ctx, changed);
 	if (changed & BLOCK_CHANGED) {
 		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
 						      ctx->dind_buf);
@@ -183,6 +198,7 @@
 		ret |= (*ctx->func)(ctx->fs, dind_block,
 				    BLOCK_COUNT_DIND, ref_block,
 				    ref_offset, ctx->priv_data);
+	check_for_ro_violation(ctx, ret);
 	return ret;
 }
 	
@@ -199,6 +215,7 @@
 		ret = (*ctx->func)(ctx->fs, tind_block,
 				   BLOCK_COUNT_TIND, ref_block,
 				   ref_offset, ctx->priv_data);
+	check_for_ro_violation(ctx, ret);
 	if (!*tind_block || (ret & BLOCK_ABORT)) {
 		ctx->bcount += limit*limit*limit;
 		return ret;
@@ -247,6 +264,7 @@
 			offset += sizeof(blk_t);
 		}
 	}
+	check_for_ro_violation(ctx, changed);
 	if (changed & BLOCK_CHANGED) {
 		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
 						      ctx->tind_buf);
@@ -259,7 +277,7 @@
 		ret |= (*ctx->func)(ctx->fs, tind_block,
 				    BLOCK_COUNT_TIND, ref_block,
 				    ref_offset, ctx->priv_data);
-	
+	check_for_ro_violation(ctx, ret);
 	return ret;
 }
 	
@@ -334,6 +352,7 @@
 					   &inode.osd1.hurd1.h_i_translator,
 					   BLOCK_COUNT_TRANSLATOR,
 					   0, 0, priv_data);
+			check_for_ro_violation(&ctx, ret);
 			if (ret & BLOCK_ABORT)
 				goto abort_exit;
 		}
@@ -350,6 +369,7 @@
 				goto abort_exit;
 		}
 	}
+	check_for_ro_violation(&ctx, ret);
 	if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
 		ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
 					 0, EXT2_IND_BLOCK, &ctx);
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 003c0a3..3e7b7b0 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -78,7 +78,7 @@
 	ctx.func = func;
 	ctx.priv_data = priv_data;
 	ctx.errcode = 0;
-	retval = ext2fs_block_iterate2(fs, dir, 0, 0,
+	retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_READ_ONLY, 0,
 				       ext2fs_process_dir_block, &ctx);
 	if (!block_buf)
 		ext2fs_free_mem(&ctx.buf);
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index eda4bb4..2f44219 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -326,5 +326,8 @@
 ec	EXT2_ET_TDB_ERR_RDONLY,
 	"TDB: Write not permitted"
 
+ec	EXT2_ET_RO_BLOCK_ITERATE,
+	"Attempt to modify a block mapping via a read-only block iterator"
+
 	end
 
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 19213b8..731033f 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -259,6 +259,9 @@
  * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be
  * called for data blocks only.
  *
+ * BLOCK_FLAG_READ_ONLY is a promise by the caller that it will not 
+ * modify returned block number.
+ *
  * BLOCK_FLAG_NO_LARGE is for internal use only.  It informs
  * ext2fs_block_iterate2 that large files won't be accepted.
  */
@@ -266,6 +269,7 @@
 #define BLOCK_FLAG_HOLE		1
 #define BLOCK_FLAG_DEPTH_TRAVERSE	2
 #define BLOCK_FLAG_DATA_ONLY	4
+#define BLOCK_FLAG_READ_ONLY	8
 
 #define BLOCK_FLAG_NO_LARGE	0x1000
 
diff --git a/lib/ext2fs/read_bb.c b/lib/ext2fs/read_bb.c
index c717adc..ff7e292 100644
--- a/lib/ext2fs/read_bb.c
+++ b/lib/ext2fs/read_bb.c
@@ -86,8 +86,8 @@
 
 	rb.bb_list = *bb_list;
 	rb.err = 0;
-	retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0,
-				      mark_bad_block, &rb);
+	retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_READ_ONLY,
+				       0, mark_bad_block, &rb);
 	if (retval)
 		return retval;