diff --git a/tools/audit_dump.py b/tools/audit_dump.py index d8fae1df40..6fd814c168 100644 --- a/tools/audit_dump.py +++ b/tools/audit_dump.py @@ -12,7 +12,8 @@ import unwrap_self DESCRIPTION = """Audit the imports of an extracted title before it is launched. Unwraps the dump's eboot.bin and the modules it ships (sce_module/, sce_modules/ and prx/, Unity's Media/Modules/ and Media/Plugins/, and .prx files beside eboot.bin) into a work directory, converts them with relinker --registry, and runs tools/import_audit.py on the -registries with the unwrapped modules as --modules. The dump is only read. Exit code: that of import_audit.py, or 2 +registries with the unwrapped modules as --modules. The dump is only read; the source/ and output/ directories of a +reused --work directory are emptied first. Exit code: that of import_audit.py, or 2 when unwrapping or conversion fails.""" STANDARD_DIRECTORIES = ("sce_module", "sce_modules", "prx") @@ -35,7 +36,7 @@ def stage(source, destination): def modules_of(directory): - return sorted(path for path in directory.iterdir() if path.is_file() and path.suffix.lower() in MODULE_SUFFIXES) + return sorted(path for path in directory.iterdir() if (path.is_file() or path.is_symlink()) and path.suffix.lower() in MODULE_SUFFIXES) def prepare(dump, source, log=print): @@ -92,6 +93,8 @@ def main(argv=None): with tempfile.TemporaryDirectory() as temporary: work = Path(args.work) if args.work else Path(temporary) source, output = work / "source", work / "output" + for generated in (source, output): + shutil.rmtree(generated, ignore_errors=True) output.mkdir(parents=True, exist_ok=True) try: module_directories, extra = prepare(dump, source) diff --git a/tools/tests/test_audit_dump.py b/tools/tests/test_audit_dump.py index 486a4741d9..1f9ca4572e 100644 --- a/tools/tests/test_audit_dump.py +++ b/tools/tests/test_audit_dump.py @@ -1,5 +1,6 @@ import contextlib import io +import os import subprocess import sys import tempfile @@ -101,6 +102,36 @@ class AuditDumpTests(unittest.TestCase): self.assertEqual(argv[:2], [str(output / "app.libc.prx.guest.prx.registry.json"), str(output / "app.registry.json")]) self.assertEqual(argv[2:], ["--libs", "libs-a", "--libs", "libs-b", "--modules", str(work / "source" / "sce_module"), "--json", "report.json"]) + def test_a_reused_work_directory_does_not_audit_registries_left_by_an_earlier_run(self): + work = self.root / "work" + stale = work / "output" / "app.old.prx.guest.prx.registry.json" + stale.parent.mkdir(parents=True) + stale.write_text("[]") + (work / "source" / "sce_module").mkdir(parents=True) + (work / "source" / "sce_module" / "gone.prx").write_bytes(ELF) + + def relink(command, capture_output, text): + (Path(command[-1]).parent / "app.registry.json").write_text("[]") + return subprocess.CompletedProcess(command, 0, "", "") + + with mock.patch.object(subprocess, "run", side_effect=relink), mock.patch.object(audit_dump.import_audit, "main", return_value=0) as audit: + code, _ = self.run_main(self.dump, "--relinker", self.relinker, "--libs", "libs", "--work", work) + self.assertEqual(code, 0) + self.assertEqual(audit.call_args.args[0][:2], [str(work / "output" / "app.registry.json"), "--libs"]) + self.assertFalse((work / "source" / "sce_module" / "gone.prx").exists()) + + def test_a_broken_module_link_fails_instead_of_being_skipped(self): + (self.dump / "sce_module").mkdir() + try: + os.symlink(self.dump / "sce_module" / "missing.prx", self.dump / "sce_module" / "libc.prx") + except OSError: + self.skipTest("symbolic links unavailable") + with mock.patch.object(subprocess, "run") as run: + code, errors = self.run_main(self.dump, "--relinker", self.relinker, "--libs", "libs") + self.assertEqual(code, 2) + self.assertIn("libc.prx", errors) + run.assert_not_called() + def test_a_relinker_failure_returns_2_with_its_message(self): with mock.patch.object(subprocess, "run", return_value=subprocess.CompletedProcess([], 3, "", "bad input")), mock.patch.object(audit_dump.import_audit, "main") as audit: code, errors = self.run_main(self.dump, "--relinker", self.relinker, "--libs", "libs")