@@ -35,6 +35,26 @@ package final class LocalOCILayoutClient: ContentClient {
3535 return c
3636 }
3737
38+ private func calculateFileDigest( at url: URL ) throws -> SHA256Digest {
39+ let fileHandle = try FileHandle ( forReadingFrom: url)
40+ defer {
41+ try ? fileHandle. close ( )
42+ }
43+
44+ var hasher = SHA256 ( )
45+ let chunkSize = Int ( getpagesize ( ) ) * 1024
46+
47+ while true {
48+ let chunk = fileHandle. readData ( ofLength: chunkSize)
49+ if chunk. isEmpty {
50+ break
51+ }
52+ hasher. update ( data: chunk)
53+ }
54+
55+ return hasher. finalize ( )
56+ }
57+
3858 package func fetch< T: Codable > ( name: String , descriptor: Descriptor ) async throws -> T {
3959 let c = try await self . _fetch ( digest: descriptor. digest)
4060 return try c. decode ( )
@@ -44,7 +64,8 @@ package final class LocalOCILayoutClient: ContentClient {
4464 let c = try await self . _fetch ( digest: descriptor. digest)
4565 let fileManager = FileManager . default
4666 let filePath = file. absolutePath ( )
47- if !fileManager. fileExists ( atPath: filePath) {
67+
68+ do {
4869 let src = c. path
4970 try fileManager. copyItem ( at: src, to: file)
5071
@@ -53,7 +74,33 @@ package final class LocalOCILayoutClient: ContentClient {
5374 ProgressEvent ( event: " add-size " , value: fileSize)
5475 ] )
5576 }
77+ } catch let error as NSError {
78+ guard error. code == NSFileWriteFileExistsError else {
79+ throw error
80+ }
81+
82+ do {
83+ let expectedDigest = try c. digest ( )
84+ let existingDigest = try calculateFileDigest ( at: file)
85+
86+ guard existingDigest. digestString == expectedDigest. digestString else {
87+ throw ContainerizationError (
88+ . internalError,
89+ message:
90+ " File \( filePath) exists but contains different content. Expected digest: \( expectedDigest. digestString) , existing digest: \( existingDigest. digestString) "
91+ )
92+ }
93+
94+ if let progress, let fileSize = fileManager. fileSize ( atPath: filePath) {
95+ await progress ( [
96+ ProgressEvent ( event: " add-size " , value: fileSize)
97+ ] )
98+ }
99+ } catch {
100+ throw error
101+ }
56102 }
103+
57104 let size = try Int64 ( c. size ( ) )
58105 let digest = try c. digest ( )
59106 return ( size, digest)
0 commit comments