diff options
Diffstat (limited to 'scripts/lib/wic')
28 files changed, 4657 insertions, 0 deletions
diff --git a/scripts/lib/wic/__init__.py b/scripts/lib/wic/__init__.py new file mode 100644 index 0000000000..85876b138c --- /dev/null +++ b/scripts/lib/wic/__init__.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python -tt +# +# Copyright (c) 2007 Red Hat, Inc. +# Copyright (c) 2011 Intel, Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; version 2 of the License +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +class WicError(Exception): + pass diff --git a/scripts/lib/wic/canned-wks/common.wks.inc b/scripts/lib/wic/canned-wks/common.wks.inc new file mode 100644 index 0000000000..5cf2fd1f3e --- /dev/null +++ b/scripts/lib/wic/canned-wks/common.wks.inc @@ -0,0 +1,3 @@ +# This file is included into 3 canned wks files from this directory +part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 +part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 diff --git a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg new file mode 100644 index 0000000000..d5a07d2048 --- /dev/null +++ b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg @@ -0,0 +1,27 @@ +# This is an example configuration file for syslinux. +TIMEOUT 50 +ALLOWOPTIONS 1 +SERIAL 0 115200 +PROMPT 0 + +UI vesamenu.c32 +menu title Select boot options +menu tabmsg Press [Tab] to edit, [Return] to select + +DEFAULT Graphics console boot + +LABEL Graphics console boot +KERNEL /vmlinuz +APPEND label=boot root=/dev/sda2 rootwait + +LABEL Serial console boot +KERNEL /vmlinuz +APPEND label=boot root=/dev/sda2 rootwait console=ttyS0,115200 + +LABEL Graphics console install +KERNEL /vmlinuz +APPEND label=install root=/dev/sda2 rootwait + +LABEL Serial console install +KERNEL /vmlinuz +APPEND label=install root=/dev/sda2 rootwait console=ttyS0,115200 diff --git a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks new file mode 100644 index 0000000000..3529e05c87 --- /dev/null +++ b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks @@ -0,0 +1,8 @@ +# short-description: Create a 'pcbios' direct disk image with custom bootloader config +# long-description: Creates a partitioned legacy BIOS disk image that the user +# can directly dd to boot media. The bootloader configuration source is a user file. + +include common.wks.inc + +bootloader --configfile="directdisk-bootloader-config.cfg" + diff --git a/scripts/lib/wic/canned-wks/directdisk-gpt.wks b/scripts/lib/wic/canned-wks/directdisk-gpt.wks new file mode 100644 index 0000000000..8d7d8de6ea --- /dev/null +++ b/scripts/lib/wic/canned-wks/directdisk-gpt.wks @@ -0,0 +1,10 @@ +# short-description: Create a 'pcbios' direct disk image +# long-description: Creates a partitioned legacy BIOS disk image that the user +# can directly dd to boot media. + + +part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 +part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid + +bootloader --ptable gpt --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8" + diff --git a/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks b/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks new file mode 100644 index 0000000000..f61d941d6d --- /dev/null +++ b/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks @@ -0,0 +1,23 @@ +# short-description: Create multi rootfs image using rootfs plugin +# long-description: Creates a partitioned disk image with two rootfs partitions +# using rootfs plugin. +# +# Partitions can use either +# - indirect rootfs references to image recipe(s): +# wic create directdisk-multi-indirect-recipes -e core-image-minimal \ +# --rootfs-dir rootfs1=core-image-minimal +# --rootfs-dir rootfs2=core-image-minimal-dev +# +# - or paths to rootfs directories: +# wic create directdisk-multi-rootfs \ +# --rootfs-dir rootfs1=tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0-r0/rootfs/ +# --rootfs-dir rootfs2=tmp/work/qemux86_64-poky-linux/core-image-minimal-dev/1.0-r0/rootfs/ +# +# - or any combinations of -r and --rootfs command line options + +part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 +part / --source rootfs --rootfs-dir=rootfs1 --ondisk sda --fstype=ext4 --label platform --align 1024 +part /rescue --source rootfs --rootfs-dir=rootfs2 --ondisk sda --fstype=ext4 --label secondary --align 1024 + +bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8" + diff --git a/scripts/lib/wic/canned-wks/directdisk.wks b/scripts/lib/wic/canned-wks/directdisk.wks new file mode 100644 index 0000000000..8c8e06b02c --- /dev/null +++ b/scripts/lib/wic/canned-wks/directdisk.wks @@ -0,0 +1,8 @@ +# short-description: Create a 'pcbios' direct disk image +# long-description: Creates a partitioned legacy BIOS disk image that the user +# can directly dd to boot media. + +include common.wks.inc + +bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8" + diff --git a/scripts/lib/wic/canned-wks/mkefidisk.wks b/scripts/lib/wic/canned-wks/mkefidisk.wks new file mode 100644 index 0000000000..9f534fe184 --- /dev/null +++ b/scripts/lib/wic/canned-wks/mkefidisk.wks @@ -0,0 +1,11 @@ +# short-description: Create an EFI disk image +# long-description: Creates a partitioned EFI disk image that the user +# can directly dd to boot media. + +part /boot --source bootimg-efi --sourceparams="loader=grub-efi" --ondisk sda --label msdos --active --align 1024 + +part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid + +part swap --ondisk sda --size 44 --label swap1 --fstype=swap + +bootloader --ptable gpt --timeout=5 --append="rootfstype=ext4 console=ttyS0,115200 console=tty0" diff --git a/scripts/lib/wic/canned-wks/mkhybridiso.wks b/scripts/lib/wic/canned-wks/mkhybridiso.wks new file mode 100644 index 0000000000..9d34e9b477 --- /dev/null +++ b/scripts/lib/wic/canned-wks/mkhybridiso.wks @@ -0,0 +1,7 @@ +# short-description: Create a hybrid ISO image +# long-description: Creates an EFI and legacy bootable hybrid ISO image +# which can be used on optical media as well as USB media. + +part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi,image_name=HYBRID_ISO_IMG" --ondisk cd --label HYBRIDISO --fstype=ext4 + +bootloader --timeout=15 --append="" diff --git a/scripts/lib/wic/canned-wks/qemux86-directdisk.wks b/scripts/lib/wic/canned-wks/qemux86-directdisk.wks new file mode 100644 index 0000000000..db30bbced0 --- /dev/null +++ b/scripts/lib/wic/canned-wks/qemux86-directdisk.wks @@ -0,0 +1,8 @@ +# short-description: Create a qemu machine 'pcbios' direct disk image +# long-description: Creates a partitioned legacy BIOS disk image that the user +# can directly use to boot a qemu machine. + +include common.wks.inc + +bootloader --timeout=0 --append="vga=0 uvesafb.mode_option=640x480-32 root=/dev/sda2 rw mem=256M ip=192.168.7.2::192.168.7.1:255.255.255.0 oprofile.timer=1 rootfstype=ext4 " + diff --git a/scripts/lib/wic/canned-wks/sdimage-bootpart.wks b/scripts/lib/wic/canned-wks/sdimage-bootpart.wks new file mode 100644 index 0000000000..7ffd632f4a --- /dev/null +++ b/scripts/lib/wic/canned-wks/sdimage-bootpart.wks @@ -0,0 +1,6 @@ +# short-description: Create SD card image with a boot partition +# long-description: Creates a partitioned SD card image. Boot files +# are located in the first vfat partition. + +part /boot --source bootimg-partition --ondisk mmcblk --fstype=vfat --label boot --active --align 4 --size 16 +part / --source rootfs --ondisk mmcblk --fstype=ext4 --label root --align 4 diff --git a/scripts/lib/wic/canned-wks/systemd-bootdisk.wks b/scripts/lib/wic/canned-wks/systemd-bootdisk.wks new file mode 100644 index 0000000000..4bd9d6a65f --- /dev/null +++ b/scripts/lib/wic/canned-wks/systemd-bootdisk.wks @@ -0,0 +1,11 @@ +# short-description: Create an EFI disk image with systemd-boot +# long-description: Creates a partitioned EFI disk image that the user +# can directly dd to boot media. The selected bootloader is systemd-boot. + +part /boot --source bootimg-efi --sourceparams="loader=systemd-boot" --ondisk sda --label msdos --active --align 1024 + +part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid + +part swap --ondisk sda --size 44 --label swap1 --fstype=swap + +bootloader --ptable gpt --timeout=5 --append="rootwait rootfstype=ext4 console=ttyS0,115200 console=tty0" diff --git a/scripts/lib/wic/engine.py b/scripts/lib/wic/engine.py new file mode 100644 index 0000000000..f59821fea6 --- /dev/null +++ b/scripts/lib/wic/engine.py @@ -0,0 +1,257 @@ +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Copyright (c) 2013, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION + +# This module implements the image creation engine used by 'wic' to +# create images. The engine parses through the OpenEmbedded kickstart +# (wks) file specified and generates images that can then be directly +# written onto media. +# +# AUTHORS +# Tom Zanussi <tom.zanussi (at] linux.intel.com> +# + +import logging +import os + +from wic import WicError +from wic.pluginbase import PluginMgr +from wic.utils.misc import get_bitbake_var + +logger = logging.getLogger('wic') + +def verify_build_env(): + """ + Verify that the build environment is sane. + + Returns True if it is, false otherwise + """ + if not os.environ.get("BUILDDIR"): + raise WicError("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)") + + return True + + +CANNED_IMAGE_DIR = "lib/wic/canned-wks" # relative to scripts +SCRIPTS_CANNED_IMAGE_DIR = "scripts/" + CANNED_IMAGE_DIR +WIC_DIR = "wic" + +def build_canned_image_list(path): + layers_path = get_bitbake_var("BBLAYERS") + canned_wks_layer_dirs = [] + + if layers_path is not None: + for layer_path in layers_path.split(): + for wks_path in (WIC_DIR, SCRIPTS_CANNED_IMAGE_DIR): + cpath = os.path.join(layer_path, wks_path) + if os.path.isdir(cpath): + canned_wks_layer_dirs.append(cpath) + + cpath = os.path.join(path, CANNED_IMAGE_DIR) + canned_wks_layer_dirs.append(cpath) + + return canned_wks_layer_dirs + +def find_canned_image(scripts_path, wks_file): + """ + Find a .wks file with the given name in the canned files dir. + + Return False if not found + """ + layers_canned_wks_dir = build_canned_image_list(scripts_path) + + for canned_wks_dir in layers_canned_wks_dir: + for root, dirs, files in os.walk(canned_wks_dir): + for fname in files: + if fname.endswith("~") or fname.endswith("#"): + continue + if fname.endswith(".wks") and wks_file + ".wks" == fname: + fullpath = os.path.join(canned_wks_dir, fname) + return fullpath + return None + + +def list_canned_images(scripts_path): + """ + List the .wks files in the canned image dir, minus the extension. + """ + layers_canned_wks_dir = build_canned_image_list(scripts_path) + + for canned_wks_dir in layers_canned_wks_dir: + for root, dirs, files in os.walk(canned_wks_dir): + for fname in files: + if fname.endswith("~") or fname.endswith("#"): + continue + if fname.endswith(".wks"): + fullpath = os.path.join(canned_wks_dir, fname) + with open(fullpath) as wks: + for line in wks: + desc = "" + idx = line.find("short-description:") + if idx != -1: + desc = line[idx + len("short-description:"):].strip() + break + basename = os.path.splitext(fname)[0] + print(" %s\t\t%s" % (basename.ljust(30), desc)) + + +def list_canned_image_help(scripts_path, fullpath): + """ + List the help and params in the specified canned image. + """ + found = False + with open(fullpath) as wks: + for line in wks: + if not found: + idx = line.find("long-description:") + if idx != -1: + print() + print(line[idx + len("long-description:"):].strip()) + found = True + continue + if not line.strip(): + break + idx = line.find("#") + if idx != -1: + print(line[idx + len("#:"):].rstrip()) + else: + break + + +def list_source_plugins(): + """ + List the available source plugins i.e. plugins available for --source. + """ + plugins = PluginMgr.get_plugins('source') + + for plugin in plugins: + print(" %s" % plugin) + + +def wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir, + native_sysroot, options): + """ + Create image + + wks_file - user-defined OE kickstart file + rootfs_dir - absolute path to the build's /rootfs dir + bootimg_dir - absolute path to the build's boot artifacts directory + kernel_dir - absolute path to the build's kernel directory + native_sysroot - absolute path to the build's native sysroots dir + image_output_dir - dirname to create for image + options - wic command line options (debug, bmap, etc) + + Normally, the values for the build artifacts values are determined + by 'wic -e' from the output of the 'bitbake -e' command given an + image name e.g. 'core-image-minimal' and a given machine set in + local.conf. If that's the case, the variables get the following + values from the output of 'bitbake -e': + + rootfs_dir: IMAGE_ROOTFS + kernel_dir: DEPLOY_DIR_IMAGE + native_sysroot: STAGING_DIR_NATIVE + + In the above case, bootimg_dir remains unset and the + plugin-specific image creation code is responsible for finding the + bootimg artifacts. + + In the case where the values are passed in explicitly i.e 'wic -e' + is not used but rather the individual 'wic' options are used to + explicitly specify these values. + """ + try: + oe_builddir = os.environ["BUILDDIR"] + except KeyError: + raise WicError("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)") + + if not os.path.exists(options.outdir): + os.makedirs(options.outdir) + + pname = 'direct' + plugin_class = PluginMgr.get_plugins('imager').get(pname) + if not plugin_class: + raise WicError('Unknown plugin: %s' % pname) + + plugin = plugin_class(wks_file, rootfs_dir, bootimg_dir, kernel_dir, + native_sysroot, oe_builddir, options) + + plugin.do_create() + + logger.info("The image(s) were created using OE kickstart file:\n %s", wks_file) + + +def wic_list(args, scripts_path): + """ + Print the list of images or source plugins. + """ + if len(args) < 1: + return False + + if args == ["images"]: + list_canned_images(scripts_path) + return True + elif args == ["source-plugins"]: + list_source_plugins() + return True + elif len(args) == 2 and args[1] == "help": + wks_file = args[0] + fullpath = find_canned_image(scripts_path, wks_file) + if not fullpath: + raise WicError("No image named %s found, exiting. " + "(Use 'wic list images' to list available images, " + "or specify a fully-qualified OE kickstart (.wks) " + "filename)" % wks_file) + + list_canned_image_help(scripts_path, fullpath) + return True + + return False + +def find_canned(scripts_path, file_name): + """ + Find a file either by its path or by name in the canned files dir. + + Return None if not found + """ + if os.path.exists(file_name): + return file_name + + layers_canned_wks_dir = build_canned_image_list(scripts_path) + for canned_wks_dir in layers_canned_wks_dir: + for root, dirs, files in os.walk(canned_wks_dir): + for fname in files: + if fname == file_name: + fullpath = os.path.join(canned_wks_dir, fname) + return fullpath + +def get_custom_config(boot_file): + """ + Get the custom configuration to be used for the bootloader. + + Return None if the file can't be found. + """ + # Get the scripts path of poky + scripts_path = os.path.abspath("%s/../.." % os.path.dirname(__file__)) + + cfg_file = find_canned(scripts_path, boot_file) + if cfg_file: + with open(cfg_file, "r") as f: + config = f.read() + return config diff --git a/scripts/lib/wic/filemap.py b/scripts/lib/wic/filemap.py new file mode 100644 index 0000000000..1f1aacc522 --- /dev/null +++ b/scripts/lib/wic/filemap.py @@ -0,0 +1,564 @@ +# Copyright (c) 2012 Intel, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +""" +This module implements python implements a way to get file block. Two methods +are supported - the FIEMAP ioctl and the 'SEEK_HOLE / SEEK_DATA' features of +the file seek syscall. The former is implemented by the 'FilemapFiemap' class, +the latter is implemented by the 'FilemapSeek' class. Both classes provide the +same API. The 'filemap' function automatically selects which class can be used +and returns an instance of the class. +""" + +# Disable the following pylint recommendations: +# * Too many instance attributes (R0902) +# pylint: disable=R0902 + +import os +import struct +import array +import fcntl +import tempfile +import logging + +def get_block_size(file_obj): + """ + Returns block size for file object 'file_obj'. Errors are indicated by the + 'IOError' exception. + """ + + from fcntl import ioctl + import struct + + # Get the block size of the host file-system for the image file by calling + # the FIGETBSZ ioctl (number 2). + binary_data = ioctl(file_obj, 2, struct.pack('I', 0)) + return struct.unpack('I', binary_data)[0] + +class ErrorNotSupp(Exception): + """ + An exception of this type is raised when the 'FIEMAP' or 'SEEK_HOLE' feature + is not supported either by the kernel or the file-system. + """ + pass + +class Error(Exception): + """A class for all the other exceptions raised by this module.""" + pass + + +class _FilemapBase(object): + """ + 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. The 'log' parameter is the logger object + to use for printing messages. + """ + + def __init__(self, image, log=None): + """ + Initialize a class instance. The 'image' argument is full path to the + file or file object to operate on. + """ + + self._log = log + if self._log is None: + self._log = logging.getLogger(__name__) + + self._f_image_needs_close = False + + if hasattr(image, "fileno"): + self._f_image = image + self._image_path = image.name + else: + self._image_path = image + self._open_image_file() + + 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 = get_block_size(self._f_image) + except IOError as err: + raise Error("cannot get block size for '%s': %s" + % (self._image_path, err)) + + self.blocks_cnt = self.image_size + self.block_size - 1 + self.blocks_cnt //= self.block_size + + 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)) + + self._log.debug("opened image \"%s\"" % self._image_path) + self._log.debug("block size %d, blocks count %d, image size %d" + % (self.block_size, self.blocks_cnt, self.image_size)) + + def __del__(self): + """The class destructor which just closes the image file.""" + if self._f_image_needs_close: + self._f_image.close() + + def _open_image_file(self): + """Open the image file.""" + try: + self._f_image = open(self._image_path, 'rb') + except IOError as err: + raise Error("cannot open image file '%s': %s" + % (self._image_path, err)) + + 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") + + +# The 'SEEK_HOLE' and 'SEEK_DATA' options of the file seek system call +_SEEK_DATA = 3 +_SEEK_HOLE = 4 + +def _lseek(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 + elif err.errno == os.errno.EINVAL: + raise ErrorNotSupp("the kernel or file-system does not support " + "\"SEEK_HOLE\" and \"SEEK_DATA\"") + else: + raise + +class FilemapSeek(_FilemapBase): + """ + This class uses the 'SEEK_HOLE' and 'SEEK_DATA' to find file block mapping. + Unfortunately, the current implementation requires the caller to have write + access to the image file. + """ + + def __init__(self, image, log=None): + """Refer the '_FilemapBase' class for the documentation.""" + + # Call the base class constructor first + _FilemapBase.__init__(self, image, log) + self._log.debug("FilemapSeek: initializing") + + self._probe_seek_hole() + + def _probe_seek_hole(self): + """ + Check whether the system implements 'SEEK_HOLE' and 'SEEK_DATA'. + Unfortunately, there seems to be no clean way for detecting this, + because often the system just fakes them by just assuming that all + files are fully mapped, so 'SEEK_HOLE' always returns EOF and + 'SEEK_DATA' always returns the requested offset. + + I could not invent a better way of detecting the fake 'SEEK_HOLE' + implementation than just to create a temporary file in the same + directory where the image file resides. It would be nice to change this + to something better. + """ + + directory = os.path.dirname(self._image_path) + + try: + tmp_obj = tempfile.TemporaryFile("w+", dir=directory) + except IOError as err: + raise ErrorNotSupp("cannot create a temporary in \"%s\": %s" + % (directory, err)) + + try: + os.ftruncate(tmp_obj.fileno(), self.block_size) + except OSError as err: + raise ErrorNotSupp("cannot truncate temporary file in \"%s\": %s" + % (directory, err)) + + offs = _lseek(tmp_obj, 0, _SEEK_HOLE) + if offs != 0: + # We are dealing with the stub 'SEEK_HOLE' implementation which + # always returns EOF. + self._log.debug("lseek(0, SEEK_HOLE) returned %d" % offs) + raise ErrorNotSupp("the file-system does not support " + "\"SEEK_HOLE\" and \"SEEK_DATA\" but only " + "provides a stub implementation") + + tmp_obj.close() + + def block_is_mapped(self, block): + """Refer the '_FilemapBase' class for the documentation.""" + offs = _lseek(self._f_image, block * self.block_size, _SEEK_DATA) + if offs == -1: + result = False + else: + result = (offs // self.block_size == block) + + self._log.debug("FilemapSeek: block_is_mapped(%d) returns %s" + % (block, result)) + return result + + def block_is_unmapped(self, block): + """Refer the '_FilemapBase' class for the documentation.""" + 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 = _lseek(self._f_image, end, whence1) + if start == -1 or start >= limit or start == self.image_size: + break + + end = _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 + + start_blk = start // self.block_size + end_blk = end // self.block_size - 1 + self._log.debug("FilemapSeek: yielding |
