Skip to content

Add support for dup/dup2 #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Sources/System/FileOperations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,66 @@ extension FileDescriptor {
buffer,
retryOnInterrupt: retryOnInterrupt)
}

/// Duplicate this file descriptor and return the newly created copy.
///
/// - Parameters:
/// - `target`: The desired target file descriptor, or `nil`, in which case
/// the copy is assigned to the file descriptor with the lowest raw value
/// that is not currently in use by the process.
/// - retryOnInterrupt: Whether to retry the write operation
/// if it throws ``Errno/interrupted``. The default is `true`.
/// Pass `false` to try only once and throw an error upon interruption.
/// - Returns: The new file descriptor.
///
/// If the `target` descriptor is already in use, then it is first
/// deallocated as if a close(2) call had been done first.
///
/// File descriptors are merely references to some underlying system resource.
/// The system does not distinguish between the original and the new file
/// descriptor in any way. For example, read, write and seek operations on
/// one of them also affect the logical file position in the other, and
/// append mode, non-blocking I/O and asynchronous I/O options are shared
/// between the references. If a separate pointer into the file is desired,
/// a different object reference to the file must be obtained by issuing an
/// additional call to `open`.
///
/// However, each file descriptor maintains its own close-on-exec flag.
///
///
/// The corresponding C functions are `dup` and `dup2`.
@_alwaysEmitIntoClient
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
public func duplicate(
as target: FileDescriptor? = nil,
retryOnInterrupt: Bool = true
) throws -> FileDescriptor {
try _duplicate(as: target, retryOnInterrupt: retryOnInterrupt).get()
}

// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
@usableFromInline
internal func _duplicate(
as target: FileDescriptor?,
retryOnInterrupt: Bool
) throws -> Result<FileDescriptor, Errno> {
valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
if let target = target {
return system_dup2(self.rawValue, target.rawValue)
}
return system_dup(self.rawValue)
}.map(FileDescriptor.init(rawValue:))
}

@_alwaysEmitIntoClient
@available(*, unavailable, renamed: "duplicate")
public func dup() throws -> FileDescriptor {
fatalError("Not implemented")
}

@_alwaysEmitIntoClient
@available(*, unavailable, renamed: "duplicate")
public func dup2() throws -> FileDescriptor {
fatalError("Not implemented")
}
}
20 changes: 17 additions & 3 deletions Sources/SystemInternals/Syscalls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import ucrt

#if ENABLE_MOCKING
// Strip the mock_system prefix and the arg list suffix
private func originalSyscallName(_ s: String) -> String {
precondition(s.starts(with: "system_"))
return String(s.dropFirst("system_".count).prefix { $0.isLetter })
private func originalSyscallName(_ function: String) -> String {
// `function` must be of format `system_<name>(<parameters>)`
precondition(function.starts(with: "system_"))
return String(function.dropFirst("system_".count).prefix { $0 != "(" })
}

private func mockImpl(
Expand Down Expand Up @@ -152,3 +153,16 @@ public func system_pwrite(
return pwrite(fd, buf, nbyte, offset)
}

public func system_dup(_ fd: Int32) -> Int32 {
#if ENABLE_MOCKING
if mockingEnabled { return mock(fd) }
#endif
return dup(fd)
}

public func system_dup2(_ fd: Int32, _ fd2: Int32) -> Int32 {
#if ENABLE_MOCKING
if mockingEnabled { return mock(fd, fd2) }
#endif
return dup2(fd, fd2)
}
8 changes: 8 additions & 0 deletions Tests/SystemTests/FileOperationsTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ final class FileOperationsTest: XCTestCase {
_ = try fd.close()
},

MockTestCase(name: "dup", rawFD, interruptable: true) { retryOnInterrupt in
_ = try fd.duplicate(retryOnInterrupt: retryOnInterrupt)
},

MockTestCase(name: "dup2", rawFD, 42, interruptable: true) { retryOnInterrupt in
_ = try fd.duplicate(as: FileDescriptor(rawValue: 42),
retryOnInterrupt: retryOnInterrupt)
},
]

for test in syscallTestCases { test.runAllTests() }
Expand Down