From: Artem Bityutskiy Date: Thu, 16 Jan 2014 16:17:05 +0000 (+0200) Subject: Filemap: introduce base class X-Git-Tag: v3.2~40 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7dfe1d4a8c2fe500eb43664d4c09fd1e9da497ab;p=tools%2Fbmap-tools.git Filemap: introduce base class 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 --- 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: