Compare commits

..
4 changed files with 179 additions and 12 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
with:
dotnet-version: "10.0.x"
- name: Run owned macOS 14 TCG guest and all native tests
run: dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run --full --output artifacts/native-macos-full
run: dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run --full --recovery-format raw --output artifacts/native-macos-full
- name: Always remove only this run's owned resources
if: always()
run: dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --cleanup --output artifacts/native-macos-full
@@ -9,6 +9,7 @@ on:
- codex/macos-ci-kvm-compatibility
- codex/macos-ci-tcg-supported
- codex/macos-native-full-tcg
- codex/macos-native-raw-recovery
workflow_dispatch:
jobs:
+9 -1
View File
@@ -1,6 +1,14 @@
# macOS 14 TCG prerequisite diagnostic
This manual candidate uses the existing Ubuntu/x64 Docker runner. Before downloading Apple Recovery it tests actual AVX/AVX2 instruction execution in the pinned QEMU binary, then probes a fresh macOS 14+ Recovery guest. It does not install macOS, erase a disk, provision .NET/CLT or run Meeting Assistant tests. Readiness is only a prerequisite for full native CI.
The isolated RAW Recovery comparison starts from Full candidate `2e2d702e294c39f24c6a3e44e5e94ff793d8774b`. Use `dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run --full --recovery-format raw --output artifacts/native-macos-full`; the manual Full workflow includes this option. Its only runtime variable is the Recovery disk's DMG versus RAW backend. The default remains DMG. CPU, macOS version, permissions, native process probe and all time limits are unchanged.
The existing pinned container's `qemu-img` converts the already-patched DMG, requires sector equality with `qemu-img compare`, and checks that conversion preserved the DMG's SHA256. Both images stay in this run's owned storage. A retained JSON receipt binds both hashes and the successful comparison. The original readonly virtio attachment and I/O thread are preserved; cleanup removes both images with that owned volume. Local `--validate --full --recovery-format raw --source <pristine-pinned-dockur-checkout> --output <fresh-folder>` exercises conversion failures, source mutation and strict backend selection with a mocked QEMU boundary. The generated `raw-recovery-real-qemu-fixture.sh` accepts an already-patched **disposable writable DMG copy** plus destination for an actual container QEMU comparison; it does not patch, mount or boot a guest. This comparison does not yet prove faster guest operation or native application tests.
Run 4216 passed the RAW hash and sector-equality gates, then exited before VM startup: Dockur subtracts the container's entire `memory.current`, including reclaimable file cache, from its unchanged 6-GiB limit. After the final verification reads, RAW preparation now uses the existing [GNU `dd` whole-file cache request](https://www.gnu.org/s/coreutils/manual/html_node/dd-invocation.html), `oflag=nocache conv=nocreat,notrunc,fdatasync count=0`, only on the two verified owned Recovery files. This synchronizes their pending writes and requests removal of their data cache without changing file bytes, global caches or memory limits. Either failure rejects preparation; the runtime log records cgroup usage before and after the request. The DMG default path is unchanged. A local 6-GiB-container reproduction with QEMU 11.1.0 reduced `memory.current` from 4,220,035,072 to 132,902,912 bytes and changed Dockur's available-RAM result from 2,223,198,208 to 5,826,265,088 bytes; hashes/equality passed and OOM counters stayed zero. Run 4216 used QEMU 11.1.1, so this local result still needs confirmation on that runtime and proves no guest boot or application test.
Run 4204 stopped before creating a VM because the shared host exposed 5,138,696 KiB available memory, just below the previous arbitrary 5-GiB admission threshold. Admission now budgets the unchanged 4-GiB guest plus 512 MiB for QEMU (4.5 GiB); previous guest recordings peaked below 3 GiB. The 6-GiB container cap and all guest parameters remain unchanged. Offline validation replays that captured host value and still rejects a host with only 4 GiB available. This is an admission budget, not a reservation against other host workloads; actual performance and memory remain subject to the remote result.
In readiness mode, this manual candidate uses the existing Ubuntu/x64 Docker runner. Before downloading Apple Recovery it tests actual AVX/AVX2 instruction execution in the pinned QEMU binary, then probes a fresh macOS 14+ Recovery guest. It does not install macOS, erase a disk, provision .NET/CLT or run Meeting Assistant tests. Readiness is only a prerequisite for full native CI.
## Profile and evidence
+168 -10
View File
@@ -69,13 +69,16 @@ static class NativeDiagnostic
{
if (args.Length == 0 || args.Contains("--help"))
{
Console.WriteLine("dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run|--cleanup|--validate [--full] [--output artifacts/native-macos] [--source existing-dockur-clone] [--compression-chunk readonly-qualified-chunk]");
Console.WriteLine("dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run|--cleanup|--validate [--full] [--recovery-format dmg|raw] [--output artifacts/native-macos] [--source existing-dockur-clone] [--compression-chunk readonly-qualified-chunk]");
return 0;
}
var output = Path.GetFullPath(Option(args, "--output") ?? "artifacts/native-macos");
var full = args.Contains("--full");
var recoveryFormat = Option(args, "--recovery-format") ?? "dmg";
if (recoveryFormat is not ("dmg" or "raw")) throw new ArgumentException("Recovery format must be dmg or raw.");
if (args.Contains("--validate"))
{
ValidateRunnerMemoryGate();
ValidateContracts();
ValidateBootProgress();
ValidateDiskStackCapture(output);
@@ -84,14 +87,16 @@ static class NativeDiagnostic
if (full) ValidateFullContracts();
if (Option(args, "--source") is { } source)
{
await PrepareSource(Path.GetFullPath(source), output, full ? new string('0', 32) : "validation", false, CancellationToken.None, full);
await PrepareSource(Path.GetFullPath(source), output, full ? new string('0', 32) : "validation", false, CancellationToken.None, full, recoveryFormat);
if (recoveryFormat == "raw") await ValidateRawRecoveryFixture(output, CancellationToken.None);
await ValidateResourceRetention(output);
await ValidateRecoveryPatch(output);
await ValidateTcgPreflight(output, CancellationToken.None);
if (full) await ValidateDiskSerialParser(output);
Save(Path.Combine(output, "validation.json"), new
{
success = true, profile = Profile, mode = full ? "full" : "readiness",
success = true, profile = Profile, mode = full ? "full" : "readiness", recoveryFormat,
causalSingleVariableTest = recoveryFormat == "raw", comparisonBaseCommit = "2e2d702e294c39f24c6a3e44e5e94ff793d8774b",
helperSha256 = Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "MacOsNativeDiagnostic.cs"))),
readinessSourceSha256 = Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "macos-native-readiness.sh"))),
udifChecksumBindingSha256 = Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "macos-native-udif-checksums.py"))),
@@ -153,7 +158,7 @@ static class NativeDiagnostic
throw new InvalidOperationException("This diagnostic runs on the existing Linux/x64 runner only.");
ValidateContracts();
var sourceCommit = (await Command("git", ["rev-parse", "HEAD"], output, "candidate-commit", deadline.Token)).Output.Trim();
Save(Path.Combine(output, "run-metadata.json"), new { token, startedUtc = DateTimeOffset.UtcNow, sourceCommit, dockurCommit = DockurCommit, profile = Profile, causalSingleVariableTest = false, kvm = false, cpuModel = CpuModel, recoveryMajor = 14, cpuFlags = CpuFlags, runId = Environment.GetEnvironmentVariable("GITHUB_RUN_ID"), server = Environment.GetEnvironmentVariable("GITHUB_SERVER_URL"), architecture = RuntimeInformation.ProcessArchitecture.ToString(), deadlineMinutes, mode = full ? "full" : "readiness" });
Save(Path.Combine(output, "run-metadata.json"), new { token, startedUtc = DateTimeOffset.UtcNow, sourceCommit, dockurCommit = DockurCommit, profile = Profile, recoveryFormat, causalSingleVariableTest = recoveryFormat == "raw", comparisonBaseCommit = "2e2d702e294c39f24c6a3e44e5e94ff793d8774b", kvm = false, cpuModel = CpuModel, recoveryMajor = 14, cpuFlags = CpuFlags, runId = Environment.GetEnvironmentVariable("GITHUB_RUN_ID"), server = Environment.GetEnvironmentVariable("GITHUB_SERVER_URL"), architecture = RuntimeInformation.ProcessArchitecture.ToString(), deadlineMinutes, mode = full ? "full" : "readiness" });
var info = await Command("docker", ["info", "--format", "{{json .}}"], output, "docker-info", deadline.Token);
using (var document = JsonDocument.Parse(info.Output))
{
@@ -164,16 +169,15 @@ static class NativeDiagnostic
throw new InvalidOperationException("Existing Docker resources cannot fit this bounded 2-CPU/6-GiB diagnostic; no infrastructure change was requested.");
}
await Command("sh", ["-c", "cat /proc/meminfo; printf '\n[cgroup]\n'; cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max 2>/dev/null || true; printf '\n[workspace disk]\n'; df -Pk ."], output, "runner-resources", deadline.Token);
var available = System.Text.RegularExpressions.Regex.Match(File.ReadAllText("/proc/meminfo"), @"(?m)^MemAvailable:\s+(\d+) kB$");
if (!available.Success || long.Parse(available.Groups[1].Value) < 5L * 1024 * 1024)
throw new InvalidOperationException("Existing runner memory has less than the 5-GiB available diagnostic budget; no infrastructure change was requested.");
if (!HasAvailableGuestMemory(File.ReadAllText("/proc/meminfo")))
throw new InvalidOperationException("Existing runner memory cannot fit the 4-GiB guest plus its 512-MiB QEMU overhead budget; no infrastructure change was requested.");
var source = Path.Combine(work, "dockur");
await Command("git", ["clone", "--no-checkout", "https://github.com/dockur/macos.git", source], output, "dockur-clone", deadline.Token);
await Command("git", ["-C", source, "checkout", "--detach", DockurCommit], output, "dockur-checkout", deadline.Token);
var actualCommit = (await Command("git", ["-C", source, "rev-parse", "HEAD"], output, "dockur-commit", deadline.Token)).Output.Trim();
if (actualCommit != DockurCommit) throw new InvalidOperationException("Dockur source pin mismatch.");
if (full) await PreparePayload(source, output, token, sourceCommit, deadline.Token);
await PrepareSource(source, output, token, true, deadline.Token, full);
await PrepareSource(source, output, token, true, deadline.Token, full, recoveryFormat);
await Command("docker", ["build", "--platform", "linux/amd64", "--label", OwnerLabel + "=" + token, "--tag", state.ImageTag, source], output, "docker-build", deadline.Token, echo: true);
var imageInspect = await Command("docker", ["image", "inspect", state.ImageTag], output, "image-inspect", deadline.Token);
using (var image = JsonDocument.Parse(imageInspect.Output))
@@ -385,7 +389,7 @@ static class NativeDiagnostic
return source.Remove(from, to + end.Length - from).Insert(from, originalSequence);
}
static async Task PrepareSource(string source, string output, string token, bool writeSource, CancellationToken cancellation, bool full = false)
static async Task PrepareSource(string source, string output, string token, bool writeSource, CancellationToken cancellation, bool full = false, string recoveryFormat = "dmg")
{
Directory.CreateDirectory(output);
var probe = ReadProbeAssets();
@@ -436,6 +440,11 @@ static class NativeDiagnostic
image = ReplaceOnce(image, " chmod 0755 \"$script\"\n", " chmod 0755 \"$script\" \"${script%/*}/native-process-probe-x86_64\"\n printf '%s\\n' '" + token + "' > \"${script%/*}/run.owner\" || return 1\n");
image = ReplaceOnce(image, " if ! cmp -s \"$IMAGE_TOOLS/recovery/launch.sh\" \"$script\" ||\n", " if ! cmp -s \"$IMAGE_TOOLS/recovery/launch.sh\" \"$script\" ||\n ! cmp -s \"$IMAGE_TOOLS/recovery/readiness.sh\" \"$state/readiness.sh\" ||\n ! cmp -s \"$IMAGE_ASSETS/native-process-probe/native-process-probe-x86_64\" \"$state/native-process-probe-x86_64\" ||\n ! cmp -s \"$IMAGE_ASSETS/native-process-probe/build-manifest.json\" \"$state/native-process-probe-manifest.json\" ||\n");
image = ReplaceOnce(image, " if ! result=$(python3 \"$IMAGE_TOOLS/recovery/patch.py\" \"$image\"); then\n", " info \"[recovery-original] bytes=$(stat -c%s -- \"$image\") sha256=$(sha256sum \"$image\" | awk '{print $1}')\"\n if ! result=$(python3 \"$IMAGE_TOOLS/recovery/patch.py\" \"$image\"); then\n");
if (recoveryFormat == "raw")
{
image = ReplaceOnce(image, " if ! mv -f \"$source\" \"$dest\"; then\n error \"Failed to save automated recovery image to $dest.\"\n return 1\n fi\n", RawRecoveryPreparation);
entry = ReplaceOnce(entry, ". disk.sh # Initialize disks\n", ". disk.sh # Initialize disks\n" + RawRecoveryDriveSelection + "\n");
}
if (full)
{
await PrepareFullSource(source, output, token, writeSource, cancellation);
@@ -472,6 +481,155 @@ static class NativeDiagnostic
File.WriteAllBytes(Path.Combine(probeAssets, "build-manifest.json"), probe.Manifest);
}
static bool HasAvailableGuestMemory(string meminfo)
{
var available = System.Text.RegularExpressions.Regex.Match(meminfo, @"(?m)^MemAvailable:\s+(\d+) kB$");
return available.Success && long.TryParse(available.Groups[1].Value, out var kib) && kib >= 4L * 1024 * 1024 + 512L * 1024;
}
static void ValidateRunnerMemoryGate()
{
// Run 4204: 4-GiB guest plus 512-MiB QEMU overhead fits its captured available memory.
var cases = new[]
{
("captured-run4204", "MemTotal: 16281732 kB\nMemAvailable: 5138696 kB\n", true),
("below-guest-plus-overhead", "MemAvailable: 4194304 kB\n", false),
("missing", "MemTotal: 16281732 kB\n", false),
("invalid", "MemAvailable: unavailable kB\n", false)
};
foreach (var (name, meminfo, expected) in cases)
if (HasAvailableGuestMemory(meminfo) != expected)
throw new InvalidOperationException("Existing runner memory admission failed: " + name);
}
static readonly string RawRecoveryPreparation = """
local raw="$dest.raw" pending="$dest.raw.tmp" source_hash after_hash raw_hash cache_before cache_after
[ ! -e "$raw" ] && [ ! -e "$pending" ] && [ ! -e "$raw.json" ] || {
error 'RAW Recovery output already exists.'; return 1;
}
source_hash=$(sha256sum "$source" | awk '{print $1}') || return 1
[[ "$source_hash" =~ ^[0-9a-f]{64}$ ]] || return 1
if ! qemu-img convert -f dmg -O raw "$source" "$pending" ||
! qemu-img compare -f dmg -F raw "$source" "$pending"; then
rm -f "$pending"
error 'RAW Recovery conversion or sector equality failed.'
return 1
fi
after_hash=$(sha256sum "$source" | awk '{print $1}') || { rm -f "$pending"; return 1; }
raw_hash=$(sha256sum "$pending" | awk '{print $1}') || { rm -f "$pending"; return 1; }
if [ "$source_hash" != "$after_hash" ] || [[ ! "$raw_hash" =~ ^[0-9a-f]{64}$ ]]; then
rm -f "$pending"
error 'Patched DMG changed during RAW conversion.'
return 1
fi
# These verified owned files are not guest RAM. Release only their
# persisted data cache before Dockur computes its cgroup RAM allowance.
cache_before=$(cat /sys/fs/cgroup/memory.current 2>/dev/null || printf unavailable)
if ! dd of="$pending" oflag=nocache conv=nocreat,notrunc,fdatasync count=0 status=none ||
! dd of="$source" oflag=nocache conv=nocreat,notrunc,fdatasync count=0 status=none; then
rm -f "$pending"
error 'Failed to release the verified Recovery file caches before RAM admission.'
return 1
fi
cache_after=$(cat /sys/fs/cgroup/memory.current 2>/dev/null || printf unavailable)
info "[recovery-raw-cache] files=two-owned memory-current-before=$cache_before memory-current-after=$cache_after"
if ! mv -f "$source" "$dest" || ! mv -f "$pending" "$raw"; then
rm -f "$pending"
error 'Failed to retain the patched DMG and equivalent RAW Recovery.'
return 1
fi
printf '{"sourceFormat":"dmg","targetFormat":"raw","sectorEqualityVerified":true,"sourceUnchanged":true,"sourceSha256":"%s","rawSha256":"%s"}\n' "$source_hash" "$raw_hash" > "$raw.json" || return 1
info "[recovery-raw] sector-equality=true source-unchanged=true source-sha256=$source_hash raw-sha256=$raw_hash"
""" + "\n";
static readonly string RawRecoveryDriveSelection = """
# BEGIN raw Recovery backend
raw_recovery_from="file=$STORAGE/setup.dmg,id=install,format=dmg,cache=unsafe,readonly=on,if=none"
raw_recovery_to="file=$STORAGE/setup.dmg.raw,id=install,format=raw,cache=unsafe,readonly=on,if=none"
[[ -s "$STORAGE/setup.dmg.raw" && -s "$STORAGE/setup.dmg.raw.json" && "$DISK_OPTS" == *"$raw_recovery_from"* ]] || {
error 'Verified RAW Recovery or expected DMG backend is missing.'; exit 34;
}
raw_recovery_tail=${DISK_OPTS#*"$raw_recovery_from"}
[[ "$raw_recovery_tail" != *"$raw_recovery_from"* ]] || { error 'Recovery backend is duplicated.'; exit 34; }
DISK_OPTS=${DISK_OPTS/"$raw_recovery_from"/"$raw_recovery_to"}
unset raw_recovery_from raw_recovery_to raw_recovery_tail
# END raw Recovery backend
""";
static async Task ValidateRawRecoveryFixture(string output, CancellationToken cancellation)
{
var image = File.ReadAllText(Path.Combine(output, "image.sh.patched"));
var begin = image.IndexOf("prepareAutomatedRecovery() {", StringComparison.Ordinal);
var end = image.IndexOf("\nreturn 0\n", begin, StringComparison.Ordinal);
if (begin < 0 || end < 0) throw new InvalidOperationException("Missing Recovery preparation boundary.");
var preparation = image[begin..end];
var realScript = "#!/bin/bash\nset -Eeuo pipefail\ninfo() { printf '%s\\n' \"$*\"; }\nhtml() { :; }\nerror() { printf '%s\\n' \"$*\" >&2; }\npatchRecoveryBootstrap() { :; }\n" + preparation + "\nprepareAutomatedRecovery \"$1\" \"$2\"\n";
File.WriteAllText(Path.Combine(output, "raw-recovery-real-qemu-fixture.sh"), realScript);
var mock = """
qemu-img() {
case "$1" in
info) printf '%s\n' '{"format":"dmg"}' ;;
convert)
[ "$2 $3 $4 $5" = '-f dmg -O raw' ] || return 65
[ "$TEST_CASE" != convert-failed ] || return 1
cp "$6" "$7" || return 1
[ "$TEST_CASE" != source-mutated ] || printf 'mutation' >> "$6"
;;
compare)
[ "$2 $3 $4 $5" = '-f dmg -F raw' ] || return 65
[ "$TEST_CASE" != compare-failed ] || return 1
[ "$TEST_CASE" = source-mutated ] || cmp -s "$6" "$7"
;;
*) return 65 ;;
esac
}
dd() {
[ "$2 $3 $4 $5" = 'oflag=nocache conv=nocreat,notrunc,fdatasync count=0 status=none' ] || return 65
case "$1" in
"of=$pending") [ "$TEST_CASE" != raw-cache-failed ] || return 1 ;;
"of=$source") [ "$TEST_CASE" != source-cache-failed ] || return 1 ;;
*) return 65 ;;
esac
printf '%s\n' "$1" >> "$dest.cache-eviction"
}
""";
var script = realScript.Replace(preparation, mock + "\n" + preparation, StringComparison.Ordinal);
File.WriteAllText(Path.Combine(output, "raw-recovery-mock-fixture.sh"), script);
var cases = new[] { "equal", "convert-failed", "compare-failed", "source-mutated", "raw-cache-failed", "source-cache-failed" };
foreach (var name in cases)
{
var folder = Path.Combine(output, "raw-recovery-" + name);
Directory.CreateDirectory(folder);
var input = Path.Combine(folder, "source.dmg"); var destination = Path.Combine(folder, "setup.dmg");
var bytes = Encoding.UTF8.GetBytes("known already-patched Recovery content\n");
File.WriteAllBytes(input, bytes);
var result = await Command("bash", ["-c", "TEST_CASE=\"$3\"\n" + script, "raw-recovery-fixture", input, destination, name], output, "raw-recovery-" + name, cancellation, requireSuccess: false);
var passed = name == "equal";
if ((result.ExitCode == 0) != passed || passed && (!File.Exists(destination + ".raw") || !File.ReadAllBytes(destination + ".raw").SequenceEqual(bytes) || !File.ReadAllBytes(destination).SequenceEqual(bytes))
|| !passed && File.Exists(destination + ".raw"))
throw new InvalidOperationException("RAW Recovery preparation behavior failed: " + name);
if (passed && (!File.Exists(destination + ".cache-eviction") || !File.ReadAllLines(destination + ".cache-eviction").SequenceEqual(new[] { "of=" + destination + ".raw.tmp", "of=" + input })))
throw new InvalidOperationException("RAW Recovery must release only the two verified files' caches before retaining them.");
}
var entry = File.ReadAllText(Path.Combine(output, "container-entry.sh"));
var selectionStart = entry.IndexOf("# BEGIN raw Recovery backend", StringComparison.Ordinal);
var selectionEnd = entry.IndexOf("# END raw Recovery backend", selectionStart, StringComparison.Ordinal);
if (selectionStart < 0 || selectionEnd < 0) throw new InvalidOperationException("Missing RAW drive selection boundary.");
var selection = entry[selectionStart..selectionEnd];
var storage = Path.Combine(output, "raw-recovery-equal");
var device = " -device virtio-blk-pci,drive=install,bus=pcie.0,iothread=io2 -drive file=/data.img,id=data3,format=raw";
var originalDrive = " -drive file=" + storage + "/setup.dmg,id=install,format=dmg,cache=unsafe,readonly=on,if=none";
var expectedDrive = " -drive file=" + storage + "/setup.dmg.raw,id=install,format=raw,cache=unsafe,readonly=on,if=none";
var selectionCases = new[] { ("one", originalDrive + device, true), ("missing", device, false), ("duplicate", originalDrive + originalDrive + device, false) };
foreach (var test in selectionCases)
{
var result = await Command("bash", ["-c", "set -Eeuo pipefail\nSTORAGE=\"$1\"\nDISK_OPTS=\"$2\"\nerror() { printf '%s\\n' \"$*\" >&2; }\n" + selection + "\nprintf '%s' \"$DISK_OPTS\"\n", "raw-recovery-selection", storage, test.Item2], output, "raw-recovery-selection-" + test.Item1, cancellation, requireSuccess: false);
if ((result.ExitCode == 0) != test.Item3 || test.Item3 && result.Output != expectedDrive + device)
throw new InvalidOperationException("RAW Recovery backend selection failed: " + test.Item1);
}
Save(Path.Combine(output, "raw-recovery-fixtures.json"), new { success = true, cases, selectionCases = selectionCases.Select(test => test.Item1), qemuBoundaryMocked = true, realQemuExecuted = false, guestExecuted = false });
}
static (byte[] Binary, byte[] Manifest) ReadProbeAssets()
{
var folder = Path.Combine("tools", "ci", "native-process-probe");
@@ -1275,7 +1433,7 @@ static class NativeDiagnostic
// Hash it once instead of rereading the image on every twenty-second poll.
if ((logs.Output + stage.Output).Contains("[supported-profile] accelerator=tcg", StringComparison.Ordinal)
&& !File.Exists(Path.Combine(output, "guest-container-resources.last-success.json")))
await Command("docker", ["exec", id, "sh", "-c", "printf '[qemu]\n'; qemu-system-x86_64 --version | head -n 1; printf '[Recovery hash]\n'; test -f /storage/14/setup.dmg && sha256sum /storage/14/setup.dmg || exit 1; printf '[resources]\n'; df -Pk /storage; cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max 2>/dev/null || true"], output, "guest-container-resources", cancellation, requireSuccess: false, retainSuccessful: true);
await Command("docker", ["exec", id, "sh", "-c", "printf '[qemu]\n'; qemu-system-x86_64 --version | head -n 1; printf '[Recovery hash]\n'; test -f /storage/14/setup.dmg && sha256sum /storage/14/setup.dmg || exit 1; if test -f /storage/14/setup.dmg.raw.json; then printf '[RAW Recovery equality receipt]\n'; cat /storage/14/setup.dmg.raw.json; fi; printf '[resources]\n'; df -Pk /storage; cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max 2>/dev/null || true"], output, "guest-container-resources", cancellation, requireSuccess: false, retainSuccessful: true);
}
static async Task CaptureDiskStack(string id, string output, string token, bool full, bool final, CancellationToken cancellation)