binman: Allow reading an entry from an image

It is useful to be able to extract entry contents from an image to see
what is inside. Add a simple function to read the contents of an entry,
decompressing it by default.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index ad82804..c11dd1b 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -69,6 +69,8 @@
 COMPRESS_DATA         = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
 REFCODE_DATA          = b'refcode'
 
+EXTRACT_DTB_SIZE = 0x3c9
+
 
 class TestFunctional(unittest.TestCase):
     """Functional tests for binman
@@ -2423,6 +2425,46 @@
         """Test listing the files in a sub-entry of a section"""
         self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
 
+    def _RunExtractCmd(self, entry_name, decomp=True):
+        """Extract an entry from an image
+
+        Args:
+            entry_name: Entry name to extract
+            decomp: True to decompress the data if compressed, False to leave
+                it in its raw uncompressed format
+
+        Returns:
+            data from entry
+        """
+        self._CheckLz4()
+        self._DoReadFileRealDtb('130_list_fdtmap.dts')
+        image_fname = tools.GetOutputFilename('image.bin')
+        return control.ReadEntry(image_fname, entry_name, decomp)
+
+    def testExtractSimple(self):
+        """Test extracting a single file"""
+        data = self._RunExtractCmd('u-boot')
+        self.assertEqual(U_BOOT_DATA, data)
+
+    def testExtractBadEntry(self):
+        """Test extracting a bad section path"""
+        with self.assertRaises(ValueError) as e:
+            self._RunExtractCmd('section/does-not-exist')
+        self.assertIn("Entry 'does-not-exist' not found in '/section'",
+                      str(e.exception))
+
+    def testExtractMissingFile(self):
+        """Test extracting file that does not exist"""
+        with self.assertRaises(IOError) as e:
+            control.ReadEntry('missing-file', 'name')
+
+    def testExtractBadFile(self):
+        """Test extracting an invalid file"""
+        fname = os.path.join(self._indir, 'badfile')
+        tools.WriteFile(fname, b'')
+        with self.assertRaises(ValueError) as e:
+            control.ReadEntry(fname, 'name')
+
 
 if __name__ == "__main__":
     unittest.main()