import fcntl
from bmaptools import BmapHelpers
+
class Error(Exception):
"""
A class for exceptions generated by this module. We currently support only
"""
pass
+
class _FilemapBase:
"""
This is a base class for a couple of other classes in this module. This
raise Error("the method is not implemented")
+# The 'SEEK_HOLE' and 'SEEK_DATA' options of the file seek system call
+_SEEK_DATA = 3
+_SEEK_HOLE = 4
+
class FilemapSeek(_FilemapBase):
"""
This class uses the 'SEEK_HOLE' and 'SEEK_DATA' to find file block mapping.
# Call the base class constructor first
_FilemapBase.__init__(self, image)
+ def _lseek(self, file_obj, offset, whence):
+ """This is a helper function which invokes 'os.lseek' for file object
+ 'file_obj' and with specified 'offset' and 'whence'. The 'whence'
+ argument is supposed to be either '_SEEK_DATA' or '_SEEK_HOLE'. When
+ there is no more data or hole starting from 'offset', this function
+ returns '-1'. Otherwise the data or hole position is returned."""
+
+ try:
+ return os.lseek(file_obj.fileno(), offset, whence)
+ except OSError as err:
+ # The 'lseek' system call returns the ENXIO if there is no data or
+ # hole starting from the specified offset.
+ if err.errno == os.errno.ENXIO:
+ return -1
+ else:
+ raise
+
def block_is_mapped(self, block):
"""Refer the '_FilemapBase' class for the documentation."""
- raise Error("Not implemented")
+ offs = self._lseek(self._f_image, block * self.block_size, _SEEK_DATA)
+ if offs == -1:
+ return False
+
+ return offs / self.block_size == block
def block_is_unmapped(self, block):
"""Refer the '_FilemapBase' class for the documentation."""
- raise Error("Not implemented")
+ return not self.block_is_mapped(block)
+
+ def _get_ranges(self, start, count, whence1, whence2):
+ """
+ This function implements 'get_mapped_ranges()' and
+ 'get_unmapped_ranges()' depending on what is passed in the 'whence1'
+ and 'whence2' arguments.
+ """
+
+ assert whence1 != whence2
+ end = start * self.block_size
+ limit = end + count * self.block_size
+
+ while True:
+ start = self._lseek(self._f_image, end, whence1)
+ if start == -1 or start >= limit or start == self.image_size:
+ break
+
+ end = self._lseek(self._f_image, start, whence2)
+ if end == -1 or end == self.image_size:
+ end = self.blocks_cnt * self.block_size
+ if end > limit:
+ end = limit
+
+ yield (start / self.block_size, end / self.block_size - 1)
def get_mapped_ranges(self, start, count):
"""Refer the '_FilemapBase' class for the documentation."""
- raise Error("Not implemented")
+ return self._get_ranges(start, count, _SEEK_DATA, _SEEK_HOLE)
def get_unmapped_ranges(self, start, count):
"""Refer the '_FilemapBase' class for the documentation."""
- raise Error("Not implemented")
+ return self._get_ranges(start, count, _SEEK_HOLE, _SEEK_DATA)
+
# Below goes the FIEMAP ioctl implementation, which is not very readable
# because it deals with the rather complex FIEMAP ioctl. To understand the
if hole_first < start + count:
yield (hole_first, start + count - 1)
+
def filemap(image):
"""
Create and return an instance of a Filemap class - 'FilemapFiemap' or