From 024a12ecc28931031b4e27d6e0b757e97722c8f2 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 16 Sep 2013 12:23:03 +0300 Subject: [PATCH] TransRead: remove local caching functionality 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 --- TODO | 2 -- bmaptool | 95 ++++++++++++++++++++++++++++++-------------------- bmaptools/TransRead.py | 37 ++------------------ 3 files changed, 60 insertions(+), 74 deletions(-) diff --git a/TODO b/TODO index 3e5f241..38431be 100644 --- 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. diff --git a/bmaptool b/bmaptool index 3c607e8..bcc0b6b 100755 --- 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) diff --git a/bmaptools/TransRead.py b/bmaptools/TransRead.py index 9bf4e34..171b31f 100644 --- a/bmaptools/TransRead.py +++ b/bmaptools/TransRead.py @@ -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 -- 2.7.4