Coverage Report

Created: 2025-07-01 06:18

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