From 392c39ae845d37f503f695b2c316a45bef7ae786 Mon Sep 17 00:00:00 2001 From: Owen W. Taylor Date: Nov 10 2023 18:32:40 +0000 Subject: flatpak_builder: error out cleanly on files in /usr/etc According to comments in the code, we were meant to be dropping files in /usr/etc (which conflicts with /etc with the Flatpak runtime files/ layer), but that wasn't actually implemented correctly. Since files in /usr/etc typically reflect a packaging bug, error out with a clean error message instead. --- diff --git a/flatpak_module_tools/flatpak_builder.py b/flatpak_module_tools/flatpak_builder.py index 5f32fbe..fe6c650 100644 --- a/flatpak_module_tools/flatpak_builder.py +++ b/flatpak_module_tools/flatpak_builder.py @@ -529,6 +529,11 @@ class PackageFlatpakSourceInfo(BaseFlatpakSourceInfo): return list(components) +class FileMappingError(Exception): + def __str__(self): + return f"{self.args[1]}: {self.args[0]}" + + class FlatpakBuilder: def __init__( self, source: BaseFlatpakSourceInfo, workdir, root, @@ -602,11 +607,17 @@ class FlatpakBuilder: for source, target, is_exact_match in patterns: if is_exact_match: if source == path: - return target + if isinstance(target, FileMappingError): + raise FileMappingError(target.args[0], path) + else: + return target else: m = source.match(path) if m: - return os.path.join(target, m.group(1)) + if isinstance(target, FileMappingError): + raise FileMappingError(target.args[0], path) + else: + return os.path.join(target, m.group(1)) if target else None return None @@ -628,9 +639,10 @@ class FlatpakBuilder: ("ROOT/usr", None), # We map ROOT/usr => files and ROOT/etc => files/etc. This creates - # A conflict between ROOT/usr/etc and /ROOT/etc. Just assume there - # is nothing useful in /ROOT/usr/etc. - ("ROOT/usr/etc/", None), + # A conflict between ROOT/usr/etc and /ROOT/etc. Error out if we + # find files /usr/etc/ + ("ROOT/usr/etc", None), + ("ROOT/usr/etc/", FileMappingError("/usr/etc should be empty")), ("ROOT/usr/", "files"), ("ROOT/etc/", "files/etc") diff --git a/tests/test_flatpak_builder.py b/tests/test_flatpak_builder.py index 47bd3c3..627991c 100644 --- a/tests/test_flatpak_builder.py +++ b/tests/test_flatpak_builder.py @@ -10,7 +10,7 @@ import pytest import yaml from flatpak_module_tools.flatpak_builder import ( - FlatpakBuilder, FlatpakSourceInfo, FLATPAK_METADATA_BOTH, ModuleInfo + FileMappingError, FlatpakBuilder, FlatpakSourceInfo, FLATPAK_METADATA_BOTH, ModuleInfo ) import gi @@ -495,3 +495,36 @@ def test_export_long_filenames(testapp_source, tmpdir, arch): binfiles = sorted(os.listdir(tmpdir / "processed/files/bin")) assert binfiles == ["testapp", verylongname, veryverylongname, "zzzlink", "zzzlink2", "zzzlink3"] + + +@pytest.mark.parametrize("add_file", (True, False)) +def test_usr_etc(runtime_source, tmpdir, arch, add_file): + """ + Test that we error out cleanly if a file is found in /usr/etc + """ + + os.makedirs(tmpdir / "root/usr/etc") + if add_file: + with open(tmpdir / "root/usr/etc/hello.txt", "w") as f: + os.fchmod(f.fileno(), 0o0755) + + check_call(["tar", "cfv", "export.tar", "-H", "pax", "--sort=name", + "root"], cwd=tmpdir) + + workdir = str(tmpdir / "work") + os.mkdir(workdir) + + builder = FlatpakBuilder(runtime_source, workdir, "root", + oci_arch=arch.oci) + + def export(): + with open(tmpdir / "export.tar", "rb") as f: + outfile, manifest_file = (builder._export_from_stream(f, close_stream=False)) + + if add_file: + with pytest.raises(FileMappingError, + match=r"root/usr/etc/hello.txt: /usr/etc should be empty"): + export() + else: + # An empty /usr/etc is fine + export()