Coverage Report

Created: 2025-08-25 06:58

/src/WasmEdge/include/host/wasi/environ.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/hash.h"
9
#include "common/span.h"
10
#include "host/wasi/clock.h"
11
#include "host/wasi/error.h"
12
#include "host/wasi/vfs.h"
13
#include "host/wasi/vinode.h"
14
#include "wasi/api.hpp"
15
16
#include <algorithm>
17
#include <array>
18
#include <cstdint>
19
#include <memory>
20
#include <mutex>
21
#include <random>
22
#include <shared_mutex>
23
#include <string>
24
#include <string_view>
25
#include <unordered_map>
26
#include <utility>
27
#include <vector>
28
29
namespace WasmEdge {
30
namespace Host {
31
namespace WASI {
32
33
inline namespace detail {
34
inline constexpr const int32_t kIOVMax = 1024;
35
// Large enough to store SaData in sockaddr_in6
36
// = sizeof(sockaddr_in6) - sizeof(sockaddr_in6::sin6_family)
37
inline constexpr const int32_t kMaxSaDataLen = 26;
38
} // namespace detail
39
40
struct WasiAddrStorage {
41
  uint16_t AddressFamily;
42
  uint8_t Address[128 - sizeof(uint16_t)];
43
0
  __wasi_address_family_t getAddressFamily() const noexcept {
44
0
    return static_cast<__wasi_address_family_t>(AddressFamily);
45
0
  }
46
0
  void setAddressFamily(__wasi_address_family_t AddrFamily) noexcept {
47
0
    AddressFamily = static_cast<uint16_t>(AddrFamily);
48
0
  }
49
0
  Span<uint8_t> getAddress() noexcept { return Address; }
50
0
  Span<const uint8_t> getAddress() const noexcept { return Address; }
51
};
52
static_assert(sizeof(WasiAddrStorage) == 128, "wrong size");
53
54
class EVPoller;
55
class Environ : public PollerContext {
56
public:
57
  ~Environ() noexcept;
58
59
  void init(Span<const std::string> Dirs, std::string ProgramName,
60
            Span<const std::string> Args, Span<const std::string> Envs);
61
62
  WasiExpect<void> initWithFds(Span<const std::string> Dirs,
63
                               std::string ProgramName,
64
                               Span<const std::string> Args,
65
                               Span<const std::string> Envs, int32_t StdInFd,
66
                               int32_t StdOutFd, int32_t StdErrFd);
67
68
  void fini() noexcept;
69
70
  WasiExpect<void> getAddrInfo(std::string_view Node, std::string_view Service,
71
                               const __wasi_addrinfo_t &Hint,
72
                               uint32_t MaxResLength,
73
                               Span<__wasi_addrinfo_t *> WasiAddrinfoArray,
74
                               Span<__wasi_sockaddr_t *> WasiSockaddrArray,
75
                               Span<char *> AiAddrSaDataArray,
76
                               Span<char *> AiCanonnameArray,
77
0
                               /*Out*/ __wasi_size_t &ResLength) {
78
0
    return VINode::getAddrinfo(Node, Service, Hint, MaxResLength,
79
0
                               WasiAddrinfoArray, WasiSockaddrArray,
80
0
                               AiAddrSaDataArray, AiCanonnameArray, ResLength);
81
0
  }
82
83
0
  constexpr const std::vector<std::string> &getArguments() const noexcept {
84
0
    return Arguments;
85
0
  }
86
87
  constexpr const std::vector<std::string> &
88
0
  getEnvironVariables() const noexcept {
89
0
    return EnvironVariables;
90
0
  }
91
92
0
  constexpr __wasi_exitcode_t getExitCode() const noexcept { return ExitCode; }
93
94
  /// Read command-line argument data.
95
  ///
96
  /// The size of the array should match that returned by `args_sizes_get`.
97
  ///
98
  /// Each argument is expected to be `\0` terminated.
99
  ///
100
  /// @param[out] Argv Return the pointers to arguments.
101
  /// @param[out] ArgvBuffer Return the argument string data.
102
  /// @return Nothing or WASI error
103
  WasiExpect<void> argsGet(Span<uint8_t_ptr> Argv,
104
0
                           Span<uint8_t> ArgvBuffer) const noexcept {
105
0
    for (const auto &Argument : Arguments) {
106
0
      const __wasi_size_t Size = static_cast<__wasi_size_t>(Argument.size());
107
0
      std::copy_n(Argument.begin(), Size, ArgvBuffer.begin());
108
0
      ArgvBuffer[Size] = '\0';
109
0
      ArgvBuffer = ArgvBuffer.subspan(Size + UINT32_C(1));
110
0
      if (Argv.size() > 1) {
111
0
        Argv[1] = Argv[0] + Size + UINT32_C(1);
112
0
      }
113
0
      Argv = Argv.subspan(1);
114
0
    }
115
0
    assert(ArgvBuffer.empty());
116
0
    assert(Argv.empty());
117
118
0
    return {};
119
0
  }
120
121
  /// Return command-line argument data sizes.
122
  ///
123
  /// @param[out] Argc Return the number of arguments
124
  /// @param[out] ArgvSize Return the size of the argument string data
125
  /// @return Nothing or WASI error
126
  WasiExpect<void> argsSizesGet(__wasi_size_t &Argc,
127
0
                                __wasi_size_t &ArgvSize) const noexcept {
128
0
    Argc = static_cast<__wasi_size_t>(Arguments.size());
129
0
    ArgvSize = 0;
130
0
    for (const auto &Argument : Arguments) {
131
0
      ArgvSize += static_cast<__wasi_size_t>(Argument.size()) + UINT32_C(1);
132
0
    }
133
134
0
    return {};
135
0
  }
136
137
  /// Read environment variable data.
138
  ///
139
  /// The sizes of the buffers should match that returned by
140
  /// `environ_sizes_get`.
141
  ///
142
  /// Key/value pairs are expected to be joined with `=`s, and terminated with
143
  /// `\0`s.
144
  ///
145
  /// @param[out] Env Return the pointers to environment variables.
146
  /// @param[out] EnvBuffer Return the environment variable data.
147
  /// @return Nothing or WASI error
148
  WasiExpect<void> environGet(Span<uint8_t_ptr> Env,
149
0
                              Span<uint8_t> EnvBuffer) const noexcept {
150
0
    for (const auto &EnvironVariable : EnvironVariables) {
151
0
      const __wasi_size_t Size =
152
0
          static_cast<__wasi_size_t>(EnvironVariable.size());
153
0
      std::copy_n(EnvironVariable.begin(), Size, EnvBuffer.begin());
154
0
      EnvBuffer[Size] = '\0';
155
0
      EnvBuffer = EnvBuffer.subspan(Size + UINT32_C(1));
156
0
      if (Env.size() > 1) {
157
0
        Env[1] = Env[0] + Size + UINT32_C(1);
158
0
      }
159
0
      Env = Env.subspan(1);
160
0
    }
161
0
    assert(EnvBuffer.empty());
162
0
    assert(Env.empty());
163
164
0
    return {};
165
0
  }
166
167
  /// Return environment variable data sizes.
168
  ///
169
  /// @param[out] Envc Returns the number of environment variable arguments
170
  /// @param[out] EnvSize Return the size of the environment variable data.
171
  /// @return Nothing or WASI error
172
  WasiExpect<void> environSizesGet(__wasi_size_t &Envc,
173
0
                                   __wasi_size_t &EnvSize) const noexcept {
174
0
    Envc = static_cast<__wasi_size_t>(EnvironVariables.size());
175
0
    EnvSize = 0;
176
0
    for (const auto &EnvironVariable : EnvironVariables) {
177
0
      EnvSize +=
178
0
          static_cast<__wasi_size_t>(EnvironVariable.size()) + UINT32_C(1);
179
0
    }
180
181
0
    return {};
182
0
  }
183
184
  /// Return the resolution of a clock.
185
  ///
186
  /// Implementations are required to provide a non-zero value for supported
187
  /// clocks. For unsupported clocks, return `errno::inval`.
188
  ///
189
  /// @param[in] Id The clock for which to return the resolution.
190
  /// @param[out] Resolution The resolution of the clock.
191
  /// @return Nothing or WASI error
192
  static WasiExpect<void> clockResGet(__wasi_clockid_t Id,
193
0
                                      __wasi_timestamp_t &Resolution) noexcept {
194
0
    return Clock::clockResGet(Id, Resolution);
195
0
  }
196
197
  /// Return the time value of a clock.
198
  ///
199
  /// Note: This is similar to `clock_gettime` in POSIX.
200
  ///
201
  /// @param[in] Id The clock for which to return the time.
202
  /// @param[in] Precision The maximum lag (exclusive) that the returned time
203
  /// value may have, compared to its actual value.
204
  /// @param[out] Time The time value of the clock.
205
  /// @return Nothing or WASI error
206
  static WasiExpect<void> clockTimeGet(__wasi_clockid_t Id,
207
                                       __wasi_timestamp_t Precision,
208
0
                                       __wasi_timestamp_t &Time) noexcept {
209
0
    return Clock::clockTimeGet(Id, Precision, Time);
210
0
  }
211
212
  /// Provide file advisory information on a file descriptor.
213
  ///
214
  /// Note: This is similar to `posix_fadvise` in POSIX.
215
  ///
216
  /// @param[in] Fd The file descriptor.
217
  /// @param[in] Offset The offset within the file to which the advisory
218
  /// applies.
219
  /// @param[in] Len The length of the region to which the advisory applies.
220
  /// @param[in] Advice The advice.
221
  /// @return Nothing or WASI error
222
  WasiExpect<void> fdAdvise(__wasi_fd_t Fd, __wasi_filesize_t Offset,
223
                            __wasi_filesize_t Len,
224
0
                            __wasi_advice_t Advice) const noexcept {
225
0
    auto Node = getNodeOrNull(Fd);
226
0
    if (unlikely(!Node)) {
227
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
228
0
    }
229
0
    return Node->fdAdvise(Offset, Len, Advice);
230
0
  }
231
232
  /// Force the allocation of space in a file.
233
  ///
234
  /// Note: This is similar to `posix_fallocate` in POSIX.
235
  ///
236
  /// @param[in] Offset The offset at which to start the allocation.
237
  /// @param[in] Len The length of the area that is allocated.
238
  /// @return Nothing or WASI error
239
  WasiExpect<void> fdAllocate(__wasi_fd_t Fd, __wasi_filesize_t Offset,
240
0
                              __wasi_filesize_t Len) const noexcept {
241
0
    auto Node = getNodeOrNull(Fd);
242
0
    if (unlikely(!Node)) {
243
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
244
0
    }
245
0
    return Node->fdAllocate(Offset, Len);
246
0
  }
247
248
  /// Close a file descriptor.
249
  ///
250
  /// Note: This is similar to `close` in POSIX.
251
  ///
252
  /// @return Nothing or WASI error
253
0
  WasiExpect<void> fdClose(__wasi_fd_t Fd) noexcept {
254
0
    std::unique_lock Lock(FdMutex);
255
0
    if (auto It = FdMap.find(Fd); It == FdMap.end()) {
256
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
257
0
    } else {
258
0
      close(It->second);
259
0
      FdMap.erase(It);
260
0
      return {};
261
0
    }
262
0
  }
263
264
  /// Synchronize the data of a file to disk.
265
  ///
266
  /// Note: This is similar to `fdatasync` in POSIX.
267
  ///
268
  /// @return Nothing or WASI error
269
0
  WasiExpect<void> fdDatasync(__wasi_fd_t Fd) const noexcept {
270
0
    auto Node = getNodeOrNull(Fd);
271
0
    if (unlikely(!Node)) {
272
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
273
0
    }
274
0
    return Node->fdDatasync();
275
0
  }
276
277
  /// Get the attributes of a file descriptor.
278
  ///
279
  /// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well
280
  ///
281
  /// as additional fields.
282
  /// @param[out] FdStat Result.
283
  /// @return Nothing or WASI error
284
  WasiExpect<void> fdFdstatGet(__wasi_fd_t Fd,
285
0
                               __wasi_fdstat_t &FdStat) const noexcept {
286
0
    auto Node = getNodeOrNull(Fd);
287
0
    if (unlikely(!Node)) {
288
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
289
0
    }
290
0
    return Node->fdFdstatGet(FdStat);
291
0
  }
292
293
  /// Adjust the flags associated with a file descriptor.
294
  ///
295
  /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
296
  ///
297
  /// @param[in] FdFlags The desired values of the file descriptor flags.
298
  /// @return Nothing or WASI error
299
  WasiExpect<void> fdFdstatSetFlags(__wasi_fd_t Fd,
300
0
                                    __wasi_fdflags_t FdFlags) const noexcept {
301
0
    auto Node = getNodeOrNull(Fd);
302
0
    if (unlikely(!Node)) {
303
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
304
0
    }
305
0
    return Node->fdFdstatSetFlags(FdFlags);
306
0
  }
307
308
  /// Adjust the rights associated with a file descriptor.
309
  ///
310
  /// This can only be used to remove rights, and returns `errno::notcapable` if
311
  /// called in a way that would attempt to add rights
312
  ///
313
  /// @param[in] FsRightsBase The desired rights of the file descriptor.
314
  /// @param[in] FsRightsInheriting The desired rights of the file descriptor.
315
  /// @return Nothing or WASI error
316
  WasiExpect<void>
317
  fdFdstatSetRights(__wasi_fd_t Fd, __wasi_rights_t FsRightsBase,
318
0
                    __wasi_rights_t FsRightsInheriting) noexcept {
319
0
    auto Node = getNodeOrNull(Fd);
320
0
    if (unlikely(!Node)) {
321
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
322
0
    }
323
0
    return Node->fdFdstatSetRights(FsRightsBase, FsRightsInheriting);
324
0
  }
325
326
  /// Return the attributes of an open file.
327
  ///
328
  /// @param[out] Filestat Result.
329
  /// @return Nothing or WASI error
330
  WasiExpect<void> fdFilestatGet(__wasi_fd_t Fd,
331
0
                                 __wasi_filestat_t &Filestat) const noexcept {
332
0
    auto Node = getNodeOrNull(Fd);
333
0
    if (unlikely(!Node)) {
334
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
335
0
    }
336
0
    return Node->fdFilestatGet(Filestat);
337
0
  }
338
339
  /// Adjust the size of an open file. If this increases the file's size, the
340
  /// extra bytes are filled with zeros.
341
  ///
342
  /// Note: This is similar to `ftruncate` in POSIX.
343
  ///
344
  /// @param[in] Size The desired file size.
345
  /// @return Nothing or WASI error
346
  WasiExpect<void> fdFilestatSetSize(__wasi_fd_t Fd,
347
0
                                     __wasi_filesize_t Size) const noexcept {
348
0
    auto Node = getNodeOrNull(Fd);
349
0
    if (unlikely(!Node)) {
350
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
351
0
    }
352
0
    return Node->fdFilestatSetSize(Size);
353
0
  }
354
355
  /// Adjust the timestamps of an open file or directory.
356
  ///
357
  /// Note: This is similar to `futimens` in POSIX.
358
  ///
359
  /// @param[in] ATim The desired values of the data access timestamp.
360
  /// @param[in] MTim The desired values of the data modification timestamp.
361
  /// @param[in] FstFlags A bitmask indicating which timestamps to adjust.
362
  /// @return Nothing or WASI error
363
  WasiExpect<void>
364
  fdFilestatSetTimes(__wasi_fd_t Fd, __wasi_timestamp_t ATim,
365
                     __wasi_timestamp_t MTim,
366
0
                     __wasi_fstflags_t FstFlags) const noexcept {
367
0
    auto Node = getNodeOrNull(Fd);
368
0
    if (unlikely(!Node)) {
369
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
370
0
    }
371
0
    return Node->fdFilestatSetTimes(ATim, MTim, FstFlags);
372
0
  }
373
374
  /// Read from a file descriptor, without using and updating the file
375
  /// descriptor's offset.
376
  ///
377
  /// Note: This is similar to `preadv` in POSIX.
378
  ///
379
  /// @param[in] IOVs List of scatter/gather vectors in which to store data.
380
  /// @param[in] Offset The offset within the file at which to read.
381
  /// @param[out] NRead The number of bytes read.
382
  /// @return Nothing or WASI error
383
  WasiExpect<void> fdPread(__wasi_fd_t Fd, Span<Span<uint8_t>> IOVs,
384
                           __wasi_filesize_t Offset,
385
0
                           __wasi_size_t &NRead) const noexcept {
386
0
    auto Node = getNodeOrNull(Fd);
387
0
    if (unlikely(!Node)) {
388
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
389
0
    }
390
0
    return Node->fdPread(IOVs, Offset, NRead);
391
0
  }
392
393
  /// Return a description of the given preopened file descriptor.
394
  ///
395
  /// @param[out] PreStat The buffer where the description is stored.
396
  /// @return Nothing or WASI error
397
  WasiExpect<void> fdPrestatGet(__wasi_fd_t Fd,
398
0
                                __wasi_prestat_t &PreStat) const noexcept {
399
0
    auto Node = getNodeOrNull(Fd);
400
0
    if (unlikely(!Node)) {
401
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
402
0
    }
403
0
    if (const auto &Path = Node->name(); Path.empty()) {
404
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
405
0
    } else {
406
0
      PreStat.tag = __WASI_PREOPENTYPE_DIR;
407
0
      PreStat.u.dir.pr_name_len = static_cast<__wasi_size_t>(Path.size());
408
0
    }
409
0
    return {};
410
0
  }
411
412
  /// Return a description of the given preopened file descriptor.
413
  ///
414
  /// @param[out] Buffer A buffer into which to write the preopened directory
415
  /// name.
416
  /// @return Nothing or WASI error
417
  WasiExpect<void> fdPrestatDirName(__wasi_fd_t Fd,
418
0
                                    Span<uint8_t> Buffer) const noexcept {
419
0
    auto Node = getNodeOrNull(Fd);
420
0
    if (unlikely(!Node)) {
421
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
422
0
    }
423
0
    if (const auto &Path = Node->name(); Path.empty()) {
424
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
425
0
    } else if (Buffer.size() < Path.size()) {
426
0
      return WasiUnexpect(__WASI_ERRNO_NAMETOOLONG);
427
0
    } else {
428
0
      std::copy_n(Path.begin(), Path.size(), Buffer.begin());
429
0
    }
430
0
    return {};
431
0
  }
432
433
  /// Write to a file descriptor, without using and updating the file
434
  /// descriptor's offset.
435
  ///
436
  /// Note: This is similar to `pwritev` in POSIX.
437
  ///
438
  /// @param[in] IOVs List of scatter/gather vectors from which to retrieve
439
  /// data.
440
  /// @param[in] Offset The offset within the file at which to write.
441
  /// @param[out] NWritten The number of bytes written.
442
  /// @return Nothing or WASI error
443
  WasiExpect<void> fdPwrite(__wasi_fd_t Fd, Span<Span<const uint8_t>> IOVs,
444
                            __wasi_filesize_t Offset,
445
0
                            __wasi_size_t &NWritten) const noexcept {
446
0
    auto Node = getNodeOrNull(Fd);
447
0
    if (unlikely(!Node)) {
448
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
449
0
    }
450
0
    return Node->fdPwrite(IOVs, Offset, NWritten);
451
0
  }
452
453
  /// Read from a file descriptor.
454
  ///
455
  /// Note: This is similar to `readv` in POSIX.
456
  ///
457
  /// @param[in] IOVs List of scatter/gather vectors to which to store data.
458
  /// @param[out] NRead The number of bytes read.
459
  /// @return Nothing or WASI error
460
  WasiExpect<void> fdRead(__wasi_fd_t Fd, Span<Span<uint8_t>> IOVs,
461
0
                          __wasi_size_t &NRead) const noexcept {
462
0
    auto Node = getNodeOrNull(Fd);
463
0
    if (unlikely(!Node)) {
464
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
465
0
    }
466
0
    return Node->fdRead(IOVs, NRead);
467
0
  }
468
469
  /// Read directory entries from a directory.
470
  ///
471
  /// When successful, the contents of the output buffer consist of a sequence
472
  /// of directory entries. Each directory entry consists of a `dirent` object,
473
  /// followed by `dirent::d_namlen` bytes holding the name of the directory
474
  /// entry.
475
  ///
476
  /// This function fills the output buffer as much as possible,
477
  /// potentially truncating the last directory entry. This allows the caller to
478
  /// grow its read buffer size in case it's too small to fit a single large
479
  /// directory entry, or skip the oversized directory entry.
480
  ///
481
  /// @param[out] Buffer The buffer where directory entries are stored.
482
  /// @param[in] Cookie The location within the directory to start reading
483
  /// @param[out] Size The number of bytes stored in the read buffer. If less
484
  /// than the size of the read buffer, the end of the directory has been
485
  /// reached.
486
  /// @return Nothing or WASI error
487
  WasiExpect<void> fdReaddir(__wasi_fd_t Fd, Span<uint8_t> Buffer,
488
                             __wasi_dircookie_t Cookie,
489
0
                             __wasi_size_t &Size) noexcept {
490
0
    auto Node = getNodeOrNull(Fd);
491
0
    if (unlikely(!Node)) {
492
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
493
0
    }
494
0
    return Node->fdReaddir(Buffer, Cookie, Size);
495
0
  }
496
497
  /// Atomically replace a file descriptor by renumbering another file
498
  /// descriptor.
499
  ///
500
  /// Due to the strong focus on thread safety, this environment does not
501
  /// provide a mechanism to duplicate or renumber a file descriptor to an
502
  /// arbitrary number, like `dup2()`. This would be prone to race conditions,
503
  /// as an actual file descriptor with the same number could be allocated by a
504
  /// different thread at the same time.
505
  ///
506
  /// This function provides a way to atomically renumber file descriptors,
507
  /// which would disappear if `dup2()` were to be removed entirely.
508
  ///
509
  /// @param[in] To The file descriptor to overwrite.
510
  /// @return Nothing or WASI error
511
0
  WasiExpect<void> fdRenumber(__wasi_fd_t Fd, __wasi_fd_t To) noexcept {
512
0
    std::unique_lock Lock(FdMutex);
513
0
    if (auto It = FdMap.find(Fd); It == FdMap.end()) {
514
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
515
0
    } else if (Fd == To) {
516
0
      return {};
517
0
    } else if (auto It2 = FdMap.find(To); It2 == FdMap.end()) {
518
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
519
0
    } else {
520
0
      FdMap.erase(It2);
521
0
      auto Node = FdMap.extract(It);
522
0
      Node.key() = To;
523
0
      FdMap.insert(std::move(Node));
524
0
      return {};
525
0
    }
526
0
  }
527
528
  /// Move the offset of a file descriptor.
529
  ///
530
  /// Note: This is similar to `lseek` in POSIX.
531
  ///
532
  /// @param[in] Offset The number of bytes to move.
533
  /// @param[in] Whence The base from which the offset is relative.
534
  /// @param[out] Size The new offset of the file descriptor, relative to the
535
  /// start of the file.
536
  /// @return Nothing or WASI error
537
  WasiExpect<void> fdSeek(__wasi_fd_t Fd, __wasi_filedelta_t Offset,
538
                          __wasi_whence_t Whence,
539
0
                          __wasi_filesize_t &Size) const noexcept {
540
0
    auto Node = getNodeOrNull(Fd);
541
0
    if (unlikely(!Node)) {
542
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
543
0
    }
544
0
    return Node->fdSeek(Offset, Whence, Size);
545
0
  }
546
547
  /// Synchronize the data and metadata of a file to disk.
548
  ///
549
  /// Note: This is similar to `fsync` in POSIX.
550
  ///
551
  /// @return Nothing or WASI error
552
0
  WasiExpect<void> fdSync(__wasi_fd_t Fd) const noexcept {
553
0
    auto Node = getNodeOrNull(Fd);
554
0
    if (unlikely(!Node)) {
555
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
556
0
    }
557
0
    return Node->fdSync();
558
0
  }
559
560
  /// Return the current offset of a file descriptor.
561
  ///
562
  /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
563
  ///
564
  /// @param[out] Size The current offset of the file descriptor, relative to
565
  /// the start of the file.
566
  /// @return Nothing or WASI error
567
  WasiExpect<void> fdTell(__wasi_fd_t Fd,
568
0
                          __wasi_filesize_t &Size) const noexcept {
569
0
    auto Node = getNodeOrNull(Fd);
570
0
    if (unlikely(!Node)) {
571
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
572
0
    }
573
0
    return Node->fdTell(Size);
574
0
  }
575
576
  /// Write to a file descriptor.
577
  ///
578
  /// Note: This is similar to `writev` in POSIX.
579
  ///
580
  /// @param[in] IOVs List of scatter/gather vectors from which to retrieve
581
  /// data.
582
  /// @param[out] NWritten The number of bytes written.
583
  /// @return Nothing or WASI error
584
  WasiExpect<void> fdWrite(__wasi_fd_t Fd, Span<Span<const uint8_t>> IOVs,
585
0
                           __wasi_size_t &NWritten) const noexcept {
586
0
    auto Node = getNodeOrNull(Fd);
587
0
    if (unlikely(!Node)) {
588
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
589
0
    }
590
0
    return Node->fdWrite(IOVs, NWritten);
591
0
  }
592
593
  /// Create a directory.
594
  ///
595
  /// Note: This is similar to `mkdirat` in POSIX.
596
  ///
597
  /// @param[in] Fd The working directory at which the resolution of the path
598
  /// starts.
599
  /// @param[in] Path The path at which to create the directory.
600
  /// @return Nothing or WASI error
601
0
  WasiExpect<void> pathCreateDirectory(__wasi_fd_t Fd, std::string_view Path) {
602
0
    if (!VINode::isPathValid(Path)) {
603
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
604
0
    }
605
0
    auto Node = getNodeOrNull(Fd);
606
0
    if (unlikely(!Node)) {
607
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
608
0
    }
609
0
    return VINode::pathCreateDirectory(std::move(Node), Path);
610
0
  }
611
612
  /// Return the attributes of a file or directory.
613
  ///
614
  /// Note: This is similar to `stat` in POSIX.
615
  ///
616
  /// @param[in] Fd The working directory at which the resolution of the path
617
  /// starts.
618
  /// @param[in] Path The path of the file or directory to inspect.
619
  /// @param[in] Flags Flags determining the method of how the path is resolved.
620
  /// @param[out] Filestat The buffer where the file's attributes are stored.
621
  /// @return Nothing or WASI error
622
  WasiExpect<void> pathFilestatGet(__wasi_fd_t Fd, std::string_view Path,
623
                                   __wasi_lookupflags_t Flags,
624
0
                                   __wasi_filestat_t &Filestat) {
625
0
    if (!VINode::isPathValid(Path)) {
626
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
627
0
    }
628
0
    auto Node = getNodeOrNull(Fd);
629
0
    if (unlikely(!Node)) {
630
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
631
0
    }
632
0
    return VINode::pathFilestatGet(std::move(Node), Path, Flags, Filestat);
633
0
  }
634
635
  /// Adjust the timestamps of a file or directory.
636
  ///
637
  /// Note: This is similar to `utimensat` in POSIX.
638
  ///
639
  /// @param[in] Fd The working directory at which the resolution of the path
640
  /// starts.
641
  /// @param[in] Path The path of the file or directory to inspect.
642
  /// @param[in] Flags Flags determining the method of how the path is resolved.
643
  /// @param[in] ATim The desired values of the data access timestamp.
644
  /// @param[in] MTim The desired values of the data modification timestamp.
645
  /// @param[in] FstFlags A bitmask indicating which timestamps to adjust.
646
  /// @return Nothing or WASI error
647
  WasiExpect<void> pathFilestatSetTimes(__wasi_fd_t Fd, std::string_view Path,
648
                                        __wasi_lookupflags_t Flags,
649
                                        __wasi_timestamp_t ATim,
650
                                        __wasi_timestamp_t MTim,
651
0
                                        __wasi_fstflags_t FstFlags) {
652
0
    if (!VINode::isPathValid(Path)) {
653
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
654
0
    }
655
0
    auto Node = getNodeOrNull(Fd);
656
0
    if (unlikely(!Node)) {
657
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
658
0
    }
659
0
    return VINode::pathFilestatSetTimes(std::move(Node), Path, Flags, ATim,
660
0
                                        MTim, FstFlags);
661
0
  }
662
663
  /// Create a hard link.
664
  ///
665
  /// Note: This is similar to `linkat` in POSIX.
666
  ///
667
  /// @param[in] Old The working directory at which the resolution of the old
668
  /// path starts.
669
  /// @param[in] OldPath The source path from which to link.
670
  /// @param[in] New The working directory at which the resolution of the new
671
  /// path starts.
672
  /// @param[in] NewPath The destination path at which to create the hard link.
673
  /// @param[in] LookupFlags Flags determining the method of how the path is
674
  /// resolved.
675
  /// @return Nothing or WASI error
676
  WasiExpect<void> pathLink(__wasi_fd_t Old, std::string_view OldPath,
677
                            __wasi_fd_t New, std::string_view NewPath,
678
0
                            __wasi_lookupflags_t LookupFlags) {
679
0
    if (!VINode::isPathValid(OldPath)) {
680
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
681
0
    }
682
0
    if (!VINode::isPathValid(NewPath)) {
683
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
684
0
    }
685
0
    auto OldNode = getNodeOrNull(Old);
686
0
    if (unlikely(!OldNode)) {
687
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
688
0
    }
689
0
    auto NewNode = getNodeOrNull(New);
690
0
    if (unlikely(!OldNode)) {
691
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
692
0
    }
693
0
    return VINode::pathLink(std::move(OldNode), OldPath, std::move(NewNode),
694
0
                            NewPath, LookupFlags);
695
0
  }
696
697
  /// Open a file or directory.
698
  ///
699
  /// The returned file descriptor is not guaranteed to be the lowest-numbered
700
  /// file descriptor not currently open; it is randomized to prevent
701
  /// applications from depending on making assumptions about indexes, since
702
  /// this is error-prone in multi-threaded contexts. The returned file
703
  /// descriptor is guaranteed to be less than 2**31.
704
  ///
705
  /// Note: This is similar to `openat` in POSIX.
706
  ///
707
  /// @param[in] Fd The working directory at which the resolution of the path
708
  /// starts.
709
  /// @param[in] Path The relative path of the file or directory to open,
710
  /// relative to the `path_open::fd` directory.
711
  /// @param[in] LookupFlags Flags determining the method of how the path is
712
  /// resolved.
713
  /// @param[in] OpenFlags The method by which to open the file.
714
  /// @param[in] FsRightsBase The initial rights of the newly created file
715
  /// descriptor. The implementation is allowed to return a file descriptor with
716
  /// fewer rights than specified, if and only if those rights do not apply to
717
  /// the type of file being opened. The *base* rights are rights that will
718
  /// apply to operations using the file descriptor itself.
719
  /// @param[in] FsRightsInheriting The initial rights of the newly created file
720
  /// descriptor. The implementation is allowed to return a file descriptor with
721
  /// fewer rights than specified, if and only if those rights do not apply to
722
  /// the type of file being opened. The *inheriting* rights are rights that
723
  /// apply to file descriptors derived from it.
724
  /// @param[in] FdFlags The method by which to open the file.
725
  /// @return The file descriptor of the file that has been opened, or WASI
726
  /// error.
727
  WasiExpect<__wasi_fd_t> pathOpen(__wasi_fd_t Fd, std::string_view Path,
728
                                   __wasi_lookupflags_t LookupFlags,
729
                                   __wasi_oflags_t OpenFlags,
730
                                   __wasi_rights_t FsRightsBase,
731
                                   __wasi_rights_t FsRightsInheriting,
732
0
                                   __wasi_fdflags_t FdFlags) {
733
0
    if (!VINode::isPathValid(Path)) {
734
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
735
0
    }
736
0
    auto Node = getNodeOrNull(Fd);
737
0
    if (unlikely(!Node)) {
738
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
739
0
    }
740
0
    EXPECTED_TRY(Node,
741
0
                 VINode::pathOpen(std::move(Node), Path, LookupFlags, OpenFlags,
742
0
                                  FsRightsBase, FsRightsInheriting, FdFlags));
743
744
0
    return generateRandomFdToNode(Node);
745
0
  }
746
747
  /// Read the contents of a symbolic link.
748
  ///
749
  /// Note: This is similar to `readlinkat` in POSIX.
750
  ///
751
  /// @param[in] Fd The working directory at which the resolution of the path
752
  /// starts.
753
  /// @param[in] Path The path of the symbolic link from which to read.
754
  /// @param[out] Buffer The buffer to which to write the contents of the
755
  /// symbolic link.
756
  /// @param[out] NRead The number of bytes read.
757
  /// @return Nothing or WASI error.
758
  WasiExpect<void> pathReadlink(__wasi_fd_t Fd, std::string_view Path,
759
0
                                Span<char> Buffer, __wasi_size_t &NRead) {
760
0
    if (!VINode::isPathValid(Path)) {
761
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
762
0
    }
763
0
    auto Node = getNodeOrNull(Fd);
764
0
    if (unlikely(!Node)) {
765
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
766
0
    }
767
0
    return VINode::pathReadlink(std::move(Node), Path, Buffer, NRead);
768
0
  }
769
770
  /// Remove a directory.
771
  ///
772
  /// Return `errno::notempty` if the directory is not empty.
773
  ///
774
  /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
775
  ///
776
  /// @param[in] Fd The working directory at which the resolution of the path
777
  /// starts.
778
  /// @param[in] Path The path to a directory to remove.
779
  /// @return Nothing or WASI error.
780
0
  WasiExpect<void> pathRemoveDirectory(__wasi_fd_t Fd, std::string_view Path) {
781
0
    if (!VINode::isPathValid(Path)) {
782
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
783
0
    }
784
0
    auto Node = getNodeOrNull(Fd);
785
0
    if (unlikely(!Node)) {
786
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
787
0
    }
788
0
    return VINode::pathRemoveDirectory(std::move(Node), Path);
789
0
  }
790
791
  /// Rename a file or directory.
792
  ///
793
  /// Note: This is similar to `renameat` in POSIX.
794
  ///
795
  /// @param[in] Old The working directory at which the resolution of the old
796
  /// path starts.
797
  /// @param[in] OldPath The source path of the file or directory to rename.
798
  /// @param[in] New The working directory at which the resolution of the new
799
  /// path starts.
800
  /// @param[in] NewPath The destination path to which to rename the file or
801
  /// directory.
802
  /// @return Nothing or WASI error.
803
  WasiExpect<void> pathRename(__wasi_fd_t Old, std::string_view OldPath,
804
0
                              __wasi_fd_t New, std::string_view NewPath) {
805
0
    if (!VINode::isPathValid(OldPath)) {
806
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
807
0
    }
808
0
    if (!VINode::isPathValid(NewPath)) {
809
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
810
0
    }
811
0
    auto OldNode = getNodeOrNull(Old);
812
0
    if (unlikely(!OldNode)) {
813
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
814
0
    }
815
0
    auto NewNode = getNodeOrNull(New);
816
0
    if (unlikely(!NewNode)) {
817
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
818
0
    }
819
0
    return VINode::pathRename(std::move(OldNode), OldPath, std::move(NewNode),
820
0
                              NewPath);
821
0
  }
822
823
  /// Create a symbolic link.
824
  ///
825
  /// Note: This is similar to `symlinkat` in POSIX.
826
  ///
827
  /// @param[in] OldPath The contents of the symbolic link.
828
  /// @param[in] New The working directory at which the resolution of the new
829
  /// path starts.
830
  /// @param[in] NewPath The destination path at which to create the symbolic
831
  /// link.
832
  /// @return Nothing or WASI error
833
  WasiExpect<void> pathSymlink(std::string_view OldPath, __wasi_fd_t New,
834
0
                               std::string_view NewPath) {
835
0
    if (!VINode::isPathValid(OldPath)) {
836
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
837
0
    }
838
0
    if (!VINode::isPathValid(NewPath)) {
839
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
840
0
    }
841
    // forbid absolute path
842
0
    if (!OldPath.empty() && OldPath[0] == '/') {
843
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
844
0
    }
845
0
    auto NewNode = getNodeOrNull(New);
846
0
    if (unlikely(!NewNode)) {
847
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
848
0
    }
849
0
    return VINode::pathSymlink(OldPath, std::move(NewNode), NewPath);
850
0
  }
851
852
  /// Unlink a file.
853
  ///
854
  /// Return `errno::isdir` if the path refers to a directory.
855
  ///
856
  /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
857
  ///
858
  /// @param[in] Fd The working directory at which the resolution of the path
859
  /// starts.
860
  /// @param[in] Path The path to a file to unlink.
861
  /// @return Nothing or WASI error.
862
0
  WasiExpect<void> pathUnlinkFile(__wasi_fd_t Fd, std::string_view Path) {
863
0
    if (!VINode::isPathValid(Path)) {
864
0
      return WasiUnexpect(__WASI_ERRNO_INVAL);
865
0
    }
866
0
    auto Node = getNodeOrNull(Fd);
867
0
    if (unlikely(!Node)) {
868
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
869
0
    }
870
0
    return VINode::pathUnlinkFile(std::move(Node), Path);
871
0
  }
872
873
  /// Acquire a Poller for concurrently poll for the occurrence of a set of
874
  /// events.
875
  ///
876
  /// @param[in] Events The output buffer for events.
877
  /// @return Poll helper or WASI error.
878
  WasiExpect<EVPoller> acquirePoller(Span<__wasi_event_t> Events) noexcept;
879
880
  /// Release a used Poller object.
881
  ///
882
  /// @param[in] Poller Used poller object.
883
  void releasePoller(EVPoller &&Poller) noexcept;
884
885
  /// Close unused Fd in Pollers.
886
  ///
887
  /// @param[in] Node The Node to be deleted.
888
  void close(std::shared_ptr<VINode> Node) noexcept;
889
890
  /// Terminate the process normally. An exit code of 0 indicates successful
891
  /// termination of the program. The meanings of other values is dependent on
892
  /// the environment.
893
  ///
894
  /// @param[in] Code The exit code returned by the process.
895
0
  void procExit(__wasi_exitcode_t Code) noexcept { ExitCode = Code; }
896
897
  /// Send a signal to the process of the calling thread.
898
  ///
899
  /// Note: This is similar to `raise` in POSIX.
900
  ///
901
  /// @param[in] Signal The signal condition to trigger.
902
  /// @return Nothing or WASI error
903
  WasiExpect<void> procRaise(__wasi_signal_t Signal) const noexcept;
904
905
  /// Temporarily yield execution of the calling thread.
906
  ///
907
  /// Note: This is similar to `sched_yield` in POSIX.
908
  ///
909
  /// @return Nothing or WASI error
910
  WasiExpect<void> schedYield() const noexcept;
911
912
  /// Write high-quality random data into a buffer.
913
  ///
914
  /// This function blocks when the implementation is unable to immediately
915
  /// provide sufficient high-quality random data.
916
  ///
917
  /// This function may execute slowly, so when large mounts of random data are
918
  /// required, it's advisable to use this function to seed a pseudo-random
919
  /// number generator, rather than to provide the random data directly.
920
  ///
921
  /// @param[out] Buffer The buffer to fill with random data.
922
  /// @return Nothing or WASI error
923
0
  WasiExpect<void> randomGet(Span<uint8_t> Buffer) const noexcept {
924
0
    std::uniform_int_distribution<uint32_t> Distribution;
925
0
    auto BufferSpan = cxx20::as_writable_bytes(Buffer);
926
0
    while (!BufferSpan.empty()) {
927
0
      const uint32_t Value = Distribution(Hash::RandEngine);
928
0
      const auto ValueSpan =
929
0
          cxx20::as_bytes(cxx20::span<const uint32_t, 1>(&Value, 1));
930
0
      const auto Size = std::min(BufferSpan.size(), ValueSpan.size());
931
0
      std::copy_n(ValueSpan.begin(), Size, BufferSpan.begin());
932
0
      BufferSpan = BufferSpan.subspan(Size);
933
0
    }
934
935
0
    return {};
936
0
  }
937
938
  WasiExpect<__wasi_fd_t> sockOpen(__wasi_address_family_t AddressFamily,
939
0
                                   __wasi_sock_type_t SockType) noexcept {
940
941
0
    std::shared_ptr<VINode> Node;
942
0
    if (auto Res = VINode::sockOpen(AddressFamily, SockType); unlikely(!Res)) {
943
0
      return WasiUnexpect(Res);
944
0
    } else {
945
0
      Node = std::move(*Res);
946
0
    }
947
948
0
    return generateRandomFdToNode(Node);
949
0
  }
950
951
  WasiExpect<void> sockBind(__wasi_fd_t Fd,
952
                            __wasi_address_family_t AddressFamily,
953
                            Span<const uint8_t> Address,
954
0
                            uint16_t Port) noexcept {
955
0
    auto Node = getNodeOrNull(Fd);
956
0
    if (unlikely(!Node)) {
957
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
958
0
    }
959
0
    return Node->sockBind(AddressFamily, Address, Port);
960
0
  }
961
962
0
  WasiExpect<void> sockListen(__wasi_fd_t Fd, int32_t Backlog) noexcept {
963
0
    auto Node = getNodeOrNull(Fd);
964
0
    if (unlikely(!Node)) {
965
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
966
0
    }
967
0
    return Node->sockListen(Backlog);
968
0
  }
969
970
  WasiExpect<__wasi_fd_t> sockAccept(__wasi_fd_t Fd,
971
0
                                     __wasi_fdflags_t FdFlags) noexcept {
972
0
    auto Node = getNodeOrNull(Fd);
973
0
    if (unlikely(!Node)) {
974
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
975
0
    }
976
977
0
    EXPECTED_TRY(auto NewNode, Node->sockAccept(FdFlags));
978
979
0
    return generateRandomFdToNode(NewNode);
980
0
  }
981
982
  WasiExpect<void> sockConnect(__wasi_fd_t Fd,
983
                               __wasi_address_family_t AddressFamily,
984
                               Span<const uint8_t> Address,
985
0
                               uint16_t Port) noexcept {
986
0
    auto Node = getNodeOrNull(Fd);
987
0
    if (unlikely(!Node)) {
988
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
989
0
    }
990
0
    return Node->sockConnect(AddressFamily, Address, Port);
991
0
  }
992
993
  /// Receive a message from a socket.
994
  ///
995
  /// Note: This is similar to `recv` in POSIX, though it also supports reading
996
  /// the data into multiple buffers in the manner of `readv`.
997
  ///
998
  /// @param[in] RiData List of scatter/gather vectors to which to store data.
999
  /// @param[in] RiFlags Message flags.
1000
  /// @param[out] NRead Return the number of bytes stored in RiData.
1001
  /// @param[out] RoFlags Return message flags.
1002
  /// @return Nothing or WASI error.
1003
  WasiExpect<void> sockRecv(__wasi_fd_t Fd, Span<Span<uint8_t>> RiData,
1004
                            __wasi_riflags_t RiFlags, __wasi_size_t &NRead,
1005
0
                            __wasi_roflags_t &RoFlags) const noexcept {
1006
0
    auto Node = getNodeOrNull(Fd);
1007
0
    if (unlikely(!Node)) {
1008
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1009
0
    }
1010
0
    return Node->sockRecv(RiData, RiFlags, NRead, RoFlags);
1011
0
  }
1012
1013
  /// Receive a message from a socket.
1014
  ///
1015
  /// Note: This is similar to `recvfrom` in POSIX, though it also supports
1016
  /// reading the data into multiple buffers in the manner of `readv`.
1017
  ///
1018
  /// @param[in] RiData List of scatter/gather vectors to which to store data.
1019
  /// @param[in] RiFlags Message flags.
1020
  /// @param[out] AddressFamilyPtr The pointer to store address family.
1021
  /// @param[out] Address The buffer to store address.
1022
  /// @param[out] PortPtr The pointer to store port.
1023
  /// @param[out] NRead Return the number of bytes stored in RiData.
1024
  /// @param[out] RoFlags Return message flags.
1025
  /// @return Nothing or WASI error.
1026
  WasiExpect<void> sockRecvFrom(__wasi_fd_t Fd, Span<Span<uint8_t>> RiData,
1027
                                __wasi_riflags_t RiFlags,
1028
                                __wasi_address_family_t *AddressFamilyPtr,
1029
                                Span<uint8_t> Address, uint16_t *PortPtr,
1030
                                __wasi_size_t &NRead,
1031
0
                                __wasi_roflags_t &RoFlags) const noexcept {
1032
0
    auto Node = getNodeOrNull(Fd);
1033
0
    if (unlikely(!Node)) {
1034
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1035
0
    }
1036
0
    return Node->sockRecvFrom(RiData, RiFlags, AddressFamilyPtr, Address,
1037
0
                              PortPtr, NRead, RoFlags);
1038
0
  }
1039
1040
  /// Send a message on a socket.
1041
  ///
1042
  /// Note: This is similar to `send` in POSIX, though it also supports writing
1043
  /// the data from multiple buffers in the manner of `writev`.
1044
  ///
1045
  /// @param[in] SiData List of scatter/gather vectors to which to retrieve
1046
  /// data.
1047
  /// @param[in] SiFlags Message flags.
1048
  /// @param[out] NWritten The number of bytes transmitted.
1049
  /// @return Nothing or WASI error
1050
  WasiExpect<void> sockSend(__wasi_fd_t Fd, Span<Span<const uint8_t>> SiData,
1051
                            __wasi_siflags_t SiFlags,
1052
0
                            __wasi_size_t &NWritten) const noexcept {
1053
0
    auto Node = getNodeOrNull(Fd);
1054
0
    if (unlikely(!Node)) {
1055
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1056
0
    }
1057
0
    return Node->sockSend(SiData, SiFlags, NWritten);
1058
0
  }
1059
1060
  /// Send a message on a socket.
1061
  ///
1062
  /// Note: This is similar to `sendto` in POSIX, though it also supports
1063
  /// writing the data from multiple buffers in the manner of `writev`.
1064
  ///
1065
  /// @param[in] SiData List of scatter/gather vectors to which to retrieve
1066
  /// data.
1067
  /// @param[in] SiFlags Message flags.
1068
  /// @param[in] AddressFamily Address family of the target.
1069
  /// @param[in] Address Address of the target.
1070
  /// @param[in] Port Connected port.
1071
  /// @param[out] NWritten The number of bytes transmitted.
1072
  /// @return Nothing or WASI error
1073
  WasiExpect<void> sockSendTo(__wasi_fd_t Fd, Span<Span<const uint8_t>> SiData,
1074
                              __wasi_siflags_t SiFlags,
1075
                              __wasi_address_family_t AddressFamily,
1076
                              Span<const uint8_t> Address, uint16_t Port,
1077
0
                              __wasi_size_t &NWritten) const noexcept {
1078
0
    auto Node = getNodeOrNull(Fd);
1079
0
    if (unlikely(!Node)) {
1080
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1081
0
    }
1082
0
    return Node->sockSendTo(SiData, SiFlags, AddressFamily, Address, Port,
1083
0
                            NWritten);
1084
0
  }
1085
1086
  /// Shut down socket send and receive channels.
1087
  ///
1088
  /// Note: This is similar to `shutdown` in POSIX.
1089
  ///
1090
  /// @param[in] SdFlags Which channels on the socket to shut down.
1091
  /// @return Nothing or WASI error
1092
  WasiExpect<void> sockShutdown(__wasi_fd_t Fd,
1093
0
                                __wasi_sdflags_t SdFlags) const noexcept {
1094
0
    auto Node = getNodeOrNull(Fd);
1095
0
    if (unlikely(!Node)) {
1096
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1097
0
    }
1098
0
    return Node->sockShutdown(SdFlags);
1099
0
  }
1100
1101
  WasiExpect<void> sockGetOpt(__wasi_fd_t Fd,
1102
                              __wasi_sock_opt_level_t SockOptLevel,
1103
                              __wasi_sock_opt_so_t SockOptName,
1104
0
                              Span<uint8_t> &Flag) const noexcept {
1105
0
    auto Node = getNodeOrNull(Fd);
1106
0
    if (unlikely(!Node)) {
1107
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1108
0
    }
1109
0
    return Node->sockGetOpt(SockOptLevel, SockOptName, Flag);
1110
0
  }
1111
1112
  WasiExpect<void> sockSetOpt(__wasi_fd_t Fd,
1113
                              __wasi_sock_opt_level_t SockOptLevel,
1114
                              __wasi_sock_opt_so_t SockOptName,
1115
0
                              Span<const uint8_t> Flag) const noexcept {
1116
0
    auto Node = getNodeOrNull(Fd);
1117
0
    if (unlikely(!Node)) {
1118
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1119
0
    }
1120
0
    return Node->sockSetOpt(SockOptLevel, SockOptName, Flag);
1121
0
  }
1122
1123
  /// Return the address and port of the file descriptor.
1124
  WasiExpect<void> sockGetLocalAddr(__wasi_fd_t Fd,
1125
                                    __wasi_address_family_t *AddressFamilyPtr,
1126
                                    Span<uint8_t> Address,
1127
0
                                    uint16_t *PortPtr) const noexcept {
1128
0
    auto Node = getNodeOrNull(Fd);
1129
0
    if (unlikely(!Node)) {
1130
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1131
0
    }
1132
0
    return Node->sockGetLocalAddr(AddressFamilyPtr, Address, PortPtr);
1133
0
  }
1134
1135
  /// Retrieve the remote address and port from the given file descriptor.
1136
  WasiExpect<void> sockGetPeerAddr(__wasi_fd_t Fd,
1137
                                   __wasi_address_family_t *AddressFamilyPtr,
1138
                                   Span<uint8_t> Address,
1139
0
                                   uint16_t *PortPtr) const noexcept {
1140
0
    auto Node = getNodeOrNull(Fd);
1141
0
    if (unlikely(!Node)) {
1142
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1143
0
    }
1144
0
    return Node->sockGetPeerAddr(AddressFamilyPtr, Address, PortPtr);
1145
0
  }
1146
1147
0
  WasiExpect<uint64_t> getNativeHandler(__wasi_fd_t Fd) const noexcept {
1148
0
    auto Node = getNodeOrNull(Fd);
1149
0
    if (unlikely(!Node)) {
1150
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
1151
0
    }
1152
0
    return Node->getNativeHandler();
1153
0
  }
1154
1155
0
  static std::string randomFilename() noexcept {
1156
0
    using namespace std::literals;
1157
0
    static constexpr const auto Charset =
1158
0
        "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"sv;
1159
0
    std::uniform_int_distribution<uint32_t> Distribution(
1160
0
        0, static_cast<uint32_t>(Charset.size() - 1));
1161
0
    std::array<char, 8> Buffer;
1162
0
    std::array<uint32_t, 2> Values = {Distribution(Hash::RandEngine),
1163
0
                                      Distribution(Hash::RandEngine)};
1164
0
    auto ValuesSpan = cxx20::as_bytes(cxx20::span(Values));
1165
0
    std::copy(ValuesSpan.begin(), ValuesSpan.end(),
1166
0
              cxx20::as_writable_bytes(cxx20::span(Buffer)).begin());
1167
0
    return std::string(Buffer.data(), Buffer.size());
1168
0
  }
1169
1170
private:
1171
  std::vector<std::string> Arguments;
1172
  std::vector<std::string> EnvironVariables;
1173
  __wasi_exitcode_t ExitCode = 0;
1174
1175
  mutable std::shared_mutex PollerMutex; ///< Protect PollerPool
1176
  std::vector<EVPoller> PollerPool;
1177
  friend class EVPoller;
1178
1179
  mutable std::shared_mutex FdMutex; ///< Protect FdMap
1180
  std::unordered_map<__wasi_fd_t, std::shared_ptr<VINode>> FdMap;
1181
1182
0
  std::shared_ptr<VINode> getNodeOrNull(__wasi_fd_t Fd) const {
1183
0
    std::shared_lock Lock(FdMutex);
1184
0
    if (auto It = FdMap.find(Fd); It != FdMap.end()) {
1185
0
      return It->second;
1186
0
    }
1187
0
    return {};
1188
0
  }
1189
1190
0
  WasiExpect<__wasi_fd_t> generateRandomFdToNode(std::shared_ptr<VINode> Node) {
1191
0
    std::uniform_int_distribution<__wasi_fd_t> Distribution(0, 0x7FFFFFFF);
1192
0
    bool Success = false;
1193
0
    __wasi_fd_t NewFd;
1194
0
    while (!Success) {
1195
0
      NewFd = Distribution(Hash::RandEngine);
1196
0
      std::unique_lock Lock(FdMutex);
1197
0
      Success = FdMap.emplace(NewFd, Node).second;
1198
0
    }
1199
0
    return NewFd;
1200
0
  }
1201
};
1202
1203
class EVPoller : protected VPoller {
1204
public:
1205
0
  EVPoller(EVPoller &&) = default;
1206
  EVPoller &operator=(EVPoller &&) = default;
1207
1208
  using VPoller::clock;
1209
  using VPoller::close;
1210
  using VPoller::error;
1211
  using VPoller::prepare;
1212
  using VPoller::reset;
1213
  using VPoller::result;
1214
  using VPoller::VPoller;
1215
  using VPoller::wait;
1216
1217
  /// Concurrently poll for a ready-to-read event.
1218
  ///
1219
  /// @param[in] Fd The file descriptor on which to wait for it to become ready
1220
  /// for reading.
1221
  /// @param[in] Trigger Specifying whether the notification is level-trigger or
1222
  /// edge-trigger.
1223
  /// @param[in] UserData User-provided value that may be attached to objects
1224
  /// that is retained when extracted from the implementation.
1225
  void read(__wasi_fd_t Fd, TriggerType Trigger,
1226
0
            __wasi_userdata_t UserData) noexcept {
1227
0
    if (auto Node = env().getNodeOrNull(Fd); unlikely(!Node)) {
1228
0
      VPoller::error(UserData, __WASI_ERRNO_BADF, __WASI_EVENTTYPE_FD_READ);
1229
0
    } else {
1230
0
      VPoller::read(Node, Trigger, UserData);
1231
0
    }
1232
0
  }
1233
1234
  /// Concurrently poll for a ready-to-write event.
1235
  ///
1236
  /// @param[in] Fd The file descriptor on which to wait for it to become ready
1237
  /// for writing.
1238
  /// @param[in] Trigger Specifying whether the notification is level-trigger or
1239
  /// edge-trigger.
1240
  /// @param[in] UserData User-provided value that may be attached to objects
1241
  /// that is retained when extracted from the implementation.
1242
  void write(__wasi_fd_t Fd, TriggerType Trigger,
1243
0
             __wasi_userdata_t UserData) noexcept {
1244
0
    if (auto Node = env().getNodeOrNull(Fd); unlikely(!Node)) {
1245
0
      VPoller::error(UserData, __WASI_ERRNO_BADF, __WASI_EVENTTYPE_FD_WRITE);
1246
0
    } else {
1247
0
      VPoller::write(Node, Trigger, UserData);
1248
0
    }
1249
0
  }
1250
1251
0
  void close(std::shared_ptr<VINode> Node) noexcept { VPoller::close(Node); }
1252
1253
private:
1254
0
  Environ &env() noexcept { return static_cast<Environ &>(Ctx.get()); }
1255
};
1256
1257
inline WasiExpect<EVPoller>
1258
0
Environ::acquirePoller(Span<__wasi_event_t> Events) noexcept {
1259
0
  auto Poller = [this]() noexcept -> EVPoller {
1260
0
    std::unique_lock Lock(PollerMutex);
1261
0
    if (PollerPool.empty()) {
1262
0
      return EVPoller(*this);
1263
0
    } else {
1264
0
      EVPoller Result(std::move(PollerPool.back()));
1265
0
      PollerPool.pop_back();
1266
0
      return Result;
1267
0
    }
1268
0
  }();
1269
1270
0
  if (auto Res = Poller.prepare(Events); !Res) {
1271
0
    return WasiUnexpect(Res);
1272
0
  }
1273
0
  return Poller;
1274
0
}
1275
1276
0
inline void Environ::close(std::shared_ptr<VINode> Node) noexcept {
1277
0
  std::unique_lock Lock(PollerMutex);
1278
0
  for (auto &Poller : PollerPool) {
1279
0
    Poller.close(Node);
1280
0
  }
1281
0
}
1282
1283
0
inline void Environ::releasePoller(EVPoller &&Poller) noexcept {
1284
0
  std::unique_lock Lock(PollerMutex);
1285
0
  PollerPool.push_back(std::move(Poller));
1286
0
}
1287
1288
} // namespace WASI
1289
} // namespace Host
1290
} // namespace WasmEdge