[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