Add support for reading env from named pipes (#974)

This is a fix for
[issue#956](https://github.com/apple/container/issues/956)

`FileManager.default.contents(atPath:)` returns `nil` for named pipes
(FIFOs)
and process substitutions like `/dev/fd/XX` because:
1. It expects regular files with a known size
2. Named pipes are stream-based and block until data arrives

## Solution
Use `FileHandle(forReadingFrom:)` instead, which:
- Properly handles blocking I/O
- Works with named pipes, process substitutions, and regular files
(mentioned in the
[doc](https://developer.apple.com/documentation/foundation/filehandle))

Co-authored-by: Bortniak Volodymyr <Bortnyak@users.noreply.github.com>
This commit is contained in:
Volodymyr Bortniak
2025-12-19 15:36:02 -08:00
committed by GitHub
co-authored by Bortniak Volodymyr
parent 3c3a83c98a
commit 9c239aa36c
3 changed files with 111 additions and 10 deletions
+39 -2
View File
@@ -493,10 +493,12 @@ struct ParserTest {
#expect {
_ = try Parser.envFile(path: "/nonexistent/foo_bar_baz")
} throws: { error in
guard let error = error as? ContainerizationError else {
guard let error = error as? ContainerizationError,
let cause = error.cause
else {
return false
}
return error.description.contains("not found")
return String(describing: cause).contains("No such file or directory")
}
}
@@ -579,6 +581,41 @@ struct ParserTest {
}
}
@Test
func testParseEnvFileFromNamedPipe() throws {
let pipePath = FileManager.default.temporaryDirectory
.appendingPathComponent("envfile-pipe-\(UUID().uuidString)")
// Create a named pipe (FIFO)
let result = mkfifo(pipePath.path, 0o600)
guard result == 0 else {
throw POSIXError(POSIXErrorCode(rawValue: errno) ?? .EPERM)
}
defer { try? FileManager.default.removeItem(at: pipePath) }
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
do {
let handle = try FileHandle(forWritingTo: pipePath)
try handle.write(contentsOf: "SECRET_KEY=value123\n".data(using: .utf8)!)
try handle.close()
} catch {
Issue.record(error)
}
group.leave()
}
// Read from pipe (blocks until writer connects)
let lines = try Parser.envFile(path: pipePath.path)
// Wait for write to complete
group.wait()
#expect(lines == ["SECRET_KEY=value123"])
}
// MARK: Network Parser Tests
@Test