@@ -114,7 +114,7 @@ extension VMDirectory {
114114
115115 /// Reconstructs one complete immutable base disk or published ASIF overlay
116116 /// from its Tart disk chunks, unless the shared content store already has a
117- /// verified copy.
117+ /// size-matching copy.
118118 private func pullDiskFile(
119119 registry: Registry ,
120120 group: TartDiskFileGroup ,
@@ -133,7 +133,10 @@ extension VMDirectory {
133133 try lock. lock ( )
134134 defer { try ? lock. unlock ( ) }
135135
136- if let existingURL = try contentStore. existingContentURL ( for: contentDigest) {
136+ if let existingURL = try contentStore. contentURLIfPresent ( for: contentDigest) ,
137+ let actualSize = UInt64 ( exactly: try existingURL. sizeBytes ( ) ) ,
138+ let expectedSize = group. uncompressedSize ( ) ,
139+ actualSize == expectedSize {
137140 progress. completedUnitCount += group. chunks. reduce ( 0 ) { $0 + Int64( $1. size) }
138141 return existingURL
139142 }
@@ -189,7 +192,6 @@ extension VMDirectory {
189192 var annotations = diskAnnotations
190193 annotations [ uploadTimeAnnotation] = Date ( ) . toISO ( )
191194 manifest. annotations = annotations
192-
193195 // Manifest
194196 for reference in references {
195197 defaultLogger. appendNewLine ( " pushing manifest for \( reference) ... " )
@@ -228,6 +230,15 @@ extension VMDirectory {
228230 }
229231
230232 let localManifest = try OCIManifest ( fromJSON: Data ( contentsOf: manifestURL) )
233+ // pushToRegistry() reads config.json before reaching this point. Closing
234+ // that read descriptor can release the caller's fcntl PID lock, so take a
235+ // fresh lock before hashing, uploading, and inspecting the writable overlay.
236+ let stackedDiskLock = try lock ( )
237+ guard try stackedDiskLock. trylock ( ) else {
238+ throw RuntimeError . VMIsRunning ( name)
239+ }
240+ defer { try ? stackedDiskLock. unlock ( ) }
241+
231242 let inheritedGroups : [ TartDiskFileGroup ]
232243 switch try localManifest. tartDiskRepresentation ( ) {
233244 case . flat( let base) where base. contentDigest != nil :
@@ -250,26 +261,13 @@ extension VMDirectory {
250261 ) )
251262 }
252263
253- // Keep the snapshot out of startup GC while this potentially long push
254- // hashes, uploads, and inspects it.
255- let frozenOverlayDirectory = try VMDirectory . temporary ( )
256- let frozenOverlayLock = try FileLock ( lockURL: frozenOverlayDirectory. baseURL)
257- try frozenOverlayLock. lock ( )
258- defer {
259- try ? frozenOverlayLock. unlock ( )
260- try ? FileManager . default. removeItem ( at: frozenOverlayDirectory. baseURL)
261- }
262-
263- let frozenOverlayURL = frozenOverlayDirectory. baseURL. appendingPathComponent ( " overlay.asif " )
264- try FileManager . default. copyItem ( at: overlayURL, to: frozenOverlayURL)
265-
266- let overlaySize = try FileManager . default. attributesOfItem ( atPath: frozenOverlayURL. path) [ . size] as! Int64
264+ let overlaySize = try FileManager . default. attributesOfItem ( atPath: overlayURL. path) [ . size] as! Int64
267265 defaultLogger. appendNewLine ( " pushing overlay... " )
268266 let progress = Progress ( totalUnitCount: overlaySize)
269267 ProgressObserver ( progress) . log ( defaultLogger)
270- let contentDigest = try Digest . hash ( frozenOverlayURL )
268+ let contentDigest = try Digest . hash ( overlayURL )
271269 let chunks = try await DiskV2 . push (
272- diskURL: frozenOverlayURL ,
270+ diskURL: overlayURL ,
273271 mediaType: asifOverlayMediaType,
274272 registry: registry,
275273 chunkSizeMb: chunkSizeMb,
@@ -278,7 +276,7 @@ extension VMDirectory {
278276 )
279277 layers. append ( contentsOf: annotatedChunks ( chunks, kind: . asifOverlay, contentDigest: contentDigest) )
280278
281- let blockLayout = try DiskImageStack . diskImageBlockLayout ( at: frozenOverlayURL )
279+ let blockLayout = try DiskImageStack . diskImageBlockLayout ( at: overlayURL )
282280 let diskSize = blockLayout. blockSize. multipliedReportingOverflow ( by: blockLayout. blockCount)
283281 guard !diskSize. overflow else {
284282 throw DiskImageStackError . invalidBlockLayout ( " stacked disk block layout overflows UInt64 " )
@@ -316,6 +314,8 @@ extension VMDirectory {
316314 return group. chunks
317315 }
318316
317+ // Rebuilding transport blobs republishes this file under the pinned
318+ // whole-file digest, so validate the cached bytes at this boundary.
319319 guard let contentURL = try contentStore. existingContentURL ( for: contentDigest) else {
320320 throw RuntimeError . VMMissingFiles ( " stacked VM is missing cached disk content \( contentDigest) " )
321321 }
0 commit comments