Skip to content

Commit 3fe5e1b

Browse files
Port directory enumeration related code to WASI
For now wasi-libc does not include fts(3) implementation, so mark features depending on it as unsupported on WASI. Once wasi-libc includes fts or we decide to implement and maintain our own fts-like API, we can remove these `#if os(WASI)` guards. wasi-libc issue tracking fts support: WebAssembly/wasi-libc#520 Also, wasi-libc defines some constants in a way that ClangImporter can't understand, so we need to grab them manually through _FoundationCShims in function call form.
1 parent 4440638 commit 3fe5e1b

File tree

5 files changed

+112
-3
lines changed

5 files changed

+112
-3
lines changed

Sources/FoundationEssentials/FileManager/FileManager+Directories.swift

+3
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ extension _FileManagerImpl {
193193
}
194194
}
195195
return results
196+
#elseif os(WASI)
197+
// wasi-libc does not support FTS for now
198+
throw CocoaError.errorWithFilePath(path, errno: ENOTSUP, reading: true)
196199
#else
197200
return try path.withFileSystemRepresentation { fileSystemRep in
198201
guard let fileSystemRep else {

Sources/FoundationEssentials/FileManager/FileOperations+Enumeration.swift

+18-2
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,16 @@ import posix_filesystem.dirent
115115
#elseif canImport(Glibc)
116116
import Glibc
117117
internal import _FoundationCShims
118+
#elseif os(WASI)
119+
import WASILibc
120+
internal import _FoundationCShims
118121
#endif
119122

120123
// MARK: Directory Iteration
121124

125+
// No FTS support in wasi-libc for now (https://github.com/WebAssembly/wasi-libc/issues/520)
126+
#if !os(WASI)
127+
122128
struct _FTSSequence: Sequence {
123129
enum Element {
124130
struct SwiftFTSENT {
@@ -315,10 +321,12 @@ extension Sequence<_FTSSequence.Element> {
315321
}
316322
}
317323

324+
#endif // !os(WASI)
325+
318326
struct _POSIXDirectoryContentsSequence: Sequence {
319327
#if canImport(Darwin)
320328
typealias DirectoryEntryPtr = UnsafeMutablePointer<DIR>
321-
#elseif os(Android) || canImport(Glibc)
329+
#elseif os(Android) || canImport(Glibc) || os(WASI)
322330
typealias DirectoryEntryPtr = OpaquePointer
323331
#endif
324332

@@ -343,10 +351,18 @@ struct _POSIXDirectoryContentsSequence: Sequence {
343351
continue
344352
}
345353
// Use name
346-
let fileName = withUnsafeBytes(of: &dent.pointee.d_name) { buf in
354+
let fileName: String
355+
#if os(WASI)
356+
// Use shim on WASI because wasi-libc defines `d_name` as
357+
// "flexible array member" which is not supported by
358+
// ClangImporter yet.
359+
fileName = String(cString: _platform_shims_dirent_d_name(dent))
360+
#else
361+
fileName = withUnsafeBytes(of: &dent.pointee.d_name) { buf in
347362
let ptr = buf.baseAddress!.assumingMemoryBound(to: CChar.self)
348363
return String(cString: ptr)
349364
}
365+
#endif
350366

351367
if fileName == "." || fileName == ".." || fileName == "._" {
352368
continue

Sources/FoundationEssentials/FileManager/FileOperations.swift

+47-1
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,14 @@ enum _FileOperations {
512512
// We failed for a reason other than the directory not being empty, so throw
513513
throw CocoaError.removeFileError(errno, resolve(path: pathStr))
514514
}
515-
515+
516+
#if os(WASI)
517+
518+
// wasi-libc does not support FTS, so we don't support removing non-empty directories on WASI for now.
519+
throw CocoaError(.featureUnsupported)
520+
521+
#else
522+
516523
let seq = _FTSSequence(path, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR | FTS_NOSTAT)
517524
let iterator = seq.makeIterator()
518525
var isFirst = true
@@ -561,6 +568,7 @@ enum _FileOperations {
561568
}
562569
}
563570
}
571+
#endif
564572

565573
}
566574
#endif
@@ -903,6 +911,43 @@ enum _FileOperations {
903911
}
904912
#endif
905913

914+
#if os(WASI)
915+
private static func _linkOrCopyFile(_ srcPtr: UnsafePointer<CChar>, _ dstPtr: UnsafePointer<CChar>, with fileManager: FileManager, delegate: some LinkOrCopyDelegate) throws {
916+
var stat = stat()
917+
guard lstat(srcPtr, &stat) == 0, !stat.isDirectory else {
918+
// wasi-libc does not support FTS for now, so we don't support copying/linking
919+
// directories on WASI for now.
920+
throw CocoaError(.featureUnsupported)
921+
}
922+
923+
// For now, we support only copying regular files and symlinks.
924+
// After we get FTS support (https://github.com/WebAssembly/wasi-libc/pull/522),
925+
// we can remove this method and use the below FTS-based implementation.
926+
let src = String(cString: srcPtr)
927+
let dst = String(cString: dstPtr)
928+
guard delegate.shouldPerformOnItemAtPath(src, to: dst) else { return }
929+
930+
if stat.isSymbolicLink {
931+
try withUnsafeTemporaryAllocation(of: CChar.self, capacity: FileManager.MAX_PATH_SIZE) { tempBuff in
932+
tempBuff.initialize(repeating: 0)
933+
defer { tempBuff.deinitialize() }
934+
let len = readlink(srcPtr, tempBuff.baseAddress!, FileManager.MAX_PATH_SIZE - 1)
935+
if len >= 0, symlink(tempBuff.baseAddress!, dstPtr) != -1 {
936+
return
937+
}
938+
try delegate.throwIfNecessary(errno, src, dst)
939+
}
940+
} else {
941+
if delegate.copyData {
942+
try _copyRegularFile(srcPtr, dstPtr, delegate: delegate)
943+
} else {
944+
if link(srcPtr, dstPtr) != 0 {
945+
try delegate.throwIfNecessary(errno, src, dst)
946+
}
947+
}
948+
}
949+
}
950+
#else
906951
private static func _linkOrCopyFile(_ srcPtr: UnsafePointer<CChar>, _ dstPtr: UnsafePointer<CChar>, with fileManager: FileManager, delegate: some LinkOrCopyDelegate) throws {
907952
try withUnsafeTemporaryAllocation(of: CChar.self, capacity: FileManager.MAX_PATH_SIZE) { buffer in
908953
let dstLen = Platform.copyCString(dst: buffer.baseAddress!, src: dstPtr, size: FileManager.MAX_PATH_SIZE)
@@ -1015,6 +1060,7 @@ enum _FileOperations {
10151060
}
10161061
}
10171062
}
1063+
#endif
10181064

10191065
private static func linkOrCopyFile(_ src: String, dst: String, with fileManager: FileManager, delegate: some LinkOrCopyDelegate) throws {
10201066
try src.withFileSystemRepresentation { srcPtr in

Sources/FoundationEssentials/WASILibc+Extensions.swift

+21
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,25 @@ internal var CLOCK_MONOTONIC_RAW: clockid_t {
2929
return CLOCK_MONOTONIC
3030
}
3131

32+
// MARK: - File Operations
33+
34+
internal var DT_DIR: UInt8 {
35+
return _platform_shims_DT_DIR()
36+
}
37+
internal var DT_UNKNOWN: UInt8 {
38+
return _platform_shims_DT_UNKNOWN()
39+
}
40+
internal var O_CREAT: Int32 {
41+
return _platform_shims_O_CREAT()
42+
}
43+
internal var O_EXCL: Int32 {
44+
return _platform_shims_O_EXCL()
45+
}
46+
internal var O_TRUNC: Int32 {
47+
return _platform_shims_O_TRUNC()
48+
}
49+
internal var O_WRONLY: Int32 {
50+
return _platform_shims_O_WRONLY()
51+
}
52+
3253
#endif // os(WASI)

Sources/_FoundationCShims/include/platform_shims.h

+23
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,29 @@ static inline _Nonnull clockid_t _platform_shims_clock_monotonic(void) {
7979
static inline _Nonnull clockid_t _platform_shims_clock_realtime(void) {
8080
return CLOCK_REALTIME;
8181
}
82+
83+
// Define dirent shims so that we can use them in Swift because wasi-libc defines
84+
// `d_name` as "flexible array member" which is not supported by ClangImporter yet.
85+
86+
#include <dirent.h>
87+
88+
static inline char * _Nonnull _platform_shims_dirent_d_name(struct dirent * _Nonnull entry) {
89+
return entry->d_name;
90+
}
91+
92+
// Define getter shims for constants because wasi-libc defines them as function-like macros
93+
// which are not supported by ClangImporter yet.
94+
95+
#include <stdint.h>
96+
#include <fcntl.h>
97+
#include <dirent.h>
98+
99+
static inline uint8_t _platform_shims_DT_DIR(void) { return DT_DIR; }
100+
static inline uint8_t _platform_shims_DT_UNKNOWN(void) { return DT_UNKNOWN; }
101+
static inline int32_t _platform_shims_O_CREAT(void) { return O_CREAT; }
102+
static inline int32_t _platform_shims_O_EXCL(void) { return O_EXCL; }
103+
static inline int32_t _platform_shims_O_TRUNC(void) { return O_TRUNC; }
104+
static inline int32_t _platform_shims_O_WRONLY(void) { return O_WRONLY; }
82105
#endif
83106

84107
#endif /* CSHIMS_PLATFORM_SHIMS */

0 commit comments

Comments
 (0)