import logging
import tempfile
import traceback
+import shutil
from bmaptools import BmapCreate, BmapCopy, BmapHelpers, TransRead
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):
"""
# 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
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)
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
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:
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