Source code for COT.disks.iso

# October 2016, Glenn F. Matthews
# Copyright (c) 2013-2016 the COT project developers.
# See the COPYRIGHT.txt file at the top-level directory of this distribution
# and at https://github.com/glennmatthews/cot/blob/master/COPYRIGHT.txt.
#
# This file is part of the Common OVF Tool (COT) project.
# It is subject to the license terms in the LICENSE.txt file found in the
# top-level directory of this distribution and at
# https://github.com/glennmatthews/cot/blob/master/LICENSE.txt. No part
# of COT, including this file, may be copied, modified, propagated, or
# distributed except according to the terms contained in the LICENSE.txt file.

"""Handling of ISO files."""

import os
import re

from COT.disks.disk import DiskRepresentation
from COT.helpers import helpers, HelperError, helper_select


[docs]class ISO(DiskRepresentation): """ISO 9660 disk image file representation.""" disk_format = "iso" @property def disk_subformat(self): """ISO sub-format. Possible values: - "" - not Rock Ridge - "rockridge" - has Rock Ridge extensions """ if self._disk_subformat is None: output = helpers['isoinfo'].call(['-i', self.path, '-d']) if re.search(r"Rock Ridge.*found", output): self._disk_subformat = "rockridge" else: # At this time we don't care about Joliet extensions self._disk_subformat = "" return self._disk_subformat @property def files(self): """The list of files contained in this ISO.""" if self._files is None: if helpers['isoinfo']: # TODO # It's safe to specify -R even for non-rockridge ISOs args = ["-i", self.path, "-f", "-R"] # At this time we don't support Joliet extensions output = helpers['isoinfo'].call(args) result = [] for line in output.split("\n"): # discard non-file output lines if not line or line[0] != "/": continue # Non-Rock-Ridge filenames look like this in isoinfo: # /IOSXR_CONFIG.TXT;1 # but the actual filename thus is: # /iosxr_config.txt if self.disk_subformat != "rockridge" and ";1" in line: line = line.lower()[:-2] # Strip the leading '/' result.append(line[1:]) self._files = result return self._files def _create_file(self): """Create an ISO file.""" if not self._files: raise RuntimeError("Unable to create an empty ISO file") # Default subformat is to include Rock Ridge extensions. # To not have these, use subformat="" if self._disk_subformat is None: self._disk_subformat = 'rockridge' # We can use mkisofs, genisoimage, or xorriso, and fortunately # all three take similar parameters args = ['-output', self.path, '-full-iso9660-filenames', '-iso-level', '2', '-allow-lowercase'] if self._disk_subformat == 'rockridge': args.append('-r') args += self.files helper = helper_select(['mkisofs', 'genisoimage', 'xorriso']) if helper.name == "xorriso": args = ['-as', 'mkisofs'] + args helper.call(args) self._disk_subformat = None self._files = None @classmethod
[docs] def file_is_this_type(cls, path): """Detect whether the given file is an ISO image. Args: path (str): Path to file Returns: bool: True (file is an ISO) or False (file is not an ISO) Raises: HelperError: if ``path`` is not a file at all. """ if not os.path.exists(path): raise HelperError(2, "No such file or directory: '{0}'" .format(path)) if helpers['isoinfo']: try: helpers['isoinfo'].call(['-i', path, '-d']) return True except HelperError: # Not an ISO return False # else, try to detect ISO files by file magic number with open(path, 'rb') as f: for offset in (0x8001, 0x8801, 0x9001): f.seek(offset) magic = f.read(5).decode('ascii', 'ignore') if magic == "CD001": return True return False
@classmethod
[docs] def from_other_image(cls, input_image, output_dir, output_subformat=None): """Convert the other disk image into an image of this type. Args: input_image (DiskRepresentation): Existing image representation. output_dir (str): Output directory to store the new image in. output_subformat (str): Any relevant subformat information. Raises: NotImplementedError: non-trivial to convert other types to ISO """ raise NotImplementedError("Not a valid target for conversion")