From e8e5390f6cece9d6996391896f57c974c877ee3b Mon Sep 17 00:00:00 2001 From: f-trycua Date: Mon, 21 Apr 2025 16:35:56 -0700 Subject: [PATCH] Fix first pull --- .../ImageContainerRegistry.swift | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/libs/lume/src/ContainerRegistry/ImageContainerRegistry.swift b/libs/lume/src/ContainerRegistry/ImageContainerRegistry.swift index 63dde180..5db9b411 100644 --- a/libs/lume/src/ContainerRegistry/ImageContainerRegistry.swift +++ b/libs/lume/src/ContainerRegistry/ImageContainerRegistry.swift @@ -1521,40 +1521,53 @@ class ImageContainerRegistry: @unchecked Sendable { throw PullError.fileCreationFailed(diskImgPath.path) } - // 5. Set up file handle and create sparse file - let outputHandle = try FileHandle(forWritingTo: diskImgPath) - try outputHandle.truncate(atOffset: diskSize) + // IMPORTANT: Use autoreleasepool to ensure file handle is released promptly + try autoreleasepool { + // 5. Set up file handle and create sparse file + let outputHandle = try FileHandle(forWritingTo: diskImgPath) + try outputHandle.truncate(atOffset: diskSize) + + // 6. Write test patterns at beginning and end + Logger.info("Writing test patterns to verify writability...") + let testPattern = "LUME_TEST_PATTERN".data(using: .utf8)! + try outputHandle.seek(toOffset: 0) + try outputHandle.write(contentsOf: testPattern) + try outputHandle.seek(toOffset: diskSize - UInt64(testPattern.count)) + try outputHandle.write(contentsOf: testPattern) + try outputHandle.synchronize() + + // 7. Decompress the original data at offset 0 + Logger.info("Decompressing original disk image with same mechanism as cache pull...") + let bytesWritten = try decompressChunkAndWriteSparse( + inputPath: backupPath.path, + outputHandle: outputHandle, + startOffset: 0 + ) + + Logger.info("Decompressed \(ByteCountFormatter.string(fromByteCount: Int64(bytesWritten), countStyle: .file)) of disk image data") + + // 8. Ensure all data is written to disk with an explicit sync + try outputHandle.synchronize() + + // Very important: explicitly close the handle here inside the autorelease pool + try outputHandle.close() + Logger.info("File handle explicitly closed after decompression and synchronization") + } - // 6. Write test patterns at beginning and end - Logger.info("Writing test patterns to verify writability...") - let testPattern = "LUME_TEST_PATTERN".data(using: .utf8)! - try outputHandle.seek(toOffset: 0) - try outputHandle.write(contentsOf: testPattern) - try outputHandle.seek(toOffset: diskSize - UInt64(testPattern.count)) - try outputHandle.write(contentsOf: testPattern) - try outputHandle.synchronize() - - // 7. Decompress the original data at offset 0 - Logger.info("Decompressing original disk image with same mechanism as cache pull...") - let bytesWritten = try decompressChunkAndWriteSparse( - inputPath: backupPath.path, - outputHandle: outputHandle, - startOffset: 0 - ) - - Logger.info("Decompressed \(ByteCountFormatter.string(fromByteCount: Int64(bytesWritten), countStyle: .file)) of disk image data") - - // 8. Ensure all data is written to disk with an explicit sync - try outputHandle.synchronize() - - // Very important: close the handle before optimization - try outputHandle.close() + // Wait a moment for file system operations to complete + Thread.sleep(forTimeInterval: 0.5) // 9. Optimize sparse file with cp -c (exactly matching cache pull process) if FileManager.default.fileExists(atPath: "/bin/cp") { Logger.info("Optimizing sparse file representation...") let optimizedPath = diskImgPath.path + ".optimized" + // Run a sync before optimization + let syncBeforeOptimize = Process() + syncBeforeOptimize.executableURL = URL(fileURLWithPath: "/bin/sync") + try syncBeforeOptimize.run() + syncBeforeOptimize.waitUntilExit() + let process = Process() process.executableURL = URL(fileURLWithPath: "/bin/cp") process.arguments = ["-c", diskImgPath.path, optimizedPath]