Coverage Report

Created: 2025-07-01 06:18

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