Coverage Report

Created: 2025-10-10 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/host/wasi/inode.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: 2019-2024 Second State INC
3
4
#pragma once
5
6
#include "common/defines.h"
7
#include "common/errcode.h"
8
#include "common/span.h"
9
#include "common/types.h"
10
#include "host/wasi/error.h"
11
#include "host/wasi/vfs.h"
12
#include <functional>
13
#include <limits>
14
#include <optional>
15
#include <string_view>
16
#include <vector>
17
18
#if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS
19
#include <dirent.h>
20
#include <sys/stat.h>
21
#include <unordered_map>
22
#elif WASMEDGE_OS_WINDOWS
23
#include "system/winapi.h"
24
#endif
25
26
#if WASMEDGE_OS_LINUX
27
#if defined(__GLIBC_PREREQ)
28
#if defined(_LIBCPP_GLIBC_PREREQ)
29
#undef _LIBCPP_GLIBC_PREREQ
30
#endif
31
#define _LIBCPP_GLIBC_PREREQ(a, b) 0
32
#else
33
#if defined(_LIBCPP_GLIBC_PREREQ)
34
#define __GLIBC_PREREQ(a, b) _LIBCPP_GLIBC_PREREQ(a, b)
35
#else
36
#define __GLIBC_PREREQ(a, b) 1
37
#endif
38
#endif
39
#endif
40
41
#if WASMEDGE_OS_LINUX
42
#include <sys/epoll.h>
43
#endif
44
#if WASMEDGE_OS_MACOS
45
#include <sys/event.h>
46
#endif
47
48
namespace WasmEdge {
49
namespace Host {
50
namespace WASI {
51
52
#if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS
53
struct FdHolder {
54
  FdHolder(const FdHolder &) = delete;
55
  FdHolder &operator=(const FdHolder &) = delete;
56
  FdHolder(FdHolder &&RHS) noexcept
57
0
      : Fd(std::exchange(RHS.Fd, -1)), Cleanup(RHS.Cleanup) {
58
0
    RHS.Cleanup = true;
59
0
  }
60
0
  FdHolder &operator=(FdHolder &&RHS) noexcept {
61
0
    using std::swap;
62
0
    swap(Fd, RHS.Fd);
63
0
    Cleanup = RHS.Cleanup;
64
0
    RHS.Cleanup = true;
65
0
    return *this;
66
0
  }
67
68
0
  constexpr FdHolder() noexcept : Fd(-1), Cleanup(true) {}
69
0
  ~FdHolder() noexcept {
70
0
    if (Cleanup) {
71
0
      reset();
72
0
    }
73
0
  }
74
  explicit constexpr FdHolder(int Fd, bool Cleanup = true) noexcept
75
0
      : Fd(Fd), Cleanup(Cleanup) {}
76
0
  constexpr bool ok() const noexcept { return Fd >= 0; }
77
  void reset() noexcept;
78
0
  int release() noexcept { return std::exchange(Fd, -1); }
79
0
  void emplace(int NewFd) noexcept {
80
0
    reset();
81
0
    Fd = NewFd;
82
0
  }
83
0
  int getFd() noexcept { return Fd; }
84
  int Fd = -1;
85
  bool Cleanup : 1;
86
};
87
88
struct DirHolder {
89
  DirHolder(const DirHolder &) = delete;
90
  DirHolder &operator=(const DirHolder &) = delete;
91
0
  DirHolder(DirHolder &&RHS) noexcept {
92
0
    using std::swap;
93
0
    swap(Dir, RHS.Dir);
94
0
    swap(Cookie, RHS.Cookie);
95
0
  }
96
0
  DirHolder &operator=(DirHolder &&RHS) noexcept {
97
0
    using std::swap;
98
0
    swap(Dir, RHS.Dir);
99
0
    swap(Cookie, RHS.Cookie);
100
0
    return *this;
101
0
  }
102
103
0
  DirHolder() noexcept = default;
104
0
  explicit DirHolder(DIR *D) noexcept : Dir(D) {}
105
0
  constexpr bool ok() const noexcept { return Dir != nullptr; }
106
0
  ~DirHolder() noexcept { reset(); }
107
  void reset() noexcept;
108
0
  void emplace(DIR *NewDir) noexcept {
109
0
    reset();
110
0
    Dir = NewDir;
111
0
  }
112
113
  DIR *Dir = nullptr;
114
  uint64_t Cookie = 0;
115
  static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(__wasi_dirent_t));
116
  std::vector<uint8_t> Buffer;
117
};
118
#endif
119
120
#if WASMEDGE_OS_LINUX
121
struct TimerHolder {
122
  TimerHolder(const TimerHolder &) = delete;
123
  TimerHolder &operator=(const TimerHolder &) = delete;
124
0
  TimerHolder(TimerHolder &&RHS) noexcept {
125
0
    using std::swap;
126
0
    swap(Id, RHS.Id);
127
0
  }
128
0
  TimerHolder &operator=(TimerHolder &&RHS) noexcept {
129
0
    using std::swap;
130
0
    swap(Id, RHS.Id);
131
0
    return *this;
132
0
  }
133
134
  constexpr TimerHolder() = default;
135
0
  explicit constexpr TimerHolder(timer_t T) noexcept : Id(T) {}
136
0
  ~TimerHolder() noexcept { reset(); }
137
  void reset() noexcept;
138
0
  void emplace(timer_t NewId) noexcept {
139
0
    reset();
140
0
    Id = NewId;
141
0
  }
142
  std::optional<timer_t> Id;
143
};
144
#endif
145
146
#if WASMEDGE_OS_WINDOWS
147
struct HandleHolder {
148
  enum class HandleType : uint8_t {
149
    NormalHandle,
150
    StdHandle,
151
    NormalSocket,
152
  };
153
154
  HandleHolder(const HandleHolder &) = delete;
155
  HandleHolder &operator=(const HandleHolder &) = delete;
156
  HandleHolder(HandleHolder &&RHS) noexcept {
157
    using std::swap;
158
    swap(*this, RHS);
159
  }
160
  HandleHolder &operator=(HandleHolder &&RHS) noexcept {
161
    using std::swap;
162
    swap(*this, RHS);
163
    return *this;
164
  }
165
166
  constexpr HandleHolder() noexcept = default;
167
  ~HandleHolder() noexcept { reset(); }
168
  HandleHolder(winapi::HANDLE_ Handle, bool IsStdHandle) noexcept
169
      : Handle(likely(Handle != winapi::INVALID_HANDLE_VALUE_) ? Handle
170
                                                               : nullptr),
171
        Type(IsStdHandle ? HandleType::StdHandle : HandleType::NormalHandle) {}
172
  HandleHolder(winapi::SOCKET_ Socket) noexcept
173
      : Socket(Socket), Type(HandleType::NormalSocket) {}
174
  HandleHolder(const std::filesystem::path &Path,
175
               const winapi::DWORD_ AccessFlags,
176
               const winapi::DWORD_ ShareFlags,
177
               const winapi::DWORD_ CreationDisposition,
178
               const winapi::DWORD_ AttributeFlags) noexcept;
179
  bool reopen(const winapi::DWORD_ AccessFlags, const winapi::DWORD_ ShareFlags,
180
181
              const winapi::DWORD_ AttributeFlags) noexcept;
182
  constexpr bool ok() const noexcept { return Handle != nullptr; }
183
  constexpr bool isStdHandle() const noexcept {
184
    return Type == HandleType::StdHandle;
185
  }
186
  constexpr bool isSocket() const noexcept {
187
    return Type == HandleType::NormalSocket;
188
  }
189
  void reset() noexcept;
190
  winapi::HANDLE_ release() noexcept { return std::exchange(Handle, nullptr); }
191
  winapi::HANDLE_ exchange(winapi::HANDLE_ NewHandle) noexcept {
192
    return std::exchange(Handle, NewHandle);
193
  }
194
  void emplace(winapi::HANDLE_ NewHandle) noexcept {
195
    reset();
196
    Handle = NewHandle;
197
  }
198
  friend void swap(HandleHolder &LHS, HandleHolder &RHS) noexcept {
199
    using std::swap;
200
    swap(LHS.Handle, RHS.Handle);
201
    swap(LHS.Type, RHS.Type);
202
  }
203
  WasiExpect<void> filestatGet(__wasi_filestat_t &FileStat) const noexcept;
204
205
  union {
206
    winapi::HANDLE_ Handle = nullptr;
207
    winapi::SOCKET_ Socket;
208
  };
209
  HandleType Type = HandleType::NormalHandle;
210
};
211
212
template <typename T> class FindHolderBase {
213
private:
214
  struct Proxy : T {
215
    static void doReset(T &Base) noexcept {
216
      const auto Ptr = &T::doReset;
217
      return (Base.*Ptr)();
218
    }
219
    static WasiExpect<void> doRewind(T &Base, bool First) noexcept {
220
      const auto Ptr = &T::doRewind;
221
      return (Base.*Ptr)(First);
222
    }
223
    static bool doNext(T &Base) noexcept {
224
      const auto Ptr = &T::doNext;
225
      return (Base.*Ptr)();
226
    }
227
    static WasiExpect<void> doLoadDirent(T &Base) noexcept {
228
      const auto Ptr = &T::doLoadDirent;
229
      return (Base.*Ptr)();
230
    }
231
  };
232
233
public:
234
  FindHolderBase(FindHolderBase &&RHS) noexcept {
235
    using std::swap;
236
    swap(Handle, RHS.Handle);
237
    swap(Cookie, RHS.Cookie);
238
    swap(Buffer, RHS.Buffer);
239
  }
240
  FindHolderBase &operator=(FindHolderBase &&RHS) noexcept {
241
    using std::swap;
242
    swap(Handle, RHS.Handle);
243
    swap(Cookie, RHS.Cookie);
244
    swap(Buffer, RHS.Buffer);
245
    return *this;
246
  }
247
248
  FindHolderBase() noexcept = default;
249
  FindHolderBase(const FindHolderBase &) noexcept = delete;
250
  FindHolderBase &operator=(const FindHolderBase &) noexcept = delete;
251
  constexpr bool ok() const noexcept { return Handle != nullptr; }
252
  ~FindHolderBase() noexcept { reset(); }
253
254
  WasiExpect<void> emplace(winapi::HANDLE_ PathHandle) noexcept;
255
  void reset() noexcept {
256
    Proxy::doReset(static_cast<T &>(*this));
257
    Path.clear();
258
    Handle = nullptr;
259
    Cookie = 0;
260
    Buffer.clear();
261
  }
262
  WasiExpect<void> seek(uint64_t NewCookie) noexcept;
263
  bool next() noexcept;
264
  WasiExpect<void> loadDirent() noexcept;
265
  size_t write(Span<uint8_t> Output) noexcept;
266
267
protected:
268
  auto getPath() const noexcept { return Path; }
269
  auto getHandle() const noexcept { return Handle; }
270
  void setHandle(winapi::HANDLE_ New) noexcept { Handle = New; }
271
  auto getCookie() const noexcept { return Cookie; }
272
  Span<const uint8_t> getBuffer() const noexcept { return Buffer; }
273
  Span<uint8_t> getBuffer() noexcept { return Buffer; }
274
  void resizeBuffer(size_t Size) noexcept { Buffer.resize(Size); }
275
276
private:
277
  std::filesystem::path Path;
278
  winapi::HANDLE_ Handle = nullptr;
279
  uint64_t Cookie = 0;
280
  static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(__wasi_dirent_t));
281
  std::vector<uint8_t> Buffer;
282
};
283
284
#if !WINAPI_PARTITION_DESKTOP || NTDDI_VERSION >= NTDDI_VISTA
285
class FindHolder : public FindHolderBase<FindHolder> {
286
public:
287
  const winapi::FILE_ID_BOTH_DIR_INFO_ &getData() const noexcept;
288
289
protected:
290
  void doReset() noexcept;
291
  WasiExpect<void> doRewind(bool First) noexcept;
292
  bool doNext() noexcept;
293
  WasiExpect<void> doLoadDirent() noexcept;
294
295
private:
296
  bool nextData() noexcept;
297
  WASMEDGE_WINAPI_DETAIL_EXTENSION union {
298
    winapi::FILE_ID_BOTH_DIR_INFO_ FindData;
299
    std::array<uint8_t,
300
               sizeof(winapi::FILE_ID_BOTH_DIR_INFO_) +
301
                   sizeof(wchar_t[winapi::UNICODE_STRING_MAX_CHARS_ + 1])>
302
        FindDataPadding;
303
  } FindDataUnion;
304
  uint32_t Cursor = 0;
305
  static_assert(std::numeric_limits<decltype(Cursor)>::max() >
306
                sizeof(FindDataUnion));
307
};
308
#else
309
class FindHolder : public FindHolderBase<FindHolder> {
310
protected:
311
  WasiExpect<winapi::HANDLE_> doEmplace(const std::filesystem::path &NewPath,
312
                                        winapi::HANDLE_ PathHandle) noexcept;
313
  void doReset() noexcept;
314
  WasiExpect<void> doRewind(bool First) noexcept;
315
  bool doNext() noexcept;
316
  WasiExpect<void> doLoadDirent() noexcept;
317
318
private:
319
  winapi::WIN32_FIND_DATAW_ FindData;
320
};
321
#endif
322
323
#endif
324
325
enum class TriggerType {
326
  Level,
327
  Edge,
328
};
329
class Poller;
330
331
class INode
332
#if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS
333
    : public FdHolder
334
#elif WASMEDGE_OS_WINDOWS
335
    : public HandleHolder
336
#endif
337
{
338
public:
339
  INode(const INode &) = delete;
340
  INode &operator=(const INode &) = delete;
341
0
  INode(INode &&RHS) noexcept = default;
342
  INode &operator=(INode &&RHS) noexcept = default;
343
344
  static INode stdIn() noexcept;
345
346
  static INode stdOut() noexcept;
347
348
  static INode stdErr() noexcept;
349
350
  static WasiExpect<INode> fromFd(int32_t Fd);
351
352
  /// Open a file or directory.
353
  ///
354
  /// @param[in] Path The absolute path of the file or directory to open.
355
  /// @param[in] OpenFlags The method by which to open the file.
356
  /// @param[in] FdFlags The method by which to open the file.
357
  /// @param[in] VFSFlags The method by which to open the file.
358
  /// @return The file descriptor of the file that has been opened, or WASI
359
  /// error.
360
  static WasiExpect<INode> open(std::string Path, __wasi_oflags_t OpenFlags,
361
                                __wasi_fdflags_t FdFlags,
362
                                VFS::Flags VFSFlags) noexcept;
363
364
  /// Provide file advisory information on a file descriptor.
365
  ///
366
  /// Note: This is similar to `posix_fadvise` in POSIX.
367
  ///
368
  /// @param[in] Offset The offset within the file to which the advisory
369
  /// applies.
370
  /// @param[in] Len The length of the region to which the advisory applies.
371
  /// @param[in] Advice The advice.
372
  /// @return Nothing or WASI error
373
  WasiExpect<void> fdAdvise(__wasi_filesize_t Offset, __wasi_filesize_t Len,
374
                            __wasi_advice_t Advice) const noexcept;
375
376
  /// Force the allocation of space in a file.
377
  ///
378
  /// Note: This is similar to `posix_fallocate` in POSIX.
379
  ///
380
  /// @param[in] Offset The offset at which to start the allocation.
381
  /// @param[in] Len The length of the area that is allocated.
382
  /// @return Nothing or WASI error
383
  WasiExpect<void> fdAllocate(__wasi_filesize_t Offset,
384
                              __wasi_filesize_t Len) const noexcept;
385
386
  /// Synchronize the data of a file to disk.
387
  ///
388
  /// Note: This is similar to `fdatasync` in POSIX.
389
  ///
390
  /// @return Nothing or WASI error
391
  WasiExpect<void> fdDatasync() const noexcept;
392
393
  /// Get the attributes of a file descriptor.
394
  ///
395
  /// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as
396
  /// well
397
  ///
398
  /// as additional fields.
399
  /// @param[out] FdStat Result.
400
  /// @return Nothing or WASI error
401
  WasiExpect<void> fdFdstatGet(__wasi_fdstat_t &FdStat) const noexcept;
402
403
  /// Adjust the flags associated with a file descriptor.
404
  ///
405
  /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
406
  ///
407
  /// @param[in] FdFlags The desired values of the file descriptor flags.
408
  /// @return Nothing or WASI error
409
  WasiExpect<void> fdFdstatSetFlags(__wasi_fdflags_t FdFlags) const noexcept;
410
411
  /// Return the attributes of an open file.
412
  ///
413
  /// @param[out] Filestat Result.
414
  /// @return Nothing or WASI error
415
  WasiExpect<void> fdFilestatGet(__wasi_filestat_t &Filestat) const noexcept;
416
417
  /// Adjust the size of an open file. If this increases the file's size, the
418
  /// extra bytes are filled with zeros.
419
  ///
420
  /// Note: This is similar to `ftruncate` in POSIX.
421
  ///
422
  /// @param[in] Size The desired file size.
423
  /// @return Nothing or WASI error
424
  WasiExpect<void> fdFilestatSetSize(__wasi_filesize_t Size) const noexcept;
425
426
  /// Adjust the timestamps of an open file or directory.
427
  ///
428
  /// Note: This is similar to `futimens` in POSIX.
429
  ///
430
  /// @param[in] ATim The desired values of the data access timestamp.
431
  /// @param[in] MTim The desired values of the data modification timestamp.
432
  /// @param[in] FstFlags A bitmask indicating which timestamps to adjust.
433
  /// @return Nothing or WASI error
434
  WasiExpect<void>
435
  fdFilestatSetTimes(__wasi_timestamp_t ATim, __wasi_timestamp_t MTim,
436
                     __wasi_fstflags_t FstFlags) const noexcept;
437
438
  /// Read from a file descriptor, without using and updating the file
439
  /// descriptor's offset.
440
  ///
441
  /// Note: This is similar to `preadv` in POSIX.
442
  ///
443
  /// @param[in] IOVs List of scatter/gather vectors in which to store data.
444
  /// @param[in] Offset The offset within the file at which to read.
445
  /// @param[out] NRead The number of bytes read.
446
  /// @return Nothing or WASI error
447
  WasiExpect<void> fdPread(Span<Span<uint8_t>> IOVs, __wasi_filesize_t Offset,
448
                           __wasi_size_t &NRead) const noexcept;
449
450
  /// Write to a file descriptor, without using and updating the file
451
  /// descriptor's offset.
452
  ///
453
  /// Note: This is similar to `pwritev` in POSIX.
454
  ///
455
  /// @param[in] IOVs List of scatter/gather vectors from which to retrieve
456
  /// data.
457
  /// @param[in] Offset The offset within the file at which to write.
458
  /// @param[out] NWritten The number of bytes written.
459
  /// @return Nothing or WASI error
460
  WasiExpect<void> fdPwrite(Span<Span<const uint8_t>> IOVs,
461
                            __wasi_filesize_t Offset,
462
                            __wasi_size_t &NWritten) const noexcept;
463
464
  /// Read from a file descriptor.
465
  ///
466
  /// Note: This is similar to `readv` in POSIX.
467
  ///
468
  /// @param[in] IOVs List of scatter/gather vectors to which to store data.
469
  /// @param[out] NRead The number of bytes read.
470
  /// @return Nothing or WASI error
471
  WasiExpect<void> fdRead(Span<Span<uint8_t>> IOVs,
472
                          __wasi_size_t &NRead) const noexcept;
473
474
  /// Read directory entries from a directory.
475
  ///
476
  /// When successful, the contents of the output buffer consist of a sequence
477
  /// of directory entries. Each directory entry consists of a `dirent`
478
  /// object, followed by `dirent::d_namlen` bytes holding the name of the
479
  /// directory entry.
480
  ///
481
  /// This function fills the output buffer as much as possible,
482
  /// potentially truncating the last directory entry. This allows the caller
483
  /// to grow its read buffer size in case it's too small to fit a single
484
  /// large directory entry, or skip the oversized directory entry.
485
  ///
486
  /// @param[out] Buffer The buffer where directory entries are stored.
487
  /// @param[in] Cookie The location within the directory to start reading
488
  /// @param[out] Size The number of bytes stored in the read buffer. If less
489
  /// than the size of the read buffer, the end of the directory has been
490
  /// reached.
491
  /// @return Nothing or WASI error
492
  WasiExpect<void> fdReaddir(Span<uint8_t> Buffer, __wasi_dircookie_t Cookie,
493
                             __wasi_size_t &Size) noexcept;
494
495
  /// Move the offset of a file descriptor.
496
  ///
497
  /// Note: This is similar to `lseek` in POSIX.
498
  ///
499
  /// @param[in] Offset The number of bytes to move.
500
  /// @param[in] Whence The base from which the offset is relative.
501
  /// @param[out] Size The new offset of the file descriptor, relative to the
502
  /// start of the file.
503
  /// @return Nothing or WASI error
504
  WasiExpect<void> fdSeek(__wasi_filedelta_t Offset, __wasi_whence_t Whence,
505
                          __wasi_filesize_t &Size) const noexcept;
506
507
  /// Synchronize the data and metadata of a file to disk.
508
  ///
509
  /// Note: This is similar to `fsync` in POSIX.
510
  ///
511
  /// @return Nothing or WASI error
512
  WasiExpect<void> fdSync() const noexcept;
513
514
  /// Return the current offset of a file descriptor.
515
  ///
516
  /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
517
  ///
518
  /// @param[out] Size The current offset of the file descriptor, relative to
519
  /// the start of the file.
520
  /// @return Nothing or WASI error
521
  WasiExpect<void> fdTell(__wasi_filesize_t &Size) const noexcept;
522
523
  /// Write to a file descriptor.
524
  ///
525
  /// Note: This is similar to `writev` in POSIX.
526
  ///
527
  /// @param[in] IOVs List of scatter/gather vectors from which to retrieve
528
  /// data.
529
  /// @param[out] NWritten The number of bytes written.
530
  /// @return Nothing or WASI error
531
  WasiExpect<void> fdWrite(Span<Span<const uint8_t>> IOVs,
532
                           __wasi_size_t &NWritten) const noexcept;
533
534
  /// Get the native handler.
535
  ///
536
  /// Note: Users should cast this native handler to corresponding types
537
  /// on different operating systems. E.g. int on POSIX or void * on Windows
538
  ///
539
  /// @return The native handler in uint64_t.
540
  WasiExpect<uint64_t> getNativeHandler() const noexcept;
541
542
  /// Create a directory.
543
  ///
544
  /// Note: This is similar to `mkdirat` in POSIX.
545
  ///
546
  /// @param[in] Path The path at which to create the directory.
547
  /// @return Nothing or WASI error
548
  WasiExpect<void> pathCreateDirectory(std::string Path) const noexcept;
549
550
  /// Return the attributes of a file or directory.
551
  ///
552
  /// Note: This is similar to `stat` in POSIX.
553
  ///
554
  /// @param[in] Path The path of the file or directory to inspect.
555
  /// @param[out] Filestat The buffer where the file's attributes are stored.
556
  /// @return Nothing or WASI error
557
  WasiExpect<void> pathFilestatGet(std::string Path,
558
                                   __wasi_filestat_t &Filestat) const noexcept;
559
560
  /// Adjust the timestamps of a file or directory.
561
  ///
562
  /// Note: This is similar to `utimensat` in POSIX.
563
  ///
564
  /// @param[in] Path The path of the file or directory to inspect.
565
  /// @param[in] ATim The desired values of the data access timestamp.
566
  /// @param[in] MTim The desired values of the data modification timestamp.
567
  /// @param[in] FstFlags A bitmask indicating which timestamps to adjust.
568
  /// @return Nothing or WASI error
569
  WasiExpect<void>
570
  pathFilestatSetTimes(std::string Path, __wasi_timestamp_t ATim,
571
                       __wasi_timestamp_t MTim,
572
                       __wasi_fstflags_t FstFlags) const noexcept;
573
574
  /// Create a hard link.
575
  ///
576
  /// Note: This is similar to `linkat` in POSIX.
577
  ///
578
  /// @param[in] Old The working directory at which the resolution of the old
579
  /// path starts.
580
  /// @param[in] OldPath The source path from which to link.
581
  /// @param[in] New The working directory at which the resolution of the new
582
  /// path starts.
583
  /// @param[in] NewPath The destination path at which to create the hard
584
  /// link. resolved.
585
  /// @return Nothing or WASI error
586
  static WasiExpect<void> pathLink(const INode &Old, std::string OldPath,
587
                                   const INode &New,
588
                                   std::string NewPath) noexcept;
589
590
  /// Open a file or directory.
591
  ///
592
  /// The returned file descriptor is not guaranteed to be the lowest-numbered
593
  /// file descriptor not currently open; it is randomized to prevent
594
  /// applications from depending on making assumptions about indexes, since
595
  /// this is error-prone in multi-threaded contexts. The returned file
596
  /// descriptor is guaranteed to be less than 2**31.
597
  ///
598
  /// Note: This is similar to `openat` in POSIX.
599
  ///
600
  /// @param[in] Path The relative path of the file or directory to open,
601
  /// relative to the `path_open::fd` directory.
602
  /// @param[in] OpenFlags The method by which to open the file.
603
  /// @param[in] FdFlags The method by which to open the file.
604
  /// @param[in] VFSFlags The method by which to open the file.
605
  /// @return The file descriptor of the file that has been opened, or WASI
606
  /// error.
607
  WasiExpect<INode> pathOpen(std::string Path, __wasi_oflags_t OpenFlags,
608
                             __wasi_fdflags_t FdFlags,
609
                             VFS::Flags VFSFlags) const noexcept;
610
611
  /// Read the contents of a symbolic link.
612
  ///
613
  /// Note: This is similar to `readlinkat` in POSIX.
614
  ///
615
  /// @param[in] Path The path of the symbolic link from which to read.
616
  /// @param[out] Buffer The buffer to which to write the contents of the
617
  /// symbolic link.
618
  /// @param[out] NRead The number of bytes read.
619
  /// @return Nothing or WASI error.
620
  WasiExpect<void> pathReadlink(std::string Path, Span<char> Buffer,
621
                                __wasi_size_t &NRead) const noexcept;
622
623
  /// Remove a directory.
624
  ///
625
  /// Return `errno::notempty` if the directory is not empty.
626
  ///
627
  /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
628
  ///
629
  /// @param[in] Path The path to a directory to remove.
630
  /// @return Nothing or WASI error.
631
  WasiExpect<void> pathRemoveDirectory(std::string Path) const noexcept;
632
633
  /// Rename a file or directory.
634
  ///
635
  /// Note: This is similar to `renameat` in POSIX.
636
  ///
637
  /// @param[in] Old The working directory at which the resolution of the old
638
  /// path starts.
639
  /// @param[in] OldPath The source path of the file or directory to rename.
640
  /// @param[in] New The working directory at which the resolution of the new
641
  /// path starts.
642
  /// @param[in] NewPath The destination path to which to rename the file or
643
  /// directory.
644
  /// @return Nothing or WASI error.
645
  static WasiExpect<void> pathRename(const INode &Old, std::string OldPath,
646
                                     const INode &New,
647
                                     std::string NewPath) noexcept;
648
649
  /// Create a symbolic link.
650
  ///
651
  /// Note: This is similar to `symlinkat` in POSIX.
652
  ///
653
  /// @param[in] OldPath The contents of the symbolic link.
654
  /// @param[in] NewPath The destination path at which to create the symbolic
655
  /// link.
656
  /// @return Nothing or WASI error
657
  WasiExpect<void> pathSymlink(std::string OldPath,
658
                               std::string NewPath) const noexcept;
659
660
  /// Unlink a file.
661
  ///
662
  /// Return `errno::isdir` if the path refers to a directory.
663
  ///
664
  /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
665
  ///
666
  /// @param[in] Path The path to a file to unlink.
667
  /// @return Nothing or WASI error.
668
  WasiExpect<void> pathUnlinkFile(std::string Path) const noexcept;
669
670
  static WasiExpect<void>
671
  getAddrinfo(std::string_view NodeStr, std::string_view ServiceStr,
672
              const __wasi_addrinfo_t &Hint, uint32_t MaxResLength,
673
              Span<__wasi_addrinfo_t *> WasiAddrinfoArray,
674
              Span<__wasi_sockaddr_t *> WasiSockaddrArray,
675
              Span<char *> AiAddrSaDataArray, Span<char *> AiCanonnameArray,
676
              /*Out*/ __wasi_size_t &ResLength) noexcept;
677
678
  static WasiExpect<INode> sockOpen(__wasi_address_family_t SysDomain,
679
                                    __wasi_sock_type_t SockType) noexcept;
680
681
  WasiExpect<void> sockBind(__wasi_address_family_t AddressFamily,
682
                            Span<const uint8_t> Address,
683
                            uint16_t Port) noexcept;
684
685
  WasiExpect<void> sockListen(int32_t Backlog) noexcept;
686
687
  WasiExpect<INode> sockAccept(__wasi_fdflags_t FdFlags) noexcept;
688
689
  WasiExpect<void> sockConnect(__wasi_address_family_t AddressFamily,
690
                               Span<const uint8_t> Address,
691
                               uint16_t Port) noexcept;
692
693
  /// Receive a message from a socket.
694
  ///
695
  /// Note: This is similar to `recv` in POSIX, though it also supports
696
  /// reading the data into multiple buffers in the manner of `readv`.
697
  ///
698
  /// @param[in] RiData List of scatter/gather vectors to which to store data.
699
  /// @param[in] RiFlags Message flags.
700
  /// @param[out] NRead Return the number of bytes stored in RiData.
701
  /// @param[out] RoFlags Return message flags.
702
  /// @return Nothing or WASI error.
703
  WasiExpect<void> sockRecv(Span<Span<uint8_t>> RiData,
704
                            __wasi_riflags_t RiFlags, __wasi_size_t &NRead,
705
                            __wasi_roflags_t &RoFlags) const noexcept;
706
707
  /// Receive a message from a socket.
708
  ///
709
  /// Note: This is similar to `recvfrom` in POSIX, though it also supports
710
  /// reading the data into multiple buffers in the manner of `readv`.
711
  ///
712
  /// @param[in] RiData List of scatter/gather vectors to which to store data.
713
  /// @param[in] RiFlags Message flags.
714
  /// @param[out] AddressFamilyPtr The pointer to store address family.
715
  /// @param[out] Address The buffer to store address.
716
  /// @param[out] PortPtr The pointer to store port.
717
  /// @param[out] NRead Return the number of bytes stored in RiData.
718
  /// @param[out] RoFlags Return message flags.
719
  /// @return Nothing or WASI error.
720
  WasiExpect<void> sockRecvFrom(Span<Span<uint8_t>> RiData,
721
                                __wasi_riflags_t RiFlags,
722
                                __wasi_address_family_t *AddressFamilyPtr,
723
                                Span<uint8_t> Address, uint16_t *PortPtr,
724
                                __wasi_size_t &NRead,
725
                                __wasi_roflags_t &RoFlags) const noexcept;
726
727
  /// Send a message on a socket.
728
  ///
729
  /// Note: This is similar to `send` in POSIX, though it also supports
730
  /// writing the data from multiple buffers in the manner of `writev`.
731
  ///
732
  /// @param[in] SiData List of scatter/gather vectors to which to retrieve
733
  /// data.
734
  /// @param[in] SiFlags Message flags.
735
  /// @param[out] NWritten The number of bytes transmitted.
736
  /// @return Nothing or WASI error
737
  WasiExpect<void> sockSend(Span<Span<const uint8_t>> SiData,
738
                            __wasi_siflags_t SiFlags,
739
                            __wasi_size_t &NWritten) const noexcept;
740
741
  /// Send a message on a socket.
742
  ///
743
  /// Note: This is similar to `sendto` in POSIX, though it also supports
744
  /// writing the data from multiple buffers in the manner of `writev`.
745
  ///
746
  /// @param[in] SiData List of scatter/gather vectors to which to retrieve
747
  /// data.
748
  /// @param[in] SiFlags Message flags.
749
  /// @param[in] AddressFamily Address family of the target.
750
  /// @param[in] Address Address of the target.
751
  /// @param[in] Port Connected port.
752
  /// @param[out] NWritten The number of bytes transmitted.
753
  /// @return Nothing or WASI error
754
  WasiExpect<void> sockSendTo(Span<Span<const uint8_t>> SiData,
755
                              __wasi_siflags_t SiFlags,
756
                              __wasi_address_family_t AddressFamily,
757
                              Span<const uint8_t> Address, uint16_t Port,
758
                              __wasi_size_t &NWritten) const noexcept;
759
760
  /// Shut down socket send and receive channels.
761
  ///
762
  /// Note: This is similar to `shutdown` in POSIX.
763
  ///
764
  /// @param[in] SdFlags Which channels on the socket to shut down.
765
  /// @return Nothing or WASI error
766
  WasiExpect<void> sockShutdown(__wasi_sdflags_t SdFlags) const noexcept;
767
768
  WasiExpect<void> sockGetOpt(__wasi_sock_opt_level_t SockOptLevel,
769
                              __wasi_sock_opt_so_t SockOptName,
770
                              Span<uint8_t> &Flag) const noexcept;
771
772
  WasiExpect<void> sockSetOpt(__wasi_sock_opt_level_t SockOptLevel,
773
                              __wasi_sock_opt_so_t SockOptName,
774
                              Span<const uint8_t> Flag) const noexcept;
775
776
  WasiExpect<void> sockGetLocalAddr(__wasi_address_family_t *AddressFamilyPtr,
777
                                    Span<uint8_t> Address,
778
                                    uint16_t *PortPtr) const noexcept;
779
780
  WasiExpect<void> sockGetPeerAddr(__wasi_address_family_t *AddressFamilyPtr,
781
                                   Span<uint8_t> Address,
782
                                   uint16_t *PortPtr) const noexcept;
783
784
  /// File type.
785
  WasiExpect<__wasi_filetype_t> filetype() const noexcept;
786
787
  /// Check if this inode is a directory.
788
  bool isDirectory() const noexcept;
789
790
  /// Check if this inode is a symbolic link.
791
  bool isSymlink() const noexcept;
792
793
  /// File size.
794
  WasiExpect<__wasi_filesize_t> filesize() const noexcept;
795
796
  /// Check if current user has execute permission on this inode.
797
  bool canBrowse() const noexcept;
798
799
private:
800
  friend class Poller;
801
802
#if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS
803
public:
804
  using FdHolder::FdHolder;
805
806
private:
807
  mutable std::optional<struct stat> Stat;
808
809
  DirHolder Dir;
810
811
  __wasi_filetype_t unsafeFiletype() const noexcept;
812
  WasiExpect<void> updateStat() const noexcept;
813
814
#elif WASMEDGE_OS_WINDOWS
815
public:
816
  using HandleHolder::HandleHolder;
817
818
private:
819
  __wasi_fdflags_t SavedFdFlags = {};
820
  uint8_t SavedVFSFlags = {};
821
822
  FindHolder Find;
823
#endif
824
};
825
826
class PollerContext;
827
class Poller
828
#if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS
829
    : public FdHolder
830
#endif
831
{
832
public:
833
  Poller(const Poller &) = delete;
834
  Poller &operator=(const Poller &) = delete;
835
0
  Poller(Poller &&RHS) noexcept = default;
836
  Poller &operator=(Poller &&RHS) noexcept = default;
837
  Poller(PollerContext &) noexcept;
838
839
  /// Records an error for polling.
840
  ///
841
  /// @param[in] UserData User-provided value that may be attached to objects
842
  /// that is retained when extracted from the implementation.
843
  /// @param[in] Error Occurred while processing the subscription request.
844
  /// @param[in] Type The type of event that occurred
845
  /// additionally to coalesce with other events.
846
  void error(__wasi_userdata_t UserData, __wasi_errno_t Error,
847
0
             __wasi_eventtype_t Type) noexcept {
848
0
    assuming(Events.size() < WasiEvents.size());
849
0
    auto &Event = Events.emplace_back();
850
0
    Event.Valid = true;
851
0
    Event.userdata = UserData;
852
0
    Event.error = Error;
853
0
    Event.type = Type;
854
0
    switch (Type) {
855
0
    case __WASI_EVENTTYPE_FD_READ:
856
0
    case __WASI_EVENTTYPE_FD_WRITE:
857
0
      Event.fd_readwrite.nbytes = 0;
858
0
      Event.fd_readwrite.flags = static_cast<__wasi_eventrwflags_t>(0);
859
0
      break;
860
0
    default:
861
0
      break;
862
0
    }
863
0
  }
864
865
  /// Prepare for concurrently polling for the occurrence of a set of events.
866
  ///
867
  /// @param[in] Events The output buffer for events.
868
  WasiExpect<void> prepare(Span<__wasi_event_t> Events) noexcept;
869
870
  /// Concurrently poll for a time event.
871
  ///
872
  /// @param[in] Clock The clock against which to compare the timestamp.
873
  /// @param[in] Timeout The absolute or relative timestamp
874
  /// @param[in] Precision The amount of time that the implementation may wait
875
  /// @param[in] Flags Specifying whether the timeout is absolute or relative
876
  /// additionally to coalesce with other events.
877
  /// @param[in] UserData User-provided value that may be attached to objects
878
  /// that is retained when extracted from the implementation.
879
  void clock(__wasi_clockid_t Clock, __wasi_timestamp_t Timeout,
880
             __wasi_timestamp_t Precision, __wasi_subclockflags_t Flags,
881
             __wasi_userdata_t UserData) noexcept;
882
883
  void read(const INode &Fd, TriggerType Trigger,
884
            __wasi_userdata_t UserData) noexcept;
885
886
  void write(const INode &Fd, TriggerType Trigger,
887
             __wasi_userdata_t UserData) noexcept;
888
889
  void close(const INode &Fd) noexcept;
890
  /// Concurrently poll for events.
891
  void wait() noexcept;
892
893
  /// Return number of events.
894
  ///
895
  /// @return Number of event occurred
896
0
  __wasi_size_t result() const noexcept {
897
0
    assuming(Events.size() == WasiEvents.size());
898
0
    __wasi_size_t NEvent = 0;
899
0
    for (const auto &Event : Events) {
900
0
      if (Event.Valid) {
901
0
        WasiEvents[NEvent] = Event;
902
0
        ++NEvent;
903
0
      }
904
0
    }
905
0
    return NEvent;
906
0
  }
907
908
  /// Reset all status
909
  void reset() noexcept;
910
911
  bool ok() noexcept;
912
913
protected:
914
  std::reference_wrapper<PollerContext> Ctx;
915
916
private:
917
  Span<__wasi_event_t> WasiEvents;
918
  struct OptionalEvent : __wasi_event_t {
919
    bool Valid;
920
  };
921
  std::vector<OptionalEvent> Events;
922
923
#if WASMEDGE_OS_LINUX | WASMEDGE_OS_MACOS
924
  struct FdData {
925
    OptionalEvent *ReadEvent = nullptr;
926
    OptionalEvent *WriteEvent = nullptr;
927
  };
928
  std::unordered_map<int, FdData> FdDatas;
929
  std::unordered_map<int, FdData> OldFdDatas;
930
#endif
931
932
#if WASMEDGE_OS_WINDOWS
933
  struct SocketData {
934
    OptionalEvent *ReadEvent = nullptr;
935
    OptionalEvent *WriteEvent = nullptr;
936
  };
937
  std::unordered_map<winapi::SOCKET_, SocketData> SocketDatas;
938
  winapi::FD_SET_ ReadFds = {0, {}}, WriteFds = {0, {}};
939
#endif
940
941
#if WASMEDGE_OS_LINUX
942
  friend class PollerContext;
943
  struct Timer : public FdHolder {
944
    Timer(const Timer &) = delete;
945
    Timer &operator=(const Timer &) = delete;
946
0
    Timer(Timer &&RHS) noexcept = default;
947
    Timer &operator=(Timer &&RHS) noexcept = default;
948
0
    constexpr Timer(__wasi_clockid_t C) noexcept : Clock(C) {}
949
950
    WasiExpect<void> create() noexcept;
951
952
    WasiExpect<void> setTime(__wasi_timestamp_t Timeout,
953
                             __wasi_timestamp_t Precision,
954
                             __wasi_subclockflags_t Flags) noexcept;
955
956
    __wasi_clockid_t Clock;
957
#if !__GLIBC_PREREQ(2, 8)
958
    FdHolder Notify;
959
    TimerHolder TimerId;
960
#endif
961
  };
962
963
  std::vector<Timer> Timers;
964
  std::vector<struct epoll_event> EPollEvents;
965
#endif
966
967
#if WASMEDGE_OS_MACOS
968
  std::vector<struct kevent> KEvents;
969
  uint64_t NextTimerId = 0;
970
#endif
971
#if WASMEDGE_OS_WINDOWS
972
  std::unordered_map<winapi::HANDLE_, OptionalEvent *> ConsoleReadEvent;
973
  std::unordered_map<winapi::HANDLE_, OptionalEvent *> ConsoleWriteEvent;
974
  OptionalEvent *TimeoutEvent = nullptr;
975
  winapi::TIMEVAL_ MinimumTimeout;
976
#endif
977
};
978
979
class PollerContext {
980
#if WASMEDGE_OS_LINUX
981
public:
982
  WasiExpect<Poller::Timer> acquireTimer(__wasi_clockid_t Clock) noexcept;
983
  void releaseTimer(Poller::Timer &&) noexcept;
984
985
private:
986
  std::mutex TimerMutex; ///< Protect TimerPool
987
  std::unordered_map<__wasi_clockid_t, std::vector<Poller::Timer>> TimerPool;
988
#else
989
#endif
990
};
991
992
} // namespace WASI
993
} // namespace Host
994
} // namespace WasmEdge