def _bmap_file_end(self, mapped_cnt, block_size, blocks_cnt): """ A helper funstion which generates the final parts of the block map file: the ending tags and the information about the amount of mapped blocks. """ xml = "\t</BlockMap>\n\n" size = misc.human_size(mapped_cnt * block_size) percent = (mapped_cnt * 100.0) / blocks_cnt xml += "\t<!-- Count of mapped blocks (%s or %.1f%% mapped) -->\n" \ % (size, percent) xml += "\t<MappedBlocksCount> %u </MappedBlocksCount>\n" % mapped_cnt xml += "</bmap>" return xml
def __init__(self, image, bmap): """ Initialize a class instance: * image - full path or a file-like object of the image to create bmap for * bmap - full path or a file object to use for writing the resulting bmap to """ self.image_size = None self.image_size_human = None self.block_size = None self.blocks_cnt = None self.mapped_cnt = None self.mapped_size = None self.mapped_size_human = None self.mapped_percent = None self._mapped_count_pos1 = None self._mapped_count_pos2 = None self._sha1_pos = None self._f_image_needs_close = False self._f_bmap_needs_close = False if hasattr(image, "read"): self._f_image = image self._image_path = image.name else: self._image_path = image self._open_image_file() if hasattr(bmap, "read"): self._f_bmap = bmap self._bmap_path = bmap.name else: self._bmap_path = bmap self._open_bmap_file() self.fiemap = Fiemap.Fiemap(self._f_image) self.image_size = self.fiemap.image_size self.image_size_human = human_size(self.image_size) if self.image_size == 0: raise Error("cannot generate bmap for zero-sized image file '%s'" \ % self._image_path) self.block_size = self.fiemap.block_size self.blocks_cnt = self.fiemap.blocks_cnt
def _bmap_file_start(self, block_size, image_size, blocks_cnt): """ A helper function which generates the starting contents of the block map file: the header comment, image size, block size, etc. """ xml = "<?xml version=\"1.0\" ?>\n\n" xml += "<!-- This file contains block map for an image file. The block map\n" xml += " is basically a list of block numbers in the image file. It lists\n" xml += " only those blocks which contain data (boot sector, partition\n" xml += " table, file-system metadata, files, directories, extents, etc).\n" xml += " These blocks have to be copied to the target device. The other\n" xml += " blocks do not contain any useful data and do not have to be\n" xml += " copied to the target device. Thus, using the block map users can\n" xml += " flash the image fast. So the block map is just an optimization.\n" xml += " It is OK to ignore this file and just flash the entire image to\n" xml += " the target device if the flashing speed is not important.\n\n" xml += " Note, this file contains commentaries with useful information\n" xml += " like image size in gigabytes, percentage of mapped data, etc.\n" xml += " This data is there merely to make the XML file human-readable.\n\n" xml += " The 'version' attribute is the block map file format version in\n" xml += " the 'major.minor' format. The version major number is increased\n" xml += " whenever we make incompatible changes to the block map format,\n" xml += " meaning that the bmap-aware flasher would have to be modified in\n" xml += " order to support the new format. The minor version is increased\n" xml += " in case of compatible changes. For example, if we add an attribute\n" xml += " which is optional for the bmap-aware flasher. -->\n" xml += "<bmap version=\"1.1\">\n" xml += "\t<!-- Image size in bytes (%s) -->\n" \ % misc.human_size(image_size) xml += "\t<ImageSize> %u </ImageSize>\n\n" % image_size xml += "\t<!-- Size of a block in bytes -->\n" xml += "\t<BlockSize> %u </BlockSize>\n\n" % block_size xml += "\t<!-- Count of blocks in the image file -->\n" xml += "\t<BlocksCount> %u </BlocksCount>\n\n" % blocks_cnt xml += "\t<!-- The block map which consists of elements which may either\n" xml += "\t be a range of blocks or a single block. The 'sha1' attribute\n" xml += "\t is the SHA1 checksum of the this range of blocks. -->\n" xml += "\t<BlockMap>\n" return xml
def generate(self, include_checksums=True): """ Generate bmap for the image file. If 'include_checksums' is 'True', also generate checksums for block ranges. """ # Save image file position in order to restore it at the end image_pos = self._f_image.tell() self._bmap_file_start() # Generate the block map and write it to the XML block map # file as we go. self.mapped_cnt = 0 for first, last in self.filemap.get_mapped_ranges(0, self.blocks_cnt): self.mapped_cnt += last - first + 1 if include_checksums: chksum = self._calculate_chksum(first, last) chksum = " chksum=\"%s\"" % chksum else: chksum = "" if first != last: self._f_bmap.write(" <Range%s> %s-%s </Range>\n" % (chksum, first, last)) else: self._f_bmap.write(" <Range%s> %s </Range>\n" % (chksum, first)) self.mapped_size = self.mapped_cnt * self.block_size self.mapped_size_human = human_size(self.mapped_size) self.mapped_percent = (self.mapped_cnt * 100.0) / self.blocks_cnt self._bmap_file_end() try: self._f_bmap.flush() except IOError as err: raise Error("cannot flush the bmap file '%s': %s" % (self._bmap_path, err)) self._f_image.seek(image_pos)
def generate(self, include_checksums = True): """ Generate bmap for the image file. If 'include_checksums' is 'True', also generate SHA1 checksums for block ranges. """ # Save image file position in order to restore it at the end image_pos = self._f_image.tell() self._bmap_file_start() # Generate the block map and write it to the XML block map # file as we go. self.mapped_cnt = 0 for first, last in self.fiemap.get_mapped_ranges(0, self.blocks_cnt): self.mapped_cnt += last - first + 1 if include_checksums: sha1 = self._calculate_sha1(first, last) sha1 = " sha1=\"%s\"" % sha1 else: sha1 = "" if first != last: self._f_bmap.write(" <Range%s> %s-%s </Range>\n" \ % (sha1, first, last)) else: self._f_bmap.write(" <Range%s> %s </Range>\n" \ % (sha1, first)) self.mapped_size = self.mapped_cnt * self.block_size self.mapped_size_human = human_size(self.mapped_size) self.mapped_percent = (self.mapped_cnt * 100.0) / self.blocks_cnt self._bmap_file_end() try: self._f_bmap.flush() except IOError as err: raise Error("cannot flush the bmap file '%s': %s" \ % (self._bmap_path, err)) self._f_image.seek(image_pos)
def __init__(self, image, bmap, chksum_type="sha256", log=None): """ Initialize a class instance: * image - full path or a file-like object of the image to create bmap for * bmap - full path or a file object to use for writing the resulting bmap to * chksum - type of the check sum to use in the bmap file (all checksum types which python's "hashlib" module supports are allowed). * log - the logger object to use for printing messages. """ self._log = log if self._log is None: self._log = logging.getLogger(__name__) self.image_size = None self.image_size_human = None self.block_size = None self.blocks_cnt = None self.mapped_cnt = None self.mapped_size = None self.mapped_size_human = None self.mapped_percent = None self._mapped_count_pos1 = None self._mapped_count_pos2 = None self._chksum_pos = None self._f_image_needs_close = False self._f_bmap_needs_close = False self._cs_type = chksum_type.lower() try: self._cs_len = len(hashlib.new(self._cs_type).hexdigest()) except ValueError as err: raise Error("cannot initialize hash function \"%s\": %s" % (self._cs_type, err)) if hasattr(image, "read"): self._f_image = image self._image_path = image.name else: self._image_path = image self._open_image_file() if hasattr(bmap, "read"): self._f_bmap = bmap self._bmap_path = bmap.name else: self._bmap_path = bmap self._open_bmap_file() try: self.filemap = Filemap.filemap(self._f_image, self._log) except (Filemap.Error, Filemap.ErrorNotSupp) as err: raise Error("cannot generate bmap: %s" % err) self.image_size = self.filemap.image_size self.image_size_human = human_size(self.image_size) if self.image_size == 0: raise Error("cannot generate bmap for zero-sized image file '%s'" % self._image_path) self.block_size = self.filemap.block_size self.blocks_cnt = self.filemap.blocks_cnt