avbtool: add_hash_footer: Add --calc_max_image_size option. am: bf56245c7a
am: e8b1ab9613

Change-Id: If53b4a20dc5aa248c1cc690a0230f243372cd13d
diff --git a/README.md b/README.md
index 3e60e37..51ccf54 100644
--- a/README.md
+++ b/README.md
@@ -316,7 +316,8 @@
         [--setup_rootfs_from_kernel /path/to/image.bin]                            \
         [--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image]        \
         [--signing_helper /path/to/external/signer]                                \
-        [--append_to_release_string STR]
+        [--append_to_release_string STR]                                           \
+        [--calc_max_image_size]
 
 An integrity footer containing the root digest and salt for a hashtree
 for a partition can be added to an existing image as follows. The
@@ -334,7 +335,8 @@
         [--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image]        \
         [--do_not_generate_fec] [--fec_num_roots FEC_NUM_ROOTS]                    \
         [--signing_helper /path/to/external/signer]                                \
-        [--append_to_release_string STR]
+        [--append_to_release_string STR]                                           \
+        [--calc_max_image_size]
 
 The size of an image with integrity footers can be changed using the
 `resize_image` command:
@@ -354,8 +356,13 @@
 image being operated on.
 
 To calculate the maximum size of an image that will fit in a partition
-of a given size after having used the `avbtool add_hashtree_footer`
-command on it, use the `--calc_max_image_size` option:
+of a given size after having used the `avbtool add_hash_footer` or
+`avbtool add_hashtree_footer` commands on it, use the
+`--calc_max_image_size` option:
+
+    $ avbtool add_hash_footer --partition_size $((10*1024*1024)) \
+        --calc_max_image_size
+    10416128
 
     $ avbtool add_hashtree_footer --partition_size $((10*1024*1024)) \
         --calc_max_image_size
diff --git a/avbtool b/avbtool
index 1b459f7..d5fda77 100755
--- a/avbtool
+++ b/avbtool
@@ -2050,9 +2050,6 @@
       instructions. There is one for when hashtree is not disabled and one for
       when it is.
 
-    Raises:
-      AvbError: If  |image| doesn't have a hashtree descriptor.
-
     """
 
     c = 'dm="1 vroot none ro 1,'
@@ -2491,8 +2488,8 @@
                       public_key_metadata_path, rollback_index, flags, props,
                       props_from_file, kernel_cmdlines,
                       setup_rootfs_from_kernel,
-                      include_descriptors_from_image, signing_helper,
-                      release_string, append_to_release_string,
+                      include_descriptors_from_image, calc_max_image_size,
+                      signing_helper, release_string, append_to_release_string,
                       output_vbmeta_image, do_not_append_vbmeta_image):
     """Implementation of the add_hash_footer on unsparse images.
 
@@ -2515,6 +2512,9 @@
         dm-verity kernel cmdline from.
       include_descriptors_from_image: List of file objects for which
         to insert descriptors from.
+      calc_max_image_size: Don't store the footer - instead calculate the
+        maximum image size leaving enough room for metadata with the
+        given |partition_size|.
       signing_helper: Program which signs a hash and return signature.
       release_string: None or avbtool release string.
       append_to_release_string: None or string to append.
@@ -2524,6 +2524,17 @@
     Raises:
       AvbError: If an argument is incorrect.
     """
+    # First, calculate the maximum image size such that an image
+    # this size + metadata (footer + vbmeta struct) fits in
+    # |partition_size|.
+    max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
+    max_image_size = partition_size - max_metadata_size
+
+    # If we're asked to only calculate the maximum image size, we're done.
+    if calc_max_image_size:
+      print '{}'.format(max_image_size)
+      return
+
     image = ImageHandler(image_filename)
 
     if partition_size % image.block_size != 0:
@@ -2546,12 +2557,6 @@
     # If anything goes wrong from here-on, restore the image back to
     # its original size.
     try:
-      # First, calculate the maximum image size such that an image
-      # this size + metadata (footer + vbmeta struct) fits in
-      # |partition_size|.
-      max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
-      max_image_size = partition_size - max_metadata_size
-
       # If image size exceeds the maximum image size, fail.
       if image.image_size > max_image_size:
         raise AvbError('Image size of {} exceeds maximum image '
@@ -3265,12 +3270,18 @@
                             required=True)
     sub_parser.add_argument('--partition_name',
                             help='Partition name',
-                            required=True)
+                            default=None)
     sub_parser.add_argument('--hash_algorithm',
                             help='Hash algorithm to use (default: sha256)',
                             default='sha256')
     sub_parser.add_argument('--salt',
                             help='Salt in hex (default: /dev/urandom)')
+    sub_parser.add_argument('--calc_max_image_size',
+                            help=('Don\'t store the footer - '
+                                  'instead calculate the maximum image size '
+                                  'leaving enough room for metadata with '
+                                  'the given partition size.'),
+                            action='store_true')
     sub_parser.add_argument('--output_vbmeta_image',
                             help='Also write vbmeta struct to file',
                             type=argparse.FileType('wb'))
@@ -3518,7 +3529,8 @@
   def add_hash_footer(self, args):
     """Implements the 'add_hash_footer' sub-command."""
     args = self._fixup_common_args(args)
-    self.avb.add_hash_footer(args.image.name, args.partition_size,
+    self.avb.add_hash_footer(args.image.name if args.image else None,
+                             args.partition_size,
                              args.partition_name, args.hash_algorithm,
                              args.salt, args.chain_partition, args.algorithm,
                              args.key,
@@ -3527,7 +3539,7 @@
                              args.kernel_cmdline,
                              args.setup_rootfs_from_kernel,
                              args.include_descriptors_from_image,
-                             args.signing_helper,
+                             args.calc_max_image_size, args.signing_helper,
                              args.internal_release_string,
                              args.append_to_release_string,
                              args.output_vbmeta_image,
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index 6cb9ac0..9d53f6c 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -663,6 +663,40 @@
   EXPECT_COMMAND(0, "rm -f %s.sparse", partition_path.value().c_str());
 }
 
+TEST_F(AvbToolTest, AddHashFooterCalcMaxImageSize) {
+  const size_t partition_size = 10 * 1024 * 1024;
+  base::FilePath output_path = testdir_.Append("max_size.txt");
+
+  EXPECT_COMMAND(0,
+                 "./avbtool add_hash_footer "
+                 "--partition_size %zd "
+                 "--calc_max_image_size > %s",
+                 partition_size,
+                 output_path.value().c_str());
+  std::string max_image_size_data;
+  EXPECT_TRUE(base::ReadFileToString(output_path, &max_image_size_data));
+  EXPECT_EQ("10416128\n", max_image_size_data);
+  size_t max_image_size = atoll(max_image_size_data.c_str());
+
+  // Metadata takes up 68 KiB.
+  EXPECT_EQ(68 * 1024ULL, partition_size - max_image_size);
+
+  // Check that we can add a hash footer for an image this size for
+  // such a partition size.
+  base::FilePath boot_path = GenerateImage("boot", max_image_size);
+  EXPECT_COMMAND(0,
+                 "./avbtool add_hash_footer"
+                 " --image %s"
+                 " --partition_name boot"
+                 " --partition_size %zd"
+                 " --salt deadbeef"
+                 " --algorithm SHA512_RSA4096 "
+                 " --key test/data/testkey_rsa4096.pem"
+                 " --internal_release_string \"\"",
+                 boot_path.value().c_str(),
+                 partition_size);
+}
+
 void AvbToolTest::AddHashtreeFooterTest(bool sparse_image) {
   const size_t rootfs_size = 1028 * 1024;
   const size_t partition_size = 1536 * 1024;