[bmap-tools] [PATCH 04/14] Filemap: introduce base class

Artem Bityutskiy dedekind1 at gmail.com
Tue Jan 21 12:34:25 EST 2014


From: Artem Bityutskiy <artem.bityutskiy at intel.com>

We are going to introduce another class which uses the 'SEEK_HOLE' mechanism
for getting file block map, and some of the 'class Fiemap' functionality is
going to be the same in both. Let's separate that common functionality into a
separate '_FilemapBase' class which other classes will inherit.

The '_FilemapBase' class also defines the methods child classes have to
implement and documents them.

Change-Id: Ie1e05f96c1d8d9c77913b613940dc7e2e7971359
Signed-off-by: Artem Bityutskiy <artem.bityutskiy at intel.com>
---
 bmaptools/Filemap.py | 175 +++++++++++++++++++++++++++++++--------------------
 1 file changed, 108 insertions(+), 67 deletions(-)

diff --git a/bmaptools/Filemap.py b/bmaptools/Filemap.py
index 6375a5e..83d7412 100644
--- a/bmaptools/Filemap.py
+++ b/bmaptools/Filemap.py
@@ -29,24 +29,6 @@ import array
 import fcntl
 from bmaptools import BmapHelpers
 
-# Format string for 'struct fiemap'
-_FIEMAP_FORMAT = "=QQLLLL"
-# sizeof(struct fiemap)
-_FIEMAP_SIZE = struct.calcsize(_FIEMAP_FORMAT)
-# Format string for 'struct fiemap_extent'
-_FIEMAP_EXTENT_FORMAT = "=QQQQQLLLL"
-# sizeof(struct fiemap_extent)
-_FIEMAP_EXTENT_SIZE = struct.calcsize(_FIEMAP_EXTENT_FORMAT)
-# The FIEMAP ioctl number
-_FIEMAP_IOCTL = 0xC020660B
-# This FIEMAP ioctl flag which instructs the kernel to sync the file before
-# reading the block map
-_FIEMAP_FLAG_SYNC = 0x00000001
-# Size of the buffer for 'struct fiemap_extent' elements which will be used
-# when invoking the FIEMAP ioctl. The larger is the buffer, the less times the
-# FIEMAP ioctl will be invoked.
-_FIEMAP_BUFFER_SIZE = 256 * 1024
-
 class Error(Exception):
     """
     A class for exceptions generated by this module. We currently support only
@@ -55,19 +37,17 @@ class Error(Exception):
     """
     pass
 
-class Fiemap:
+class _FilemapBase:
     """
-    This class provides API to the FIEMAP ioctl. Namely, it allows to iterate
-    over all mapped blocks and over all holes.
+    This is a base class for a couple of other classes in this module. This
+    class simply performs the common parts of the initialization process: opens
+    the image file, gets its size, etc.
     """
 
     def __init__(self, image):
         """
         Initialize a class instance. The 'image' argument is full path to the
-        file to operate on, or a file object to operate on.
-
-        This class synchronizes the image file every time it invokes the FIEMAP
-        ioctl in order to work-around early FIEMAP implementation kernel bugs.
+        file or file object to operate on.
         """
 
         self._f_image_needs_close = False
@@ -79,19 +59,11 @@ class Fiemap:
             self._image_path = image
             self._open_image_file()
 
-        self._buf_size = _FIEMAP_BUFFER_SIZE
-
-        # Calculate how many 'struct fiemap_extent' elements fit the buffer
-        self._buf_size -= _FIEMAP_SIZE
-        self._fiemap_extent_cnt = self._buf_size / _FIEMAP_EXTENT_SIZE
-        assert self._fiemap_extent_cnt > 0
-        self._buf_size = self._fiemap_extent_cnt * _FIEMAP_EXTENT_SIZE
-        self._buf_size += _FIEMAP_SIZE
-
-        # Allocate a mutable buffer for the FIEMAP ioctl
-        self._buf = array.array('B', [0] * self._buf_size)
-
-        self.image_size = os.fstat(self._f_image.fileno()).st_size
+        try:
+            self.image_size = os.fstat(self._f_image.fileno()).st_size
+        except IOError as err:
+            raise Error("cannot get information about file '%s': %s"
+                        % (self._f_image.name, err))
 
         try:
             self.block_size = BmapHelpers.get_block_size(self._f_image)
@@ -102,23 +74,20 @@ class Fiemap:
         self.blocks_cnt = self.image_size + self.block_size - 1
         self.blocks_cnt /= self.block_size
 
-        # Synchronize the image file to make sure FIEMAP returns correct values
         try:
             self._f_image.flush()
         except IOError as err:
             raise Error("cannot flush image file '%s': %s"
                         % (self._image_path, err))
+
         try:
             os.fsync(self._f_image.fileno()),
         except OSError as err:
             raise Error("cannot synchronize image file '%s': %s "
                         % (self._image_path, err.strerror))
 
-        # Check if the FIEMAP ioctl is supported
-        self.block_is_mapped(0)
-
     def __del__(self):
-        """The class destructor which closes the opened files."""
+        """The class destructor which just closes the image file."""
         if self._f_image_needs_close:
             self._f_image.close()
 
@@ -132,6 +101,98 @@ class Fiemap:
 
         self._f_image_needs_close = True
 
+    def block_is_mapped(self, block): # pylint: disable=W0613,R0201
+        """
+        This method has has to be implemented by child classes. It returns
+        'True' if block number 'block' of the image file is mapped and 'False'
+        otherwise.
+        """
+
+        raise Error("the method is not implemented")
+
+    def block_is_unmapped(self, block): # pylint: disable=W0613,R0201
+        """
+        This method has has to be implemented by child classes. It returns
+        'True' if block number 'block' of the image file is not mapped (hole)
+        and 'False' otherwise.
+        """
+
+        raise Error("the method is not implemented")
+
+    def get_mapped_ranges(self, start, count): # pylint: disable=W0613,R0201
+        """
+        This method has has to be implemented by child classes. This is a
+        generator which yields ranges of mapped blocks in the file. The ranges
+        are tuples of 2 elements: [first, last], where 'first' is the first
+        mapped block and 'last' is the last mapped block.
+
+        The ranges are yielded for the area of the file of size 'count' blocks,
+        starting from block 'start'.
+        """
+
+        raise Error("the method is not implemented")
+
+    def get_unmapped_ranges(self, start, count): # pylint: disable=W0613,R0201
+        """
+        This method has has to be implemented by child classes. Just like
+        'get_mapped_ranges()', but yields unmapped block ranges instead
+        (holes).
+        """
+
+        raise Error("the method is not implemented")
+
+
+# Format string for 'struct fiemap'
+_FIEMAP_FORMAT = "=QQLLLL"
+# sizeof(struct fiemap)
+_FIEMAP_SIZE = struct.calcsize(_FIEMAP_FORMAT)
+# Format string for 'struct fiemap_extent'
+_FIEMAP_EXTENT_FORMAT = "=QQQQQLLLL"
+# sizeof(struct fiemap_extent)
+_FIEMAP_EXTENT_SIZE = struct.calcsize(_FIEMAP_EXTENT_FORMAT)
+# The FIEMAP ioctl number
+_FIEMAP_IOCTL = 0xC020660B
+# This FIEMAP ioctl flag which instructs the kernel to sync the file before
+# reading the block map
+_FIEMAP_FLAG_SYNC = 0x00000001
+# Size of the buffer for 'struct fiemap_extent' elements which will be used
+# when invoking the FIEMAP ioctl. The larger is the buffer, the less times the
+# FIEMAP ioctl will be invoked.
+_FIEMAP_BUFFER_SIZE = 256 * 1024
+
+class Fiemap(_FilemapBase):
+    """
+    This class provides API to the FIEMAP ioctl. Namely, it allows to iterate
+    over all mapped blocks and over all holes.
+
+    This class synchronizes the image file every time it invokes the FIEMAP
+    ioctl in order to work-around early FIEMAP implementation kernel bugs.
+    """
+
+    def __init__(self, image):
+        """
+        Initialize a class instance. The 'image' argument is full the file
+        object to operate on.
+        """
+
+        # Call the base class constructor first
+        _FilemapBase.__init__(self, image)
+
+        self._buf_size = _FIEMAP_BUFFER_SIZE
+
+        # Calculate how many 'struct fiemap_extent' elements fit the buffer
+        self._buf_size -= _FIEMAP_SIZE
+        self._fiemap_extent_cnt = self._buf_size / _FIEMAP_EXTENT_SIZE
+        assert self._fiemap_extent_cnt > 0
+        self._buf_size = self._fiemap_extent_cnt * _FIEMAP_EXTENT_SIZE
+        self._buf_size += _FIEMAP_SIZE
+
+        # Allocate a mutable buffer for the FIEMAP ioctl
+        self._buf = array.array('B', [0] * self._buf_size)
+
+        # Check if the FIEMAP ioctl is supported
+        self.block_is_mapped(0)
+
     def _invoke_fiemap(self, block, count):
         """
         Invoke the FIEMAP ioctl for 'count' blocks of the file starting from
@@ -166,11 +227,7 @@ class Fiemap:
         return struct.unpack(_FIEMAP_FORMAT, self._buf[:_FIEMAP_SIZE])
 
     def block_is_mapped(self, block):
-        """
-        This function returns 'True' if block number 'block' of the image file
-        is mapped and 'False' otherwise.
-        """
-
+        """Refer the '_FilemapBase' class for the documentation."""
         struct_fiemap = self._invoke_fiemap(block, 1)
 
         # The 3rd element of 'struct_fiemap' is the 'fm_mapped_extents' field.
@@ -179,11 +236,7 @@ class Fiemap:
         return bool(struct_fiemap[3])
 
     def block_is_unmapped(self, block):
-        """
-        This function returns 'True' if block number 'block' of the image file
-        is not mapped (hole) and 'False' otherwise.
-        """
-
+        """Refer the '_FilemapBase' class for the documentation."""
         return not self.block_is_mapped(block)
 
     def _unpack_fiemap_extent(self, index):
@@ -243,15 +296,7 @@ class Fiemap:
             block = extent_block + extent_count
 
     def get_mapped_ranges(self, start, count):
-        """
-        A generator which yields ranges of mapped blocks in the file. The
-        ranges are tuples of 2 elements: [first, last], where 'first' is the
-        first mapped block and 'last' is the last mapped block.
-
-        The ranges are yielded for the area of the file of size 'count' blocks,
-        starting from block 'start'.
-        """
-
+        """Refer the '_FilemapBase' class for the documentation."""
         iterator = self._do_get_mapped_ranges(start, count)
 
         first_prev, last_prev = iterator.next()
@@ -266,11 +311,7 @@ class Fiemap:
         yield (first_prev, last_prev)
 
     def get_unmapped_ranges(self, start, count):
-        """
-        Just like 'get_mapped_ranges()', but yields unmapped block ranges
-        instead (holes).
-        """
-
+        """Refer the '_FilemapBase' class for the documentation."""
         hole_first = start
         for first, last in self._do_get_mapped_ranges(start, count):
             if first > hole_first:
-- 
1.8.3.1




More information about the Bmap-tools mailing list