浏览代码

dm verity: add ignore_zero_blocks feature

If ignore_zero_blocks is enabled dm-verity will return zeroes for blocks
matching a zero hash without validating the content.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Sami Tolvanen 9 年之前
父节点
当前提交
0cc37c2df4
共有 4 个文件被更改,包括 93 次插入10 次删除
  1. 5 0
      Documentation/device-mapper/verity.txt
  2. 7 1
      drivers/md/dm-verity-fec.c
  3. 79 8
      drivers/md/dm-verity-target.c
  4. 2 1
      drivers/md/dm-verity.h

+ 5 - 0
Documentation/device-mapper/verity.txt

@@ -79,6 +79,11 @@ restart_on_corruption
     not compatible with ignore_corruption and requires user space support to
     not compatible with ignore_corruption and requires user space support to
     avoid restart loops.
     avoid restart loops.
 
 
+ignore_zero_blocks
+    Do not verify blocks that are expected to contain zeroes and always return
+    zeroes instead. This may be useful if the partition contains unused blocks
+    that are not guaranteed to contain zeroes.
+
 use_fec_from_device <fec_dev>
 use_fec_from_device <fec_dev>
     Use forward error correction (FEC) to recover from corruption if hash
     Use forward error correction (FEC) to recover from corruption if hash
     verification fails. Use encoding data from the specified device. This
     verification fails. Use encoding data from the specified device. This

+ 7 - 1
drivers/md/dm-verity-fec.c

@@ -205,6 +205,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
 			 u64 rsb, u64 target, unsigned block_offset,
 			 u64 rsb, u64 target, unsigned block_offset,
 			 int *neras)
 			 int *neras)
 {
 {
+	bool is_zero;
 	int i, j, target_index = -1;
 	int i, j, target_index = -1;
 	struct dm_buffer *buf;
 	struct dm_buffer *buf;
 	struct dm_bufio_client *bufio;
 	struct dm_bufio_client *bufio;
@@ -264,7 +265,12 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
 
 
 		/* locate erasures if the block is on the data device */
 		/* locate erasures if the block is on the data device */
 		if (bufio == v->fec->data_bufio &&
 		if (bufio == v->fec->data_bufio &&
-		    verity_hash_for_block(v, io, block, want_digest) == 0) {
+		    verity_hash_for_block(v, io, block, want_digest,
+					  &is_zero) == 0) {
+			/* skip known zero blocks entirely */
+			if (is_zero)
+				continue;
+
 			/*
 			/*
 			 * skip if we have already found the theoretical
 			 * skip if we have already found the theoretical
 			 * maximum number (i.e. fec->roots) of erasures
 			 * maximum number (i.e. fec->roots) of erasures

+ 79 - 8
drivers/md/dm-verity-target.c

@@ -31,8 +31,9 @@
 
 
 #define DM_VERITY_OPT_LOGGING		"ignore_corruption"
 #define DM_VERITY_OPT_LOGGING		"ignore_corruption"
 #define DM_VERITY_OPT_RESTART		"restart_on_corruption"
 #define DM_VERITY_OPT_RESTART		"restart_on_corruption"
+#define DM_VERITY_OPT_IGN_ZEROES	"ignore_zero_blocks"
 
 
-#define DM_VERITY_OPTS_MAX		(1 + DM_VERITY_OPTS_FEC)
+#define DM_VERITY_OPTS_MAX		(2 + DM_VERITY_OPTS_FEC)
 
 
 static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
 static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
 
 
@@ -309,10 +310,9 @@ release_ret_r:
  * of the hash tree if necessary.
  * of the hash tree if necessary.
  */
  */
 int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
 int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
-			  sector_t block, u8 *digest)
+			  sector_t block, u8 *digest, bool *is_zero)
 {
 {
-	int i;
-	int r;
+	int r = 0, i;
 
 
 	if (likely(v->levels)) {
 	if (likely(v->levels)) {
 		/*
 		/*
@@ -324,7 +324,7 @@ int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
 		 */
 		 */
 		r = verity_verify_level(v, io, block, 0, true, digest);
 		r = verity_verify_level(v, io, block, 0, true, digest);
 		if (likely(r <= 0))
 		if (likely(r <= 0))
-			return r;
+			goto out;
 	}
 	}
 
 
 	memcpy(digest, v->root_digest, v->digest_size);
 	memcpy(digest, v->root_digest, v->digest_size);
@@ -332,10 +332,15 @@ int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
 	for (i = v->levels - 1; i >= 0; i--) {
 	for (i = v->levels - 1; i >= 0; i--) {
 		r = verity_verify_level(v, io, block, i, false, digest);
 		r = verity_verify_level(v, io, block, i, false, digest);
 		if (unlikely(r))
 		if (unlikely(r))
-			return r;
+			goto out;
 	}
 	}
+out:
+	if (!r && v->zero_digest)
+		*is_zero = !memcmp(v->zero_digest, digest, v->digest_size);
+	else
+		*is_zero = false;
 
 
-	return 0;
+	return r;
 }
 }
 
 
 /*
 /*
@@ -382,11 +387,19 @@ static int verity_bv_hash_update(struct dm_verity *v, struct dm_verity_io *io,
 	return verity_hash_update(v, verity_io_hash_desc(v, io), data, len);
 	return verity_hash_update(v, verity_io_hash_desc(v, io), data, len);
 }
 }
 
 
+static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
+			  u8 *data, size_t len)
+{
+	memset(data, 0, len);
+	return 0;
+}
+
 /*
 /*
  * Verify one "dm_verity_io" structure.
  * Verify one "dm_verity_io" structure.
  */
  */
 static int verity_verify_io(struct dm_verity_io *io)
 static int verity_verify_io(struct dm_verity_io *io)
 {
 {
+	bool is_zero;
 	struct dm_verity *v = io->v;
 	struct dm_verity *v = io->v;
 	struct bvec_iter start;
 	struct bvec_iter start;
 	unsigned b;
 	unsigned b;
@@ -396,10 +409,24 @@ static int verity_verify_io(struct dm_verity_io *io)
 		struct shash_desc *desc = verity_io_hash_desc(v, io);
 		struct shash_desc *desc = verity_io_hash_desc(v, io);
 
 
 		r = verity_hash_for_block(v, io, io->block + b,
 		r = verity_hash_for_block(v, io, io->block + b,
-					  verity_io_want_digest(v, io));
+					  verity_io_want_digest(v, io),
+					  &is_zero);
 		if (unlikely(r < 0))
 		if (unlikely(r < 0))
 			return r;
 			return r;
 
 
+		if (is_zero) {
+			/*
+			 * If we expect a zero block, don't validate, just
+			 * return zeros.
+			 */
+			r = verity_for_bv_block(v, io, &io->iter,
+						verity_bv_zero);
+			if (unlikely(r < 0))
+				return r;
+
+			continue;
+		}
+
 		r = verity_hash_init(v, desc);
 		r = verity_hash_init(v, desc);
 		if (unlikely(r < 0))
 		if (unlikely(r < 0))
 			return r;
 			return r;
@@ -604,6 +631,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
 			args++;
 			args++;
 		if (verity_fec_is_enabled(v))
 		if (verity_fec_is_enabled(v))
 			args += DM_VERITY_OPTS_FEC;
 			args += DM_VERITY_OPTS_FEC;
+		if (v->zero_digest)
+			args++;
 		if (!args)
 		if (!args)
 			return;
 			return;
 		DMEMIT(" %u", args);
 		DMEMIT(" %u", args);
@@ -620,6 +649,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
 				BUG();
 				BUG();
 			}
 			}
 		}
 		}
+		if (v->zero_digest)
+			DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
 		sz = verity_fec_status_table(v, sz, result, maxlen);
 		sz = verity_fec_status_table(v, sz, result, maxlen);
 		break;
 		break;
 	}
 	}
@@ -671,6 +702,7 @@ static void verity_dtr(struct dm_target *ti)
 
 
 	kfree(v->salt);
 	kfree(v->salt);
 	kfree(v->root_digest);
 	kfree(v->root_digest);
+	kfree(v->zero_digest);
 
 
 	if (v->tfm)
 	if (v->tfm)
 		crypto_free_shash(v->tfm);
 		crypto_free_shash(v->tfm);
@@ -688,6 +720,37 @@ static void verity_dtr(struct dm_target *ti)
 	kfree(v);
 	kfree(v);
 }
 }
 
 
+static int verity_alloc_zero_digest(struct dm_verity *v)
+{
+	int r = -ENOMEM;
+	struct shash_desc *desc;
+	u8 *zero_data;
+
+	v->zero_digest = kmalloc(v->digest_size, GFP_KERNEL);
+
+	if (!v->zero_digest)
+		return r;
+
+	desc = kmalloc(v->shash_descsize, GFP_KERNEL);
+
+	if (!desc)
+		return r; /* verity_dtr will free zero_digest */
+
+	zero_data = kzalloc(1 << v->data_dev_block_bits, GFP_KERNEL);
+
+	if (!zero_data)
+		goto out;
+
+	r = verity_hash(v, desc, zero_data, 1 << v->data_dev_block_bits,
+			v->zero_digest);
+
+out:
+	kfree(desc);
+	kfree(zero_data);
+
+	return r;
+}
+
 static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
 static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
 {
 {
 	int r;
 	int r;
@@ -718,6 +781,14 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
 			v->mode = DM_VERITY_MODE_RESTART;
 			v->mode = DM_VERITY_MODE_RESTART;
 			continue;
 			continue;
 
 
+		} else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) {
+			r = verity_alloc_zero_digest(v);
+			if (r) {
+				ti->error = "Cannot allocate zero digest";
+				return r;
+			}
+			continue;
+
 		} else if (verity_is_fec_opt_arg(arg_name)) {
 		} else if (verity_is_fec_opt_arg(arg_name)) {
 			r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
 			r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
 			if (r)
 			if (r)

+ 2 - 1
drivers/md/dm-verity.h

@@ -40,6 +40,7 @@ struct dm_verity {
 	struct crypto_shash *tfm;
 	struct crypto_shash *tfm;
 	u8 *root_digest;	/* digest of the root block */
 	u8 *root_digest;	/* digest of the root block */
 	u8 *salt;		/* salt: its size is salt_size */
 	u8 *salt;		/* salt: its size is salt_size */
+	u8 *zero_digest;	/* digest for a zero block */
 	unsigned salt_size;
 	unsigned salt_size;
 	sector_t data_start;	/* data offset in 512-byte sectors */
 	sector_t data_start;	/* data offset in 512-byte sectors */
 	sector_t hash_start;	/* hash start in blocks */
 	sector_t hash_start;	/* hash start in blocks */
@@ -123,6 +124,6 @@ extern int verity_hash(struct dm_verity *v, struct shash_desc *desc,
 		       const u8 *data, size_t len, u8 *digest);
 		       const u8 *data, size_t len, u8 *digest);
 
 
 extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
 extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
-				 sector_t block, u8 *digest);
+				 sector_t block, u8 *digest, bool *is_zero);
 
 
 #endif /* DM_VERITY_H */
 #endif /* DM_VERITY_H */