Coverage Report

Created: 2025-08-29 06:29

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