Skip to content

Commit

Permalink
NIOFileSystem: Try ${TMPDIR} first for temporary directory (#3067)
Browse files Browse the repository at this point in the history
### Motivation:

This PR aligns what temp directory NIOFileSystem will return first:
- First try `${TMPDIR}` as it's an expressed user preference
- On Darwin, try `_CS_DARWIN_USER_TMP_DIR` next. If it's set, return
that.
- Finally, fall back on `/tmp` on Darwin and Linux, and
`/data/local/tmp` on Android.

Closes #2861.

### Modifications:

- Reworks `NIOFileSystem.temporaryDirectory`.

### Result:

- NIOFileSystem will now try `${TMPDIR}` first for the temporary
directory.

### Caveats:

- We might want to align how Swift-NIO and FoundationEssentials resolve
temp directory, and [FoundationEssentials have a different
approach](https://github.com/swiftlang/swift-foundation/blob/9d57f36de757b3d5e3a2f7ffcf27aaec3033509f/Sources/FoundationEssentials/String/String%2BPath.swift#L484-L533).
But, I agree with [this
comment](#2861 (comment)),
it feels like TMPDIR is an expected default.

---------

Co-authored-by: Cory Benfield <[email protected]>
Co-authored-by: George Barnett <[email protected]>
  • Loading branch information
3 people authored Jan 27, 2025
1 parent 26d3fb9 commit 1e900b4
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 16 deletions.
36 changes: 20 additions & 16 deletions Sources/NIOFileSystem/FileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -636,31 +636,35 @@ public struct FileSystem: Sendable, FileSystemProtocol {
///
/// #### Implementation details
///
/// On Darwin this function uses `confstr(3)` and gets the value of `_CS_DARWIN_USER_TEMP_DIR`;
/// the users temporary directory. Typically items are removed after three days if they are not
/// accessed.
///
/// On Linux this returns "/tmp".
/// On all platforms, this function first attempts to read the `TMPDIR` environment variable and returns that path, omitting trailing slashes.
/// If that fails:
/// - On Darwin this function uses `confstr(3)` and gets the value of `_CS_DARWIN_USER_TEMP_DIR`;
/// the users temporary directory. Typically items are removed after three days if they are not
/// accessed.
/// - On Android this returns "/data/local/tmp".
/// - On other platforms this returns "/tmp".
///
/// - Returns: The path to a temporary directory.
public var temporaryDirectory: FilePath {
get async throws {
if let tmpdir = getenv("TMPDIR") {
return FilePath(String(cString: tmpdir))
}

#if canImport(Darwin)
return try await self.threadPool.runIfActive {
try Libc.constr(_CS_DARWIN_USER_TEMP_DIR).map { path in
FilePath(path)
}.mapError { errno in
FileSystemError.confstr(
name: "_CS_DARWIN_USER_TEMP_DIR",
errno: errno,
location: .here()
)
}.get()
let result = Libc.constr(_CS_DARWIN_USER_TEMP_DIR)
switch result {
case .success(let path):
return FilePath(path)
case .failure(_):
return FilePath("/tmp")
}
}
#elseif os(Android)
return "/data/local/tmp"
return FilePath("/data/local/tmp")
#else
return "/tmp"
return FilePath("/tmp")
#endif
}
}
Expand Down
8 changes: 8 additions & 0 deletions Tests/NIOFileSystemIntegrationTests/FileSystemTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,14 @@ extension FileSystemTests {
XCTAssertGreaterThan(info.size, 0)
}

func testTemporaryDirectoryRespectsEnvironment() async throws {
if let envTmpDir = getenv("TMPDIR") {
let envTmpDirString = String(cString: envTmpDir)
let fsTempDirectory = try await fs.temporaryDirectory
XCTAssertEqual(fsTempDirectory, FilePath(envTmpDirString))
}
}

func testReadChunksRange() async throws {
try await self.fs.withFileHandle(forReadingAt: FilePath(#filePath)) { handle in
let info = try await handle.info()
Expand Down

0 comments on commit 1e900b4

Please sign in to comment.