forked from Manuel/meeting-assistant
Observe owned diskutil stack and live StorageKit in Recovery
This commit is contained in:
@@ -29,6 +29,7 @@ static class NativeDiagnostic
|
||||
const string FullState = "/storage/14/ci-state";
|
||||
const string NativeVersionSource = "/System/Library/CoreServices/SystemVersion.plist";
|
||||
const string NativeVersionMethod = "guest-file/host-xml";
|
||||
const int MaximumDiskStackBytes = 512 * 1024;
|
||||
static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
|
||||
const string OriginalBootstrap = "[ ! -e /tmp/m ]&&{ /sbin/mount_9p installstate >/dev/null 2>&1;exec /Volumes/installstate/launch.sh;};: >/tmp/m\n";
|
||||
const string MountOnlyBootstrap = "[ ! -e /tmp/m ]&& /sbin/mount_9p installstate >/dev/null 2>&1; : >/tmp/m\n";
|
||||
@@ -73,6 +74,7 @@ static class NativeDiagnostic
|
||||
{
|
||||
ValidateContracts();
|
||||
ValidateBootProgress();
|
||||
ValidateDiskStackCapture(output);
|
||||
await ValidateNativeSystemVersion(output);
|
||||
if (full) ValidateFullContracts();
|
||||
if (Option(args, "--source") is { } source)
|
||||
@@ -94,7 +96,11 @@ static class NativeDiagnostic
|
||||
nativeVersionMaximumBytes = 4096, nativeVersionResponseRequired = false,
|
||||
nativeVersionCandidateIsQualifiedReadiness = false, nativeVersionBindingRequiredBeforePermit = true,
|
||||
nativeVersionFixtures = "native-system-version-fixtures.json", nativeProductVersionCommandRemoved = true,
|
||||
diskReadinessAttemptLimit = 1, diskCommandLimitSeconds = 120, diskThreadObservationLimitSeconds = 60, stackSamplingUsed = false,
|
||||
diskReadinessAttemptLimit = 1, diskCommandLimitSeconds = 600, diskCommandExtendedForDiagnosticObservation = true,
|
||||
diskObservationCommandLimitSeconds = 60, stackSamplingRequested = true, stackSamplingDurationSeconds = 1,
|
||||
stackSamplingIntervalMilliseconds = 100, stackSamplingMayDie = true, stackSamplingRuntimeSucceeded = false,
|
||||
stackObservationIsQualifiedReadiness = false, diskStackMaximumCapturedBytes = MaximumDiskStackBytes,
|
||||
diskManagementDiagnosticLabel = "com.apple.storagekitd", diskStackCaptureFixtureCases = 4,
|
||||
resultNegativeCases = 6, containerNegativeCases = 11, recoveryPositiveCases = 4, recoveryNegativeCases = 12,
|
||||
independentFixtureCrc32Readback = true, resourceSnapshotRetention = true, cpuProfileSourceContractsVerified = true,
|
||||
preflightGateFixtureCases = 8, qemuRuntimePreflightExecuted = false, templateIsoDownloaded = false,
|
||||
@@ -279,6 +285,9 @@ static class NativeDiagnostic
|
||||
baseline = ReplaceOnce(baseline, "while (( attempt < 1 && SECONDS - readiness_start < 600 )); do", "while (( SECONDS - readiness_start < 600 )); do");
|
||||
if (Hash(Encoding.UTF8.GetBytes(baseline)) != "4d428f594dac14eff64ed87b172c81ecf85ac91da8c5460cd6ec4b1d310800c3")
|
||||
throw new InvalidOperationException("Outside seven explicit diagnostic blocks, two explicit native SystemVersion getter blocks and one-attempt limit, baseline identity/service/disk gates and watchdogs must remain identical.");
|
||||
foreach (var required in new[] { "command_limit=600; fi", "run_command management_before /bin/launchctl print system/com.apple.storagekitd", "observe_live_command storagekit-live /bin/launchctl print system/com.apple.storagekitd", "observe_command diskutil-stack /usr/bin/sample \"$disk_process\" 1 100 -mayDie -file \"$stack_output\"", "stack_output=\"$STATE_DIR/diskutil-stack.txt\"", "read -r -t 60 -u 9", "\"$BASH_VERSION\"" })
|
||||
if (!readiness.Contains(required, StringComparison.Ordinal)) throw new InvalidOperationException("Owned optional disk observation contract changed: " + required);
|
||||
if (readiness.Contains("PENDING_OUTPUTS+=(\"$stack_output\")", StringComparison.Ordinal)) throw new InvalidOperationException("Stack reports must be bounded directly by the Linux host, without another guest copy.");
|
||||
if (Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "macos-native-bootstrap.sh"))) != "94f069e116fdc7685a4d233cab6fa50df9f39274386bb82157674061e74fadb5")
|
||||
throw new InvalidOperationException("Compatibility profile must preserve the baseline Apple recoveryosd wrapper.");
|
||||
if (Hash(Encoding.UTF8.GetBytes(OriginalDaemon13)) != "af9d7f6c1948079bd4384d27b6882678d6fb4e338fcf6a8be8f84fceef174ad6") throw new InvalidOperationException("macOS 13 allowlist bytes differ from the independently read comparison plist.");
|
||||
@@ -1158,6 +1167,7 @@ static class NativeDiagnostic
|
||||
ReportCpuPreflight(output, stage.ExitCode == 0 ? stage.Output : logs.Output);
|
||||
await Command("docker", ["exec", id, "head", "-c", "4096", "/run/shm/kernel-handoffs.log"], output, "capture-kernel-handoffs", cancellation, requireSuccess: false, retainSuccessful: true);
|
||||
if (token is not null) await CaptureNativeSystemVersion(id, output, token, full, cancellation);
|
||||
if (token is not null) await CaptureDiskStack(id, output, token, full, final, cancellation);
|
||||
var files = new List<(string, string)> { ("proof.log", "guest-proof.log"), ("result.json", "guest-result.json") };
|
||||
if (full) files.AddRange([("guest-phase.json", "guest-phase.json"), ("full-result.json", "full-result.json"), ("firstboot.log", "firstboot.log"), ("unattended-firstboot.log", "unattended-firstboot.log"), ("install.log", "install.log"), ("apple.log", "apple.log"), ("disk-ownership-ioreg.log", "disk-ownership-ioreg.log"), ("installed-root.plist", "installed-root.plist"), ("apfs-containers.plist", "apfs-containers.plist"), ("physical-store.plist", "physical-store.plist"), ("clt-catalog.log", "clt-catalog.log"), ("clt-install.log", "clt-install.log"), ("clt-sdk.log", "clt-sdk.log")]);
|
||||
foreach (var file in files)
|
||||
@@ -1182,6 +1192,55 @@ static class NativeDiagnostic
|
||||
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);
|
||||
}
|
||||
|
||||
static async Task CaptureDiskStack(string id, string output, string token, bool full, bool final, CancellationToken cancellation)
|
||||
{
|
||||
var sourcePath = (full ? FullState : "/dev/shm/installstate") + "/diskutil-stack.txt";
|
||||
// The sample writes directly through 9p. Capture changing bytes with Linux
|
||||
// coreutils; absence/empty output must never suppress a later or final read.
|
||||
var capture = await Command("docker", ["exec", id, "bash", "-o", "pipefail", "-c", "head -c 524289 '" + sourcePath + "' | base64 -w 0"], output, "capture-diskutil-stack", cancellation, requireSuccess: false);
|
||||
if (capture.ExitCode != 0 || string.IsNullOrEmpty(capture.Output)) return;
|
||||
SaveDiskStackObservation(output, token, sourcePath, Convert.FromBase64String(capture.Output), final);
|
||||
}
|
||||
|
||||
static void SaveDiskStackObservation(string output, string token, string sourcePath, byte[] raw, bool final)
|
||||
{
|
||||
if (raw.Length == 0) return;
|
||||
if (raw.Length > MaximumDiskStackBytes + 1) throw new InvalidOperationException("Disk stack transport exceeded its one-byte sentinel.");
|
||||
var truncated = raw.Length > MaximumDiskStackBytes;
|
||||
var bounded = truncated ? raw[..MaximumDiskStackBytes] : raw;
|
||||
File.WriteAllBytes(Path.Combine(output, "diskutil-stack.txt"), bounded);
|
||||
Save(Path.Combine(output, "diskutil-stack-capture.json"), new { token, sourcePath, capturedBytes = bounded.Length, sha256 = Hash(bounded), maximumCapturedBytes = MaximumDiskStackBytes, truncated, finalCapture = final, snapshotOnly = true, sampleCompletionVerified = false, qualifiedReadiness = false, sampleExitAndTimeoutEvidence = "guest-proof.log", capturedUtc = DateTimeOffset.UtcNow });
|
||||
}
|
||||
|
||||
static void ValidateDiskStackCapture(string output)
|
||||
{
|
||||
var fixture = Path.Combine(output, "validation-disk-stack-capture");
|
||||
Directory.CreateDirectory(fixture);
|
||||
var cases = new List<object>();
|
||||
foreach (var size in new[] { 0, 64, MaximumDiskStackBytes, MaximumDiskStackBytes + 1 })
|
||||
{
|
||||
var folder = Path.Combine(fixture, size.ToString());
|
||||
Directory.CreateDirectory(folder);
|
||||
var raw = Enumerable.Repeat((byte)'s', size).ToArray();
|
||||
SaveDiskStackObservation(folder, "fixture", FullState + "/diskutil-stack.txt", raw, false);
|
||||
if (size == 0)
|
||||
{
|
||||
if (Directory.EnumerateFiles(folder).Any()) throw new InvalidOperationException("Empty stack capture was finalized.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var retained = File.ReadAllBytes(Path.Combine(folder, "diskutil-stack.txt"));
|
||||
using var receipt = JsonDocument.Parse(File.ReadAllText(Path.Combine(folder, "diskutil-stack-capture.json")));
|
||||
if (retained.Length != Math.Min(size, MaximumDiskStackBytes) || !retained.AsSpan().SequenceEqual(raw.AsSpan(0, retained.Length))
|
||||
|| receipt.RootElement.GetProperty("truncated").GetBoolean() != (size > MaximumDiskStackBytes)
|
||||
|| receipt.RootElement.GetProperty("sampleCompletionVerified").GetBoolean() || receipt.RootElement.GetProperty("qualifiedReadiness").GetBoolean())
|
||||
throw new InvalidOperationException("Stack capture changed bytes/bounds or qualified an observation.");
|
||||
}
|
||||
cases.Add(new { size, success = true });
|
||||
}
|
||||
Save(Path.Combine(fixture, "validation.json"), new { success = true, cases, emptyCaptureFinalized = false, maximumCapturedBytes = MaximumDiskStackBytes, qualifiedReadiness = false, guestExecuted = false });
|
||||
}
|
||||
|
||||
static int NativeVersionRequestLength(string request, string token)
|
||||
{
|
||||
var fields = request.Split('\n');
|
||||
|
||||
Reference in New Issue
Block a user