diff --git a/Sources/CSystem/include/CSystemWASI.h b/Sources/CSystem/include/CSystemWASI.h index 9877853e..1c8cd0f2 100644 --- a/Sources/CSystem/include/CSystemWASI.h +++ b/Sources/CSystem/include/CSystemWASI.h @@ -1,7 +1,7 @@ /* This source file is part of the Swift System open source project - Copyright (c) 2024 Apple Inc. and the Swift System project authors + Copyright (c) 2024 - 2025 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information @@ -11,8 +11,10 @@ #if __wasi__ +#include #include #include +#include // For NAME_MAX // wasi-libc defines the following constants in a way that Clang Importer can't // understand, so we need to expose them manually. @@ -28,4 +30,37 @@ static inline int32_t _getConst_O_WRONLY(void) { return O_WRONLY; } static inline int32_t _getConst_EWOULDBLOCK(void) { return EWOULDBLOCK; } static inline int32_t _getConst_EOPNOTSUPP(void) { return EOPNOTSUPP; } +static inline uint8_t _getConst_DT_DIR(void) { return DT_DIR; } + +// Modified dirent struct that can be imported to Swift +struct _system_dirent { + ino_t d_ino; + unsigned char d_type; + // char d_name[] cannot be imported to Swift + char d_name[NAME_MAX + 1]; +}; + +// Convert WASI dirent with d_name[] to _system_dirent +static inline +struct _system_dirent * +_system_dirent_from_wasi_dirent(const struct dirent *wasi_dirent) { + + // Match readdir behavior and use thread-local storage for the converted dirent + static __thread struct _system_dirent _converted_dirent; + + if (wasi_dirent == NULL) { + return NULL; + } + + memset(&_converted_dirent, 0, sizeof(struct _system_dirent)); + + _converted_dirent.d_ino = wasi_dirent->d_ino; + _converted_dirent.d_type = wasi_dirent->d_type; + + strncpy(_converted_dirent.d_name, wasi_dirent->d_name, NAME_MAX); + _converted_dirent.d_name[NAME_MAX] = '\0'; + + return &_converted_dirent; +} + #endif diff --git a/Sources/System/FileOperations.swift b/Sources/System/FileOperations.swift index fc9fc932..2a8509bd 100644 --- a/Sources/System/FileOperations.swift +++ b/Sources/System/FileOperations.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift System open source project - Copyright (c) 2020 - 2024 Apple Inc. and the Swift System project authors + Copyright (c) 2020 - 2025 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information @@ -509,6 +509,7 @@ extension FileDescriptor { } } +#if !os(WASI) // WASI has no umask extension FilePermissions { /// The file creation permission mask (aka "umask"). /// @@ -549,3 +550,4 @@ extension FilePermissions { return system_umask(mode) } } +#endif diff --git a/Sources/System/Internals/Syscalls.swift b/Sources/System/Internals/Syscalls.swift index 1627273c..f6eb5339 100644 --- a/Sources/System/Internals/Syscalls.swift +++ b/Sources/System/Internals/Syscalls.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift System open source project - Copyright (c) 2020 - 2024 Apple Inc. and the Swift System project authors + Copyright (c) 2020 - 2025 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information @@ -14,6 +14,7 @@ import Glibc #elseif canImport(Musl) import Musl #elseif canImport(WASILibc) +import CSystem import WASILibc #elseif os(Windows) import ucrt @@ -189,9 +190,14 @@ internal func system_confstr( #if !os(Windows) internal let SYSTEM_AT_REMOVE_DIR = AT_REMOVEDIR +#if os(WASI) +internal let SYSTEM_DT_DIR = _getConst_DT_DIR() +internal typealias system_dirent = _system_dirent +#else internal let SYSTEM_DT_DIR = DT_DIR internal typealias system_dirent = dirent -#if os(Linux) || os(Android) || os(FreeBSD) || os(OpenBSD) +#endif +#if os(Linux) || os(Android) || os(FreeBSD) || os(OpenBSD) || os(WASI) internal typealias system_DIRPtr = OpaquePointer #else internal typealias system_DIRPtr = UnsafeMutablePointer @@ -216,8 +222,12 @@ internal func system_fdopendir( internal func system_readdir( _ dir: system_DIRPtr -) -> UnsafeMutablePointer? { +) -> UnsafeMutablePointer? { + #if os(WASI) + return _system_dirent_from_wasi_dirent(readdir(dir)) + #else return readdir(dir) + #endif } internal func system_rewinddir( @@ -246,11 +256,13 @@ internal func system_openat( } #endif +#if !os(WASI) // WASI has no umask internal func system_umask( _ mode: CInterop.Mode ) -> CInterop.Mode { return umask(mode) } +#endif internal func system_getenv( _ name: UnsafePointer diff --git a/Tests/SystemTests/ErrnoTest.swift b/Tests/SystemTests/ErrnoTest.swift index da09657e..5f4551ba 100644 --- a/Tests/SystemTests/ErrnoTest.swift +++ b/Tests/SystemTests/ErrnoTest.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift System open source project - Copyright (c) 2020 Apple Inc. and the Swift System project authors + Copyright (c) 2020 - 2025 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information @@ -36,7 +36,7 @@ final class ErrnoTest: XCTestCase { XCTAssert(ENOMEM == Errno.noMemory.rawValue) XCTAssert(EACCES == Errno.permissionDenied.rawValue) XCTAssert(EFAULT == Errno.badAddress.rawValue) -#if !os(Windows) +#if !os(Windows) && !os(WASI) XCTAssert(ENOTBLK == Errno.notBlockDevice.rawValue) #endif XCTAssert(EBUSY == Errno.resourceBusy.rawValue) @@ -74,9 +74,11 @@ final class ErrnoTest: XCTestCase { XCTAssert(WSAEOPNOTSUPP == Errno.notSupported.rawValue) XCTAssert(WSAEPFNOSUPPORT == Errno.protocolFamilyNotSupported.rawValue) #else - XCTAssert(ESOCKTNOSUPPORT == Errno.socketTypeNotSupported.rawValue) XCTAssert(ENOTSUP == Errno.notSupported.rawValue) +#if !os(WASI) + XCTAssert(ESOCKTNOSUPPORT == Errno.socketTypeNotSupported.rawValue) XCTAssert(EPFNOSUPPORT == Errno.protocolFamilyNotSupported.rawValue) +#endif #endif XCTAssert(EAFNOSUPPORT == Errno.addressFamilyNotSupported.rawValue) XCTAssert(EADDRINUSE == Errno.addressInUse.rawValue) @@ -91,7 +93,7 @@ final class ErrnoTest: XCTestCase { XCTAssert(ENOTCONN == Errno.socketNotConnected.rawValue) #if os(Windows) XCTAssert(WSAESHUTDOWN == Errno.socketShutdown.rawValue) -#else +#elseif !os(WASI) XCTAssert(ESHUTDOWN == Errno.socketShutdown.rawValue) #endif XCTAssert(ETIMEDOUT == Errno.timedOut.rawValue) @@ -100,7 +102,7 @@ final class ErrnoTest: XCTestCase { XCTAssert(ENAMETOOLONG == Errno.fileNameTooLong.rawValue) #if os(Windows) XCTAssert(WSAEHOSTDOWN == Errno.hostIsDown.rawValue) -#else +#elseif !os(WASI) XCTAssert(EHOSTDOWN == Errno.hostIsDown.rawValue) #endif XCTAssert(EHOSTUNREACH == Errno.noRouteToHost.rawValue) @@ -115,7 +117,9 @@ final class ErrnoTest: XCTestCase { XCTAssert(WSAEDQUOT == Errno.diskQuotaExceeded.rawValue) XCTAssert(WSAESTALE == Errno.staleNFSFileHandle.rawValue) #else +#if !os(WASI) XCTAssert(EUSERS == Errno.tooManyUsers.rawValue) +#endif XCTAssert(EDQUOT == Errno.diskQuotaExceeded.rawValue) XCTAssert(ESTALE == Errno.staleNFSFileHandle.rawValue) #endif @@ -171,7 +175,7 @@ final class ErrnoTest: XCTestCase { XCTAssert(EPROTO == Errno.protocolError.rawValue) #endif -#if !os(Windows) && !os(FreeBSD) +#if !os(Windows) && !os(FreeBSD) && !os(WASI) XCTAssert(ENODATA == Errno.noData.rawValue) XCTAssert(ENOSR == Errno.noStreamResources.rawValue) XCTAssert(ENOSTR == Errno.notStream.rawValue) @@ -181,11 +185,13 @@ final class ErrnoTest: XCTestCase { XCTAssert(EOPNOTSUPP == Errno.notSupportedOnSocket.rawValue) // From headers but not man page +#if !os(WASI) // Would need to use _getConst func from CSystem XCTAssert(EWOULDBLOCK == Errno.wouldBlock.rawValue) +#endif #if os(Windows) XCTAssert(WSAETOOMANYREFS == Errno.tooManyReferences.rawValue) XCTAssert(WSAEREMOTE == Errno.tooManyRemoteLevels.rawValue) -#else +#elseif !os(WASI) XCTAssert(ETOOMANYREFS == Errno.tooManyReferences.rawValue) XCTAssert(EREMOTE == Errno.tooManyRemoteLevels.rawValue) #endif diff --git a/Tests/SystemTests/FileOperationsTest.swift b/Tests/SystemTests/FileOperationsTest.swift index 18adde37..479e503f 100644 --- a/Tests/SystemTests/FileOperationsTest.swift +++ b/Tests/SystemTests/FileOperationsTest.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift System open source project - Copyright (c) 2020 Apple Inc. and the Swift System project authors + Copyright (c) 2020 - 2025 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information @@ -16,10 +16,13 @@ import XCTest #endif #if canImport(Android) import Android +#elseif os(WASI) +import CSystem #endif @available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) final class FileOperationsTest: XCTestCase { + #if !os(WASI) // Would need to use _getConst funcs from CSystem func testSyscalls() { let fd = FileDescriptor(rawValue: 1) @@ -88,6 +91,7 @@ final class FileOperationsTest: XCTestCase { for test in syscallTestCases { test.runAllTests() } } + #endif // !os(WASI) func testWriteFromEmptyBuffer() throws { #if os(Windows) @@ -153,6 +157,7 @@ final class FileOperationsTest: XCTestCase { // TODO: Test writeAll, writeAll(toAbsoluteOffset), closeAfter } + #if !os(WASI) // WASI has no pipe func testAdHocPipe() throws { // Ad-hoc test testing `Pipe` functionality. // We cannot test `Pipe` using `MockTestCase` because it calls `pipe` with a pointer to an array local to the `Pipe`, the address of which we do not know prior to invoking `Pipe`. @@ -171,6 +176,7 @@ final class FileOperationsTest: XCTestCase { } } } + #endif func testAdHocOpen() { // Ad-hoc test touching a file system. @@ -211,8 +217,13 @@ final class FileOperationsTest: XCTestCase { func testGithubIssues() { // https://github.com/apple/swift-system/issues/26 + #if os(WASI) + let openOptions = _getConst_O_WRONLY() | _getConst_O_CREAT() + #else + let openOptions = O_WRONLY | O_CREAT + #endif let issue26 = MockTestCase( - name: "open", .interruptable, "a path", O_WRONLY | O_CREAT, 0o020 + name: "open", .interruptable, "a path", openOptions, 0o020 ) { retryOnInterrupt in _ = try FileDescriptor.open( @@ -221,7 +232,6 @@ final class FileOperationsTest: XCTestCase { retryOnInterrupt: retryOnInterrupt) } issue26.runAllTests() - } func testResizeFile() throws { diff --git a/Tests/SystemTests/FileTypesTest.swift b/Tests/SystemTests/FileTypesTest.swift index 5258709a..a818dfba 100644 --- a/Tests/SystemTests/FileTypesTest.swift +++ b/Tests/SystemTests/FileTypesTest.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift System open source project - Copyright (c) 2020 - 2021 Apple Inc. and the Swift System project authors + Copyright (c) 2020 - 2025 Apple Inc. and the Swift System project authors Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information @@ -32,6 +32,7 @@ final class FileDescriptorTest: XCTestCase { XCTAssertEqual(O_WRONLY, FileDescriptor.AccessMode.writeOnly.rawValue) XCTAssertEqual(O_RDWR, FileDescriptor.AccessMode.readWrite.rawValue) +#if !os(WASI) // Would need to use _getConst funcs from CSystem #if !os(Windows) XCTAssertEqual(O_NONBLOCK, FileDescriptor.OpenOptions.nonBlocking.rawValue) #endif @@ -39,6 +40,7 @@ final class FileDescriptorTest: XCTestCase { XCTAssertEqual(O_CREAT, FileDescriptor.OpenOptions.create.rawValue) XCTAssertEqual(O_TRUNC, FileDescriptor.OpenOptions.truncate.rawValue) XCTAssertEqual(O_EXCL, FileDescriptor.OpenOptions.exclusiveCreate.rawValue) +#endif // !os(WASI #if !os(Windows) XCTAssertEqual(O_NOFOLLOW, FileDescriptor.OpenOptions.noFollow.rawValue) XCTAssertEqual(O_CLOEXEC, FileDescriptor.OpenOptions.closeOnExec.rawValue)