TransRead: remove local caching functionality
authorArtem Bityutskiy <artem.bityutskiy@intel.com>
Mon, 16 Sep 2013 09:23:03 +0000 (12:23 +0300)
committerArtem Bityutskiy <artem.bityutskiy@intel.com>
Tue, 17 Sep 2013 11:17:28 +0000 (14:17 +0300)
Remove the local caching functionality of the TransRead module. This
functionality does not really belong to the module, and it only makes things
more complex. It is better to either make users of TransRead to locally cache
remote files, or create a small wrapper over TransRead for these purposes.

Since currently there is only one user of this functionality, we just implement
local caching in bmaptool directly.

Change-Id: I649a5e761b9bc4b6b10d035bfd790357eaf6c373
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@intel.com>
TODO
bmaptool
bmaptools/TransRead.py

diff --git a/TODO b/TODO
index 3e5f241cd85c43c3c64e1aac63485c510dd94ec5..38431bef412283ea63277d80fdcb63e72fd7a8c8 100644 (file)
--- a/TODO
+++ b/TODO
@@ -13,5 +13,3 @@ Current TODO list, any help with these is appreciated.
 7. Verify that unmapped areas have all zeroes to guarantee integrity.
 8. Update the man pages
 9. Teach bmaptool to update the alternate GPT partition
-10. Remove the whole "local=True" thing from TransRead completely and
-    do that in bmaptool directly.
index 3c607e8cb3036e3f924f5c6ad8a59eb798d6b2cd..bcc0b6b6672d23cc1f07e030e582e3e0ef28bbf6 100755 (executable)
--- a/bmaptool
+++ b/bmaptool
@@ -56,6 +56,7 @@ import time
 import logging
 import tempfile
 import traceback
+import shutil
 from bmaptools import BmapCreate, BmapCopy, BmapHelpers, TransRead
 
 def copy_command_open_blkdev(path, log):
@@ -95,35 +96,66 @@ def copy_command_open_blkdev(path, log):
 
     return NamedFile(file_obj, path)
 
-def find_and_open_bmap(image_path, log):
+def find_and_open_bmap(args, log):
     """
-    When the user does not specify the bmap file, we try to find it at the same
-    place where the image file is located. We search for a file with the same
-    path and basename, but with a ".bmap" extension. Since the image may
-    contain more than one extension (e.g., image.raw.bz2), try to remove them
-    one-by-one.
-
-    This function returns a file-like object for the bmap file if it has been
-    found, and 'None' otherwise.
+    This function opens the bmap file and returns the corresponding file object
+    as the bmap file path.
+
+    If the user specified the bmap file explicitely, we just open the provided
+    path. Otherwise,  we try to find the bmap file at the same place where the
+    image file is located. We search for a file with the same path and
+    basename, but with a ".bmap" extension. Since the image may contain more
+    than one extension (e.g., image.raw.bz2), we try to remove them one-by-one.
+
+    Additionally, this function makes sure that the returned file object
+    corresponds to a local file, not a remote file. We do this by creating a
+    temporary local copy of the bmap file, if needed.
     """
 
-    bmap_path = None
-
-    while True:
-        bmap_path = image_path + ".bmap"
+    if args.nobmap:
+        return (None, None)
 
+    if args.bmap:
         try:
-            bmap_obj = TransRead.TransRead(bmap_path, logger=log)
-            bmap_obj.close()
-            return bmap_path
-        except TransRead.Error:
-            pass
+            bmap_obj = TransRead.TransRead(args.bmap, logger=log)
+        except TransRead.Error as err:
+            log.error("cannot open bmap file '%s': %s" % (args.bmap, str(err)))
+            raise SystemExit(1)
+        bmap_path = args.bmap
+    else:
+        # Automatically discover the bmap file
+        image_path = args.image
+        while True:
+            bmap_path = image_path + ".bmap"
+            try:
+                bmap_obj = TransRead.TransRead(bmap_path, logger=log)
+                log.info("discovered bmap file '%s'" % bmap_path)
+                break
+            except TransRead.Error:
+                pass
+
+            image_path, ext = os.path.splitext(image_path)
+            if ext == '':
+                return (None, None)
+
+    if not bmap_obj.is_compressed and not bmap_obj.is_url:
+        return (bmap_obj, bmap_path)
+
+    # Current implementation of BmapCopy requires a local file for block map,
+    # because it memory-maps it ('mmap()').
+    try:
+        # Create a temporary file for the bmap
+        tmp_obj = tempfile.TemporaryFile("w+")
+    except IOError as err:
+        log.error("cannot create a temporary file for bmap: %s" % err)
+        raise SystemExit(1)
 
-        image_path, ext = os.path.splitext(image_path)
-        if ext == '':
-            break
+    shutil.copyfileobj(bmap_obj, tmp_obj)
 
-    return None
+    tmp_obj.flush()
+    tmp_obj.seek(0)
+    bmap_obj.close()
+    return (tmp_obj, bmap_path)
 
 def copy_command_open_all(args, log):
     """
@@ -147,21 +179,7 @@ def copy_command_open_all(args, log):
 
     # Open the bmap file. Try to discover the bmap file automatically if it
     # was not specified.
-    bmap_path = args.bmap
-    if not bmap_path and not args.nobmap:
-        bmap_path = find_and_open_bmap(args.image, log)
-        if bmap_path:
-            log.info("discovered bmap file '%s'" % bmap_path)
-
-    bmap_obj = None
-    if bmap_path:
-        try:
-            # The current implementation of BmapCopy requires a local file for
-            # the bmap file.
-            bmap_obj = TransRead.TransRead(bmap_path, local=True, logger=log)
-        except TransRead.Error as err:
-            log.error("cannot open bmap file '%s': %s" % (bmap_path, str(err)))
-            raise SystemExit(1)
+    (bmap_obj, bmap_path) = find_and_open_bmap(args, log)
 
     # Try to open the destination file. If it does not exist, a new regular
     # file will be created. If it exists and it is a regular file - it'll be
@@ -210,7 +228,8 @@ def copy_command(args, log):
         if args.nobmap:
             log.info("no bmap given, copy entire image to '%s'" % args.dest)
         else:
-            log.error("please, use --nobmap option to flash without bmap")
+            log.error("bmap file not found, please, use --nobmap option to "
+                      "flash without bmap")
             raise SystemExit(1)
     else:
         log.info("block map format version %s" % writer.bmap_version)
index 9bf4e3420df72281e220b42bf693a725895db244..171b31fc267f258c7ec9989c5376a89a3b5f96cd 100644 (file)
@@ -219,17 +219,11 @@ class TransRead:
     this class are file-like objects which you can read and seek only forward.
     """
 
-    def __init__(self, filepath, local=False, logger=None):
+    def __init__(self, filepath, logger=None):
         """
         Class constructor. The 'filepath' argument is the full path to the file
-        to read transparently. If 'local' is True, then the file-like object is
-        guaranteed to be backed by an uncompressed local file. This means that
-        if the source file is compressed and/or an URL, then it will first be
-        copied to an temporary local file, and then all the subsequent
-        operations will be done with the uncompresed local copy.
-
-        The "logger" argument is the logger object to use for printing
-        messages.
+        to read transparently. The "logger" argument is the logger object to
+        use for printing messages.
         """
 
         self._logger = logger
@@ -274,9 +268,6 @@ class TransRead:
 
         self._open_compressed_file()
 
-        if local and (self.is_url or self.is_compressed):
-            self._create_local_copy()
-
     def __del__(self):
         """The class destructor which closes opened files."""
         for _file_obj in self._f_objs:
@@ -515,28 +506,6 @@ class TransRead:
         self.is_url = True
         self._f_objs.append(f_obj)
 
-    def _create_local_copy(self):
-        """Create a local copy of a remote or compressed file."""
-        import tempfile
-
-        try:
-            tmp_file_obj = tempfile.NamedTemporaryFile("w+")
-        except IOError as err:
-            raise Error("cannot create a temporary file: %s" % err)
-
-        while True:
-            chunk = self.read(1024 * 1024)
-            if not chunk:
-                break
-
-            tmp_file_obj.write(chunk)
-
-        tmp_file_obj.flush()
-
-        self.close()
-        self.__init__(tmp_file_obj.name, local = False)
-        tmp_file_obj.close()
-
     def read(self, size=-1):
         """
         Read the data from the file or URL and and uncompress it on-the-fly if