Coverage Report

Created: 2026-09-28 06:22

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