Coverage Report

Created: 2025-08-29 06:29

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