The changes introduced in #1113 will use the configured bootloader e.g grub to boot the ISO image in BIOS and EFI mode. The creation process works flawlessly if xorriso is used. However if cdrtools are configured the options passed to e.g mkisofs were wrong. In addition it's not possible to create a hybrid ISO based on isohybrid if the loader is not isolinux. If cdrtools are in use the process to make an ISO hybrid bootable is always based on isohybrid and thus only works with isolinux. This patch also covers this case with a warning message and the consequence that we have to skip the hybrid setup in this case.
275 lines
9.4 KiB
Python
275 lines
9.4 KiB
Python
# Copyright (c) 2015 SUSE Linux GmbH. All rights reserved.
|
|
#
|
|
# This file is part of kiwi.
|
|
#
|
|
# kiwi 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, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# kiwi 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 kiwi. If not, see <http://www.gnu.org/licenses/>
|
|
#
|
|
import os
|
|
import re
|
|
from tempfile import NamedTemporaryFile
|
|
from collections import (
|
|
namedtuple,
|
|
OrderedDict
|
|
)
|
|
|
|
# project
|
|
from kiwi.defaults import Defaults
|
|
from kiwi.iso_tools.base import IsoToolsBase
|
|
from kiwi.utils.command_capabilities import CommandCapabilities
|
|
from kiwi.path import Path
|
|
from kiwi.command import Command
|
|
from kiwi.exceptions import KiwiIsoToolError
|
|
|
|
|
|
class IsoToolsCdrTools(IsoToolsBase):
|
|
"""
|
|
**cdrkit/cdrtools wrapper class**
|
|
|
|
Implementation of Parameter API for iso creation tools using
|
|
the cdrkit/cdrtools projects. Addressed here are the option
|
|
compatible tools mkisofs and genisoimage
|
|
"""
|
|
def has_iso_hybrid_capability(self):
|
|
"""
|
|
Indicate if the iso tool has the capability to embed a
|
|
partition table into the iso such that it can be
|
|
used as both; an iso and a disk
|
|
|
|
:return: True or False
|
|
|
|
:rtype: bool
|
|
"""
|
|
return False
|
|
|
|
def get_tool_name(self):
|
|
"""
|
|
There are tools by J.Schilling and tools from the community
|
|
Depending on what is installed a decision needs to be made.
|
|
mkisofs is preferred over genisoimage
|
|
|
|
:raises KiwiIsoToolError: if no iso creation tool is found
|
|
:return: tool name
|
|
|
|
:rtype: str
|
|
"""
|
|
iso_creation_tools = ['mkisofs', 'genisoimage']
|
|
for tool in iso_creation_tools:
|
|
tool_found = Path.which(tool)
|
|
if tool_found:
|
|
return tool_found
|
|
|
|
raise KiwiIsoToolError(
|
|
'No iso creation tool found, searched for: %s'.format(
|
|
iso_creation_tools
|
|
)
|
|
)
|
|
|
|
def init_iso_creation_parameters(self, custom_args=None):
|
|
"""
|
|
Create a set of standard parameters
|
|
|
|
:param list custom_args: custom ISO creation args
|
|
"""
|
|
efi_mode = False
|
|
if custom_args:
|
|
if custom_args.get('efi_mode'):
|
|
efi_mode = True
|
|
if 'mbr_id' in custom_args:
|
|
self.iso_parameters += [
|
|
'-A', custom_args['mbr_id']
|
|
]
|
|
if 'publisher' in custom_args:
|
|
self.iso_parameters += [
|
|
'-publisher', custom_args['publisher']
|
|
]
|
|
if 'preparer' in custom_args:
|
|
self.iso_parameters += [
|
|
'-p', custom_args['preparer']
|
|
]
|
|
if 'volume_id' in custom_args:
|
|
self.iso_parameters += [
|
|
'-V', custom_args['volume_id']
|
|
]
|
|
if 'udf' in custom_args:
|
|
self.iso_parameters += [
|
|
'-iso-level', '3', '-udf'
|
|
]
|
|
catalog_file = self.boot_path + '/boot.catalog'
|
|
self.iso_parameters += [
|
|
'-R', '-J', '-f', '-pad', '-joliet-long',
|
|
'-sort', self._create_sortfile(),
|
|
'-no-emul-boot', '-boot-load-size', '4', '-boot-info-table',
|
|
'-hide', catalog_file,
|
|
'-hide-joliet', catalog_file,
|
|
]
|
|
if efi_mode:
|
|
loader_file = os.sep.join(
|
|
[
|
|
self.boot_path, 'loader',
|
|
Defaults.get_isolinux_bios_grub_loader()
|
|
]
|
|
)
|
|
else:
|
|
loader_file = self.boot_path + '/loader/isolinux.bin'
|
|
self.iso_loaders += [
|
|
'-b', loader_file, '-c', catalog_file
|
|
]
|
|
|
|
def add_efi_loader_parameters(self):
|
|
"""
|
|
Add ISO creation parameters to embed the EFI loader
|
|
|
|
In order to boot the ISO from EFI, the EFI binary is added as
|
|
alternative loader to the ISO creation parameter list. The
|
|
EFI binary must be included into a fat filesystem in order
|
|
to become recognized by the firmware. For details about this
|
|
file refer to _create_embedded_fat_efi_image() from
|
|
bootloader/config/grub2.py
|
|
"""
|
|
loader_file = self.boot_path + '/efi'
|
|
if os.path.exists(os.sep.join([self.source_dir, loader_file])):
|
|
self.iso_loaders.append('-eltorito-alt-boot')
|
|
iso_tool = self.get_tool_name()
|
|
if iso_tool and CommandCapabilities.has_option_in_help(
|
|
iso_tool, '-eltorito-platform', raise_on_error=False
|
|
):
|
|
self.iso_loaders += ['-eltorito-platform', 'efi']
|
|
self.iso_loaders += [
|
|
'-b', loader_file, '-no-emul-boot', '-joliet-long'
|
|
]
|
|
loader_file_512_byte_blocks = os.path.getsize(
|
|
os.sep.join([self.source_dir, loader_file])
|
|
) / 512
|
|
# boot-load-size is stored in a 16bit range, thus we only
|
|
# set the value if it fits into that range
|
|
if loader_file_512_byte_blocks <= 0xffff:
|
|
self.iso_loaders.append(
|
|
'-boot-load-size'
|
|
)
|
|
self.iso_loaders.append(
|
|
format(int(loader_file_512_byte_blocks))
|
|
)
|
|
|
|
def create_iso(self, filename, hidden_files=None):
|
|
"""
|
|
Creates the iso file with the given filename using cdrtools
|
|
|
|
:param str filename: output filename
|
|
:param list hidden_files: list of hidden files
|
|
"""
|
|
hidden_files_parameters = []
|
|
if hidden_files:
|
|
for hidden_file in hidden_files:
|
|
hidden_files_parameters.append('-hide')
|
|
hidden_files_parameters.append(hidden_file)
|
|
hidden_files_parameters.append('-hide-joliet')
|
|
hidden_files_parameters.append(hidden_file)
|
|
iso_parameters = \
|
|
hidden_files_parameters + \
|
|
self.iso_parameters + \
|
|
self.iso_loaders
|
|
Command.run(
|
|
[
|
|
self.get_tool_name()
|
|
] + iso_parameters + [
|
|
'-o', filename, self.source_dir
|
|
]
|
|
)
|
|
|
|
def list_iso(self, isofile):
|
|
"""
|
|
List contents of an ISO image
|
|
|
|
:param str isofile: path to the ISO file
|
|
|
|
:return: formatted isoinfo result
|
|
|
|
:rtype: dict
|
|
"""
|
|
listing_type = namedtuple(
|
|
'listing_type', ['name', 'filetype', 'start']
|
|
)
|
|
listing = Command.run(
|
|
[self._get_isoinfo_tool(), '-R', '-l', '-i', isofile]
|
|
)
|
|
listing_result = {}
|
|
for line in listing.output.split(os.linesep):
|
|
iso_entry = re.search(
|
|
'.*(-[-rwx]{9}).*\s\[\s*(\d+)(\s+\d+)?\]\s+(.*?)\s*$', line
|
|
)
|
|
if iso_entry:
|
|
entry_type = iso_entry.group(1)
|
|
entry_name = iso_entry.group(4)
|
|
entry_addr = int(iso_entry.group(2))
|
|
listing_result[entry_addr] = listing_type(
|
|
name=entry_name,
|
|
filetype=entry_type,
|
|
start=entry_addr
|
|
)
|
|
return OrderedDict(
|
|
sorted(listing_result.items())
|
|
)
|
|
|
|
def _get_isoinfo_tool(self):
|
|
"""
|
|
There are tools by J.Schilling and tools from the community
|
|
This method searches in all paths which could provide an
|
|
isoinfo tool. The first match makes the decision
|
|
|
|
:raises KiwiIsoToolError: if no isoinfo tool found
|
|
:return: the isoinfo tool to use
|
|
|
|
:rtype: str
|
|
"""
|
|
alternative_lookup_paths = ['/usr/lib/genisoimage']
|
|
isoinfo = Path.which('isoinfo', alternative_lookup_paths)
|
|
if isoinfo:
|
|
return isoinfo
|
|
|
|
raise KiwiIsoToolError(
|
|
'No isoinfo tool found, searched in PATH: %s and %s' %
|
|
(os.environ.get('PATH'), alternative_lookup_paths)
|
|
)
|
|
|
|
def _create_sortfile(self):
|
|
"""
|
|
Create isolinux sort file
|
|
|
|
:return: iso sort file name
|
|
|
|
:rtype: str
|
|
"""
|
|
self.iso_sortfile = NamedTemporaryFile()
|
|
catalog_file = \
|
|
self.source_dir + '/' + self.boot_path + '/boot.catalog'
|
|
loader_file = \
|
|
self.source_dir + '/' + self.boot_path + '/loader/isolinux.bin'
|
|
with open(self.iso_sortfile.name, 'w') as sortfile:
|
|
sortfile.write('%s 3\n' % catalog_file)
|
|
sortfile.write('%s 2\n' % loader_file)
|
|
|
|
boot_files = list(os.walk(self.source_dir + '/' + self.boot_path))
|
|
boot_files += list(os.walk(self.source_dir + '/EFI'))
|
|
for basedir, dirnames, filenames in sorted(boot_files):
|
|
for entry in sorted(dirnames + filenames):
|
|
if entry in filenames and entry == 'efi':
|
|
sortfile.write('%s/%s 1000001\n' % (basedir, entry))
|
|
else:
|
|
sortfile.write('%s/%s 1\n' % (basedir, entry))
|
|
|
|
sortfile.write(
|
|
'%s/%s 1000000\n' % (self.source_dir, 'header_end')
|
|
)
|
|
return self.iso_sortfile.name
|