Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/host/wasi/vinode.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "host/wasi/vinode.h"
5
#include "common/errcode.h"
6
#include "common/spdlog.h"
7
#include "host/wasi/environ.h"
8
#include "host/wasi/vfs.h"
9
#include <algorithm>
10
#include <cstddef>
11
#include <numeric>
12
#include <string>
13
14
using namespace std::literals;
15
16
namespace WasmEdge {
17
namespace Host {
18
namespace WASI {
19
20
namespace {
21
22
static inline constexpr const uint8_t kMaxNestedLinks = 8;
23
24
}
25
26
VINode::VINode(INode Node, __wasi_rights_t FRB, __wasi_rights_t FRI,
27
               std::string N)
28
0
    : Node(std::move(Node)), FsRightsBase(FRB), FsRightsInheriting(FRI),
29
0
      Name(std::move(N)) {}
30
31
std::shared_ptr<VINode> VINode::stdIn(__wasi_rights_t FRB,
32
0
                                      __wasi_rights_t FRI) {
33
0
  return std::make_shared<VINode>(INode::stdIn(), FRB, FRI);
34
0
}
35
36
std::shared_ptr<VINode> VINode::stdOut(__wasi_rights_t FRB,
37
0
                                       __wasi_rights_t FRI) {
38
0
  return std::make_shared<VINode>(INode::stdOut(), FRB, FRI);
39
0
}
40
41
std::shared_ptr<VINode> VINode::stdErr(__wasi_rights_t FRB,
42
0
                                       __wasi_rights_t FRI) {
43
0
  return std::make_shared<VINode>(INode::stdErr(), FRB, FRI);
44
0
}
45
46
WasiExpect<std::shared_ptr<VINode>>
47
0
VINode::fromFd(int32_t Fd, __wasi_rights_t FRB, __wasi_rights_t FRI) {
48
0
  auto NodeResult = INode::fromFd(Fd);
49
0
  if (!NodeResult) {
50
0
    return WasiUnexpect(NodeResult.error());
51
0
  }
52
0
  return std::make_shared<VINode>(std::move(*NodeResult), FRB, FRI);
53
0
}
54
0
std::string VINode::canonicalGuest(std::string_view Path) {
55
0
  std::vector<std::string_view> Parts;
56
57
0
  while (!Path.empty() && Path.front() == '/') {
58
0
    Path = Path.substr(1);
59
0
  }
60
0
  while (!Path.empty()) {
61
0
    auto Slash = Path.find('/');
62
0
    const auto Part = Path.substr(0, Slash);
63
0
    auto Remain = Path.substr(Part.size());
64
0
    while (!Remain.empty() && Remain.front() == '/') {
65
0
      Remain = Remain.substr(1);
66
0
    }
67
0
    if (Part.front() == '.' && Part.size() == 2 && Part[1] == '.') {
68
0
      if (!Parts.empty()) {
69
0
        Parts.pop_back();
70
0
      }
71
0
    } else if (Part.front() != '.' || Parts.size() != 1) {
72
0
      Parts.push_back(Part);
73
0
    }
74
0
    if (Remain.empty()) {
75
0
      break;
76
0
    }
77
0
    Path = Remain;
78
0
  }
79
0
  if (Parts.empty()) {
80
0
    Parts.push_back({});
81
0
  }
82
83
0
  std::string Result;
84
0
  Result.reserve(std::accumulate(
85
0
      Parts.begin(), Parts.end(), Parts.size(),
86
0
      [](size_t L, std::string_view P) { return L + P.size(); }));
87
0
  std::for_each(Parts.begin(), Parts.end(), [&Result](std::string_view P) {
88
0
    Result += P;
89
0
    Result += '/';
90
0
  });
91
0
  if (!Result.empty()) {
92
0
    Result.pop_back();
93
0
  }
94
95
0
  return Result;
96
0
}
97
98
WasiExpect<std::shared_ptr<VINode>> VINode::bind(__wasi_rights_t FRB,
99
                                                 __wasi_rights_t FRI,
100
                                                 std::string Name,
101
0
                                                 std::string SystemPath) {
102
0
  EXPECTED_TRY(auto Node,
103
0
               INode::open(std::move(SystemPath), __WASI_OFLAGS_DIRECTORY,
104
0
                           __wasi_fdflags_t(0), VFS::Read));
105
0
  return std::make_shared<VINode>(std::move(Node), FRB, FRI, std::move(Name));
106
0
}
107
108
WasiExpect<void> VINode::pathCreateDirectory(std::shared_ptr<VINode> Fd,
109
0
                                             std::string_view Path) {
110
0
  if (!Fd->can(__WASI_RIGHTS_PATH_CREATE_DIRECTORY)) {
111
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
112
0
  }
113
0
  EXPECTED_TRY(auto Buffer, resolvePath(Fd, Path, false));
114
0
  return Fd->Node.pathCreateDirectory(std::string(Path));
115
0
}
116
117
WasiExpect<void> VINode::pathFilestatGet(std::shared_ptr<VINode> Fd,
118
                                         std::string_view Path,
119
                                         __wasi_lookupflags_t Flags,
120
0
                                         __wasi_filestat_t &Filestat) {
121
0
  if (!Fd->can(__WASI_RIGHTS_PATH_FILESTAT_GET)) {
122
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
123
0
  }
124
0
  EXPECTED_TRY(auto Buffer, resolvePath(Fd, Path, Flags));
125
0
  return Fd->Node.pathFilestatGet(std::string(Path), Filestat);
126
0
}
127
128
WasiExpect<void> VINode::pathFilestatSetTimes(std::shared_ptr<VINode> Fd,
129
                                              std::string_view Path,
130
                                              __wasi_lookupflags_t Flags,
131
                                              __wasi_timestamp_t ATim,
132
                                              __wasi_timestamp_t MTim,
133
0
                                              __wasi_fstflags_t FstFlags) {
134
0
  if (!Fd->can(__WASI_RIGHTS_PATH_FILESTAT_SET_TIMES)) {
135
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
136
0
  }
137
0
  EXPECTED_TRY(auto Buffer, resolvePath(Fd, Path, Flags));
138
0
  return Fd->Node.pathFilestatSetTimes(std::string(Path), ATim, MTim, FstFlags);
139
0
}
140
141
WasiExpect<void> VINode::pathLink(std::shared_ptr<VINode> Old,
142
                                  std::string_view OldPath,
143
                                  std::shared_ptr<VINode> New,
144
                                  std::string_view NewPath,
145
0
                                  __wasi_lookupflags_t LookupFlags) {
146
0
  if (unlikely(!New)) {
147
0
    return WasiUnexpect(__WASI_ERRNO_BADF);
148
0
  }
149
0
  if (!Old->can(__WASI_RIGHTS_PATH_LINK_SOURCE)) {
150
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
151
0
  }
152
0
  if (!New->can(__WASI_RIGHTS_PATH_LINK_TARGET)) {
153
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
154
0
  }
155
0
  EXPECTED_TRY(auto OldBuffer, resolvePath(Old, OldPath, LookupFlags));
156
0
  EXPECTED_TRY(auto NewBuffer, resolvePath(New, NewPath, LookupFlags));
157
158
0
  return INode::pathLink(Old->Node, std::string(OldPath), New->Node,
159
0
                         std::string(NewPath));
160
0
}
161
162
WasiExpect<std::shared_ptr<VINode>>
163
VINode::pathOpen(std::shared_ptr<VINode> Fd, std::string_view Path,
164
                 __wasi_lookupflags_t LookupFlags, __wasi_oflags_t OpenFlags,
165
                 __wasi_rights_t FsRightsBase,
166
0
                 __wasi_rights_t FsRightsInheriting, __wasi_fdflags_t FdFlags) {
167
0
  if (OpenFlags & __WASI_OFLAGS_DIRECTORY) {
168
0
    FsRightsBase &= ~__WASI_RIGHTS_FD_SEEK;
169
0
  } else {
170
0
    FsRightsBase &= ~__WASI_RIGHTS_PATH_FILESTAT_GET;
171
0
    FsRightsInheriting &= ~__WASI_RIGHTS_PATH_FILESTAT_GET;
172
0
  }
173
174
0
  __wasi_rights_t RequiredRights = __WASI_RIGHTS_PATH_OPEN;
175
176
0
  if (OpenFlags & __WASI_OFLAGS_CREAT) {
177
0
    RequiredRights |= __WASI_RIGHTS_PATH_CREATE_FILE;
178
0
  }
179
0
  if (OpenFlags & __WASI_OFLAGS_TRUNC) {
180
0
    RequiredRights |= __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
181
0
  }
182
183
0
  if (!Fd->can(RequiredRights)) {
184
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
185
0
  }
186
0
  const auto InheritingRights = imply(Fd->fsRightsInheriting());
187
0
  FsRightsBase &= InheritingRights;
188
0
  FsRightsInheriting &= InheritingRights;
189
0
  __wasi_rights_t RequiredFlagRights = static_cast<__wasi_rights_t>(0);
190
0
  if (FdFlags & __WASI_FDFLAGS_APPEND) {
191
0
    RequiredFlagRights |= __WASI_RIGHTS_FD_WRITE;
192
0
  }
193
0
  if (FdFlags & __WASI_FDFLAGS_DSYNC) {
194
0
    RequiredFlagRights |= __WASI_RIGHTS_FD_DATASYNC;
195
0
  }
196
0
  if (FdFlags & (__WASI_FDFLAGS_RSYNC | __WASI_FDFLAGS_SYNC)) {
197
0
    RequiredFlagRights |= __WASI_RIGHTS_FD_SYNC;
198
0
  }
199
0
  if ((InheritingRights & RequiredFlagRights) != RequiredFlagRights) {
200
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
201
0
  }
202
0
  const bool Read =
203
0
      (FsRightsBase & (__WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_READDIR)) != 0;
204
0
  const bool Write =
205
0
      (FsRightsBase & (__WASI_RIGHTS_FD_WRITE | __WASI_RIGHTS_FD_ALLOCATE |
206
0
                       __WASI_RIGHTS_FD_FILESTAT_SET_SIZE)) != 0;
207
0
  EXPECTED_TRY(auto Buffer, resolvePath(Fd, Path, LookupFlags));
208
0
  VFS::Flags VFSFlags = static_cast<VFS::Flags>(0);
209
0
  if (Read) {
210
0
    VFSFlags |= VFS::Read;
211
0
  }
212
0
  if (Write) {
213
0
    VFSFlags |= VFS::Write;
214
0
  }
215
0
  return Fd->directOpen(Path, OpenFlags, FdFlags, VFSFlags, FsRightsBase,
216
0
                        FsRightsInheriting);
217
0
}
218
219
WasiExpect<void> VINode::pathReadlink(std::shared_ptr<VINode> Fd,
220
                                      std::string_view Path, Span<char> Buffer,
221
0
                                      __wasi_size_t &NRead) {
222
0
  if (!Fd->can(__WASI_RIGHTS_PATH_READLINK)) {
223
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
224
0
  }
225
0
  EXPECTED_TRY(auto PathBuffer,
226
0
               resolvePath(Fd, Path, static_cast<__wasi_lookupflags_t>(0)));
227
228
0
  return Fd->Node.pathReadlink(std::string(Path), Buffer, NRead);
229
0
}
230
231
WasiExpect<void> VINode::pathRemoveDirectory(std::shared_ptr<VINode> Fd,
232
0
                                             std::string_view Path) {
233
0
  if (!Fd->can(__WASI_RIGHTS_PATH_REMOVE_DIRECTORY)) {
234
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
235
0
  }
236
0
  EXPECTED_TRY(auto Buffer, resolvePath(Fd, Path, false));
237
238
0
  return Fd->Node.pathRemoveDirectory(std::string(Path));
239
0
}
240
241
WasiExpect<void> VINode::pathRename(std::shared_ptr<VINode> Old,
242
                                    std::string_view OldPath,
243
                                    std::shared_ptr<VINode> New,
244
0
                                    std::string_view NewPath) {
245
0
  if (!Old->can(__WASI_RIGHTS_PATH_RENAME_SOURCE)) {
246
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
247
0
  }
248
0
  if (!New->can(__WASI_RIGHTS_PATH_RENAME_TARGET)) {
249
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
250
0
  }
251
0
  EXPECTED_TRY(auto OldBuffer, resolvePath(Old, OldPath, false));
252
0
  EXPECTED_TRY(auto NewBuffer, resolvePath(New, NewPath, false));
253
254
0
  return INode::pathRename(Old->Node, std::string(OldPath), New->Node,
255
0
                           std::string(NewPath));
256
0
}
257
258
WasiExpect<void> VINode::pathSymlink(std::string_view OldPath,
259
                                     std::shared_ptr<VINode> New,
260
0
                                     std::string_view NewPath) {
261
0
  if (unlikely(!New)) {
262
0
    return WasiUnexpect(__WASI_ERRNO_BADF);
263
0
  }
264
0
  if (!New->can(__WASI_RIGHTS_PATH_SYMLINK)) {
265
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
266
0
  }
267
  // Resolve the new path without following its trailing component (an existing
268
  // link must not be followed, so creating over it fails with EEXIST), and
269
  // capture the link directory's depth below the fd.
270
0
  uint32_t ParentDepth = 0;
271
0
  EXPECTED_TRY(auto NewBuffer,
272
0
               resolvePath(New, NewPath, static_cast<__wasi_lookupflags_t>(0),
273
0
                           static_cast<VFS::Flags>(0), 0, true, &ParentDepth));
274
275
  // Reject relative targets that resolve outside the preopen root; store
276
  // in-bounds targets as-is.
277
0
  if (isSymlinkTargetEscaping(OldPath, ParentDepth)) {
278
0
    return WasiUnexpect(__WASI_ERRNO_PERM);
279
0
  }
280
0
  return New->Node.pathSymlink(std::string(OldPath), std::string(NewPath));
281
0
}
282
283
WasiExpect<void> VINode::pathUnlinkFile(std::shared_ptr<VINode> Fd,
284
0
                                        std::string_view Path) {
285
0
  if (!Fd->can(__WASI_RIGHTS_PATH_UNLINK_FILE)) {
286
0
    return WasiUnexpect(__WASI_ERRNO_NOTCAPABLE);
287
0
  }
288
0
  EXPECTED_TRY(auto Buffer,
289
0
               resolvePath(Fd, Path, static_cast<__wasi_lookupflags_t>(0)));
290
291
0
  return Fd->Node.pathUnlinkFile(std::string(Path));
292
0
}
293
294
WasiExpect<void>
295
VINode::getAddrinfo(std::string_view Node, std::string_view Service,
296
                    const __wasi_addrinfo_t &Hint, uint32_t MaxResLength,
297
                    Span<__wasi_addrinfo_t *> WasiAddrinfoArray,
298
                    Span<__wasi_sockaddr_t *> WasiSockaddrArray,
299
                    Span<Span<char>> AiAddrSaDataArray,
300
                    Span<Span<char>> AiCanonnameArray,
301
0
                    /*Out*/ __wasi_size_t &ResLength) noexcept {
302
0
  return INode::getAddrinfo(Node, Service, Hint, MaxResLength,
303
0
                            WasiAddrinfoArray, WasiSockaddrArray,
304
0
                            AiAddrSaDataArray, AiCanonnameArray, ResLength);
305
0
}
306
307
WasiExpect<std::shared_ptr<VINode>>
308
VINode::sockOpen(__wasi_address_family_t SysDomain,
309
0
                 __wasi_sock_type_t SockType) {
310
0
  EXPECTED_TRY(auto Node, INode::sockOpen(SysDomain, SockType));
311
0
  __wasi_rights_t Rights =
312
0
      __WASI_RIGHTS_SOCK_OPEN | __WASI_RIGHTS_SOCK_CLOSE |
313
0
      __WASI_RIGHTS_SOCK_RECV | __WASI_RIGHTS_SOCK_RECV_FROM |
314
0
      __WASI_RIGHTS_SOCK_SEND | __WASI_RIGHTS_SOCK_SEND_TO |
315
0
      __WASI_RIGHTS_SOCK_SHUTDOWN | __WASI_RIGHTS_SOCK_BIND |
316
0
      __WASI_RIGHTS_POLL_FD_READWRITE | __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS |
317
0
      __WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_WRITE;
318
0
  return std::make_shared<VINode>(std::move(Node), Rights, Rights);
319
0
}
320
321
WasiExpect<std::shared_ptr<VINode>>
322
0
VINode::sockAccept(__wasi_fdflags_t FdFlags) {
323
0
  EXPECTED_TRY(auto NewNode, Node.sockAccept(FdFlags));
324
0
  __wasi_rights_t Rights =
325
0
      __WASI_RIGHTS_SOCK_RECV | __WASI_RIGHTS_SOCK_RECV_FROM |
326
0
      __WASI_RIGHTS_SOCK_SEND | __WASI_RIGHTS_SOCK_SEND_TO |
327
0
      __WASI_RIGHTS_SOCK_SHUTDOWN | __WASI_RIGHTS_POLL_FD_READWRITE |
328
0
      __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS | __WASI_RIGHTS_FD_READ |
329
0
      __WASI_RIGHTS_FD_WRITE;
330
0
  return std::make_shared<VINode>(std::move(NewNode), Rights, Rights,
331
0
                                  std::string());
332
0
}
333
334
WasiExpect<std::shared_ptr<VINode>>
335
VINode::directOpen(std::string_view Path, __wasi_oflags_t OpenFlags,
336
                   __wasi_fdflags_t FdFlags, VFS::Flags VFSFlags,
337
                   __wasi_rights_t RightsBase,
338
0
                   __wasi_rights_t RightsInheriting) {
339
0
  std::string PathStr(Path);
340
341
0
  EXPECTED_TRY(auto NewNode,
342
0
               Node.pathOpen(std::move(PathStr), OpenFlags, FdFlags, VFSFlags));
343
0
  return std::make_shared<VINode>(std::move(NewNode), RightsBase,
344
0
                                  RightsInheriting);
345
0
}
346
347
WasiExpect<std::vector<char>>
348
VINode::resolvePath(std::shared_ptr<VINode> &Fd, std::string_view &Path,
349
                    __wasi_lookupflags_t LookupFlags, VFS::Flags VFSFlags,
350
                    uint8_t LinkCount, bool FollowTrailingSlashes,
351
0
                    uint32_t *ResolvedDepth) {
352
0
  std::vector<std::shared_ptr<VINode>> PartFds;
353
0
  std::vector<char> Buffer;
354
0
  do {
355
    // check empty path
356
0
    if (Path.empty() && (VFSFlags & VFS::AllowEmpty) == 0) {
357
0
      return WasiUnexpect(__WASI_ERRNO_NOENT);
358
0
    }
359
360
    // check absolute path
361
0
    if (!Path.empty() && Path[0] == '/') {
362
0
      return WasiUnexpect(__WASI_ERRNO_PERM);
363
0
    }
364
365
0
    if (!Fd) {
366
0
      return WasiUnexpect(__WASI_ERRNO_BADF);
367
0
    }
368
369
0
    if (!Fd->isDirectory()) {
370
0
      return WasiUnexpect(__WASI_ERRNO_NOTDIR);
371
0
    }
372
373
0
    if (!Fd->canBrowse()) {
374
0
      return WasiUnexpect(__WASI_ERRNO_ACCES);
375
0
    }
376
377
0
    do {
378
      // check self type
379
0
      auto Slash = Path.find('/');
380
0
      const auto Part = Path.substr(0, Slash);
381
0
      auto Remain = Path.substr(Part.size());
382
0
      while (!Remain.empty() && Remain[0] == '/') {
383
0
        Remain = Remain.substr(1);
384
0
      }
385
0
      const bool LastPart = Remain.empty() && (!FollowTrailingSlashes ||
386
0
                                               Slash == std::string_view::npos);
387
388
0
      if (!Part.empty() && Part[0] == '.') {
389
0
        if (Part.size() == 1) {
390
0
          if (LastPart) {
391
0
            if (ResolvedDepth) {
392
0
              *ResolvedDepth = static_cast<uint32_t>(PartFds.size());
393
0
            }
394
0
            return Buffer;
395
0
          }
396
0
          Path = Remain;
397
0
          continue;
398
0
        }
399
0
        if (Part.size() == 2 && Part[1] == '.') {
400
0
          if (PartFds.empty()) {
401
0
            return WasiUnexpect(__WASI_ERRNO_PERM);
402
0
          }
403
0
          Fd = std::move(PartFds.back());
404
0
          PartFds.pop_back();
405
0
          Path = Remain;
406
0
          if (LastPart) {
407
0
            Path = "."sv;
408
0
            if (ResolvedDepth) {
409
0
              *ResolvedDepth = static_cast<uint32_t>(PartFds.size());
410
0
            }
411
0
            return Buffer;
412
0
          }
413
0
          continue;
414
0
        }
415
0
      }
416
417
0
      if (LastPart && !(LookupFlags & __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW)) {
418
0
        Path = Part;
419
0
        if (ResolvedDepth) {
420
0
          *ResolvedDepth = static_cast<uint32_t>(PartFds.size());
421
0
        }
422
0
        return Buffer;
423
0
      }
424
425
0
      __wasi_filestat_t Filestat{};
426
0
      if (auto Res = Fd->Node.pathFilestatGet(std::string(Part), Filestat);
427
0
          unlikely(!Res)) {
428
0
        if (LastPart) {
429
0
          Path = Part;
430
0
          if (ResolvedDepth) {
431
0
            *ResolvedDepth = static_cast<uint32_t>(PartFds.size());
432
0
          }
433
0
          return Buffer;
434
0
        }
435
0
        return WasiUnexpect(Res);
436
0
      }
437
438
0
      if (Filestat.filetype == __WASI_FILETYPE_SYMBOLIC_LINK) {
439
0
        if (++LinkCount >= kMaxNestedLinks) {
440
0
          return WasiUnexpect(__WASI_ERRNO_LOOP);
441
0
        }
442
443
0
        std::vector<char> NewBuffer(16384);
444
0
        __wasi_size_t NRead;
445
0
        EXPECTED_TRY(
446
0
            Fd->Node.pathReadlink(std::string(Part), NewBuffer, NRead));
447
0
        NewBuffer.resize(NRead);
448
        // Don't drop Buffer now because Path may referencing it.
449
0
        if (!Remain.empty()) {
450
0
          if (NewBuffer.back() != '/') {
451
0
            NewBuffer.push_back('/');
452
0
          }
453
0
          NewBuffer.insert(NewBuffer.end(), Remain.begin(), Remain.end());
454
0
        }
455
        // slow retry
456
0
        Buffer = std::move(NewBuffer);
457
0
        Path = std::string_view(Buffer.data(), Buffer.size());
458
0
        break;
459
0
      }
460
461
0
      if (LastPart) {
462
0
        Path = Part;
463
0
        if (ResolvedDepth) {
464
0
          *ResolvedDepth = static_cast<uint32_t>(PartFds.size());
465
0
        }
466
0
        return Buffer;
467
0
      }
468
469
0
      if (Filestat.filetype != __WASI_FILETYPE_DIRECTORY) {
470
0
        return WasiUnexpect(__WASI_ERRNO_NOTDIR);
471
0
      }
472
473
0
      EXPECTED_TRY(auto Child, Fd->Node.pathOpen(
474
0
                                   std::string(Part), __WASI_OFLAGS_DIRECTORY,
475
0
                                   static_cast<__wasi_fdflags_t>(0), VFSFlags));
476
      // fast retry
477
0
      PartFds.push_back(std::exchange(
478
0
          Fd, std::make_shared<VINode>(std::move(Child), Fd->FsRightsBase,
479
0
                                       Fd->FsRightsInheriting)));
480
0
      Path = Remain;
481
0
      if (Path.empty()) {
482
0
        Path = "."sv;
483
0
        if (ResolvedDepth) {
484
0
          *ResolvedDepth = static_cast<uint32_t>(PartFds.size());
485
0
        }
486
0
        return {};
487
0
      }
488
0
      continue;
489
0
    } while (true);
490
0
  } while (true);
491
0
}
492
493
} // namespace WASI
494
} // namespace Host
495
} // namespace WasmEdge