From 2ea258fe0e0bb77711db4e4027edaac11936777e Mon Sep 17 00:00:00 2001 From: Pradeep Date: Sat, 8 Aug 2026 22:48:11 +0530 Subject: [PATCH 1/7] Refactor tmpfsMounts to process tmpfs path correctly --- .../ContainerAPIService/Client/Parser.swift | 17 +++++++++++++++-- Tests/ContainerAPIClientTests/ParserTest.swift | 13 +++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Sources/Services/ContainerAPIService/Client/Parser.swift b/Sources/Services/ContainerAPIService/Client/Parser.swift index 559796bca..63e691a37 100644 --- a/Sources/Services/ContainerAPIService/Client/Parser.swift +++ b/Sources/Services/ContainerAPIService/Client/Parser.swift @@ -340,11 +340,24 @@ public struct Parser { public static let defaultDirectives = ["type": "virtiofs"] public static func tmpfsMounts(_ mounts: [String]) throws -> [Filesystem] { - let mounts = mounts.dedupe() var result: [Filesystem] = [] result.reserveCapacity(mounts.count) + var seenDestinations: Set = [] + for tmpfs in mounts { - let fs = Filesystem.tmpfs(destination: tmpfs, options: []) + let parts = tmpfs.split(separator: ":", maxSplits: 1) + let destination = String(parts[0]) + let options = parts.count == 2 ? String(parts[1]).split(separator: ",").map(String.init) : [] + + if destination.isEmpty { + throw ContainerizationError(.invalidArgument, message: "mount destination cannot be empty") + } + + let normalizedDest = FilePath(destination).lexicallyNormalized().string + if seenDestinations.contains(normalizedDest) { continue } + seenDestinations.insert(normalizedDest) + + let fs = Filesystem.tmpfs(destination: destination, options: options) try validateMount(.filesystem(fs)) result.append(fs) } diff --git a/Tests/ContainerAPIClientTests/ParserTest.swift b/Tests/ContainerAPIClientTests/ParserTest.swift index 7684ff936..7d5b2c3a4 100644 --- a/Tests/ContainerAPIClientTests/ParserTest.swift +++ b/Tests/ContainerAPIClientTests/ParserTest.swift @@ -1523,6 +1523,19 @@ struct ParserTest { #expect(result.count == 20) } + @Test("tmpfsMounts parses mount options and dedupes on destination path") + func testTmpfsMountsWithColons() throws { + let mounts = [ + "/mnt/scratch:rw,exec", + "/mnt/scratch", // Should be deduped based on path + "/mnt/cache:ro" + ] + let result = try Parser.tmpfsMounts(mounts) + #expect(result.count == 2) + #expect(result[0].destination == "/mnt/scratch") + #expect(result[0].options == ["rw", "exec"]) + } + @Test("volumes with large input") func testVolumesLargeInput() throws { let volumes = (0..<20).map { "vol\($0):/mnt/vol\($0)" } From 0d6b000e2a1e1ea0f5676ed211ed0dcdd378cad0 Mon Sep 17 00:00:00 2001 From: Pradeep Date: Sat, 8 Aug 2026 23:12:41 +0530 Subject: [PATCH 2/7] Handle empty path string edge case --- Sources/Services/ContainerAPIService/Client/Parser.swift | 2 +- Tests/ContainerAPIClientTests/ParserTest.swift | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Sources/Services/ContainerAPIService/Client/Parser.swift b/Sources/Services/ContainerAPIService/Client/Parser.swift index 63e691a37..9dba31557 100644 --- a/Sources/Services/ContainerAPIService/Client/Parser.swift +++ b/Sources/Services/ContainerAPIService/Client/Parser.swift @@ -345,7 +345,7 @@ public struct Parser { var seenDestinations: Set = [] for tmpfs in mounts { - let parts = tmpfs.split(separator: ":", maxSplits: 1) + let parts = tmpfs.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false) let destination = String(parts[0]) let options = parts.count == 2 ? String(parts[1]).split(separator: ",").map(String.init) : [] diff --git a/Tests/ContainerAPIClientTests/ParserTest.swift b/Tests/ContainerAPIClientTests/ParserTest.swift index 7d5b2c3a4..b6fb444a8 100644 --- a/Tests/ContainerAPIClientTests/ParserTest.swift +++ b/Tests/ContainerAPIClientTests/ParserTest.swift @@ -1536,6 +1536,13 @@ struct ParserTest { #expect(result[0].options == ["rw", "exec"]) } + @Test("tmpfsMounts throws on empty destination") + func testTmpfsMountsEmptyDestination() throws { + #expect(throws: ContainerizationError.self) { + _ = try Parser.tmpfsMounts([""]) + } + } + @Test("volumes with large input") func testVolumesLargeInput() throws { let volumes = (0..<20).map { "vol\($0):/mnt/vol\($0)" } From b39dd2e8794deea4e0030e5f1f3e09eb55f42323 Mon Sep 17 00:00:00 2001 From: Pradeep Date: Sun, 9 Aug 2026 14:25:58 +0530 Subject: [PATCH 3/7] Handle absolute path check --- Sources/Services/ContainerAPIService/Client/Parser.swift | 7 ++++++- Tests/ContainerAPIClientTests/ParserTest.swift | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Sources/Services/ContainerAPIService/Client/Parser.swift b/Sources/Services/ContainerAPIService/Client/Parser.swift index 9dba31557..49989805c 100644 --- a/Sources/Services/ContainerAPIService/Client/Parser.swift +++ b/Sources/Services/ContainerAPIService/Client/Parser.swift @@ -353,7 +353,12 @@ public struct Parser { throw ContainerizationError(.invalidArgument, message: "mount destination cannot be empty") } - let normalizedDest = FilePath(destination).lexicallyNormalized().string + let filePath = FilePath(destination) + guard filePath.isAbsolute else { + throw ContainerizationError(.invalidArgument, message: "\(destination) is not an absolute path") + } + + let normalizedDest = filePath.lexicallyNormalized().string if seenDestinations.contains(normalizedDest) { continue } seenDestinations.insert(normalizedDest) diff --git a/Tests/ContainerAPIClientTests/ParserTest.swift b/Tests/ContainerAPIClientTests/ParserTest.swift index b6fb444a8..757c0e317 100644 --- a/Tests/ContainerAPIClientTests/ParserTest.swift +++ b/Tests/ContainerAPIClientTests/ParserTest.swift @@ -1543,6 +1543,13 @@ struct ParserTest { } } + @Test("tmpfsMounts throws on non-absolute destination") + func testTmpfsMountsNonAbsoluteDestination() throws { + #expect(throws: ContainerizationError.self) { + _ = try Parser.tmpfsMounts(["relative/path:rw"]) + } + } + @Test("volumes with large input") func testVolumesLargeInput() throws { let volumes = (0..<20).map { "vol\($0):/mnt/vol\($0)" } From d668575a2e45799b4ddca3552f2c16dc5aa9dcfa Mon Sep 17 00:00:00 2001 From: Pradeep Date: Sun, 9 Aug 2026 22:41:09 +0530 Subject: [PATCH 4/7] Fix Formatting issues --- .../Services/ContainerAPIService/Client/Parser.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Sources/Services/ContainerAPIService/Client/Parser.swift b/Sources/Services/ContainerAPIService/Client/Parser.swift index 49989805c..177ec7266 100644 --- a/Sources/Services/ContainerAPIService/Client/Parser.swift +++ b/Sources/Services/ContainerAPIService/Client/Parser.swift @@ -350,16 +350,18 @@ public struct Parser { let options = parts.count == 2 ? String(parts[1]).split(separator: ",").map(String.init) : [] if destination.isEmpty { - throw ContainerizationError(.invalidArgument, message: "mount destination cannot be empty") + throw ContainerizationError(.invalidArgument, message: "mount destination cannot be empty") } - + let filePath = FilePath(destination) guard filePath.isAbsolute else { - throw ContainerizationError(.invalidArgument, message: "\(destination) is not an absolute path") + throw ContainerizationError(.invalidArgument, message: "\(destination) is not an absolute path") } - + let normalizedDest = filePath.lexicallyNormalized().string - if seenDestinations.contains(normalizedDest) { continue } + if seenDestinations.contains(normalizedDest) { + continue + } seenDestinations.insert(normalizedDest) let fs = Filesystem.tmpfs(destination: destination, options: options) From 64c38440087f881f44e0b68ef151c4a36f97f3dd Mon Sep 17 00:00:00 2001 From: Pradeep Date: Sun, 9 Aug 2026 23:42:24 +0530 Subject: [PATCH 5/7] Fix ForceUnwrap linter warning --- Sources/Services/ContainerAPIService/Client/Parser.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Services/ContainerAPIService/Client/Parser.swift b/Sources/Services/ContainerAPIService/Client/Parser.swift index 177ec7266..2306da739 100644 --- a/Sources/Services/ContainerAPIService/Client/Parser.swift +++ b/Sources/Services/ContainerAPIService/Client/Parser.swift @@ -318,7 +318,7 @@ public struct Parser { let rlimits = try Parser.rlimits(processFlags.ulimits) return .init( - executable: commandToRun.first!, + executable: commandToRun[0], arguments: [String](commandToRun.dropFirst()), environment: envvars, workingDirectory: workingDir, From b9b3442232a28c95cecd60188d761508062bef32 Mon Sep 17 00:00:00 2001 From: J Logan Date: Sun, 9 Aug 2026 13:07:54 -0700 Subject: [PATCH 6/7] Update Tests/ContainerAPIClientTests/ParserTest.swift --- Tests/ContainerAPIClientTests/ParserTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ContainerAPIClientTests/ParserTest.swift b/Tests/ContainerAPIClientTests/ParserTest.swift index 757c0e317..ec7d8fbfa 100644 --- a/Tests/ContainerAPIClientTests/ParserTest.swift +++ b/Tests/ContainerAPIClientTests/ParserTest.swift @@ -1528,7 +1528,7 @@ struct ParserTest { let mounts = [ "/mnt/scratch:rw,exec", "/mnt/scratch", // Should be deduped based on path - "/mnt/cache:ro" + "/mnt/cache:ro", ] let result = try Parser.tmpfsMounts(mounts) #expect(result.count == 2) From a8c7a9f27be3aef6bc636209f68ca90398f9d5e7 Mon Sep 17 00:00:00 2001 From: J Logan Date: Sun, 9 Aug 2026 13:11:05 -0700 Subject: [PATCH 7/7] Update Tests/ContainerAPIClientTests/ParserTest.swift --- Tests/ContainerAPIClientTests/ParserTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ContainerAPIClientTests/ParserTest.swift b/Tests/ContainerAPIClientTests/ParserTest.swift index ec7d8fbfa..dbb3aa3c0 100644 --- a/Tests/ContainerAPIClientTests/ParserTest.swift +++ b/Tests/ContainerAPIClientTests/ParserTest.swift @@ -1527,7 +1527,7 @@ struct ParserTest { func testTmpfsMountsWithColons() throws { let mounts = [ "/mnt/scratch:rw,exec", - "/mnt/scratch", // Should be deduped based on path + "/mnt/scratch", // Should be deduped based on path "/mnt/cache:ro", ] let result = try Parser.tmpfsMounts(mounts)