Filemap: implement the FilemapSeek class
authorArtem Bityutskiy <artem.bityutskiy@intel.com>
Sat, 18 Jan 2014 13:46:03 +0000 (15:46 +0200)
committerArtem Bityutskiy <artem.bityutskiy@intel.com>
Tue, 21 Jan 2014 17:12:09 +0000 (19:12 +0200)
Change-Id: Idf8bf086ead16affede86a1839f29536b8e90bbd
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@intel.com>
bmaptools/Filemap.py

index 76d2a5f64299810b475da6aab1e76ff550098744..8104ff02010b7598b167edb2e6b47252662b43fb 100644 (file)
@@ -28,6 +28,7 @@ import array
 import fcntl
 from bmaptools import BmapHelpers
 
+
 class Error(Exception):
     """
     A class for exceptions generated by this module. We currently support only
@@ -36,6 +37,7 @@ class Error(Exception):
     """
     pass
 
+
 class _FilemapBase:
     """
     This is a base class for a couple of other classes in this module. This
@@ -140,6 +142,10 @@ class _FilemapBase:
 
         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.
@@ -151,21 +157,67 @@ class FilemapSeek(_FilemapBase):
         # 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
@@ -352,6 +404,7 @@ class FilemapFiemap(_FilemapBase):
         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