Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/host/wasi/vinode.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/filesystem.h"
7
#include "common/types.h"
8
#include "host/wasi/error.h"
9
#include "host/wasi/inode.h"
10
#include "host/wasi/vfs.h"
11
12
#include <cstdint>
13
#include <functional>
14
#include <memory>
15
#include <string>
16
#include <string_view>
17
#include <utility>
18
#include <vector>
19
20
namespace WasmEdge {
21
namespace Host {
22
namespace WASI {
23
24
class VPoller;
25
class VINode : public std::enable_shared_from_this<VINode> {
26
public:
27
  VINode(const VINode &) = delete;
28
  VINode &operator=(const VINode &) = delete;
29
  VINode(VINode &&) = default;
30
  VINode &operator=(VINode &&) = default;
31
32
  /// Create a VINode with a parent.
33
  ///
34
  /// @param[in] Node System INode.
35
  VINode(INode Node);
36
37
  /// Create an orphan VINode.
38
  ///
39
  /// @param[in] Node System INode.
40
  /// @param[in] FRB The desired rights of the VINode.
41
  /// @param[in] FRI The desired rights of the VINode.
42
  VINode(INode Node, __wasi_rights_t FRB, __wasi_rights_t FRI,
43
         std::string N = {});
44
45
  /// Check path is valid.
46
0
  static bool isPathValid(std::string_view Path) noexcept {
47
0
    return Path.find('\0') == std::string_view::npos;
48
0
  }
49
50
  /// Check whether a relative symlink target would resolve outside the base
51
  /// directory. Each component descends a level and each `..` ascends one; it
52
  /// escapes if the level ever drops below the base.
53
  /// @param[in] Target Symlink target (link contents).
54
  /// @param[in] Depth The link directory's depth below the base fd.
55
  /// @return Whether the target escapes the base directory.
56
  static bool isSymlinkTargetEscaping(std::string_view Target,
57
0
                                      uint32_t Depth) noexcept {
58
0
    int64_t Level = static_cast<int64_t>(Depth);
59
0
    std::string_view Path = Target;
60
0
    while (!Path.empty()) {
61
0
      const auto Slash = Path.find('/');
62
0
      const auto Part = Path.substr(0, Slash);
63
0
      Path = (Slash == std::string_view::npos) ? std::string_view()
64
0
                                               : Path.substr(Slash + 1);
65
0
      if (Part.empty() || (Part.size() == 1 && Part[0] == '.')) {
66
0
        continue;
67
0
      }
68
0
      if (Part.size() == 2 && Part[0] == '.' && Part[1] == '.') {
69
0
        if (--Level < 0) {
70
0
          return true;
71
0
        }
72
0
        continue;
73
0
      }
74
0
      ++Level;
75
0
    }
76
0
    return false;
77
0
  }
78
79
  static std::shared_ptr<VINode> stdIn(__wasi_rights_t FRB,
80
                                       __wasi_rights_t FRI);
81
  static std::shared_ptr<VINode> stdOut(__wasi_rights_t FRB,
82
                                        __wasi_rights_t FRI);
83
  static std::shared_ptr<VINode> stdErr(__wasi_rights_t FRB,
84
                                        __wasi_rights_t FRI);
85
  static WasiExpect<std::shared_ptr<VINode>>
86
  fromFd(int32_t Fd, __wasi_rights_t FRB, __wasi_rights_t FRI);
87
88
  static std::string canonicalGuest(std::string_view Path);
89
90
  static WasiExpect<std::shared_ptr<VINode>> bind(__wasi_rights_t FRB,
91
                                                  __wasi_rights_t FRI,
92
                                                  std::string Name,
93
                                                  std::string SystemPath);
94
95
0
  constexpr const std::string &name() const { return Name; }
96
97
  /// Provide file advisory information on a file descriptor.
98
  ///
99
  /// Note: This is similar to `posix_fadvise` in POSIX.
100
  ///
101
  /// @param[in] Offset The offset within the file to which the advisory
102
  /// applies.
103
  /// @param[in] Len The length of the region to which the advisory applies.
104
  /// @param[in] Advice The advice.
105
  /// @return Nothing or WASI error
106
  WasiExpect<void> fdAdvise(__wasi_filesize_t Offset, __wasi_filesize_t Len,
107
0
                            __wasi_advice_t Advice) const noexcept {
108
0
    if (!can(__WASI_RIGHTS_FD_ADVISE)) {
109
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
110
0
    }
111
0
    return Node.fdAdvise(Offset, Len, Advice);
112
0
  }
113
114
  /// Force the allocation of space in a file.
115
  ///
116
  /// Note: This is similar to `posix_fallocate` in POSIX.
117
  ///
118
  /// @param[in] Offset The offset at which to start the allocation.
119
  /// @param[in] Len The length of the area that is allocated.
120
  /// @return Nothing or WASI error
121
  WasiExpect<void> fdAllocate(__wasi_filesize_t Offset,
122
0
                              __wasi_filesize_t Len) const noexcept {
123
0
    if (!can(__WASI_RIGHTS_FD_ALLOCATE)) {
124
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
125
0
    }
126
0
    return Node.fdAllocate(Offset, Len);
127
0
  }
128
129
  /// Synchronize the data of a file to disk.
130
  ///
131
  /// Note: This is similar to `fdatasync` in POSIX.
132
  ///
133
  /// @return Nothing or WASI error
134
0
  WasiExpect<void> fdDatasync() const noexcept {
135
0
    if (!can(__WASI_RIGHTS_FD_DATASYNC)) {
136
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
137
0
    }
138
0
    return Node.fdDatasync();
139
0
  }
140
141
  /// Get the attributes of a file descriptor.
142
  ///
143
  /// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well
144
  ///
145
  /// as additional fields.
146
  /// @param[out] FdStat Result.
147
  /// @return Nothing or WASI error
148
0
  WasiExpect<void> fdFdstatGet(__wasi_fdstat_t &FdStat) const noexcept {
149
0
    FdStat.fs_rights_base = EndianValue(FsRightsBase).le();
150
0
    FdStat.fs_rights_inheriting = EndianValue(FsRightsInheriting).le();
151
0
    return Node.fdFdstatGet(FdStat);
152
0
  }
153
154
  /// Adjust the flags associated with a file descriptor.
155
  ///
156
  /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
157
  ///
158
  /// @param[in] FdFlags The desired values of the file descriptor flags.
159
  /// @return Nothing or WASI error
160
0
  WasiExpect<void> fdFdstatSetFlags(__wasi_fdflags_t FdFlags) const noexcept {
161
0
    __wasi_rights_t AdditionalRequiredRights = static_cast<__wasi_rights_t>(0);
162
163
0
    if (FdFlags & __WASI_FDFLAGS_DSYNC) {
164
0
      AdditionalRequiredRights |= __WASI_RIGHTS_FD_DATASYNC;
165
0
    }
166
0
    if (FdFlags & __WASI_FDFLAGS_RSYNC) {
167
0
      AdditionalRequiredRights |= __WASI_RIGHTS_FD_SYNC;
168
0
    }
169
0
    if (FdFlags & __WASI_FDFLAGS_SYNC) {
170
0
      AdditionalRequiredRights |= __WASI_RIGHTS_FD_SYNC;
171
0
    }
172
173
0
    if (!can(__WASI_RIGHTS_FD_FDSTAT_SET_FLAGS | AdditionalRequiredRights)) {
174
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
175
0
    }
176
0
    return Node.fdFdstatSetFlags(FdFlags);
177
0
  }
178
179
  /// Adjust the rights associated with a file descriptor.
180
  ///
181
  /// This can only be used to remove rights, and returns `errno::notcapable` if
182
  /// called in a way that would attempt to add rights
183
  ///
184
  /// @param[in] RightsBase The desired rights of the file descriptor.
185
  /// @param[in] RightsInheriting The desired rights of the file descriptor.
186
  /// @return Nothing or WASI error
187
  WasiExpect<void>
188
  fdFdstatSetRights(__wasi_rights_t RightsBase,
189
0
                    __wasi_rights_t RightsInheriting) noexcept {
190
0
    if (!can(RightsBase, RightsInheriting)) {
191
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
192
0
    }
193
0
    FsRightsBase = RightsBase;
194
0
    FsRightsInheriting = RightsInheriting;
195
196
0
    return {};
197
0
  }
198
199
  /// Return the attributes of an open file.
200
  ///
201
  /// @param[out] Filestat Result.
202
  /// @return Nothing or WASI error
203
0
  WasiExpect<void> fdFilestatGet(__wasi_filestat_t &Filestat) const noexcept {
204
0
    if (!can(__WASI_RIGHTS_FD_FILESTAT_GET)) {
205
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
206
0
    }
207
0
    return Node.fdFilestatGet(Filestat);
208
0
  }
209
210
  /// Adjust the size of an open file. If this increases the file's size, the
211
  /// extra bytes are filled with zeros.
212
  ///
213
  /// Note: This is similar to `ftruncate` in POSIX.
214
  ///
215
  /// @param[in] Size The desired file size.
216
  /// @return Nothing or WASI error
217
0
  WasiExpect<void> fdFilestatSetSize(__wasi_filesize_t Size) const noexcept {
218
0
    if (!can(__WASI_RIGHTS_FD_FILESTAT_SET_SIZE)) {
219
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
220
0
    }
221
0
    return Node.fdFilestatSetSize(Size);
222
0
  }
223
224
  /// Adjust the timestamps of an open file or directory.
225
  ///
226
  /// Note: This is similar to `futimens` in POSIX.
227
  ///
228
  /// @param[in] ATim The desired values of the data access timestamp.
229
  /// @param[in] MTim The desired values of the data modification timestamp.
230
  /// @param[in] FstFlags A bitmask indicating which timestamps to adjust.
231
  /// @return Nothing or WASI error
232
  WasiExpect<void>
233
  fdFilestatSetTimes(__wasi_timestamp_t ATim, __wasi_timestamp_t MTim,
234
0
                     __wasi_fstflags_t FstFlags) const noexcept {
235
0
    if (!can(__WASI_RIGHTS_FD_FILESTAT_SET_TIMES)) {
236
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
237
0
    }
238
0
    return Node.fdFilestatSetTimes(ATim, MTim, FstFlags);
239
0
  }
240
241
  /// Read from a file descriptor, without using and updating the file
242
  /// descriptor's offset.
243
  ///
244
  /// Note: This is similar to `preadv` in POSIX.
245
  ///
246
  /// @param[in] IOVs List of scatter/gather vectors in which to store data.
247
  /// @param[in] Offset The offset within the file at which to read.
248
  /// @param[out] NRead The number of bytes read.
249
  /// @return Nothing or WASI error
250
  WasiExpect<void> fdPread(Span<Span<uint8_t>> IOVs, __wasi_filesize_t Offset,
251
0
                           __wasi_size_t &NRead) const noexcept {
252
0
    if (!can(__WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_SEEK)) {
253
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
254
0
    }
255
0
    return Node.fdPread(IOVs, Offset, NRead);
256
0
  }
257
258
  /// Write to a file descriptor, without using and updating the file
259
  /// descriptor's offset.
260
  ///
261
  /// Note: This is similar to `pwritev` in POSIX.
262
  ///
263
  /// @param[in] IOVs List of scatter/gather vectors from which to retrieve
264
  /// data.
265
  /// @param[in] Offset The offset within the file at which to write.
266
  /// @param[out] NWritten The number of bytes written.
267
  /// @return Nothing or WASI error
268
  WasiExpect<void> fdPwrite(Span<Span<const uint8_t>> IOVs,
269
                            __wasi_filesize_t Offset,
270
0
                            __wasi_size_t &NWritten) const noexcept {
271
0
    if (!can(__WASI_RIGHTS_FD_WRITE | __WASI_RIGHTS_FD_SEEK)) {
272
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
273
0
    }
274
0
    return Node.fdPwrite(IOVs, Offset, NWritten);
275
0
  }
276
277
  /// Read from a file descriptor.
278
  ///
279
  /// Note: This is similar to `readv` in POSIX.
280
  ///
281
  /// @param[in] IOVs List of scatter/gather vectors to which to store data.
282
  /// @param[out] NRead The number of bytes read.
283
  /// @return Nothing or WASI error
284
  WasiExpect<void> fdRead(Span<Span<uint8_t>> IOVs,
285
0
                          __wasi_size_t &NRead) const noexcept {
286
0
    if (!can(__WASI_RIGHTS_FD_READ)) {
287
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
288
0
    }
289
0
    return Node.fdRead(IOVs, NRead);
290
0
  }
291
292
  /// Read directory entries from a directory.
293
  ///
294
  /// When successful, the contents of the output buffer consist of a sequence
295
  /// of directory entries. Each directory entry consists of a `dirent` object,
296
  /// followed by `dirent::d_namlen` bytes holding the name of the directory
297
  /// entry.
298
  ///
299
  /// This function fills the output buffer as much as possible,
300
  /// potentially truncating the last directory entry. This allows the caller to
301
  /// grow its read buffer size in case it's too small to fit a single large
302
  /// directory entry, or skip the oversized directory entry.
303
  ///
304
  /// @param[out] Buffer The buffer where directory entries are stored.
305
  /// @param[in] Cookie The location within the directory to start reading
306
  /// @param[out] Size The number of bytes stored in the read buffer. If less
307
  /// than the size of the read buffer, the end of the directory has been
308
  /// reached.
309
  /// @return Nothing or WASI error
310
  WasiExpect<void> fdReaddir(Span<uint8_t> Buffer, __wasi_dircookie_t Cookie,
311
0
                             __wasi_size_t &Size) noexcept {
312
0
    if (!can(__WASI_RIGHTS_FD_READDIR)) {
313
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
314
0
    }
315
0
    return Node.fdReaddir(Buffer, Cookie, Size);
316
0
  }
317
318
  /// Move the offset of a file descriptor.
319
  ///
320
  /// Note: This is similar to `lseek` in POSIX.
321
  ///
322
  /// @param[in] Offset The number of bytes to move.
323
  /// @param[in] Whence The base from which the offset is relative.
324
  /// @param[out] Size The new offset of the file descriptor, relative to the
325
  /// start of the file.
326
  /// @return Nothing or WASI error
327
  WasiExpect<void> fdSeek(__wasi_filedelta_t Offset, __wasi_whence_t Whence,
328
0
                          __wasi_filesize_t &Size) const noexcept {
329
0
    if (!can(__WASI_RIGHTS_FD_SEEK)) {
330
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
331
0
    }
332
0
    return Node.fdSeek(Offset, Whence, Size);
333
0
  }
334
335
  /// Synchronize the data and metadata of a file to disk.
336
  ///
337
  /// Note: This is similar to `fsync` in POSIX.
338
  ///
339
  /// @return Nothing or WASI error
340
0
  WasiExpect<void> fdSync() const noexcept {
341
0
    if (!can(__WASI_RIGHTS_FD_SYNC)) {
342
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
343
0
    }
344
0
    return Node.fdSync();
345
0
  }
346
347
  /// Return the current offset of a file descriptor.
348
  ///
349
  /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
350
  ///
351
  /// @param[out] Size The current offset of the file descriptor, relative to
352
  /// the start of the file.
353
  /// @return Nothing or WASI error
354
0
  WasiExpect<void> fdTell(__wasi_filesize_t &Size) const noexcept {
355
0
    if (!can(__WASI_RIGHTS_FD_TELL)) {
356
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
357
0
    }
358
0
    return Node.fdTell(Size);
359
0
  }
360
361
  /// Write to a file descriptor.
362
  ///
363
  /// Note: This is similar to `writev` in POSIX.
364
  ///
365
  /// @param[in] IOVs List of scatter/gather vectors from which to retrieve
366
  /// data.
367
  /// @param[out] NWritten The number of bytes written.
368
  /// @return Nothing or WASI error
369
  WasiExpect<void> fdWrite(Span<Span<const uint8_t>> IOVs,
370
0
                           __wasi_size_t &NWritten) const noexcept {
371
0
    if (!can(__WASI_RIGHTS_FD_WRITE)) {
372
0
      return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
373
0
    }
374
0
    return Node.fdWrite(IOVs, NWritten);
375
0
  }
376
377
  /// Get the native handler.
378
  ///
379
  /// Note: Users should cast this native handler to corresponding types
380
  /// on different operating systems. E.g. int on POSIX or void * on Windows
381
  ///
382
  /// @return The native handler in uint64_t.
383
0
  WasiExpect<uint64_t> getNativeHandler() const noexcept {
384
0
    return Node.getNativeHandler();
385
0
  }
386
387
  /// Create a directory.
388
  ///
389
  /// Note: This is similar to `mkdirat` in POSIX.
390
  ///
391
  /// @param[in] Fd The working directory at which the resolution of the path
392
  /// starts.
393
  /// @param[in] Path The path at which to create the directory.
394
  /// @return Nothing or WASI error
395
  static WasiExpect<void> pathCreateDirectory(std::shared_ptr<VINode> Fd,
396
                                              std::string_view Path);
397
398
  /// Return the attributes of a file or directory.
399
  ///
400
  /// Note: This is similar to `stat` in POSIX.
401
  ///
402
  /// @param[in] Fd The working directory at which the resolution of the path
403
  /// starts.
404
  /// @param[in] Path The path of the file or directory to inspect.
405
  /// @param[in] Flags Flags determining the method of how the path is resolved.
406
  /// @param[out] Filestat The buffer where the file's attributes are stored.
407
  /// @return Nothing or WASI error
408
  static WasiExpect<void> pathFilestatGet(std::shared_ptr<VINode> Fd,
409
                                          std::string_view Path,
410
                                          __wasi_lookupflags_t Flags,
411
                                          __wasi_filestat_t &Filestat);
412
413
  /// Adjust the timestamps of a file or directory.
414
  ///
415
  /// Note: This is similar to `utimensat` in POSIX.
416
  ///
417
  /// @param[in] Fd The working directory at which the resolution of the path
418
  /// starts.
419
  /// @param[in] Path The path of the file or directory to inspect.
420
  /// @param[in] Flags Flags determining the method of how the path is resolved.
421
  /// @param[in] ATim The desired values of the data access timestamp.
422
  /// @param[in] MTim The desired values of the data modification timestamp.
423
  /// @param[in] FstFlags A bitmask indicating which timestamps to adjust.
424
  /// @return Nothing or WASI error
425
  static WasiExpect<void>
426
  pathFilestatSetTimes(std::shared_ptr<VINode> Fd, std::string_view Path,
427
                       __wasi_lookupflags_t Flags, __wasi_timestamp_t ATim,
428
                       __wasi_timestamp_t MTim, __wasi_fstflags_t FstFlags);
429
430
  /// Create a hard link.
431
  ///
432
  /// Note: This is similar to `linkat` in POSIX.
433
  ///
434
  /// @param[in] Old The working directory at which the resolution of the old
435
  /// path starts.
436
  /// @param[in] OldPath The source path from which to link.
437
  /// @param[in] New The working directory at which the resolution of the new
438
  /// path starts.
439
  /// @param[in] NewPath The destination path at which to create the hard link.
440
  /// @param[in] LookupFlags Flags determining the method of how the path is
441
  /// resolved.
442
  /// @return Nothing or WASI error
443
  static WasiExpect<void> pathLink(std::shared_ptr<VINode> Old,
444
                                   std::string_view OldPath,
445
                                   std::shared_ptr<VINode> New,
446
                                   std::string_view NewPath,
447
                                   __wasi_lookupflags_t LookupFlags);
448
449
  /// Open a file or directory.
450
  ///
451
  /// The returned file descriptor is not guaranteed to be the lowest-numbered
452
  /// file descriptor not currently open; it is randomized to prevent
453
  /// applications from depending on making assumptions about indexes, since
454
  /// this is error-prone in multi-threaded contexts. The returned file
455
  /// descriptor is guaranteed to be less than 2**31.
456
  ///
457
  /// Note: This is similar to `openat` in POSIX.
458
  ///
459
  /// @param[in] Fd The working directory at which the resolution of the path
460
  /// starts.
461
  /// @param[in] Path The relative path of the file or directory to open,
462
  /// relative to the `path_open::fd` directory.
463
  /// @param[in] LookupFlags Flags determining the method of how the path is
464
  /// resolved.
465
  /// @param[in] OpenFlags The method by which to open the file.
466
  /// @param[in] FsRightsBase The initial rights of the newly created file
467
  /// descriptor. The implementation is allowed to return a file descriptor with
468
  /// fewer rights than specified, if and only if those rights do not apply to
469
  /// the type of file being opened. The *base* rights are rights that will
470
  /// apply to operations using the file descriptor itself.
471
  /// @param[in] FsRightsInheriting The initial rights of the newly created file
472
  /// descriptor. The implementation is allowed to return a file descriptor with
473
  /// fewer rights than specified, if and only if those rights do not apply to
474
  /// the type of file being opened. The *inheriting* rights are rights that
475
  /// apply to file descriptors derived from it.
476
  /// @param[in] FdFlags The method by which to open the file.
477
  /// @return The file descriptor of the file that has been opened, or WASI
478
  /// error.
479
  static WasiExpect<std::shared_ptr<VINode>>
480
  pathOpen(std::shared_ptr<VINode> Fd, std::string_view Path,
481
           __wasi_lookupflags_t LookupFlags, __wasi_oflags_t OpenFlags,
482
           __wasi_rights_t FsRightsBase, __wasi_rights_t FsRightsInheriting,
483
           __wasi_fdflags_t FdFlags);
484
485
  /// Read the contents of a symbolic link.
486
  ///
487
  /// Note: This is similar to `readlinkat` in POSIX.
488
  ///
489
  /// @param[in] Fd The working directory at which the resolution of the path
490
  /// starts.
491
  /// @param[in] Path The path of the symbolic link from which to read.
492
  /// @param[out] Buffer The buffer to which to write the contents of the
493
  /// symbolic link.
494
  /// @param[out] NRead The number of bytes read.
495
  /// @return Nothing or WASI error.
496
  static WasiExpect<void> pathReadlink(std::shared_ptr<VINode> Fd,
497
                                       std::string_view Path, Span<char> Buffer,
498
                                       __wasi_size_t &NRead);
499
500
  /// Remove a directory.
501
  ///
502
  /// Return `errno::notempty` if the directory is not empty.
503
  ///
504
  /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
505
  ///
506
  /// @param[in] Fd The working directory at which the resolution of the path
507
  /// starts.
508
  /// @param[in] Path The path to a directory to remove.
509
  /// @return Nothing or WASI error.
510
  static WasiExpect<void> pathRemoveDirectory(std::shared_ptr<VINode> Fd,
511
                                              std::string_view Path);
512
513
  /// Rename a file or directory.
514
  ///
515
  /// Note: This is similar to `renameat` in POSIX.
516
  ///
517
  /// @param[in] Old The working directory at which the resolution of the old
518
  /// path starts.
519
  /// @param[in] OldPath The source path of the file or directory to rename.
520
  /// @param[in] New The working directory at which the resolution of the new
521
  /// path starts.
522
  /// @param[in] NewPath The destination path to which to rename the file or
523
  /// directory.
524
  /// @return Nothing or WASI error.
525
  static WasiExpect<void> pathRename(std::shared_ptr<VINode> Old,
526
                                     std::string_view OldPath,
527
                                     std::shared_ptr<VINode> New,
528
                                     std::string_view NewPath);
529
530
  /// Create a symbolic link.
531
  ///
532
  /// Note: This is similar to `symlinkat` in POSIX.
533
  ///
534
  /// @param[in] OldPath The contents of the symbolic link.
535
  /// @param[in] New The working directory at which the resolution of the new
536
  /// path starts.
537
  /// @param[in] NewPath The destination path at which to create the symbolic
538
  /// link.
539
  /// @return Nothing or WASI error
540
  static WasiExpect<void> pathSymlink(std::string_view OldPath,
541
                                      std::shared_ptr<VINode> New,
542
                                      std::string_view NewPath);
543
544
  /// Unlink a file.
545
  ///
546
  /// Return `errno::isdir` if the path refers to a directory.
547
  ///
548
  /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
549
  ///
550
  /// @param[in] Fd The working directory at which the resolution of the path
551
  /// starts.
552
  /// @param[in] Path The path to a file to unlink.
553
  /// @return Nothing or WASI error.
554
  static WasiExpect<void> pathUnlinkFile(std::shared_ptr<VINode> Fd,
555
                                         std::string_view Path);
556
557
  static WasiExpect<void>
558
  getAddrinfo(std::string_view Node, std::string_view Service,
559
              const __wasi_addrinfo_t &Hint, uint32_t MaxResLength,
560
              Span<__wasi_addrinfo_t *> WasiAddrinfoArray,
561
              Span<__wasi_sockaddr_t *> WasiSockaddrArray,
562
              Span<char *> AiAddrSaDataArray, Span<char *> AiCanonnameArray,
563
              /*Out*/ __wasi_size_t &ResLength) noexcept;
564
565
  static WasiExpect<std::shared_ptr<VINode>>
566
  sockOpen(__wasi_address_family_t SysDomain, __wasi_sock_type_t SockType);
567
568
  WasiExpect<void> sockBind(__wasi_address_family_t AddressFamily,
569
                            Span<const uint8_t> Address,
570
0
                            uint16_t Port) noexcept {
571
0
    return Node.sockBind(AddressFamily, Address, Port);
572
0
  }
573
574
0
  WasiExpect<void> sockListen(int32_t Backlog) noexcept {
575
0
    return Node.sockListen(Backlog);
576
0
  }
577
578
  WasiExpect<std::shared_ptr<VINode>> sockAccept(__wasi_fdflags_t FdFlags);
579
580
  WasiExpect<void> sockConnect(__wasi_address_family_t AddressFamily,
581
                               Span<const uint8_t> Address,
582
0
                               uint16_t Port) noexcept {
583
0
    return Node.sockConnect(AddressFamily, Address, Port);
584
0
  }
585
586
  /// Receive a message from a socket.
587
  ///
588
  /// Note: This is similar to `recv` in POSIX, though it also supports reading
589
  /// the data into multiple buffers in the manner of `readv`.
590
  ///
591
  /// @param[in] RiData List of scatter/gather vectors to which to store data.
592
  /// @param[in] RiFlags Message flags.
593
  /// @param[out] NRead Return the number of bytes stored in RiData.
594
  /// @param[out] RoFlags Return message flags.
595
  /// @return Nothing or WASI error.
596
  WasiExpect<void> sockRecv(Span<Span<uint8_t>> RiData,
597
                            __wasi_riflags_t RiFlags, __wasi_size_t &NRead,
598
0
                            __wasi_roflags_t &RoFlags) const noexcept {
599
0
    return Node.sockRecv(RiData, RiFlags, NRead, RoFlags);
600
0
  }
601
602
  /// Receive a message from a socket.
603
  ///
604
  /// Note: This is similar to `recvfrom` in POSIX, though it also supports
605
  /// reading the data into multiple buffers in the manner of `readv`.
606
  ///
607
  /// @param[in] RiData List of scatter/gather vectors to which to store data.
608
  /// @param[in] RiFlags Message flags.
609
  /// @param[out] AddressFamilyPtr The pointer to store address family.
610
  /// @param[out] Address The buffer to store address.
611
  /// @param[out] PortPtr The pointer to store port.
612
  /// @param[out] NRead Return the number of bytes stored in RiData.
613
  /// @param[out] RoFlags Return message flags.
614
  /// @return Nothing or WASI error.
615
  WasiExpect<void> sockRecvFrom(Span<Span<uint8_t>> RiData,
616
                                __wasi_riflags_t RiFlags,
617
                                __wasi_address_family_t *AddressFamilyPtr,
618
                                Span<uint8_t> Address, uint16_t *PortPtr,
619
                                __wasi_size_t &NRead,
620
0
                                __wasi_roflags_t &RoFlags) const noexcept {
621
0
    return Node.sockRecvFrom(RiData, RiFlags, AddressFamilyPtr, Address,
622
0
                             PortPtr, NRead, RoFlags);
623
0
  }
624
625
  /// Send a message on a socket.
626
  ///
627
  /// Note: This is similar to `send` in POSIX, though it also supports writing
628
  /// the data from multiple buffers in the manner of `writev`.
629
  ///
630
  /// @param[in] SiData List of scatter/gather vectors to which to retrieve
631
  /// data.
632
  /// @param[in] SiFlags Message flags.
633
  /// @param[out] NWritten The number of bytes transmitted.
634
  /// @return Nothing or WASI error
635
  WasiExpect<void> sockSend(Span<Span<const uint8_t>> SiData,
636
                            __wasi_siflags_t SiFlags,
637
0
                            __wasi_size_t &NWritten) const noexcept {
638
0
    return Node.sockSend(SiData, SiFlags, NWritten);
639
0
  }
640
641
  /// Send a message on a socket.
642
  ///
643
  /// Note: This is similar to `send` in POSIX, though it also supports writing
644
  /// the data from multiple buffers in the manner of `writev`.
645
  ///
646
  /// @param[in] SiData List of scatter/gather vectors to which to retrieve
647
  /// data.
648
  /// @param[in] SiFlags Message flags.
649
  /// @param[in] AddressFamily Address family of the target.
650
  /// @param[in] Address Address of the target.
651
  /// @param[in] Port Connected port.
652
  /// @param[out] NWritten The number of bytes transmitted.
653
  /// @return Nothing or WASI error
654
  WasiExpect<void> sockSendTo(Span<Span<const uint8_t>> SiData,
655
                              __wasi_siflags_t SiFlags,
656
                              __wasi_address_family_t AddressFamily,
657
                              Span<const uint8_t> Address, uint16_t Port,
658
0
                              __wasi_size_t &NWritten) const noexcept {
659
0
    return Node.sockSendTo(SiData, SiFlags, AddressFamily, Address, Port,
660
0
                           NWritten);
661
0
  }
662
663
  /// Shut down socket send and receive channels.
664
  ///
665
  /// Note: This is similar to `shutdown` in POSIX.
666
  ///
667
  /// @param[in] SdFlags Which channels on the socket to shut down.
668
  /// @return Nothing or WASI error
669
0
  WasiExpect<void> sockShutdown(__wasi_sdflags_t SdFlags) const noexcept {
670
0
    return Node.sockShutdown(SdFlags);
671
0
  }
672
673
  WasiExpect<void> sockGetOpt(__wasi_sock_opt_level_t SockOptLevel,
674
                              __wasi_sock_opt_so_t SockOptName,
675
0
                              Span<uint8_t> &Flag) const noexcept {
676
0
    return Node.sockGetOpt(SockOptLevel, SockOptName, Flag);
677
0
  }
678
679
  WasiExpect<void> sockSetOpt(__wasi_sock_opt_level_t SockOptLevel,
680
                              __wasi_sock_opt_so_t SockOptName,
681
0
                              Span<const uint8_t> Flag) const noexcept {
682
0
    return Node.sockSetOpt(SockOptLevel, SockOptName, Flag);
683
0
  }
684
685
  WasiExpect<void> sockGetLocalAddr(__wasi_address_family_t *AddressFamilyPtr,
686
                                    Span<uint8_t> Address,
687
0
                                    uint16_t *PortPtr) const noexcept {
688
0
    return Node.sockGetLocalAddr(AddressFamilyPtr, Address, PortPtr);
689
0
  }
690
691
  WasiExpect<void> sockGetPeerAddr(__wasi_address_family_t *AddressFamilyPtr,
692
                                   Span<uint8_t> Address,
693
0
                                   uint16_t *PortPtr) const noexcept {
694
0
    return Node.sockGetPeerAddr(AddressFamilyPtr, Address, PortPtr);
695
0
  }
696
697
0
  __wasi_rights_t fsRightsBase() const noexcept { return FsRightsBase; }
698
699
0
  __wasi_rights_t fsRightsInheriting() const noexcept {
700
0
    return FsRightsInheriting;
701
0
  }
702
703
  /// Check if this vinode is a directory.
704
0
  bool isDirectory() const noexcept { return Node.isDirectory(); }
705
706
  /// Check if current user has execute permission on this vinode directory.
707
0
  bool canBrowse() const noexcept { return Node.canBrowse(); }
708
709
  /// Check if this vinode is a symbolic link.
710
0
  bool isSymlink() const noexcept { return Node.isSymlink(); }
711
712
0
  static constexpr __wasi_rights_t imply(__wasi_rights_t Rights) noexcept {
713
0
    if (Rights & __WASI_RIGHTS_FD_SEEK) {
714
0
      Rights |= __WASI_RIGHTS_FD_TELL;
715
0
    }
716
0
    if (Rights & __WASI_RIGHTS_FD_SYNC) {
717
0
      Rights |= __WASI_RIGHTS_FD_DATASYNC;
718
0
    }
719
0
    return Rights;
720
0
  }
721
722
  constexpr bool can(__wasi_rights_t RequiredRights,
723
                     __wasi_rights_t RequiredInheritingRights =
724
0
                         static_cast<__wasi_rights_t>(0)) const noexcept {
725
0
    const auto Base = imply(FsRightsBase);
726
0
    const auto Inheriting = imply(FsRightsInheriting);
727
0
    return (Base & RequiredRights) == RequiredRights &&
728
0
           (Inheriting & RequiredInheritingRights) == RequiredInheritingRights;
729
0
  }
730
731
private:
732
  INode Node;
733
  __wasi_rights_t FsRightsBase;
734
  __wasi_rights_t FsRightsInheriting;
735
  std::string Name;
736
737
  friend class VPoller;
738
739
  /// Open path without resolve.
740
  /// @param Path Path, contains one element only.
741
  /// @param OpenFlags WASI open flags.
742
  /// @return VINode found, or WASI error.
743
  WasiExpect<std::shared_ptr<VINode>>
744
  directOpen(std::string_view Path, __wasi_oflags_t OpenFlags,
745
             __wasi_fdflags_t FdFlags, VFS::Flags VFSFlags,
746
             __wasi_rights_t RightsBase, __wasi_rights_t RightsInheriting);
747
748
  /// Resolve path until last element.
749
  /// @param[in,out] Fd Fd. Return parent of last part if found.
750
  /// @param[in,out] Path path. Return last part of path if found.
751
  /// @param[in] LookupFlags WASI lookup flags.
752
  /// @param[in] VFSFlags Internal lookup flags.
753
  /// @param[in] LinkCount Counting symbolic link lookup times.
754
  /// @param[in] FollowTrailingSlashes If Path ends with slash, open it and set
755
  /// Path to ".".
756
  /// @param[out] ResolvedDepth If not null, set to the parent's depth below
757
  /// `Fd`. Left untouched on error paths.
758
  /// @return Allocated buffer, or WASI error.
759
  static WasiExpect<std::vector<char>> resolvePath(
760
      std::shared_ptr<VINode> &Fd, std::string_view &Path,
761
      __wasi_lookupflags_t LookupFlags = __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW,
762
      VFS::Flags VFSFlags = static_cast<VFS::Flags>(0), uint8_t LinkCount = 0,
763
      bool FollowTrailingSlashes = true, uint32_t *ResolvedDepth = nullptr);
764
765
  /// Proxy function for `resolvePath`.
766
  /// @param[in,out] Fd Fd. Return parent of last part if found.
767
  /// @param[in,out] Path path. Return last part of path if found.
768
  /// @param[in] FollowTrailingSlashes If Path ends with slash, open it and set
769
  /// Path to ".".
770
  /// @return Allocated buffer, or WASI error.
771
  static inline WasiExpect<std::vector<char>>
772
  resolvePath(std::shared_ptr<VINode> &Fd, std::string_view &Path,
773
0
              bool FollowTrailingSlashes) {
774
0
    return resolvePath(Fd, Path, __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW,
775
0
                       static_cast<VFS::Flags>(0), 0, FollowTrailingSlashes);
776
0
  }
777
};
778
779
class VPoller : protected Poller {
780
public:
781
  using Poller::clock;
782
  using Poller::close;
783
  using Poller::error;
784
  using Poller::ok;
785
  using Poller::Poller;
786
  using Poller::prepare;
787
  using Poller::reset;
788
  using Poller::result;
789
  using Poller::wait;
790
791
  void read(std::shared_ptr<VINode> Node, TriggerType Trigger,
792
0
            __wasi_userdata_t UserData) noexcept {
793
0
    if (!Node->can(__WASI_RIGHTS_POLL_FD_READWRITE) &&
794
0
        !Node->can(__WASI_RIGHTS_FD_READ)) {
795
0
      Poller::error(UserData, __WASI_ERRNO_NOTCAPABLE,
796
0
                    __WASI_EVENTTYPE_FD_READ);
797
0
    } else {
798
0
      Poller::read(Node->Node, Trigger, UserData);
799
0
    }
800
0
  }
801
802
  void write(std::shared_ptr<VINode> Node, TriggerType Trigger,
803
0
             __wasi_userdata_t UserData) noexcept {
804
0
    if (!Node->can(__WASI_RIGHTS_POLL_FD_READWRITE) &&
805
0
        !Node->can(__WASI_RIGHTS_FD_WRITE)) {
806
0
      Poller::error(UserData, __WASI_ERRNO_NOTCAPABLE,
807
0
                    __WASI_EVENTTYPE_FD_WRITE);
808
0
    } else {
809
0
      Poller::write(Node->Node, Trigger, UserData);
810
0
    }
811
0
  }
812
813
0
  void close(std::shared_ptr<VINode> Node) noexcept {
814
0
    Poller::close(Node->Node);
815
0
  }
816
};
817
818
} // namespace WASI
819
} // namespace Host
820
} // namespace WasmEdge