From dee1cc9bb82cac4419ad01d31c14e31a8f03d3fd Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 18 Jan 2014 15:46:03 +0200 Subject: [PATCH] Filemap: implement the FilemapSeek class Change-Id: Idf8bf086ead16affede86a1839f29536b8e90bbd Signed-off-by: Artem Bityutskiy --- bmaptools/Filemap.py | 61 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/bmaptools/Filemap.py b/bmaptools/Filemap.py index 76d2a5f..8104ff0 100644 --- a/bmaptools/Filemap.py +++ b/bmaptools/Filemap.py @@ -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 -- 2.34.1