2015-02-10 13:19:34 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
|
|
# 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 Library General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
2016-09-21 12:49:13 +00:00
|
|
|
# along with this program; if not, see <https://gnu.org/licenses/>.
|
2015-02-10 13:19:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
from fnmatch import fnmatch
|
2017-02-20 09:34:55 +00:00
|
|
|
import contextlib
|
2017-09-21 07:29:53 +00:00
|
|
|
from six.moves import shlex_quote
|
2015-02-10 13:19:34 +00:00
|
|
|
|
|
|
|
from kobo.shortcuts import force_list, relative_path, run
|
2017-02-20 09:34:55 +00:00
|
|
|
from pungi import util
|
2015-02-10 13:19:34 +00:00
|
|
|
|
|
|
|
|
2019-02-21 12:56:58 +00:00
|
|
|
def get_boot_options(arch, createfrom, efi=True, hfs_compat=True):
|
2016-10-12 11:56:19 +00:00
|
|
|
"""Checks to see what we need as the -b option for mkisofs"""
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if arch in ("arm", "armhfp"):
|
|
|
|
result = []
|
|
|
|
return result
|
|
|
|
|
2020-02-03 03:50:06 +00:00
|
|
|
if arch in ("aarch64",):
|
2016-10-12 11:56:19 +00:00
|
|
|
result = [
|
2020-02-03 03:50:06 +00:00
|
|
|
"-eltorito-alt-boot",
|
|
|
|
"-e",
|
|
|
|
"images/efiboot.img",
|
|
|
|
"-no-emul-boot",
|
2016-10-12 11:56:19 +00:00
|
|
|
]
|
|
|
|
return result
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if arch in ("i386", "i686", "x86_64"):
|
|
|
|
result = [
|
2020-02-03 03:50:06 +00:00
|
|
|
"-b",
|
|
|
|
"isolinux/isolinux.bin",
|
|
|
|
"-c",
|
|
|
|
"isolinux/boot.cat",
|
|
|
|
"-no-emul-boot",
|
|
|
|
"-boot-load-size",
|
|
|
|
"4",
|
|
|
|
"-boot-info-table",
|
2016-10-12 11:56:19 +00:00
|
|
|
]
|
2015-07-24 12:46:47 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
# EFI args
|
|
|
|
if arch == "x86_64":
|
2020-02-03 03:50:06 +00:00
|
|
|
result.extend(
|
|
|
|
["-eltorito-alt-boot", "-e", "images/efiboot.img", "-no-emul-boot"]
|
|
|
|
)
|
2016-10-12 11:56:19 +00:00
|
|
|
return result
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if arch == "ia64":
|
|
|
|
result = [
|
2020-02-03 03:50:06 +00:00
|
|
|
"-b",
|
|
|
|
"images/boot.img",
|
|
|
|
"-no-emul-boot",
|
2016-10-12 11:56:19 +00:00
|
|
|
]
|
2015-02-10 13:19:34 +00:00
|
|
|
return result
|
|
|
|
|
2019-02-21 12:56:58 +00:00
|
|
|
if arch in ("ppc", "ppc64") or (arch == "ppc64le" and hfs_compat):
|
2016-10-12 11:56:19 +00:00
|
|
|
result = [
|
2020-02-03 03:50:06 +00:00
|
|
|
"-part",
|
|
|
|
"-hfs",
|
|
|
|
"-r",
|
|
|
|
"-l",
|
|
|
|
"-sysid",
|
|
|
|
"PPC",
|
|
|
|
"-no-desktop",
|
|
|
|
"-allow-multidot",
|
|
|
|
"-chrp-boot",
|
|
|
|
"-map",
|
|
|
|
os.path.join(createfrom, "mapping"), # -map %s/ppc/mapping
|
|
|
|
"-hfs-bless",
|
|
|
|
"/ppc/mac", # must be the last
|
2016-10-12 11:56:19 +00:00
|
|
|
]
|
|
|
|
return result
|
2016-04-28 21:27:19 +00:00
|
|
|
|
2019-02-21 12:56:58 +00:00
|
|
|
if arch == "ppc64le" and not hfs_compat:
|
2019-02-20 10:47:33 +00:00
|
|
|
result = [
|
2020-02-03 03:50:06 +00:00
|
|
|
"-r",
|
|
|
|
"-l",
|
|
|
|
"-sysid",
|
|
|
|
"PPC",
|
|
|
|
"-chrp-boot",
|
2019-02-20 10:47:33 +00:00
|
|
|
]
|
|
|
|
return result
|
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if arch == "sparc":
|
|
|
|
result = [
|
2020-02-03 03:50:06 +00:00
|
|
|
"-G",
|
|
|
|
"/boot/isofs.b",
|
|
|
|
"-B",
|
|
|
|
"...",
|
|
|
|
"-s",
|
|
|
|
"/boot/silo.conf",
|
|
|
|
"-sparc-label",
|
|
|
|
'"sparc"',
|
2016-10-12 11:56:19 +00:00
|
|
|
]
|
|
|
|
return result
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if arch in ("s390", "s390x"):
|
|
|
|
result = [
|
2020-02-03 03:50:06 +00:00
|
|
|
"-eltorito-boot",
|
|
|
|
"images/cdboot.img",
|
|
|
|
"-no-emul-boot",
|
2016-10-12 11:56:19 +00:00
|
|
|
]
|
|
|
|
return result
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
raise ValueError("Unknown arch: %s" % arch)
|
2015-02-10 13:19:34 +00:00
|
|
|
|
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
def _truncate_volid(volid):
|
|
|
|
if len(volid) > 32:
|
|
|
|
volid = volid.replace("-", "")
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if len(volid) > 32:
|
|
|
|
volid = volid.replace(" ", "")
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if len(volid) > 32:
|
|
|
|
volid = volid.replace("Supplementary", "Supp")
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if len(volid) > 32:
|
|
|
|
raise ValueError("Volume ID must be less than 32 character: %s" % volid)
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
return volid
|
2015-02-10 13:19:34 +00:00
|
|
|
|
|
|
|
|
2020-02-03 03:50:06 +00:00
|
|
|
def get_mkisofs_cmd(
|
|
|
|
iso,
|
|
|
|
paths,
|
|
|
|
appid=None,
|
|
|
|
volid=None,
|
|
|
|
volset=None,
|
|
|
|
exclude=None,
|
|
|
|
verbose=False,
|
|
|
|
boot_args=None,
|
|
|
|
input_charset="utf-8",
|
|
|
|
graft_points=None,
|
2020-11-16 03:56:53 +00:00
|
|
|
use_xorrisofs=False,
|
2020-02-03 03:50:06 +00:00
|
|
|
):
|
2016-10-12 11:56:19 +00:00
|
|
|
# following options are always enabled
|
|
|
|
untranslated_filenames = True
|
|
|
|
translation_table = True
|
|
|
|
joliet = True
|
|
|
|
joliet_long = True
|
|
|
|
rock = True
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2020-11-16 03:56:53 +00:00
|
|
|
cmd = ["/usr/bin/xorrisofs" if use_xorrisofs else "/usr/bin/genisoimage"]
|
2016-10-12 11:56:19 +00:00
|
|
|
if appid:
|
|
|
|
cmd.extend(["-appid", appid])
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if untranslated_filenames:
|
|
|
|
cmd.append("-untranslated-filenames")
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if volid:
|
|
|
|
cmd.extend(["-volid", _truncate_volid(volid)])
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if joliet:
|
|
|
|
cmd.append("-J")
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if joliet_long:
|
|
|
|
cmd.append("-joliet-long")
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if volset:
|
|
|
|
cmd.extend(["-volset", volset])
|
|
|
|
|
|
|
|
if rock:
|
|
|
|
cmd.append("-rational-rock")
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
cmd.append("-verbose")
|
|
|
|
|
2020-11-16 03:56:53 +00:00
|
|
|
if not use_xorrisofs and translation_table:
|
2016-10-12 11:56:19 +00:00
|
|
|
cmd.append("-translation-table")
|
|
|
|
|
|
|
|
if input_charset:
|
|
|
|
cmd.extend(["-input-charset", input_charset])
|
|
|
|
|
|
|
|
if exclude:
|
|
|
|
for i in force_list(exclude):
|
|
|
|
cmd.extend(["-x", i])
|
|
|
|
|
|
|
|
if boot_args:
|
|
|
|
cmd.extend(boot_args)
|
|
|
|
|
|
|
|
cmd.extend(["-o", iso])
|
2015-02-10 13:19:34 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
if graft_points:
|
|
|
|
cmd.append("-graft-points")
|
|
|
|
cmd.extend(["-path-list", graft_points])
|
|
|
|
else:
|
|
|
|
# we're either using graft points or file lists, not both
|
|
|
|
cmd.extend(force_list(paths))
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
|
|
|
|
|
|
|
|
def get_implantisomd5_cmd(iso_path, supported=False):
|
|
|
|
cmd = ["/usr/bin/implantisomd5"]
|
|
|
|
if supported:
|
|
|
|
cmd.append("--supported-iso")
|
|
|
|
cmd.append(iso_path)
|
|
|
|
return cmd
|
|
|
|
|
|
|
|
|
|
|
|
def get_checkisomd5_cmd(iso_path, just_print=False):
|
|
|
|
cmd = ["/usr/bin/checkisomd5"]
|
|
|
|
if just_print:
|
|
|
|
cmd.append("--md5sumonly")
|
|
|
|
cmd.append(iso_path)
|
|
|
|
return cmd
|
|
|
|
|
|
|
|
|
2017-02-20 09:35:10 +00:00
|
|
|
def get_checkisomd5_data(iso_path, logger=None):
|
2016-10-12 11:56:19 +00:00
|
|
|
cmd = get_checkisomd5_cmd(iso_path, just_print=True)
|
2017-09-15 07:42:33 +00:00
|
|
|
retcode, output = run(cmd, universal_newlines=True)
|
2017-02-20 09:35:10 +00:00
|
|
|
items = [line.strip().rsplit(":", 1) for line in output.splitlines()]
|
|
|
|
items = dict([(k, v.strip()) for k, v in items])
|
2020-02-03 03:50:06 +00:00
|
|
|
md5 = items.get(iso_path, "")
|
2017-03-02 08:10:27 +00:00
|
|
|
if len(md5) != 32:
|
|
|
|
# We have seen cases where the command finished successfully, but
|
|
|
|
# returned garbage value. We need to handle it, otherwise there would
|
|
|
|
# be a crash once we try to write image metadata.
|
|
|
|
# This only logs information about the problem and leaves the hash
|
|
|
|
# empty, which is valid from productmd point of view.
|
|
|
|
if logger:
|
2020-02-03 03:50:06 +00:00
|
|
|
logger.critical("Implanted MD5 in %s is not valid: %r", iso_path, md5)
|
|
|
|
logger.critical(
|
|
|
|
"Ran command %r; exit code %r; output %r", cmd, retcode, output
|
|
|
|
)
|
2017-03-02 08:10:27 +00:00
|
|
|
return None
|
2017-02-20 09:35:10 +00:00
|
|
|
return items
|
|
|
|
|
|
|
|
|
|
|
|
def get_implanted_md5(iso_path, logger=None):
|
|
|
|
return (get_checkisomd5_data(iso_path, logger=logger) or {}).get(iso_path)
|
2016-10-12 11:56:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_isohybrid_cmd(iso_path, arch):
|
|
|
|
# isohybrid is in syslinux which is x86 only
|
|
|
|
cmd = ["/usr/bin/isohybrid"]
|
|
|
|
# uefi is only supported on x86_64
|
|
|
|
if arch == "x86_64":
|
|
|
|
cmd.append("--uefi")
|
|
|
|
cmd.append(iso_path)
|
|
|
|
return cmd
|
|
|
|
|
|
|
|
|
2021-08-30 13:30:28 +00:00
|
|
|
def get_manifest_cmd(iso_name, xorriso=False):
|
|
|
|
if xorriso:
|
|
|
|
return """xorriso -dev %s --find |
|
|
|
|
tail -n+2 |
|
|
|
|
tr -d "'" |
|
|
|
|
cut -c2- |
|
|
|
|
sort >> %s.manifest""" % (
|
|
|
|
shlex_quote(iso_name),
|
|
|
|
shlex_quote(iso_name),
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
return "isoinfo -R -f -i %s | grep -v '/TRANS.TBL$' | sort >> %s.manifest" % (
|
|
|
|
shlex_quote(iso_name),
|
|
|
|
shlex_quote(iso_name),
|
|
|
|
)
|
2016-10-12 11:56:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_volume_id(path):
|
|
|
|
cmd = ["isoinfo", "-d", "-i", path]
|
2017-09-15 07:42:33 +00:00
|
|
|
retcode, output = run(cmd, universal_newlines=True)
|
2016-10-12 11:56:19 +00:00
|
|
|
|
|
|
|
for line in output.splitlines():
|
|
|
|
line = line.strip()
|
|
|
|
if line.startswith("Volume id:"):
|
|
|
|
return line[11:].strip()
|
|
|
|
|
|
|
|
raise RuntimeError("Could not read Volume ID")
|
|
|
|
|
|
|
|
|
2020-08-19 12:11:23 +00:00
|
|
|
def get_graft_points(compose_top_dir, paths, exclusive_paths=None, exclude=None):
|
2016-10-12 11:56:19 +00:00
|
|
|
# path priority in ascending order (1st = lowest prio)
|
|
|
|
# paths merge according to priority
|
|
|
|
# exclusive paths override whole dirs
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
exclude = exclude or []
|
|
|
|
exclusive_paths = exclusive_paths or []
|
|
|
|
|
|
|
|
for i in paths:
|
|
|
|
if isinstance(i, dict):
|
|
|
|
tree = i
|
|
|
|
else:
|
|
|
|
tree = _scan_tree(i)
|
|
|
|
result = _merge_trees(result, tree)
|
|
|
|
|
|
|
|
for i in exclusive_paths:
|
|
|
|
tree = _scan_tree(i)
|
|
|
|
result = _merge_trees(result, tree, exclusive=True)
|
|
|
|
|
2020-08-19 12:11:23 +00:00
|
|
|
# Resolve possible symlinks pointing outside of the compose top dir.
|
2020-01-03 14:48:12 +00:00
|
|
|
# This fixes an issue if link_type is set to "symlink" and therefore
|
|
|
|
# the RPM packages are symbolic links to /mnt/koji filesystem.
|
|
|
|
# Without this, the symbolic links would be simply copied into the ISO
|
|
|
|
# without the real RPMs.
|
|
|
|
for key in result.keys():
|
|
|
|
path = result[key]
|
|
|
|
if os.path.islink(path):
|
|
|
|
real_path = os.readlink(path)
|
|
|
|
abspath = os.path.normpath(os.path.join(os.path.dirname(path), real_path))
|
2020-08-19 12:11:23 +00:00
|
|
|
if not abspath.startswith(compose_top_dir):
|
2020-01-03 14:48:12 +00:00
|
|
|
result[key] = abspath
|
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
# TODO: exclude
|
|
|
|
return result
|
|
|
|
|
2017-12-14 09:08:30 +00:00
|
|
|
|
2016-10-12 11:56:19 +00:00
|
|
|
def _paths_from_list(root, paths):
|
|
|
|
root = os.path.abspath(root).rstrip("/") + "/"
|
|
|
|
result = {}
|
|
|
|
for i in paths:
|
|
|
|
i = os.path.normpath(os.path.join(root, i))
|
2020-02-03 03:50:06 +00:00
|
|
|
key = i[len(root) :]
|
2016-10-12 11:56:19 +00:00
|
|
|
result[key] = i
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def _scan_tree(path):
|
|
|
|
path = os.path.abspath(path)
|
|
|
|
result = {}
|
|
|
|
for root, dirs, files in os.walk(path):
|
|
|
|
for f in files:
|
|
|
|
abspath = os.path.join(root, f)
|
|
|
|
relpath = relative_path(abspath, path.rstrip("/") + "/")
|
|
|
|
result[relpath] = abspath
|
|
|
|
|
|
|
|
# include empty dirs
|
|
|
|
if root != path:
|
|
|
|
abspath = os.path.join(root, "")
|
|
|
|
relpath = relative_path(abspath, path.rstrip("/") + "/")
|
|
|
|
result[relpath] = abspath
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def _merge_trees(tree1, tree2, exclusive=False):
|
|
|
|
# tree2 has higher priority
|
|
|
|
result = tree2.copy()
|
2020-02-03 03:50:06 +00:00
|
|
|
all_dirs = set(
|
|
|
|
[os.path.dirname(i).rstrip("/") for i in result if os.path.dirname(i) != ""]
|
|
|
|
)
|
2016-10-12 11:56:19 +00:00
|
|
|
|
|
|
|
for i in tree1:
|
|
|
|
dn = os.path.dirname(i)
|
|
|
|
if exclusive:
|
|
|
|
match = False
|
|
|
|
for a in all_dirs:
|
|
|
|
if dn == a or dn.startswith("%s/" % a):
|
|
|
|
match = True
|
2015-02-10 13:19:34 +00:00
|
|
|
break
|
2016-10-12 11:56:19 +00:00
|
|
|
if match:
|
2015-02-10 13:19:34 +00:00
|
|
|
continue
|
2016-10-12 11:56:19 +00:00
|
|
|
|
|
|
|
if i in result:
|
|
|
|
continue
|
|
|
|
|
|
|
|
result[i] = tree1[i]
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def write_graft_points(file_name, h, exclude=None):
|
|
|
|
exclude = exclude or []
|
|
|
|
result = {}
|
|
|
|
seen_dirs = set()
|
|
|
|
for i in sorted(h, reverse=True):
|
|
|
|
dn = os.path.dirname(i)
|
|
|
|
|
|
|
|
if not i.endswith("/"):
|
|
|
|
result[i] = h[i]
|
|
|
|
seen_dirs.add(dn)
|
|
|
|
continue
|
|
|
|
|
|
|
|
found = False
|
|
|
|
for j in seen_dirs:
|
|
|
|
if j.startswith(dn):
|
|
|
|
found = True
|
|
|
|
break
|
|
|
|
if not found:
|
|
|
|
result[i] = h[i]
|
|
|
|
seen_dirs.add(dn)
|
|
|
|
|
|
|
|
f = open(file_name, "w")
|
2017-11-02 13:51:00 +00:00
|
|
|
for i in sorted(result, key=graft_point_sort_key):
|
2016-10-12 11:56:19 +00:00
|
|
|
# make sure all files required for boot come first,
|
|
|
|
# otherwise there may be problems with booting (large LBA address, etc.)
|
|
|
|
found = False
|
|
|
|
for excl in exclude:
|
|
|
|
if fnmatch(i, excl):
|
|
|
|
found = True
|
|
|
|
break
|
|
|
|
if found:
|
|
|
|
continue
|
|
|
|
f.write("%s=%s\n" % (i, h[i]))
|
|
|
|
f.close()
|
2015-02-10 13:19:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _is_rpm(path):
|
2017-11-02 13:51:00 +00:00
|
|
|
return path.endswith(".rpm")
|
2015-02-10 13:19:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _is_image(path):
|
|
|
|
if path.startswith("images/"):
|
|
|
|
return True
|
|
|
|
if path.startswith("isolinux/"):
|
|
|
|
return True
|
|
|
|
if path.startswith("EFI/"):
|
|
|
|
return True
|
|
|
|
if path.startswith("etc/"):
|
|
|
|
return True
|
|
|
|
if path.startswith("ppc/"):
|
|
|
|
return True
|
|
|
|
if path.endswith(".img"):
|
|
|
|
return True
|
|
|
|
if path.endswith(".ins"):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2017-11-02 13:51:00 +00:00
|
|
|
def graft_point_sort_key(x):
|
|
|
|
"""
|
|
|
|
Images are sorted first, followed by other files. RPMs always come last.
|
|
|
|
In the same group paths are sorted alphabetically.
|
|
|
|
"""
|
|
|
|
return (0 if _is_image(x) else 2 if _is_rpm(x) else 1, x)
|
2017-02-20 09:34:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
@contextlib.contextmanager
|
2018-01-16 08:13:55 +00:00
|
|
|
def mount(image, logger=None, use_guestmount=True):
|
2017-02-20 09:34:55 +00:00
|
|
|
"""Mount an image and make sure it's unmounted.
|
|
|
|
|
|
|
|
The yielded path will only be valid in the with block and is removed once
|
|
|
|
the image is unmounted.
|
|
|
|
"""
|
2020-02-03 03:50:06 +00:00
|
|
|
with util.temp_dir(prefix="iso-mount-") as mount_dir:
|
2017-12-21 02:18:00 +00:00
|
|
|
ret, __ = run(["which", "guestmount"], can_fail=True)
|
2018-01-16 08:13:55 +00:00
|
|
|
# return code 0 means that guestmount is available
|
|
|
|
guestmount_available = use_guestmount and not bool(ret)
|
2017-12-21 02:18:00 +00:00
|
|
|
if guestmount_available:
|
|
|
|
# use guestmount to mount the image, which doesn't require root privileges
|
|
|
|
# LIBGUESTFS_BACKEND=direct: running qemu directly without libvirt
|
2020-02-03 03:50:06 +00:00
|
|
|
env = {
|
|
|
|
"LIBGUESTFS_BACKEND": "direct",
|
|
|
|
"LIBGUESTFS_DEBUG": "1",
|
|
|
|
"LIBGUESTFS_TRACE": "1",
|
|
|
|
}
|
2017-12-21 02:18:00 +00:00
|
|
|
cmd = ["guestmount", "-a", image, "-m", "/dev/sda", mount_dir]
|
2020-01-29 14:15:47 +00:00
|
|
|
# guestmount caches files for faster mounting. However,
|
|
|
|
# systemd-tmpfiles is cleaning it up if the files have not been
|
|
|
|
# used in 30 days. It seems to be leaving an empty appliance.d
|
|
|
|
# directory in place, which is causing guestmount to fail because
|
|
|
|
# the files are missing and it only checks the directory.
|
|
|
|
#
|
|
|
|
# Thus we check if the directory is empty, and remove it in such
|
|
|
|
# case. There is still a possible race condition that the cleanup
|
|
|
|
# will happen between our check and libguestfs using the directory.
|
|
|
|
# The performance penalty for never reusing the cached files is too
|
|
|
|
# high given how unlikely the race condition is.
|
|
|
|
#
|
|
|
|
# https://bugzilla.redhat.com/show_bug.cgi?id=1771976
|
|
|
|
guestfs_tmp_dir = "/var/tmp/.guestfs-%d" % os.getuid()
|
|
|
|
try:
|
|
|
|
if not os.listdir(os.path.join(guestfs_tmp_dir, "appliance.d")):
|
|
|
|
if logger:
|
|
|
|
logger.info("Cleaning up %s", guestfs_tmp_dir)
|
|
|
|
util.rmtree(guestfs_tmp_dir)
|
|
|
|
except OSError:
|
|
|
|
# The directory is missing. That's fine for us too.
|
|
|
|
pass
|
2017-12-21 02:18:00 +00:00
|
|
|
else:
|
|
|
|
env = {}
|
|
|
|
cmd = ["mount", "-o", "loop", image, mount_dir]
|
2017-09-15 07:42:33 +00:00
|
|
|
ret, out = run(cmd, env=env, can_fail=True, universal_newlines=True)
|
2017-05-16 08:28:55 +00:00
|
|
|
if ret != 0:
|
2020-02-06 07:09:32 +00:00
|
|
|
# The mount command failed, something is wrong.
|
|
|
|
# Log the output and raise an exception.
|
2017-05-16 08:28:55 +00:00
|
|
|
if logger:
|
2020-02-03 03:50:06 +00:00
|
|
|
logger.error(
|
|
|
|
"Command %s exited with %s and output:\n%s" % (cmd, ret, out)
|
|
|
|
)
|
|
|
|
raise RuntimeError("Failed to mount %s" % image)
|
2017-02-20 09:34:55 +00:00
|
|
|
try:
|
|
|
|
yield mount_dir
|
|
|
|
finally:
|
2017-12-21 02:18:00 +00:00
|
|
|
if guestmount_available:
|
2020-02-03 03:50:06 +00:00
|
|
|
util.run_unmount_cmd(["fusermount", "-u", mount_dir], path=mount_dir)
|
2017-12-21 02:18:00 +00:00
|
|
|
else:
|
2020-02-03 03:50:06 +00:00
|
|
|
util.run_unmount_cmd(["umount", mount_dir], path=mount_dir)
|