diff --git a/Sources/Testing/Attachments/Attachment.swift b/Sources/Testing/Attachments/Attachment.swift index f8d242c8c..45966c66a 100644 --- a/Sources/Testing/Attachments/Attachment.swift +++ b/Sources/Testing/Attachments/Attachment.swift @@ -473,7 +473,7 @@ extension Attachment where AttachableValue: ~Copyable { // file exists at this path (note "x" in the mode string), an error will // be thrown and we'll try again by adding a suffix. let preferredPath = appendPathComponent(preferredName, to: directoryPath) - file = try FileHandle(atPath: preferredPath, mode: "wxeb") + file = try FileHandle(atPath: preferredPath, mode: "wxb") result = preferredPath } catch { // Split the extension(s) off the preferred name. The first component in @@ -489,7 +489,7 @@ extension Attachment where AttachableValue: ~Copyable { // Propagate any error *except* EEXIST, which would indicate that the // name was already in use (so we should try again with a new suffix.) do { - file = try FileHandle(atPath: preferredPath, mode: "wxeb") + file = try FileHandle(atPath: preferredPath, mode: "wxb") result = preferredPath break } catch let error as CError where error.rawValue == swt_EEXIST() { diff --git a/Sources/Testing/ExitTests/SpawnProcess.swift b/Sources/Testing/ExitTests/SpawnProcess.swift index 66143a7e0..c365f33ea 100644 --- a/Sources/Testing/ExitTests/SpawnProcess.swift +++ b/Sources/Testing/ExitTests/SpawnProcess.swift @@ -124,27 +124,11 @@ func spawnExecutable( guard let fd else { throw SystemError(description: "A child process cannot inherit a file handle without an associated file descriptor. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new") } - if let standardFD, standardFD != fd { + if let standardFD { _ = posix_spawn_file_actions_adddup2(fileActions, fd, standardFD) } else { #if SWT_TARGET_OS_APPLE _ = posix_spawn_file_actions_addinherit_np(fileActions, fd) -#else - // posix_spawn_file_actions_adddup2() will automatically clear - // FD_CLOEXEC after forking but before execing even if the old and - // new file descriptors are equal. This behavior is supported by - // Glibc ≥ 2.29, FreeBSD, OpenBSD, and Android (Bionic) and is - // standardized in POSIX.1-2024 (see https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_adddup2.html - // and https://www.austingroupbugs.net/view.php?id=411). - _ = posix_spawn_file_actions_adddup2(fileActions, fd, fd) -#if canImport(Glibc) - if _slowPath(glibcVersion.major < 2 || (glibcVersion.major == 2 && glibcVersion.minor < 29)) { - // This system is using an older version of glibc that does not - // implement FD_CLOEXEC clearing in posix_spawn_file_actions_adddup2(), - // so we must clear it here in the parent process. - try setFD_CLOEXEC(false, onFileDescriptor: fd) - } -#endif #endif highestFD = max(highestFD, fd) } @@ -173,6 +157,8 @@ func spawnExecutable( #if !SWT_NO_DYNAMIC_LINKING // This platform doesn't have POSIX_SPAWN_CLOEXEC_DEFAULT, but we can at // least close all file descriptors higher than the highest inherited one. + // We are assuming here that the caller didn't set FD_CLOEXEC on any of + // these file descriptors. _ = _posix_spawn_file_actions_addclosefrom_np?(fileActions, highestFD + 1) #endif #elseif os(FreeBSD) @@ -231,42 +217,36 @@ func spawnExecutable( } #elseif os(Windows) return try _withStartupInfoEx(attributeCount: 1) { startupInfo in - func inherit(_ fileHandle: borrowing FileHandle) throws -> HANDLE? { + func inherit(_ fileHandle: borrowing FileHandle, as outWindowsHANDLE: inout HANDLE?) throws { try fileHandle.withUnsafeWindowsHANDLE { windowsHANDLE in guard let windowsHANDLE else { throw SystemError(description: "A child process cannot inherit a file handle without an associated Windows handle. Please file a bug report at https://github.com/swiftlang/swift-testing/issues/new") } - - // Ensure the file handle can be inherited by the child process. - guard SetHandleInformation(windowsHANDLE, DWORD(HANDLE_FLAG_INHERIT), DWORD(HANDLE_FLAG_INHERIT)) else { - throw Win32Error(rawValue: GetLastError()) - } - - return windowsHANDLE + outWindowsHANDLE = windowsHANDLE } } - func inherit(_ fileHandle: borrowing FileHandle?) throws -> HANDLE? { + func inherit(_ fileHandle: borrowing FileHandle?, as outWindowsHANDLE: inout HANDLE?) throws { if fileHandle != nil { - return try inherit(fileHandle!) + try inherit(fileHandle!, as: &outWindowsHANDLE) } else { - return nil + outWindowsHANDLE = nil } } // Forward standard I/O streams. - startupInfo.pointee.StartupInfo.hStdInput = try inherit(standardInput) - startupInfo.pointee.StartupInfo.hStdOutput = try inherit(standardOutput) - startupInfo.pointee.StartupInfo.hStdError = try inherit(standardError) + try inherit(standardInput, as: &startupInfo.pointee.StartupInfo.hStdInput) + try inherit(standardOutput, as: &startupInfo.pointee.StartupInfo.hStdOutput) + try inherit(standardError, as: &startupInfo.pointee.StartupInfo.hStdError) startupInfo.pointee.StartupInfo.dwFlags |= STARTF_USESTDHANDLES // Ensure standard I/O streams and any explicitly added file handles are // inherited by the child process. var inheritedHandles = [HANDLE?](repeating: nil, count: additionalFileHandles.count + 3) - inheritedHandles[0] = startupInfo.pointee.StartupInfo.hStdInput - inheritedHandles[1] = startupInfo.pointee.StartupInfo.hStdOutput - inheritedHandles[2] = startupInfo.pointee.StartupInfo.hStdError + try inherit(standardInput, as: &inheritedHandles[0]) + try inherit(standardOutput, as: &inheritedHandles[1]) + try inherit(standardError, as: &inheritedHandles[2]) for i in 0 ..< additionalFileHandles.count { - inheritedHandles[i + 3] = try inherit(additionalFileHandles[i].pointee) + try inherit(additionalFileHandles[i].pointee, as: &inheritedHandles[i + 3]) } inheritedHandles = inheritedHandles.compactMap(\.self) diff --git a/Sources/Testing/Support/FileHandle.swift b/Sources/Testing/Support/FileHandle.swift index 1c5447460..2a2bfe967 100644 --- a/Sources/Testing/Support/FileHandle.swift +++ b/Sources/Testing/Support/FileHandle.swift @@ -73,13 +73,6 @@ struct FileHandle: ~Copyable, Sendable { return } - // On Windows, "N" is used rather than "e" to signify that a file handle is - // not inherited. - var mode = mode - if let eIndex = mode.firstIndex(of: "e") { - mode.replaceSubrange(eIndex ... eIndex, with: "N") - } - // Windows deprecates fopen() as insecure, so call _wfopen_s() instead. let fileHandle = try path.withCString(encodedAs: UTF16.self) { path in try mode.withCString(encodedAs: UTF16.self) { mode in @@ -105,12 +98,8 @@ struct FileHandle: ~Copyable, Sendable { /// - path: The path to read from. /// /// - Throws: Any error preventing the stream from being opened. - /// - /// By default, the resulting file handle is not inherited by any child - /// processes (that is, `FD_CLOEXEC` is set on POSIX-like systems and - /// `HANDLE_FLAG_INHERIT` is cleared on Windows.). init(forReadingAtPath path: String) throws { - try self.init(atPath: path, mode: "reb") + try self.init(atPath: path, mode: "rb") } /// Initialize an instance of this type to write to the given path. @@ -119,12 +108,8 @@ struct FileHandle: ~Copyable, Sendable { /// - path: The path to write to. /// /// - Throws: Any error preventing the stream from being opened. - /// - /// By default, the resulting file handle is not inherited by any child - /// processes (that is, `FD_CLOEXEC` is set on POSIX-like systems and - /// `HANDLE_FLAG_INHERIT` is cleared on Windows.). init(forWritingAtPath path: String) throws { - try self.init(atPath: path, mode: "web") + try self.init(atPath: path, mode: "wb") } /// Initialize an instance of this type with an existing C file handle. @@ -460,17 +445,6 @@ extension FileHandle { #if !SWT_NO_PIPES // MARK: - Pipes -#if !SWT_TARGET_OS_APPLE && !os(Windows) && !SWT_NO_DYNAMIC_LINKING -/// Create a pipe with flags. -/// -/// This function declaration is provided because `pipe2()` is only declared if -/// `_GNU_SOURCE` is set, but setting it causes build errors due to conflicts -/// with Swift's Glibc module. -private let _pipe2 = symbol(named: "pipe2").map { - castCFunction(at: $0, to: (@convention(c) (UnsafeMutablePointer, CInt) -> CInt).self) -} -#endif - extension FileHandle { /// Make a pipe connecting two new file handles. /// @@ -487,36 +461,15 @@ extension FileHandle { /// - Bug: This function should return a tuple containing the file handles /// instead of returning them via `inout` arguments. Swift does not support /// tuples with move-only elements. ([104669935](rdar://104669935)) - /// - /// By default, the resulting file handles are not inherited by any child - /// processes (that is, `FD_CLOEXEC` is set on POSIX-like systems and - /// `HANDLE_FLAG_INHERIT` is cleared on Windows.). static func makePipe(readEnd: inout FileHandle?, writeEnd: inout FileHandle?) throws { -#if !os(Windows) - var pipe2Called = false -#endif - var (fdReadEnd, fdWriteEnd) = try withUnsafeTemporaryAllocation(of: CInt.self, capacity: 2) { fds in #if os(Windows) - guard 0 == _pipe(fds.baseAddress, 0, _O_BINARY | _O_NOINHERIT) else { + guard 0 == _pipe(fds.baseAddress, 0, _O_BINARY) else { throw CError(rawValue: swt_errno()) } #else -#if !SWT_TARGET_OS_APPLE && !os(Windows) && !SWT_NO_DYNAMIC_LINKING - if let _pipe2 { - guard 0 == _pipe2(fds.baseAddress!, O_CLOEXEC) else { - throw CError(rawValue: swt_errno()) - } - pipe2Called = true - } -#endif - - if !pipe2Called { - // pipe2() is not available. Use pipe() instead and simulate O_CLOEXEC - // to the best of our ability. - guard 0 == pipe(fds.baseAddress!) else { - throw CError(rawValue: swt_errno()) - } + guard 0 == pipe(fds.baseAddress!) else { + throw CError(rawValue: swt_errno()) } #endif return (fds[0], fds[1]) @@ -526,15 +479,6 @@ extension FileHandle { Self._close(fdWriteEnd) } -#if !os(Windows) - if !pipe2Called { - // pipe2() is not available. Use pipe() instead and simulate O_CLOEXEC - // to the best of our ability. - try setFD_CLOEXEC(true, onFileDescriptor: fdReadEnd) - try setFD_CLOEXEC(true, onFileDescriptor: fdWriteEnd) - } -#endif - do { defer { fdReadEnd = -1 @@ -688,35 +632,4 @@ func canonicalizePath(_ path: String) -> String? { return nil #endif } - -#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) -/// Set the given file descriptor's `FD_CLOEXEC` flag. -/// -/// - Parameters: -/// - flag: The new value of `fd`'s `FD_CLOEXEC` flag. -/// - fd: The file descriptor. -/// -/// - Throws: Any error that occurred while setting the flag. -func setFD_CLOEXEC(_ flag: Bool, onFileDescriptor fd: CInt) throws { - switch swt_getfdflags(fd) { - case -1: - // An error occurred reading the flags for this file descriptor. - throw CError(rawValue: swt_errno()) - case let oldValue: - let newValue = if flag { - oldValue & ~FD_CLOEXEC - } else { - oldValue | FD_CLOEXEC - } - if oldValue == newValue { - // No need to make a second syscall as nothing has changed. - return - } - if -1 == swt_setfdflags(fd, newValue) { - // An error occurred setting the flags for this file descriptor. - throw CError(rawValue: swt_errno()) - } - } -} -#endif #endif diff --git a/Sources/Testing/Support/Versions.swift b/Sources/Testing/Support/Versions.swift index 1229e80b0..1eb7f4e48 100644 --- a/Sources/Testing/Support/Versions.swift +++ b/Sources/Testing/Support/Versions.swift @@ -153,30 +153,6 @@ let swiftStandardLibraryVersion: String = { return "unknown" }() -#if canImport(Glibc) -/// The (runtime, not compile-time) version of glibc in use on this system. -/// -/// This value is not part of the public interface of the testing library. -let glibcVersion: (major: Int, minor: Int) = { - // Default to the statically available version number if the function call - // fails for some reason. - var major = Int(clamping: __GLIBC__) - var minor = Int(clamping: __GLIBC_MINOR__) - - if let strVersion = gnu_get_libc_version() { - withUnsafeMutablePointer(to: &major) { major in - withUnsafeMutablePointer(to: &minor) { minor in - withVaList([major, minor]) { args in - _ = vsscanf(strVersion, "%zd.%zd", args) - } - } - } - } - - return (major, minor) -}() -#endif - // MARK: - sysctlbyname() Wrapper #if !SWT_NO_SYSCTL && SWT_TARGET_OS_APPLE diff --git a/Sources/_TestingInternals/include/Includes.h b/Sources/_TestingInternals/include/Includes.h index 1b95151cb..bfc87b001 100644 --- a/Sources/_TestingInternals/include/Includes.h +++ b/Sources/_TestingInternals/include/Includes.h @@ -53,10 +53,6 @@ #include #endif -#if __has_include() -#include -#endif - #if __has_include() && !defined(__wasi__) #include #endif diff --git a/Sources/_TestingInternals/include/Stubs.h b/Sources/_TestingInternals/include/Stubs.h index 636ea9aff..ad1a84d13 100644 --- a/Sources/_TestingInternals/include/Stubs.h +++ b/Sources/_TestingInternals/include/Stubs.h @@ -160,26 +160,6 @@ static int swt_EEXIST(void) { return EEXIST; } -#if defined(F_GETFD) -/// Call `fcntl(F_GETFD)`. -/// -/// This function is provided because `fcntl()` is a variadic function and -/// cannot be imported directly into Swift. -static int swt_getfdflags(int fd) { - return fcntl(fd, F_GETFD); -} -#endif - -#if defined(F_SETFD) -/// Call `fcntl(F_SETFD)`. -/// -/// This function is provided because `fcntl()` is a variadic function and -/// cannot be imported directly into Swift. -static int swt_setfdflags(int fd, int flags) { - return fcntl(fd, F_SETFD, flags); -} -#endif - SWT_ASSUME_NONNULL_END #endif