-"""
-This module implements the block map (AKA bmap) generating functionality and
-provides corresponding API (in a form of the BmapCreate class).
+""" This module implements the block map (bmap) creation functionality and
+provides the corresponding API in form of the 'BmapCreate' class.
The idea is that while images files may generally be very large (e.g., 4GiB),
they may nevertheless contain only little real data, e.g., 512MiB. This data
are files, directories, file-system meta-data, partition table, etc. When
-flashing the image to the target device, you do not have to copy all the 4GiB
-of data, you can copy only 512MiB of it, which is 4 times less, so flashing
-should presumably be 4 times faster.
+copying the image to the target device, you do not have to copy all the 4GiB of
+data, you can copy only 512MiB of it, which is 4 times less, so copying should
+presumably be 4 times faster.
The block map file is an XML file which contains a list of blocks which have to
be copied to the target device. The other blocks are not used and there is no
-need to copy them.
-
-The image file has to be a sparse file. Generally, this often means that when
-you generate this image file, you should start with a huge sparse file which
-contains a single hole spanning the entire file. Then you should partition it,
-write all the data (probably by means of loop-back mounting the image file or
-parts of it), etc. The end result should be a sparse file where holes represent
-the areas which do not have to be flashed. On the other hand, the mapped file
-areas represent the areas which have to be flashed. The block map file lists
-these areas.
+need to copy them. The XML file also contains some additional information like
+block size, image size, count of mapped blocks, etc. There are also many
+commentaries, so it is human-readable.
+
+The image has to be a sparse file. Generally, this means that when you generate
+this image file, you should start with a huge sparse file which contains a
+single hole spanning the entire file. Then you should partition it, write all
+the data (probably by means of loop-back mounting the image or parts of it),
+etc. The end result should be a sparse file where mapped areas represent useful
+parts of the image and holes represent useless parts of the image, which do not
+have to be copied when copying the image to the target device.
At the moment this module uses the FIBMAP ioctl to detect holes. However, it is
possible to speed it up by using presumably faster FIBMAP ioctl (and fall-back
-to FIBMAP if the kernel is too old and does not support FIBMAP).
-"""
+to FIBMAP if the kernel is too old and does not support FIBMAP). """
import os
import hashlib
bmap_version = "1.2"
class Error(Exception):
- """ A class for exceptions of BmapCreate. We currently support only one
- type of exceptions, and we basically throw human-readable problem
- description in case of errors. """
+ """ A class for exceptions generated by the 'BmapCreate' module. We
+ currently support only one type of exceptions, and we basically throw
+ human-readable problem description in case of errors. """
def __init__(self, strerror, errno = None):
Exception.__init__(self, strerror)
return self.strerror
class BmapCreate:
- """ This class the bmap creation functionality. To generate a bmap for an
- image (which is supposedly a sparse file) you should first create an
- instance of 'BmapCreate' and provide:
- * full path to the image to create bmap for
- * a logger object to output the generated bmap to
+ """ This class implements the bmap creation functionality. To generate a
+ bmap for an image (which is supposedly a sparse file), you should first
+ create an instance of 'BmapCreate' and provide:
+
+ * full path to the image to create bmap for
+ * a logger object to output the generated bmap to
- Then you should invoke the 'generate()' method of this class. It will
- use the FIEMAP ioctl to generate the bmap, and fall-back to the FIBMAP
- ioctl if FIEMAP is not supported. """
+ Then you should invoke the 'generate()' method of this class. It will use
+ the FIEMAP ioctl to generate the bmap, and fall-back to the FIBMAP ioctl if
+ FIEMAP is not supported. """
def __init__(self, image_path, output):
""" Initialize a class instance:
- * image_path - full path to the image file to generate bmap for
- * output - a logger object to write the generated bmap to """
+ * image_path - full path to the image file to generate bmap for
+ * output - a logger object to write the generated bmap to """
self._image_path = image_path
self._output = output
self._output.info(xml)
def _is_mapped_fibmap(self, block):
- """ A helper function which returns True if block number 'block' of the
- image file is mapped and False otherwise.
+ """ A helper function which returns 'True' if block number 'block' of
+ the image file is mapped and 'False' otherwise.
- This function uses the FIBMAP ioctl (number 1) to detect whether
- 'block' is mapped to the disk. The ioctl returns zero if 'block'
- is not mapped and non-zero disk block number if it is mapped.
- Unfortunately, FIBMAP requires root rights, unlike FIEMAP.
+ This function uses the FIBMAP ioctl (number 1) to detect whether
+ 'block' is mapped to the disk. The ioctl returns zero if 'block' is not
+ mapped and non-zero disk block number if it is mapped. Unfortunately,
+ FIBMAP requires root rights, unlike FIEMAP.
- This function should only be used if the more advanced FIEMAP ioctl
- is not supported, probably because the Linux kernel is too old. """
+ This function should only be used if the more advanced FIEMAP ioctl is
+ not supported, probably because the Linux kernel is too old. """
try:
binary_data = ioctl(self._f_image, 1, struct.pack('I', block))
return result != 0
def _is_mapped_fiemap(self, block):
- """ A helper function which returns True if block number 'block' of the
- image file is mapped and False otherwise.
+ """ A helper function which returns 'True' if block number 'block' of the
+ image file is mapped and 'False' otherwise.
- This function uses the FIEMAP ioctl to detect whether 'block' is
- mapped to the disk. However, we do not use all the power of this
- ioctl: we call it for each and every block, while there is a
- possibility to call it once for a range of blocks, which is a lot
- faster when dealing with huge files. """
+ This function uses the FIEMAP ioctl to detect whether 'block' is mapped
+ to the disk. However, we do not use all the power of this ioctl: we
+ call it for each and every block, while there is a possibility to call
+ it once for a range of blocks, which is a lot faster when dealing with
+ huge files. """
# I know that the below cruft is not readable. To understand that, you
# need to know the FIEMAP interface, which is documented in the
def _is_mapped(self, block):
""" A helper function which returns True if block number 'block' of the
- image file is mapped and False otherwise. """
+ image file is mapped and False otherwise. """
if self.fiemap_supported:
return self._is_mapped_fiemap(block)
else:
def _get_ranges(self):
""" A helper function which generates ranges of mapped image file
- blocks. It uses the FIBMAP ioctl to check which blocks are mapped.
- Of course, the image file must have been created as a sparse file
- originally, otherwise all blocks will be mapped. And it is also
- essential to generate the block map before the file had been copied
- anywhere or compressed, because otherwise we lose the information
- about unmapped blocks. """
+ blocks. """
for key, group in groupby(xrange(self.bmap_blocks_cnt), self._is_mapped):
if key:
def _bmap_file_end(self):
""" A helper function which generates the final parts of the block map
- file: the ending tags and the information about the amount of
- mapped blocks. """
+ file: the ending tags and the information about the amount of mapped
+ blocks. """
xml = "\t</BlockMap>\n\n"
xml += "\t<!-- Count of mapped blocks (%s or %.1f%% mapped) -->\n" \
def _calculate_sha1(self, first, last):
""" A helper function which calculates SHA1 checksum for the range of
- blocks of the image file: from block 'first' to block 'last'. """
+ blocks of the image file: from block 'first' to block 'last'. """
start = first * self.bmap_block_size
end = (last + 1) * self.bmap_block_size
return hash_obj.hexdigest()
def generate(self, include_checksums = True):
- """ Generate bmap for the image file. If 'include_checksums' is True,
- also generate SHA1 checksums for block ranges. """
+ """ Generate bmap for the image file. If 'include_checksums' is 'True',
+ also generate SHA1 checksums for block ranges. """
self._bmap_file_start()
self._f_image.seek(0)