Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/host/wasi/inode-linux.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "common/defines.h"
5
#include "common/types.h"
6
#if WASMEDGE_OS_LINUX
7
8
#include "common/errcode.h"
9
#include "common/variant.h"
10
#include "host/wasi/environ.h"
11
#include "host/wasi/inode.h"
12
#include "host/wasi/vfs.h"
13
#include "linux.h"
14
#include <algorithm>
15
#include <cstddef>
16
#include <cstdint>
17
#include <cstring>
18
#include <new>
19
#include <string>
20
#include <string_view>
21
#include <vector>
22
23
using namespace std::literals;
24
25
namespace WasmEdge {
26
namespace Host {
27
namespace WASI {
28
29
namespace {
30
31
0
inline constexpr bool isSpecialFd(int Fd) noexcept {
32
0
  switch (Fd) {
33
0
  case STDIN_FILENO:
34
0
  case STDOUT_FILENO:
35
0
  case STDERR_FILENO:
36
0
    return true;
37
0
  default:
38
0
    return false;
39
0
  }
40
0
}
41
42
inline constexpr __wasi_size_t
43
0
calculateAddrinfoLinkedListSize(struct addrinfo *const Addrinfo) {
44
0
  __wasi_size_t Length = 0;
45
0
  for (struct addrinfo *TmpPointer = Addrinfo; TmpPointer != nullptr;
46
0
       TmpPointer = TmpPointer->ai_next) {
47
0
    Length++;
48
0
  }
49
0
  return Length;
50
0
};
51
52
constexpr int openFlags(__wasi_oflags_t OpenFlags, __wasi_fdflags_t FdFlags,
53
0
                        VFS::Flags VFSFlags) noexcept {
54
0
  int Flags = O_NOFOLLOW;
55
0
#ifdef O_CLOEXEC
56
0
  Flags |= O_CLOEXEC;
57
0
#endif
58
59
0
  if (VFSFlags & VFS::Read) {
60
0
    if (VFSFlags & VFS::Write) {
61
0
      Flags |= O_RDWR;
62
0
    } else {
63
0
      Flags |= O_RDONLY;
64
0
    }
65
0
  } else if (VFSFlags & VFS::Write) {
66
0
    Flags |= O_WRONLY;
67
0
  } else {
68
0
#ifdef O_PATH
69
0
    if (OpenFlags == __WASI_OFLAGS_DIRECTORY) {
70
0
      Flags |= O_PATH;
71
0
    } else {
72
0
      Flags |= O_RDONLY;
73
0
    }
74
#else
75
    Flags |= O_RDONLY;
76
#endif
77
0
  }
78
79
0
  if (OpenFlags & __WASI_OFLAGS_CREAT) {
80
0
    Flags |= O_CREAT;
81
0
  }
82
0
  if (OpenFlags & __WASI_OFLAGS_DIRECTORY) {
83
0
    Flags |= O_DIRECTORY;
84
0
  }
85
0
  if (OpenFlags & __WASI_OFLAGS_EXCL) {
86
0
    Flags |= O_EXCL;
87
0
  }
88
0
  if ((OpenFlags & __WASI_OFLAGS_TRUNC) && (VFSFlags & VFS::Write)) {
89
0
    Flags |= O_TRUNC;
90
0
  }
91
92
  // Convert file descriptor flags.
93
0
  if ((FdFlags & __WASI_FDFLAGS_APPEND) != 0) {
94
0
    Flags |= O_APPEND;
95
0
  }
96
0
  if ((FdFlags & __WASI_FDFLAGS_DSYNC) != 0) {
97
0
#ifdef O_DSYNC
98
0
    Flags |= O_DSYNC;
99
#else
100
    Flags |= O_SYNC;
101
#endif
102
0
  }
103
0
  if ((FdFlags & __WASI_FDFLAGS_NONBLOCK) != 0) {
104
0
    Flags |= O_NONBLOCK;
105
0
  }
106
0
  if ((FdFlags & __WASI_FDFLAGS_RSYNC) != 0) {
107
0
#ifdef O_RSYNC
108
0
    Flags |= O_RSYNC;
109
#else
110
    Flags |= O_SYNC;
111
#endif
112
0
  }
113
0
  if ((FdFlags & __WASI_FDFLAGS_SYNC) != 0) {
114
0
    Flags |= O_SYNC;
115
0
  }
116
117
0
  return Flags;
118
0
}
119
120
std::pair<const char *, std::unique_ptr<char[]>>
121
0
createNullTerminatedString(std::string_view View) noexcept {
122
0
  const char *CStr = nullptr;
123
0
  std::unique_ptr<char[]> Buffer;
124
0
  if (!View.empty()) {
125
0
    if (const auto Pos = View.find_first_of('\0');
126
0
        Pos != std::string_view::npos) {
127
0
      CStr = View.data();
128
0
    } else {
129
0
      Buffer = std::make_unique<char[]>(View.size() + 1);
130
0
      std::copy(View.begin(), View.end(), Buffer.get());
131
0
      CStr = Buffer.get();
132
0
    }
133
0
  }
134
0
  return {CStr, std::move(Buffer)};
135
0
}
136
137
} // namespace
138
139
0
void FdHolder::reset() noexcept {
140
0
  if (likely(ok())) {
141
0
    if (likely(!isSpecialFd(Fd))) {
142
0
      ::close(Fd);
143
0
    }
144
0
    Fd = -1;
145
0
  }
146
0
}
147
148
0
void TimerHolder::reset() noexcept {
149
0
  if (likely(Id.has_value())) {
150
0
    timer_delete(*Id);
151
0
    Id.reset();
152
0
  }
153
0
}
154
155
0
void DirHolder::reset() noexcept {
156
0
  if (likely(Dir != nullptr)) {
157
0
    closedir(Dir);
158
0
    Dir = nullptr;
159
0
    Cookie = 0;
160
0
  }
161
0
}
162
163
namespace {
164
0
WasiExpect<INode> createStdNode(int32_t Fd) {
165
0
  if (Fd < 0) {
166
0
    spdlog::error("    Invalid file descriptor: {}"sv, Fd);
167
0
    return WasiUnexpect(__WASI_ERRNO_BADF);
168
0
  }
169
170
0
  if (fcntl(Fd, F_GETFD) == -1) {
171
0
    spdlog::error("    Invalid file descriptor: {}"sv, Fd);
172
0
    return WasiUnexpect(__WASI_ERRNO_BADF);
173
0
  }
174
175
0
  return INode(Fd, false);
176
0
}
177
} // namespace
178
179
0
INode INode::stdIn() noexcept { return INode(STDIN_FILENO); }
180
181
0
INode INode::stdOut() noexcept { return INode(STDOUT_FILENO); }
182
183
0
INode INode::stdErr() noexcept { return INode(STDERR_FILENO); }
184
185
0
WasiExpect<INode> INode::fromFd(int32_t FdNum) { return createStdNode(FdNum); }
186
187
WasiExpect<INode> INode::open(std::string Path, __wasi_oflags_t OpenFlags,
188
                              __wasi_fdflags_t FdFlags,
189
0
                              VFS::Flags VFSFlags) noexcept {
190
0
  const int Flags = openFlags(OpenFlags, FdFlags, VFSFlags);
191
192
0
  if (auto NewFd = ::open(Path.c_str(), Flags, 0644); unlikely(NewFd < 0)) {
193
0
    return WasiUnexpect(fromErrNo(errno));
194
0
  } else {
195
0
    INode New(NewFd);
196
197
#ifndef O_CLOEXEC
198
    if (auto Res = ::fcntl(New.Fd, F_SETFD, FD_CLOEXEC); unlikely(Res != 0)) {
199
      return WasiUnexpect(fromErrNo(errno));
200
    }
201
#endif
202
0
    return New;
203
0
  }
204
0
}
205
206
WasiExpect<void> INode::fdAdvise(__wasi_filesize_t Offset,
207
                                 __wasi_filesize_t Len,
208
0
                                 __wasi_advice_t Advice) const noexcept {
209
0
  if (auto Res = ::posix_fadvise(Fd, Offset, Len, toAdvice(Advice));
210
0
      unlikely(Res != 0)) {
211
0
    return WasiUnexpect(fromErrNo(errno));
212
0
  }
213
214
0
  return {};
215
0
}
216
217
WasiExpect<void> INode::fdAllocate(__wasi_filesize_t Offset,
218
0
                                   __wasi_filesize_t Len) const noexcept {
219
0
  if (auto Res = ::posix_fallocate(Fd, Offset, Len); unlikely(Res != 0)) {
220
    // https://man7.org/linux/man-pages/man3/posix_fallocate.3.html
221
    // posix_fallocate will not set errno, so use the return value directly.
222
0
    return WasiUnexpect(fromErrNo(Res));
223
0
  }
224
225
0
  return {};
226
0
}
227
228
0
WasiExpect<void> INode::fdDatasync() const noexcept {
229
0
  if (auto Res = ::fdatasync(Fd); unlikely(Res != 0)) {
230
0
    return WasiUnexpect(fromErrNo(errno));
231
0
  }
232
233
0
  return {};
234
0
}
235
236
0
WasiExpect<void> INode::fdFdstatGet(__wasi_fdstat_t &FdStat) const noexcept {
237
0
  EXPECTED_TRY(updateStat());
238
239
0
  if (int FdFlags = ::fcntl(Fd, F_GETFL); unlikely(FdFlags < 0)) {
240
0
    return WasiUnexpect(fromErrNo(errno));
241
0
  } else {
242
0
    FdStat.fs_filetype = EndianValue(unsafeFiletype()).le();
243
244
0
    FdStat.fs_flags = static_cast<__wasi_fdflags_t>(0);
245
0
    if (FdFlags & O_APPEND) {
246
0
      FdStat.fs_flags |= __WASI_FDFLAGS_APPEND;
247
0
    }
248
0
    if (FdFlags & O_DSYNC) {
249
0
      FdStat.fs_flags |= __WASI_FDFLAGS_DSYNC;
250
0
    }
251
0
    if (FdFlags & O_NONBLOCK) {
252
0
      FdStat.fs_flags |= __WASI_FDFLAGS_NONBLOCK;
253
0
    }
254
0
    if (FdFlags & O_SYNC) {
255
0
      FdStat.fs_flags |= __WASI_FDFLAGS_RSYNC | __WASI_FDFLAGS_SYNC;
256
0
    }
257
0
  }
258
0
  FdStat.fs_flags = EndianValue(FdStat.fs_flags).le();
259
260
0
  return {};
261
0
}
262
263
WasiExpect<void>
264
0
INode::fdFdstatSetFlags(__wasi_fdflags_t FdFlags) const noexcept {
265
0
  int SysFlag = 0;
266
0
  if (FdFlags & __WASI_FDFLAGS_NONBLOCK) {
267
0
    SysFlag |= O_NONBLOCK;
268
0
  }
269
0
  if (FdFlags & __WASI_FDFLAGS_APPEND) {
270
0
    SysFlag |= O_APPEND;
271
0
  }
272
0
  if (FdFlags & __WASI_FDFLAGS_DSYNC) {
273
0
    SysFlag |= O_DSYNC;
274
0
  }
275
0
  if (FdFlags & __WASI_FDFLAGS_RSYNC) {
276
0
    SysFlag |= O_RSYNC;
277
0
  }
278
0
  if (FdFlags & __WASI_FDFLAGS_SYNC) {
279
0
    SysFlag |= O_SYNC;
280
0
  }
281
282
0
  if (auto Res = ::fcntl(Fd, F_SETFL, SysFlag); unlikely(Res != 0)) {
283
0
    return WasiUnexpect(fromErrNo(errno));
284
0
  }
285
286
0
  return {};
287
0
}
288
289
WasiExpect<void>
290
0
INode::fdFilestatGet(__wasi_filestat_t &Filestat) const noexcept {
291
0
  EXPECTED_TRY(updateStat());
292
293
  // Zeroing out these values to prevent leaking information about the host
294
  // environment from special fd such as stdin, stdout and stderr.
295
0
  Filestat.dev = isSpecialFd(Fd) ? 0 : EndianValue(Stat->st_dev).le();
296
0
  Filestat.ino = isSpecialFd(Fd) ? 0 : EndianValue(Stat->st_ino).le();
297
0
  Filestat.filetype = EndianValue(unsafeFiletype()).le();
298
0
  Filestat.nlink = isSpecialFd(Fd) ? 0 : EndianValue(Stat->st_nlink).le();
299
0
  Filestat.size = isSpecialFd(Fd) ? 0 : EndianValue(Stat->st_size).le();
300
0
  Filestat.atim = isSpecialFd(Fd) ? 0 : fromTimespec(Stat->st_atim).le();
301
0
  Filestat.mtim = isSpecialFd(Fd) ? 0 : fromTimespec(Stat->st_mtim).le();
302
0
  Filestat.ctim = isSpecialFd(Fd) ? 0 : fromTimespec(Stat->st_ctim).le();
303
304
0
  return {};
305
0
}
306
307
WasiExpect<void>
308
0
INode::fdFilestatSetSize(__wasi_filesize_t Size) const noexcept {
309
0
  if (auto Res = ::ftruncate(Fd, Size); unlikely(Res == -1)) {
310
0
    return WasiUnexpect(fromErrNo(errno));
311
0
  }
312
313
0
  return {};
314
0
}
315
316
WasiExpect<void>
317
INode::fdFilestatSetTimes(__wasi_timestamp_t ATim, __wasi_timestamp_t MTim,
318
0
                          __wasi_fstflags_t FstFlags) const noexcept {
319
0
#if __GLIBC_PREREQ(2, 6) || __BIONIC__
320
0
  timespec SysTimespec[2];
321
0
  if (FstFlags & __WASI_FSTFLAGS_ATIM) {
322
0
    SysTimespec[0] = toTimespec(ATim);
323
0
  } else if (FstFlags & __WASI_FSTFLAGS_ATIM_NOW) {
324
0
    SysTimespec[0].tv_nsec = UTIME_NOW;
325
0
  } else {
326
0
    SysTimespec[0].tv_nsec = UTIME_OMIT;
327
0
  }
328
0
  if (FstFlags & __WASI_FSTFLAGS_MTIM) {
329
0
    SysTimespec[1] = toTimespec(MTim);
330
0
  } else if (FstFlags & __WASI_FSTFLAGS_MTIM_NOW) {
331
0
    SysTimespec[1].tv_nsec = UTIME_NOW;
332
0
  } else {
333
0
    SysTimespec[1].tv_nsec = UTIME_OMIT;
334
0
  }
335
336
0
  if (auto Res = ::futimens(Fd, SysTimespec); unlikely(Res != 0)) {
337
0
    return WasiUnexpect(fromErrNo(errno));
338
0
  }
339
#else
340
  bool NeedNow = false;
341
  bool NeedFile = false;
342
  if (FstFlags & __WASI_FSTFLAGS_ATIM) {
343
    // Nothing to do.
344
  } else if (FstFlags & __WASI_FSTFLAGS_ATIM_NOW) {
345
    NeedNow = true;
346
  } else {
347
    NeedFile = true;
348
  }
349
  if (FstFlags & __WASI_FSTFLAGS_MTIM) {
350
    // Nothing to do.
351
  } else if (FstFlags & __WASI_FSTFLAGS_MTIM_NOW) {
352
    NeedNow = true;
353
  } else {
354
    NeedFile = true;
355
  }
356
357
  if (NeedFile) {
358
    EXPECTED_TRY(updateStat());
359
  }
360
361
  timespec Now;
362
  if (NeedNow) {
363
    if (auto Res = ::clock_gettime(CLOCK_REALTIME, &Now); unlikely(Res != 0)) {
364
      return WasiUnexpect(fromErrNo(errno));
365
    }
366
  }
367
368
  timeval SysTimeval[2];
369
  if (FstFlags & __WASI_FSTFLAGS_ATIM) {
370
    SysTimeval[0] = toTimeval(ATim);
371
  } else if (FstFlags & __WASI_FSTFLAGS_ATIM_NOW) {
372
    SysTimeval[0] = toTimeval(Now);
373
  } else {
374
    SysTimeval[0] = toTimeval(Stat->st_atim);
375
  }
376
  if (FstFlags & __WASI_FSTFLAGS_MTIM) {
377
    SysTimeval[1] = toTimeval(MTim);
378
  } else if (FstFlags & __WASI_FSTFLAGS_MTIM_NOW) {
379
    SysTimeval[1] = toTimeval(Now);
380
  } else {
381
    SysTimeval[1] = toTimeval(Stat->st_mtim);
382
  }
383
384
  if (auto Res = ::futimes(Fd, SysTimeval); unlikely(Res != 0)) {
385
    return WasiUnexpect(fromErrNo(errno));
386
  }
387
#endif
388
389
0
  return {};
390
0
}
391
392
WasiExpect<void> INode::fdPread(Span<Span<uint8_t>> IOVs,
393
                                __wasi_filesize_t Offset,
394
0
                                __wasi_size_t &NRead) const noexcept {
395
0
  iovec SysIOVs[kIOVMax];
396
0
  size_t SysIOVsSize = 0;
397
0
  for (auto &IOV : IOVs) {
398
0
    SysIOVs[SysIOVsSize].iov_base = IOV.data();
399
0
    SysIOVs[SysIOVsSize].iov_len = IOV.size();
400
0
    ++SysIOVsSize;
401
0
  }
402
403
0
#if __GLIBC_PREREQ(2, 10)
404
  // Store read bytes length.
405
0
  if (auto Res = ::preadv(Fd, SysIOVs, SysIOVsSize, Offset);
406
0
      unlikely(Res < 0)) {
407
0
    return WasiUnexpect(fromErrNo(errno));
408
0
  } else {
409
0
    NRead = EndianValue(static_cast<__wasi_size_t>(Res)).le();
410
0
  }
411
#else
412
  const auto OldOffset = ::lseek(Fd, 0, SEEK_CUR);
413
  if (OldOffset < 0) {
414
    return WasiUnexpect(fromErrNo(errno));
415
  }
416
  if (::lseek(Fd, Offset, SEEK_SET) < 0 ||
417
      ::lseek(Fd, OldOffset, SEEK_SET) < 0) {
418
    return WasiUnexpect(fromErrNo(errno));
419
  }
420
  if (auto Res = ::readv(Fd, SysIOVs, SysIOVsSize); unlikely(Res < 0)) {
421
    ::lseek(Fd, OldOffset, SEEK_SET);
422
    return WasiUnexpect(fromErrNo(errno));
423
  } else {
424
    if (::lseek(Fd, OldOffset, SEEK_SET) < 0) {
425
      return WasiUnexpect(fromErrNo(errno));
426
    }
427
    NRead = EndianValue(static_cast<__wasi_size_t>(Res)).le();
428
  }
429
#endif
430
431
0
  return {};
432
0
}
433
434
WasiExpect<void> INode::fdPwrite(Span<Span<const uint8_t>> IOVs,
435
                                 __wasi_filesize_t Offset,
436
0
                                 __wasi_size_t &NWritten) const noexcept {
437
0
  iovec SysIOVs[kIOVMax];
438
0
  size_t SysIOVsSize = 0;
439
0
  for (auto &IOV : IOVs) {
440
0
    SysIOVs[SysIOVsSize].iov_base = const_cast<uint8_t *>(IOV.data());
441
0
    SysIOVs[SysIOVsSize].iov_len = IOV.size();
442
0
    ++SysIOVsSize;
443
0
  }
444
445
0
#if __GLIBC_PREREQ(2, 10)
446
0
  if (auto Res = ::pwritev(Fd, SysIOVs, SysIOVsSize, Offset);
447
0
      unlikely(Res < 0)) {
448
0
    return WasiUnexpect(fromErrNo(errno));
449
0
  } else {
450
0
    NWritten = EndianValue(static_cast<__wasi_size_t>(Res)).le();
451
0
  }
452
#else
453
  const auto OldOffset = ::lseek(Fd, 0, SEEK_CUR);
454
  if (OldOffset < 0) {
455
    return WasiUnexpect(fromErrNo(errno));
456
  }
457
  if (::lseek(Fd, Offset, SEEK_SET) < 0 ||
458
      ::lseek(Fd, OldOffset, SEEK_SET) < 0) {
459
    return WasiUnexpect(fromErrNo(errno));
460
  }
461
  if (auto Res = ::writev(Fd, SysIOVs, SysIOVsSize); unlikely(Res < 0)) {
462
    ::lseek(Fd, OldOffset, SEEK_SET);
463
    return WasiUnexpect(fromErrNo(errno));
464
  } else {
465
    if (::lseek(Fd, OldOffset, SEEK_SET) < 0) {
466
      return WasiUnexpect(fromErrNo(errno));
467
    }
468
    NWritten = EndianValue(static_cast<__wasi_size_t>(Res)).le();
469
  }
470
#endif
471
472
0
  return {};
473
0
}
474
475
WasiExpect<void> INode::fdRead(Span<Span<uint8_t>> IOVs,
476
0
                               __wasi_size_t &NRead) const noexcept {
477
0
  iovec SysIOVs[kIOVMax];
478
0
  size_t SysIOVsSize = 0;
479
0
  for (auto &IOV : IOVs) {
480
0
    SysIOVs[SysIOVsSize].iov_base = IOV.data();
481
0
    SysIOVs[SysIOVsSize].iov_len = IOV.size();
482
0
    ++SysIOVsSize;
483
0
  }
484
485
0
  if (auto Res = ::readv(Fd, SysIOVs, SysIOVsSize); unlikely(Res < 0)) {
486
0
    return WasiUnexpect(fromErrNo(errno));
487
0
  } else {
488
0
    NRead = EndianValue(static_cast<__wasi_size_t>(Res)).le();
489
0
  }
490
491
0
  return {};
492
0
}
493
494
// Due to the unfortunate design of wasi::fd_readdir, It's nearly impossible to
495
// provide a correct implementation. The below implementation is just a
496
// workaround for most usages and may not be correct in some edge cases. The
497
// readdir entry API is going to be updated to use a stream type, so we don't
498
// have to deal with it right now.
499
WasiExpect<void> INode::fdReaddir(Span<uint8_t> Buffer,
500
                                  __wasi_dircookie_t Cookie,
501
0
                                  __wasi_size_t &Size) noexcept {
502
0
  if (unlikely(!Dir.ok())) {
503
0
    if (FdHolder NewFd(::dup(Fd)); unlikely(!NewFd.ok())) {
504
0
      return WasiUnexpect(fromErrNo(errno));
505
0
    } else if (DIR *D = ::fdopendir(NewFd.Fd); unlikely(!D)) {
506
0
      return WasiUnexpect(fromErrNo(errno));
507
0
    } else {
508
0
      NewFd.release();
509
0
      Dir.emplace(D);
510
0
    }
511
0
  }
512
513
0
  if (Cookie == 0) {
514
0
    ::rewinddir(Dir.Dir);
515
0
  } else if (unlikely(Cookie != Dir.Cookie)) {
516
0
    ::seekdir(Dir.Dir, Cookie);
517
0
  }
518
519
0
  Size = 0;
520
0
  do {
521
0
    if (!Dir.Buffer.empty()) {
522
0
      const auto NewDataSize =
523
0
          std::min<uint32_t>(Buffer.size(), Dir.Buffer.size());
524
0
      std::copy(Dir.Buffer.begin(), Dir.Buffer.begin() + NewDataSize,
525
0
                Buffer.begin());
526
0
      Buffer = Buffer.subspan(NewDataSize);
527
0
      Size += NewDataSize;
528
0
      Dir.Buffer.clear();
529
0
      if (unlikely(Buffer.empty())) {
530
0
        break;
531
0
      }
532
0
    }
533
0
    errno = 0;
534
0
    dirent *SysDirent = ::readdir(Dir.Dir);
535
0
    if (SysDirent == nullptr) {
536
0
      if (errno != 0) {
537
0
        return WasiUnexpect(fromErrNo(errno));
538
0
      }
539
      // End of entries
540
0
      break;
541
0
    }
542
0
    Dir.Cookie = SysDirent->d_off;
543
0
    std::string_view Name = SysDirent->d_name;
544
545
0
    Dir.Buffer.resize(sizeof(__wasi_dirent_t) + Name.size());
546
547
0
    __wasi_dirent_t *const Dirent =
548
0
        reinterpret_cast<__wasi_dirent_t *>(Dir.Buffer.data());
549
0
    Dirent->d_next = Dir.Cookie;
550
0
    Dirent->d_ino = SysDirent->d_ino;
551
0
    Dirent->d_type = fromFileType(SysDirent->d_type);
552
0
    Dirent->d_namlen = Name.size();
553
0
    std::copy(Name.cbegin(), Name.cend(),
554
0
              Dir.Buffer.begin() + sizeof(__wasi_dirent_t));
555
0
  } while (!Buffer.empty());
556
0
  Size = EndianValue(Size).le();
557
0
  return {};
558
0
}
559
560
WasiExpect<void> INode::fdSeek(__wasi_filedelta_t Offset,
561
                               __wasi_whence_t Whence,
562
0
                               __wasi_filesize_t &Size) const noexcept {
563
0
  if (auto Res = ::lseek(Fd, Offset, toWhence(Whence)); unlikely(Res < 0)) {
564
0
    return WasiUnexpect(fromErrNo(errno));
565
0
  } else {
566
0
    Size = EndianValue(static_cast<__wasi_filesize_t>(Res)).le();
567
0
  }
568
569
0
  return {};
570
0
}
571
572
0
WasiExpect<void> INode::fdSync() const noexcept {
573
0
  if (auto Res = ::fsync(Fd); unlikely(Res != 0)) {
574
0
    return WasiUnexpect(fromErrNo(errno));
575
0
  }
576
577
0
  return {};
578
0
}
579
580
0
WasiExpect<void> INode::fdTell(__wasi_filesize_t &Size) const noexcept {
581
0
  if (auto Res = ::lseek(Fd, 0, SEEK_CUR); unlikely(Res < 0)) {
582
0
    return WasiUnexpect(fromErrNo(errno));
583
0
  } else {
584
0
    Size = EndianValue(static_cast<__wasi_filesize_t>(Res)).le();
585
0
  }
586
587
0
  return {};
588
0
}
589
590
WasiExpect<void> INode::fdWrite(Span<Span<const uint8_t>> IOVs,
591
0
                                __wasi_size_t &NWritten) const noexcept {
592
0
  iovec SysIOVs[kIOVMax];
593
0
  size_t SysIOVsSize = 0;
594
0
  for (auto &IOV : IOVs) {
595
0
    SysIOVs[SysIOVsSize].iov_base = const_cast<uint8_t *>(IOV.data());
596
0
    SysIOVs[SysIOVsSize].iov_len = IOV.size();
597
0
    ++SysIOVsSize;
598
0
  }
599
600
0
  if (auto Res = ::writev(Fd, SysIOVs, SysIOVsSize); unlikely(Res < 0)) {
601
0
    return WasiUnexpect(fromErrNo(errno));
602
0
  } else {
603
0
    NWritten = EndianValue(static_cast<__wasi_size_t>(Res)).le();
604
0
  }
605
606
0
  return {};
607
0
}
608
609
0
WasiExpect<uint64_t> INode::getNativeHandler() const noexcept {
610
0
  return static_cast<uint64_t>(Fd);
611
0
}
612
613
0
WasiExpect<void> INode::pathCreateDirectory(std::string Path) const noexcept {
614
0
  if (auto Res = ::mkdirat(Fd, Path.c_str(), 0755); unlikely(Res != 0)) {
615
0
    return WasiUnexpect(fromErrNo(errno));
616
0
  }
617
618
0
  return {};
619
0
}
620
621
WasiExpect<void>
622
INode::pathFilestatGet(std::string Path,
623
0
                       __wasi_filestat_t &Filestat) const noexcept {
624
0
  struct stat SysFStat;
625
0
  if (int Res = ::fstatat(Fd, Path.c_str(), &SysFStat, AT_SYMLINK_NOFOLLOW);
626
0
      unlikely(Res != 0)) {
627
0
    return WasiUnexpect(fromErrNo(errno));
628
0
  }
629
630
0
  Filestat.dev = EndianValue(SysFStat.st_dev).le();
631
0
  Filestat.ino = EndianValue(SysFStat.st_ino).le();
632
0
  Filestat.filetype = fromFileType(SysFStat.st_mode);
633
0
  Filestat.nlink = EndianValue(SysFStat.st_nlink).le();
634
0
  Filestat.size = EndianValue(SysFStat.st_size).le();
635
0
  Filestat.atim = fromTimespec(SysFStat.st_atim).le();
636
0
  Filestat.mtim = fromTimespec(SysFStat.st_mtim).le();
637
0
  Filestat.ctim = fromTimespec(SysFStat.st_ctim).le();
638
639
0
  return {};
640
0
}
641
642
WasiExpect<void>
643
INode::pathFilestatSetTimes(std::string Path, __wasi_timestamp_t ATim,
644
                            __wasi_timestamp_t MTim,
645
0
                            __wasi_fstflags_t FstFlags) const noexcept {
646
0
#if __GLIBC_PREREQ(2, 6) || __BIONIC__
647
0
  timespec SysTimespec[2];
648
0
  if (FstFlags & __WASI_FSTFLAGS_ATIM) {
649
0
    SysTimespec[0] = toTimespec(ATim);
650
0
  } else if (FstFlags & __WASI_FSTFLAGS_ATIM_NOW) {
651
0
    SysTimespec[0].tv_nsec = UTIME_NOW;
652
0
  } else {
653
0
    SysTimespec[0].tv_nsec = UTIME_OMIT;
654
0
  }
655
0
  if (FstFlags & __WASI_FSTFLAGS_MTIM) {
656
0
    SysTimespec[1] = toTimespec(MTim);
657
0
  } else if (FstFlags & __WASI_FSTFLAGS_MTIM_NOW) {
658
0
    SysTimespec[1].tv_nsec = UTIME_NOW;
659
0
  } else {
660
0
    SysTimespec[1].tv_nsec = UTIME_OMIT;
661
0
  }
662
663
0
  if (auto Res =
664
0
          ::utimensat(Fd, Path.c_str(), SysTimespec, AT_SYMLINK_NOFOLLOW);
665
0
      unlikely(Res != 0)) {
666
0
    return WasiUnexpect(fromErrNo(errno));
667
0
  }
668
#else
669
  bool NeedNow = false;
670
  bool NeedFile = false;
671
  if (FstFlags & __WASI_FSTFLAGS_ATIM) {
672
    // Nothing to do.
673
  } else if (FstFlags & __WASI_FSTFLAGS_ATIM_NOW) {
674
    NeedNow = true;
675
  } else {
676
    NeedFile = true;
677
  }
678
  if (FstFlags & __WASI_FSTFLAGS_MTIM) {
679
    // Nothing to do.
680
  } else if (FstFlags & __WASI_FSTFLAGS_MTIM_NOW) {
681
    NeedNow = true;
682
  } else {
683
    NeedFile = true;
684
  }
685
686
#ifdef O_PATH
687
  const int OFlags = O_PATH | O_SYMLINK;
688
#else
689
  const int OFlags = O_RDONLY | O_SYMLINK;
690
#endif
691
692
  FdHolder Target(::openat(Fd, Path.c_str(), OFlags));
693
  if (unlikely(!Target.ok())) {
694
    return WasiUnexpect(fromErrNo(errno));
695
  }
696
697
  struct stat SysStat;
698
  if (NeedFile) {
699
    if (auto Res = ::fstat(Target.Fd, &SysStat); unlikely(Res != 0)) {
700
      return WasiUnexpect(fromErrNo(errno));
701
    }
702
  }
703
704
  timespec Now;
705
  if (NeedNow) {
706
    if (auto Res = ::clock_gettime(CLOCK_REALTIME, &Now); unlikely(Res != 0)) {
707
      return WasiUnexpect(fromErrNo(errno));
708
    }
709
  }
710
711
  timeval SysTimeval[2];
712
  if (FstFlags & __WASI_FSTFLAGS_ATIM) {
713
    SysTimeval[0] = toTimeval(ATim);
714
  } else if (FstFlags & __WASI_FSTFLAGS_ATIM_NOW) {
715
    SysTimeval[0] = toTimeval(Now);
716
  } else {
717
    SysTimeval[0] = toTimeval(SysStat.st_atim);
718
  }
719
  if (FstFlags & __WASI_FSTFLAGS_MTIM) {
720
    SysTimeval[1] = toTimeval(MTim);
721
  } else if (FstFlags & __WASI_FSTFLAGS_MTIM_NOW) {
722
    SysTimeval[1] = toTimeval(Now);
723
  } else {
724
    SysTimeval[1] = toTimeval(SysStat.st_mtim);
725
  }
726
727
  if (auto Res = ::futimes(Target.Fd, SysTimeval); unlikely(Res != 0)) {
728
    return WasiUnexpect(fromErrNo(errno));
729
  }
730
#endif
731
732
0
  return {};
733
0
}
734
735
WasiExpect<void> INode::pathLink(const INode &Old, std::string OldPath,
736
                                 const INode &New,
737
0
                                 std::string NewPath) noexcept {
738
0
  if (auto Res = ::linkat(Old.Fd, OldPath.c_str(), New.Fd, NewPath.c_str(), 0);
739
0
      unlikely(Res != 0)) {
740
0
    return WasiUnexpect(fromErrNo(errno));
741
0
  }
742
743
0
  return {};
744
0
}
745
746
WasiExpect<INode> INode::pathOpen(std::string Path, __wasi_oflags_t OpenFlags,
747
                                  __wasi_fdflags_t FdFlags,
748
0
                                  VFS::Flags VFSFlags) const noexcept {
749
0
  const int Flags = openFlags(OpenFlags, FdFlags, VFSFlags);
750
751
0
  if (auto NewFd = ::openat(Fd, Path.c_str(), Flags, 0644);
752
0
      unlikely(NewFd < 0)) {
753
0
    return WasiUnexpect(fromErrNo(errno));
754
0
  } else {
755
0
    INode New(NewFd);
756
757
#ifndef O_CLOEXEC
758
    if (auto Res = ::fcntl(New.Fd, F_SETFD, FD_CLOEXEC); unlikely(Res != 0)) {
759
      return WasiUnexpect(fromErrNo(errno));
760
    }
761
#endif
762
0
    return New;
763
0
  }
764
0
}
765
766
WasiExpect<void> INode::pathReadlink(std::string Path, Span<char> Buffer,
767
0
                                     __wasi_size_t &NRead) const noexcept {
768
0
  if (auto Res = ::readlinkat(Fd, Path.c_str(), Buffer.data(), Buffer.size());
769
0
      unlikely(Res < 0)) {
770
0
    return WasiUnexpect(fromErrNo(errno));
771
0
  } else {
772
0
    NRead = EndianValue(static_cast<__wasi_size_t>(Res)).le();
773
0
  }
774
775
0
  return {};
776
0
}
777
778
0
WasiExpect<void> INode::pathRemoveDirectory(std::string Path) const noexcept {
779
0
  if (auto Res = ::unlinkat(Fd, Path.c_str(), AT_REMOVEDIR);
780
0
      unlikely(Res < 0)) {
781
0
    return WasiUnexpect(fromErrNo(errno));
782
0
  }
783
784
0
  return {};
785
0
}
786
787
WasiExpect<void> INode::pathRename(const INode &Old, std::string OldPath,
788
                                   const INode &New,
789
0
                                   std::string NewPath) noexcept {
790
0
  if (auto Res = ::renameat(Old.Fd, OldPath.c_str(), New.Fd, NewPath.c_str());
791
0
      unlikely(Res != 0)) {
792
0
    return WasiUnexpect(fromErrNo(errno));
793
0
  }
794
795
0
  return {};
796
0
}
797
798
WasiExpect<void> INode::pathSymlink(std::string OldPath,
799
0
                                    std::string NewPath) const noexcept {
800
0
  if (auto Res = ::symlinkat(OldPath.c_str(), Fd, NewPath.c_str());
801
0
      unlikely(Res != 0)) {
802
0
    return WasiUnexpect(fromErrNo(errno));
803
0
  }
804
805
0
  return {};
806
0
}
807
808
0
WasiExpect<void> INode::pathUnlinkFile(std::string Path) const noexcept {
809
0
  if (auto Res = ::unlinkat(Fd, Path.c_str(), 0); unlikely(Res < 0)) {
810
0
    return WasiUnexpect(fromErrNo(errno));
811
0
  }
812
813
0
  return {};
814
0
}
815
816
WasiExpect<void> INode::getAddrinfo(std::string_view Node,
817
                                    std::string_view Service,
818
                                    const __wasi_addrinfo_t &Hint,
819
                                    uint32_t MaxResLength,
820
                                    Span<__wasi_addrinfo_t *> WasiAddrinfoArray,
821
                                    Span<__wasi_sockaddr_t *> WasiSockaddrArray,
822
                                    Span<char *> AiAddrSaDataArray,
823
                                    Span<char *> AiCanonnameArray,
824
0
                                    /*Out*/ __wasi_size_t &ResLength) noexcept {
825
0
  const auto [NodeCStr, NodeBuf] = createNullTerminatedString(Node);
826
0
  const auto [ServiceCStr, ServiceBuf] = createNullTerminatedString(Service);
827
828
0
  struct addrinfo SysHint;
829
0
  SysHint.ai_flags = toAIFlags(Hint.ai_flags);
830
0
  SysHint.ai_family = toAddressFamily(Hint.ai_family);
831
0
  SysHint.ai_socktype = toSockType(Hint.ai_socktype);
832
0
  SysHint.ai_protocol = toProtocol(Hint.ai_protocol);
833
0
  SysHint.ai_addrlen = Hint.ai_addrlen;
834
0
  SysHint.ai_addr = nullptr;
835
0
  SysHint.ai_canonname = nullptr;
836
0
  SysHint.ai_next = nullptr;
837
838
0
  struct addrinfo *SysResPtr = nullptr;
839
0
  if (auto Res = ::getaddrinfo(NodeCStr, ServiceCStr, &SysHint, &SysResPtr);
840
0
      unlikely(Res < 0)) {
841
0
    return WasiUnexpect(fromEAIErrNo(Res));
842
0
  }
843
  // calculate ResLength
844
0
  if (ResLength = calculateAddrinfoLinkedListSize(SysResPtr);
845
0
      ResLength > MaxResLength) {
846
0
    ResLength = MaxResLength;
847
0
  }
848
849
0
  struct addrinfo *SysResItem = SysResPtr;
850
0
  for (uint32_t Idx = 0; Idx < ResLength; Idx++) {
851
0
    auto &CurAddrinfo = WasiAddrinfoArray[Idx];
852
0
    CurAddrinfo->ai_flags = fromAIFlags(SysResItem->ai_flags);
853
0
    CurAddrinfo->ai_socktype = fromSockType(SysResItem->ai_socktype);
854
0
    CurAddrinfo->ai_protocol = fromProtocol(SysResItem->ai_protocol);
855
0
    CurAddrinfo->ai_family = fromAddressFamily(SysResItem->ai_family);
856
0
    CurAddrinfo->ai_addrlen = SysResItem->ai_addrlen;
857
858
    // process ai_canonname in addrinfo
859
0
    if (SysResItem->ai_canonname != nullptr) {
860
0
      CurAddrinfo->ai_canonname_len = std::strlen(SysResItem->ai_canonname);
861
0
      auto &CurAiCanonname = AiCanonnameArray[Idx];
862
0
      std::memcpy(CurAiCanonname, SysResItem->ai_canonname,
863
0
                  CurAddrinfo->ai_canonname_len + 1);
864
0
    } else {
865
0
      CurAddrinfo->ai_canonname_len = 0;
866
0
    }
867
868
    // process socket address
869
0
    if (SysResItem->ai_addrlen > 0) {
870
0
      auto &CurSockaddr = WasiSockaddrArray[Idx];
871
0
      CurSockaddr->sa_family =
872
0
          fromAddressFamily(SysResItem->ai_addr->sa_family);
873
874
      // process sa_data in socket address
875
0
      size_t SaSize = 0;
876
0
      switch (CurSockaddr->sa_family) {
877
0
      case __WASI_ADDRESS_FAMILY_INET4:
878
0
        SaSize = sizeof(sockaddr_in) - offsetof(sockaddr_in, sin_port);
879
0
        break;
880
0
      case __WASI_ADDRESS_FAMILY_INET6:
881
0
        SaSize = sizeof(sockaddr_in6) - offsetof(sockaddr_in6, sin6_port);
882
0
        break;
883
0
      default:
884
0
        assumingUnreachable();
885
0
      }
886
0
      std::memcpy(AiAddrSaDataArray[Idx], SysResItem->ai_addr->sa_data, SaSize);
887
0
      CurSockaddr->sa_data_len = __wasi_size_t(SaSize);
888
0
    }
889
    // process ai_next in addrinfo
890
0
    SysResItem = SysResItem->ai_next;
891
0
  }
892
0
  ::freeaddrinfo(SysResPtr);
893
894
0
  return {};
895
0
}
896
897
WasiExpect<INode> INode::sockOpen(__wasi_address_family_t AddressFamily,
898
0
                                  __wasi_sock_type_t SockType) noexcept {
899
0
  int SysProtocol = IPPROTO_IP;
900
0
  int SysDomain = 0;
901
0
  int SysType = 0;
902
903
0
  switch (AddressFamily) {
904
0
  case __WASI_ADDRESS_FAMILY_INET4:
905
0
    SysDomain = AF_INET;
906
0
    break;
907
0
  case __WASI_ADDRESS_FAMILY_INET6:
908
0
    SysDomain = AF_INET6;
909
0
    break;
910
0
  case __WASI_ADDRESS_FAMILY_AF_UNIX:
911
0
    SysDomain = AF_UNIX;
912
0
    break;
913
0
  default:
914
0
    return WasiUnexpect(__WASI_ERRNO_INVAL);
915
0
  }
916
917
0
  switch (SockType) {
918
0
  case __WASI_SOCK_TYPE_SOCK_DGRAM:
919
0
    SysType = SOCK_DGRAM;
920
0
    break;
921
0
  case __WASI_SOCK_TYPE_SOCK_STREAM:
922
0
    SysType = SOCK_STREAM;
923
0
    break;
924
0
  default:
925
0
    return WasiUnexpect(__WASI_ERRNO_INVAL);
926
0
  }
927
928
0
  if (auto NewFd = ::socket(SysDomain, SysType, SysProtocol);
929
0
      unlikely(NewFd < 0)) {
930
0
    return WasiUnexpect(fromErrNo(errno));
931
0
  } else {
932
0
    INode New(NewFd);
933
0
    return New;
934
0
  }
935
0
}
936
937
struct SockEmptyAddr {};
938
using VarAddrT = std::variant<SockEmptyAddr, sockaddr_storage, sockaddr,
939
                              sockaddr_in, sockaddr_in6, sockaddr_un>;
940
941
struct VarAddrBuf {
942
0
  template <typename T> sockaddr *operator()(T &V) {
943
0
    return reinterpret_cast<struct sockaddr *>(&V);
944
0
  }
Unexecuted instantiation: sockaddr* WasmEdge::Host::WASI::VarAddrBuf::operator()<sockaddr_storage>(sockaddr_storage&)
Unexecuted instantiation: sockaddr* WasmEdge::Host::WASI::VarAddrBuf::operator()<sockaddr>(sockaddr&)
Unexecuted instantiation: sockaddr* WasmEdge::Host::WASI::VarAddrBuf::operator()<sockaddr_in>(sockaddr_in&)
Unexecuted instantiation: sockaddr* WasmEdge::Host::WASI::VarAddrBuf::operator()<sockaddr_in6>(sockaddr_in6&)
Unexecuted instantiation: sockaddr* WasmEdge::Host::WASI::VarAddrBuf::operator()<sockaddr_un>(sockaddr_un&)
945
0
  sockaddr *operator()(SockEmptyAddr &) { return nullptr; }
946
};
947
948
struct VarAddrSize {
949
0
  template <typename T> int operator()(const T &) { return sizeof(T); }
Unexecuted instantiation: int WasmEdge::Host::WASI::VarAddrSize::operator()<sockaddr_storage>(sockaddr_storage const&)
Unexecuted instantiation: int WasmEdge::Host::WASI::VarAddrSize::operator()<sockaddr>(sockaddr const&)
Unexecuted instantiation: int WasmEdge::Host::WASI::VarAddrSize::operator()<sockaddr_in>(sockaddr_in const&)
Unexecuted instantiation: int WasmEdge::Host::WASI::VarAddrSize::operator()<sockaddr_in6>(sockaddr_in6 const&)
950
0
  int operator()(const SockEmptyAddr &) { return 0; }
951
0
  int operator()(const sockaddr_un &U) {
952
0
    const auto Len = strnlen(U.sun_path, sizeof(U.sun_path));
953
0
    if (Len == 0 || Len == sizeof(U.sun_path)) {
954
0
      return sizeof(sockaddr_un);
955
0
    }
956
0
    return static_cast<int>(offsetof(sockaddr_un, sun_path) + Len + 1);
957
0
  }
958
};
959
960
static VarAddrT sockAddressAssignHelper(__wasi_address_family_t AddrFamily,
961
                                        const Span<const uint8_t> &Address,
962
0
                                        uint16_t Port) {
963
0
  VarAddrT Addr;
964
0
  if (Address.size() == 0) {
965
0
    Addr.emplace<SockEmptyAddr>();
966
0
  } else if (AddrFamily == __WASI_ADDRESS_FAMILY_INET4) {
967
0
    auto &ServerAddr4 = Addr.emplace<sockaddr_in>();
968
969
0
    ServerAddr4.sin_family = AF_INET;
970
0
    ServerAddr4.sin_port = htons(Port);
971
0
    assuming(Address.size() >= sizeof(in_addr));
972
0
    std::memcpy(&ServerAddr4.sin_addr, Address.data(), sizeof(in_addr));
973
0
  } else if (AddrFamily == __WASI_ADDRESS_FAMILY_INET6) {
974
0
    auto &ServerAddr6 = Addr.emplace<sockaddr_in6>();
975
976
0
    ServerAddr6.sin6_family = AF_INET6;
977
0
    ServerAddr6.sin6_port = htons(Port);
978
0
    ServerAddr6.sin6_flowinfo = 0;
979
0
    assuming(Address.size() >= sizeof(in6_addr));
980
0
    std::memcpy(&ServerAddr6.sin6_addr, Address.data(), sizeof(in6_addr));
981
0
  } else if (AddrFamily == __WASI_ADDRESS_FAMILY_AF_UNIX) {
982
0
    auto &ServerAddrUN = Addr.emplace<sockaddr_un>();
983
984
0
    ServerAddrUN.sun_family = AF_UNIX;
985
    // The length of sockaddr_un::sun_path depends on the current system.
986
    // We should always check its size.
987
0
    assuming(Address.size() >= sizeof(sockaddr_un::sun_path));
988
0
    std::memcpy(&ServerAddrUN.sun_path, Address.data(),
989
0
                sizeof(sockaddr_un::sun_path));
990
0
  } else {
991
0
    assumingUnreachable();
992
0
  }
993
994
0
  return Addr;
995
0
}
996
997
WasiExpect<void> INode::sockBind(__wasi_address_family_t AddressFamily,
998
                                 Span<const uint8_t> Address,
999
0
                                 uint16_t Port) noexcept {
1000
0
  auto AddressBuffer = sockAddressAssignHelper(AddressFamily, Address, Port);
1001
1002
0
  auto ServerAddr = std::visit(VarAddrBuf(), AddressBuffer);
1003
0
  int Size = std::visit(VarAddrSize(), AddressBuffer);
1004
1005
0
  if (auto Res = ::bind(Fd, ServerAddr, Size); unlikely(Res < 0)) {
1006
0
    return WasiUnexpect(fromErrNo(errno));
1007
0
  }
1008
0
  return {};
1009
0
}
1010
1011
0
WasiExpect<void> INode::sockListen(int32_t Backlog) noexcept {
1012
0
  if (auto Res = ::listen(Fd, Backlog); unlikely(Res < 0)) {
1013
0
    return WasiUnexpect(fromErrNo(errno));
1014
0
  }
1015
0
  return {};
1016
0
}
1017
1018
0
WasiExpect<INode> INode::sockAccept(__wasi_fdflags_t FdFlags) noexcept {
1019
0
  int NewFd;
1020
0
  if (NewFd = ::accept(Fd, nullptr, nullptr); unlikely(NewFd < 0)) {
1021
0
    return WasiUnexpect(fromErrNo(errno));
1022
0
  }
1023
1024
0
  INode New(NewFd);
1025
1026
0
  if (FdFlags & __WASI_FDFLAGS_NONBLOCK) {
1027
0
    int SysFlag = fcntl(NewFd, F_GETFL, 0);
1028
0
    SysFlag |= O_NONBLOCK;
1029
0
    if (auto Res = ::fcntl(NewFd, F_SETFL, SysFlag); unlikely(Res != 0)) {
1030
0
      return WasiUnexpect(fromErrNo(errno));
1031
0
    }
1032
0
  }
1033
1034
0
  return New;
1035
0
}
1036
1037
WasiExpect<void> INode::sockConnect(__wasi_address_family_t AddressFamily,
1038
                                    Span<const uint8_t> Address,
1039
0
                                    uint16_t Port) noexcept {
1040
0
  auto AddressBuffer = sockAddressAssignHelper(AddressFamily, Address, Port);
1041
1042
0
  auto ClientAddr = std::visit(VarAddrBuf(), AddressBuffer);
1043
0
  int Size = std::visit(VarAddrSize(), AddressBuffer);
1044
1045
0
  if (auto Res = ::connect(Fd, ClientAddr, Size); unlikely(Res < 0)) {
1046
0
    return WasiUnexpect(fromErrNo(errno));
1047
0
  }
1048
1049
0
  return {};
1050
0
}
1051
1052
WasiExpect<void> INode::sockRecv(Span<Span<uint8_t>> RiData,
1053
                                 __wasi_riflags_t RiFlags, __wasi_size_t &NRead,
1054
0
                                 __wasi_roflags_t &RoFlags) const noexcept {
1055
0
  return sockRecvFrom(RiData, RiFlags, nullptr, {}, nullptr, NRead, RoFlags);
1056
0
}
1057
1058
WasiExpect<void> INode::sockRecvFrom(Span<Span<uint8_t>> RiData,
1059
                                     __wasi_riflags_t RiFlags,
1060
                                     __wasi_address_family_t *AddressFamilyPtr,
1061
                                     Span<uint8_t> Address, uint16_t *PortPtr,
1062
                                     __wasi_size_t &NRead,
1063
0
                                     __wasi_roflags_t &RoFlags) const noexcept {
1064
0
  int SysRiFlags = 0;
1065
0
  if (RiFlags & __WASI_RIFLAGS_RECV_PEEK) {
1066
0
    SysRiFlags |= MSG_PEEK;
1067
0
  }
1068
0
  if (RiFlags & __WASI_RIFLAGS_RECV_WAITALL) {
1069
0
    SysRiFlags |= MSG_WAITALL;
1070
0
  }
1071
1072
0
  iovec SysIOVs[kIOVMax];
1073
0
  size_t SysIOVsSize = 0;
1074
0
  for (auto &IOV : RiData) {
1075
0
    SysIOVs[SysIOVsSize].iov_base = IOV.data();
1076
0
    SysIOVs[SysIOVsSize].iov_len = IOV.size();
1077
0
    ++SysIOVsSize;
1078
0
  }
1079
1080
0
  const bool NeedAddress =
1081
0
      AddressFamilyPtr != nullptr || !Address.empty() || PortPtr != nullptr;
1082
0
  sockaddr_storage SockAddr = {};
1083
0
  msghdr SysMsgHdr;
1084
0
  if (NeedAddress) {
1085
0
    SysMsgHdr.msg_name = &SockAddr;
1086
0
    SysMsgHdr.msg_namelen = sizeof(SockAddr);
1087
0
  } else {
1088
0
    SysMsgHdr.msg_name = nullptr;
1089
0
    SysMsgHdr.msg_namelen = 0;
1090
0
  }
1091
0
  SysMsgHdr.msg_iov = SysIOVs;
1092
0
  SysMsgHdr.msg_iovlen = SysIOVsSize;
1093
0
  SysMsgHdr.msg_control = nullptr;
1094
0
  SysMsgHdr.msg_controllen = 0;
1095
0
  SysMsgHdr.msg_flags = 0;
1096
1097
  // Store recv bytes length and flags.
1098
0
  if (auto Res = ::recvmsg(Fd, &SysMsgHdr, SysRiFlags); unlikely(Res < 0)) {
1099
0
    return WasiUnexpect(fromErrNo(errno));
1100
0
  } else {
1101
0
    NRead = EndianValue(static_cast<__wasi_size_t>(Res)).le();
1102
0
  }
1103
1104
0
  if (NeedAddress) {
1105
0
    switch (SockAddr.ss_family) {
1106
0
    case AF_UNSPEC: {
1107
0
      spdlog::warn("remote address unavailable"sv);
1108
      // If ss_family is AF_UNSPEC, accessing the other members is
1109
      // undefined.
1110
0
      break;
1111
0
    }
1112
0
    case AF_INET: {
1113
0
      const auto &SockAddr4 = reinterpret_cast<sockaddr_in &>(SockAddr);
1114
0
      if (AddressFamilyPtr) {
1115
0
        *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_INET4;
1116
0
      }
1117
0
      if (Address.size() >= sizeof(in_addr)) {
1118
0
        std::memcpy(Address.data(), &SockAddr4.sin_addr, sizeof(in_addr));
1119
0
      }
1120
0
      if (PortPtr != nullptr) {
1121
0
        *PortPtr = SockAddr4.sin_port;
1122
0
      }
1123
0
      break;
1124
0
    }
1125
0
    case AF_INET6: {
1126
0
      const auto &SockAddr6 = reinterpret_cast<sockaddr_in6 &>(SockAddr);
1127
0
      if (AddressFamilyPtr) {
1128
0
        *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_INET6;
1129
0
      }
1130
0
      if (Address.size() >= sizeof(in6_addr)) {
1131
0
        std::memcpy(Address.data(), &SockAddr6.sin6_addr, sizeof(in6_addr));
1132
0
      }
1133
0
      if (PortPtr != nullptr) {
1134
0
        *PortPtr = SockAddr6.sin6_port;
1135
0
      }
1136
0
      break;
1137
0
    }
1138
0
    case AF_UNIX: {
1139
0
      const auto &SockAddrUN = reinterpret_cast<sockaddr_un &>(SockAddr);
1140
0
      if (AddressFamilyPtr) {
1141
0
        *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_AF_UNIX;
1142
0
      }
1143
0
      if (Address.size() >= sizeof(sockaddr_un::sun_path)) {
1144
0
        std::memcpy(Address.data(), &SockAddrUN.sun_path,
1145
0
                    sizeof(sockaddr_un::sun_path));
1146
0
      } else {
1147
0
        return WasiUnexpect(__WASI_ERRNO_INVAL);
1148
0
      }
1149
0
      break;
1150
0
    }
1151
0
    default:
1152
0
      return WasiUnexpect(__WASI_ERRNO_NOSYS);
1153
0
    }
1154
0
  }
1155
1156
0
  RoFlags = static_cast<__wasi_roflags_t>(0);
1157
0
  if (SysMsgHdr.msg_flags & MSG_TRUNC) {
1158
0
    RoFlags |= __WASI_ROFLAGS_RECV_DATA_TRUNCATED;
1159
0
  }
1160
0
  RoFlags = EndianValue(RoFlags).le();
1161
1162
0
  return {};
1163
0
}
1164
1165
WasiExpect<void> INode::sockSend(Span<Span<const uint8_t>> SiData,
1166
                                 __wasi_siflags_t SiFlags,
1167
0
                                 __wasi_size_t &NWritten) const noexcept {
1168
0
  return sockSendTo(SiData, SiFlags, __WASI_ADDRESS_FAMILY_UNSPEC, {}, 0,
1169
0
                    NWritten);
1170
0
}
1171
1172
WasiExpect<void> INode::sockSendTo(Span<Span<const uint8_t>> SiData,
1173
                                   __wasi_siflags_t,
1174
                                   __wasi_address_family_t AddressFamily,
1175
                                   Span<const uint8_t> Address, uint16_t Port,
1176
0
                                   __wasi_size_t &NWritten) const noexcept {
1177
0
  int SysSiFlags = MSG_NOSIGNAL;
1178
0
  sockaddr *ClientAddr = nullptr;
1179
0
  socklen_t MsgNameLen = 0;
1180
0
  VarAddrT AddressBuffer;
1181
1182
0
  if (Address.size()) {
1183
0
    AddressBuffer = sockAddressAssignHelper(AddressFamily, Address, Port);
1184
0
    ClientAddr = std::visit(VarAddrBuf(), AddressBuffer);
1185
0
    MsgNameLen = std::visit(VarAddrSize(), AddressBuffer);
1186
0
  }
1187
1188
0
  iovec SysIOVs[kIOVMax];
1189
0
  size_t SysIOVsSize = 0;
1190
0
  for (auto &IOV : SiData) {
1191
0
    SysIOVs[SysIOVsSize].iov_base = const_cast<uint8_t *>(IOV.data());
1192
0
    SysIOVs[SysIOVsSize].iov_len = IOV.size();
1193
0
    ++SysIOVsSize;
1194
0
  }
1195
1196
0
  msghdr SysMsgHdr;
1197
0
  SysMsgHdr.msg_name = MsgNameLen == 0 ? nullptr : ClientAddr;
1198
0
  SysMsgHdr.msg_namelen = MsgNameLen;
1199
0
  SysMsgHdr.msg_iov = SysIOVs;
1200
0
  SysMsgHdr.msg_iovlen = SysIOVsSize;
1201
0
  SysMsgHdr.msg_control = nullptr;
1202
0
  SysMsgHdr.msg_controllen = 0;
1203
1204
  // Store recv bytes length and flags.
1205
0
  if (auto Res = ::sendmsg(Fd, &SysMsgHdr, SysSiFlags); unlikely(Res < 0)) {
1206
0
    return WasiUnexpect(fromErrNo(errno));
1207
0
  } else {
1208
0
    NWritten = EndianValue(static_cast<__wasi_size_t>(Res)).le();
1209
0
  }
1210
1211
0
  return {};
1212
0
}
1213
1214
0
WasiExpect<void> INode::sockShutdown(__wasi_sdflags_t SdFlags) const noexcept {
1215
0
  int SysFlags = 0;
1216
0
  if (SdFlags == __WASI_SDFLAGS_RD) {
1217
0
    SysFlags = SHUT_RD;
1218
0
  } else if (SdFlags == __WASI_SDFLAGS_WR) {
1219
0
    SysFlags = SHUT_WR;
1220
0
  } else if (SdFlags == (__WASI_SDFLAGS_RD | __WASI_SDFLAGS_WR)) {
1221
0
    SysFlags = SHUT_RDWR;
1222
0
  }
1223
1224
0
  if (auto Res = ::shutdown(Fd, SysFlags); unlikely(Res < 0)) {
1225
0
    return WasiUnexpect(fromErrNo(errno));
1226
0
  }
1227
1228
0
  return {};
1229
0
}
1230
1231
WasiExpect<void> INode::sockGetOpt(__wasi_sock_opt_level_t SockOptLevel,
1232
                                   __wasi_sock_opt_so_t SockOptName,
1233
0
                                   Span<uint8_t> &Flag) const noexcept {
1234
0
  auto SysSockOptLevel = toSockOptLevel(SockOptLevel);
1235
0
  auto SysSockOptName = toSockOptSoName(SockOptName);
1236
0
  socklen_t Size = static_cast<socklen_t>(Flag.size());
1237
0
  if (auto Res =
1238
0
          ::getsockopt(Fd, SysSockOptLevel, SysSockOptName, Flag.data(), &Size);
1239
0
      unlikely(Res < 0)) {
1240
0
    return WasiUnexpect(fromErrNo(errno));
1241
0
  }
1242
1243
0
  switch (SockOptName) {
1244
0
  case __WASI_SOCK_OPT_SO_ERROR: {
1245
0
    assuming(Size == sizeof(int32_t));
1246
0
    Flag = Flag.first(static_cast<size_t>(Size));
1247
0
    auto *Error = reinterpret_cast<int32_t *>(Flag.data());
1248
0
    *Error = static_cast<int32_t>(fromErrNo(*Error));
1249
0
    break;
1250
0
  }
1251
0
  case __WASI_SOCK_OPT_SO_TYPE: {
1252
0
    assuming(Size == sizeof(int32_t));
1253
0
    Flag = Flag.first(static_cast<size_t>(Size));
1254
0
    assuming(Flag.size() == sizeof(int32_t));
1255
0
    auto &SockType = *reinterpret_cast<int32_t *>(Flag.data());
1256
0
    SockType = static_cast<int32_t>(fromSockType(SockType));
1257
0
    break;
1258
0
  }
1259
0
  default:
1260
0
    Flag = Flag.first(static_cast<size_t>(Size));
1261
0
  }
1262
1263
0
  return {};
1264
0
}
1265
1266
WasiExpect<void> INode::sockSetOpt(__wasi_sock_opt_level_t SockOptLevel,
1267
                                   __wasi_sock_opt_so_t SockOptName,
1268
0
                                   Span<const uint8_t> Flag) const noexcept {
1269
0
  auto SysSockOptLevel = toSockOptLevel(SockOptLevel);
1270
0
  auto SysSockOptName = toSockOptSoName(SockOptName);
1271
1272
0
  if (auto Res = ::setsockopt(Fd, SysSockOptLevel, SysSockOptName, Flag.data(),
1273
0
                              Flag.size());
1274
0
      unlikely(Res < 0)) {
1275
0
    return WasiUnexpect(fromErrNo(errno));
1276
0
  }
1277
1278
0
  return {};
1279
0
}
1280
1281
WasiExpect<void>
1282
INode::sockGetLocalAddr(__wasi_address_family_t *AddressFamilyPtr,
1283
                        Span<uint8_t> Address,
1284
0
                        uint16_t *PortPtr) const noexcept {
1285
0
  sockaddr_storage SocketAddr = {};
1286
0
  socklen_t Slen = sizeof(SocketAddr);
1287
1288
0
  if (auto Res =
1289
0
          ::getsockname(Fd, reinterpret_cast<sockaddr *>(&SocketAddr), &Slen);
1290
0
      unlikely(Res < 0)) {
1291
0
    return WasiUnexpect(fromErrNo(errno));
1292
0
  }
1293
1294
0
  switch (SocketAddr.ss_family) {
1295
0
  case AF_INET: {
1296
0
    if (Address.size() < sizeof(in_addr)) {
1297
0
      return WasiUnexpect(__WASI_ERRNO_NOMEM);
1298
0
    }
1299
0
    const auto &SocketAddr4 = reinterpret_cast<sockaddr_in &>(SocketAddr);
1300
0
    if (AddressFamilyPtr) {
1301
0
      *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_INET4;
1302
0
    }
1303
0
    if (PortPtr) {
1304
0
      *PortPtr = ntohs(SocketAddr4.sin_port);
1305
0
    }
1306
0
    std::memcpy(Address.data(), &SocketAddr4.sin_addr, sizeof(in_addr));
1307
0
    return {};
1308
0
  }
1309
0
  case AF_INET6: {
1310
0
    if (Address.size() < sizeof(in6_addr)) {
1311
0
      return WasiUnexpect(__WASI_ERRNO_NOMEM);
1312
0
    }
1313
0
    const auto &SocketAddr6 = reinterpret_cast<sockaddr_in6 &>(SocketAddr);
1314
0
    if (AddressFamilyPtr) {
1315
0
      *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_INET6;
1316
0
    }
1317
0
    if (PortPtr) {
1318
0
      *PortPtr = ntohs(SocketAddr6.sin6_port);
1319
0
    }
1320
0
    std::memcpy(Address.data(), &SocketAddr6.sin6_addr, sizeof(in6_addr));
1321
0
    return {};
1322
0
  }
1323
0
  case AF_UNIX: {
1324
0
    if (Address.size() < sizeof(sockaddr_un::sun_path)) {
1325
0
      return WasiUnexpect(__WASI_ERRNO_NOMEM);
1326
0
    }
1327
0
    const auto &SocketAddrUN = reinterpret_cast<sockaddr_un &>(SocketAddr);
1328
0
    if (AddressFamilyPtr) {
1329
0
      *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_AF_UNIX;
1330
0
    }
1331
1332
0
    std::memcpy(Address.data(), &SocketAddrUN.sun_path,
1333
0
                sizeof(sockaddr_un::sun_path));
1334
0
    return {};
1335
0
  }
1336
0
  default:
1337
0
    return WasiUnexpect(__WASI_ERRNO_NOSYS);
1338
0
  }
1339
0
}
1340
1341
WasiExpect<void>
1342
INode::sockGetPeerAddr(__wasi_address_family_t *AddressFamilyPtr,
1343
                       Span<uint8_t> Address,
1344
0
                       uint16_t *PortPtr) const noexcept {
1345
0
  sockaddr_storage SocketAddr = {};
1346
0
  socklen_t Slen = sizeof(SocketAddr);
1347
1348
0
  if (auto Res =
1349
0
          ::getpeername(Fd, reinterpret_cast<sockaddr *>(&SocketAddr), &Slen);
1350
0
      unlikely(Res < 0)) {
1351
0
    return WasiUnexpect(fromErrNo(errno));
1352
0
  }
1353
1354
0
  switch (SocketAddr.ss_family) {
1355
0
  case AF_INET: {
1356
0
    if (Address.size() < sizeof(in_addr)) {
1357
0
      return WasiUnexpect(__WASI_ERRNO_NOMEM);
1358
0
    }
1359
0
    const auto &SocketAddr4 = reinterpret_cast<sockaddr_in &>(SocketAddr);
1360
0
    if (AddressFamilyPtr) {
1361
0
      *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_INET4;
1362
0
    }
1363
0
    if (PortPtr) {
1364
0
      *PortPtr = ntohs(SocketAddr4.sin_port);
1365
0
    }
1366
0
    std::memcpy(Address.data(), &SocketAddr4.sin_addr, sizeof(in_addr));
1367
0
    return {};
1368
0
  }
1369
0
  case AF_INET6: {
1370
0
    if (Address.size() < sizeof(in6_addr)) {
1371
0
      return WasiUnexpect(__WASI_ERRNO_NOMEM);
1372
0
    }
1373
0
    const auto &SocketAddr6 = reinterpret_cast<sockaddr_in6 &>(SocketAddr);
1374
0
    if (AddressFamilyPtr) {
1375
0
      *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_INET6;
1376
0
    }
1377
0
    if (PortPtr) {
1378
0
      *PortPtr = ntohs(SocketAddr6.sin6_port);
1379
0
    }
1380
0
    std::memcpy(Address.data(), &SocketAddr6.sin6_addr, sizeof(in6_addr));
1381
0
    return {};
1382
0
  }
1383
0
  case AF_UNIX: {
1384
0
    if (Address.size() < sizeof(sockaddr_un::sun_path)) {
1385
0
      return WasiUnexpect(__WASI_ERRNO_NOMEM);
1386
0
    }
1387
0
    const auto &SocketAddrUN = reinterpret_cast<sockaddr_un &>(SocketAddr);
1388
0
    if (AddressFamilyPtr) {
1389
0
      *AddressFamilyPtr = __WASI_ADDRESS_FAMILY_AF_UNIX;
1390
0
    }
1391
1392
0
    std::memcpy(Address.data(), &SocketAddrUN.sun_path,
1393
0
                sizeof(sockaddr_un::sun_path));
1394
0
    return {};
1395
0
  }
1396
0
  default:
1397
0
    return WasiUnexpect(__WASI_ERRNO_NOSYS);
1398
0
  }
1399
0
}
1400
1401
0
__wasi_filetype_t INode::unsafeFiletype() const noexcept {
1402
0
  return fromFileType(static_cast<mode_t>(Stat->st_mode));
1403
0
}
1404
1405
0
WasiExpect<__wasi_filetype_t> INode::filetype() const noexcept {
1406
0
  if (!Stat) {
1407
0
    EXPECTED_TRY(updateStat());
1408
0
  }
1409
0
  return unsafeFiletype();
1410
0
}
1411
1412
0
bool INode::isDirectory() const noexcept {
1413
0
  if (!Stat) {
1414
0
    if (!updateStat()) {
1415
0
      return false;
1416
0
    }
1417
0
  }
1418
0
  return (Stat->st_mode & S_IFMT) == S_IFDIR;
1419
0
}
1420
1421
0
bool INode::isSymlink() const noexcept {
1422
0
  if (!Stat) {
1423
0
    if (!updateStat()) {
1424
0
      return false;
1425
0
    }
1426
0
  }
1427
0
  return (Stat->st_mode & S_IFMT) == S_IFLNK;
1428
0
}
1429
1430
0
WasiExpect<__wasi_filesize_t> INode::filesize() const noexcept {
1431
0
  if (!Stat) {
1432
0
    EXPECTED_TRY(updateStat());
1433
0
  }
1434
0
  return Stat->st_size;
1435
0
}
1436
1437
0
bool INode::canBrowse() const noexcept {
1438
0
  return ::faccessat(Fd, ".", X_OK, 0) == 0;
1439
0
}
1440
1441
0
WasiExpect<void> INode::updateStat() const noexcept {
1442
0
  Stat.emplace();
1443
0
  if (unlikely(::fstat(Fd, &*Stat) != 0)) {
1444
0
    return WasiUnexpect(fromErrNo(errno));
1445
0
  }
1446
0
  return {};
1447
0
}
1448
1449
WasiExpect<Poller::Timer>
1450
0
PollerContext::acquireTimer(__wasi_clockid_t Clock) noexcept {
1451
0
  std::unique_lock Lock(TimerMutex);
1452
0
  if (auto &Timers = TimerPool.try_emplace(Clock).first->second;
1453
0
      Timers.empty()) {
1454
0
    Poller::Timer Result(Clock);
1455
0
    if (auto Res = Result.create(); unlikely(!Res)) {
1456
0
      return WasiUnexpect(fromErrNo(errno));
1457
0
    }
1458
0
    return Result;
1459
0
  } else {
1460
0
    Poller::Timer Result(std::move(Timers.back()));
1461
0
    Timers.pop_back();
1462
0
    return Result;
1463
0
  }
1464
0
}
1465
1466
0
void PollerContext::releaseTimer(Poller::Timer &&Timer) noexcept {
1467
0
  std::unique_lock Lock(TimerMutex);
1468
0
  const auto Clock = Timer.Clock;
1469
0
  try {
1470
0
    TimerPool.try_emplace(Clock).first->second.push_back(std::move(Timer));
1471
0
  } catch (std::bad_alloc &) {
1472
    // giving up caching timer
1473
0
  }
1474
0
}
1475
1476
#if __GLIBC_PREREQ(2, 8)
1477
0
WasiExpect<void> Poller::Timer::create() noexcept {
1478
0
  if (const auto NewFd =
1479
0
          ::timerfd_create(toClockId(Clock), TFD_NONBLOCK | TFD_CLOEXEC);
1480
0
      unlikely(NewFd < 0)) {
1481
0
    return WasiUnexpect(fromErrNo(errno));
1482
0
  } else {
1483
0
    FdHolder::emplace(NewFd);
1484
0
  }
1485
0
  return {};
1486
0
}
1487
1488
WasiExpect<void> Poller::Timer::setTime(__wasi_timestamp_t Timeout,
1489
                                        __wasi_timestamp_t,
1490
0
                                        __wasi_subclockflags_t Flags) noexcept {
1491
  // disarm timer
1492
0
  {
1493
0
    itimerspec Spec{toTimespec(0), toTimespec(0)};
1494
0
    if (auto Res = ::timerfd_settime(Fd, 0, &Spec, nullptr);
1495
0
        unlikely(Res < 0)) {
1496
0
      errno = 0;
1497
0
    }
1498
0
  }
1499
1500
0
  int SysFlags = 0;
1501
0
  if (Flags & __WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) {
1502
0
    SysFlags |= TFD_TIMER_ABSTIME;
1503
0
  }
1504
  // Zero timeout has a special meaning. When itimerspec is set to 0, it will
1505
  // disarm the timer.
1506
0
  Timeout = std::max<__wasi_timestamp_t>(Timeout, 1U);
1507
0
  itimerspec Spec{toTimespec(0), toTimespec(Timeout)};
1508
0
  if (auto Res = ::timerfd_settime(Fd, SysFlags, &Spec, nullptr);
1509
0
      unlikely(Res < 0)) {
1510
0
    return WasiUnexpect(fromErrNo(errno));
1511
0
  }
1512
1513
0
  return {};
1514
0
}
1515
#else
1516
namespace {
1517
static void sigevCallback(union sigval Value) noexcept {
1518
  const uint64_t One = 1;
1519
  ::write(Value.sival_int, &One, sizeof(One));
1520
}
1521
1522
WasiExpect<void> setTimerPipeFlags(int Fd) noexcept {
1523
  const int StatusFlags = ::fcntl(Fd, F_GETFL);
1524
  if (unlikely(StatusFlags < 0)) {
1525
    return WasiUnexpect(fromErrNo(errno));
1526
  }
1527
1528
  if (unlikely(::fcntl(Fd, F_SETFD, FD_CLOEXEC) != 0 ||
1529
               ::fcntl(Fd, F_SETFL, StatusFlags | O_NONBLOCK) != 0)) {
1530
    return WasiUnexpect(fromErrNo(errno));
1531
  }
1532
1533
  return {};
1534
}
1535
} // namespace
1536
1537
WasiExpect<void> Poller::Timer::create() noexcept {
1538
  {
1539
    int PipeFd[2] = {-1, -1};
1540
1541
    if (auto Res = ::pipe(PipeFd); unlikely(Res != 0)) {
1542
      return WasiUnexpect(fromErrNo(errno));
1543
    }
1544
    FdHolder::emplace(PipeFd[0]);
1545
    Notify.emplace(PipeFd[1]);
1546
  }
1547
1548
  timer_t TId;
1549
  {
1550
    sigevent Event;
1551
    Event.sigev_notify = SIGEV_THREAD;
1552
    Event.sigev_notify_function = &sigevCallback;
1553
    Event.sigev_value.sival_int = Notify.Fd;
1554
    Event.sigev_notify_attributes = nullptr;
1555
1556
    EXPECTED_TRY(setTimerPipeFlags(Fd));
1557
    EXPECTED_TRY(setTimerPipeFlags(Notify.Fd));
1558
1559
    if (unlikely(::timer_create(toClockId(Clock), &Event, &TId) < 0)) {
1560
      return WasiUnexpect(fromErrNo(errno));
1561
    }
1562
  }
1563
  TimerId.emplace(TId);
1564
  return {};
1565
}
1566
1567
WasiExpect<void> Poller::Timer::setTime(__wasi_timestamp_t Timeout,
1568
                                        __wasi_timestamp_t,
1569
                                        __wasi_subclockflags_t Flags) noexcept {
1570
  if (unlikely(!TimerId.Id)) {
1571
    return WasiUnexpect(__WASI_ERRNO_INVAL);
1572
  }
1573
1574
  // disarm timer
1575
  {
1576
    itimerspec Spec{toTimespec(0), toTimespec(0)};
1577
    if (auto Res = ::timer_settime(*TimerId.Id, 0, &Spec, nullptr);
1578
        unlikely(Res < 0)) {
1579
      errno = 0;
1580
    }
1581
  }
1582
  // reset pipe
1583
  {
1584
    uint64_t Buffer[16];
1585
    while (true) {
1586
      if (auto Res = ::read(Fd, &Buffer, sizeof(Buffer)); Res <= 0) {
1587
        break;
1588
      }
1589
    }
1590
  }
1591
1592
  int SysFlags = 0;
1593
  if (Flags & __WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) {
1594
    SysFlags |= TIMER_ABSTIME;
1595
  }
1596
  // Zero timeout has a special meaning. When itimerspec is set to 0, it will
1597
  // disarm the timer.
1598
  Timeout = std::max<__wasi_timestamp_t>(Timeout, 1U);
1599
  itimerspec Spec{toTimespec(0), toTimespec(Timeout)};
1600
  if (auto Res = ::timer_settime(*TimerId.Id, SysFlags, &Spec, nullptr);
1601
      unlikely(Res < 0)) {
1602
    return WasiUnexpect(fromErrNo(errno));
1603
  }
1604
  return {};
1605
}
1606
#endif
1607
1608
Poller::Poller(PollerContext &C) noexcept
1609
0
    : FdHolder(
1610
0
#if __GLIBC_PREREQ(2, 9)
1611
0
          ::epoll_create1(EPOLL_CLOEXEC)
1612
#else
1613
          // Guessing a number that might be sufficient for linux before 2.6.8
1614
          ::epoll_create(32)
1615
#endif
1616
0
              ),
1617
0
      Ctx(&C) {
1618
#if !__GLIBC_PREREQ(2, 9)
1619
  if (auto Res = ::fcntl(Fd, F_SETFD, FD_CLOEXEC); unlikely(Res != 0)) {
1620
    FdHolder::reset();
1621
    return;
1622
  }
1623
#endif
1624
0
}
1625
1626
0
WasiExpect<void> Poller::prepare(Span<__wasi_event_t> E) noexcept {
1627
0
  WasiEvents = E;
1628
0
  try {
1629
0
    Events.reserve(E.size());
1630
0
    Timers.reserve(E.size());
1631
0
    EPollEvents.reserve(E.size());
1632
0
  } catch (std::bad_alloc &) {
1633
0
    return WasiUnexpect(__WASI_ERRNO_NOMEM);
1634
0
  }
1635
1636
0
  return {};
1637
0
}
1638
1639
void Poller::clock(__wasi_clockid_t Clock, __wasi_timestamp_t Timeout,
1640
                   __wasi_timestamp_t Precision, __wasi_subclockflags_t Flags,
1641
0
                   __wasi_userdata_t UserData) noexcept {
1642
0
  assuming(Events.size() < WasiEvents.size());
1643
0
  auto &Event = Events.emplace_back();
1644
0
  Event.Valid = false;
1645
0
  Event.userdata = UserData;
1646
0
  Event.type = __WASI_EVENTTYPE_CLOCK;
1647
1648
0
  if (auto Res = Ctx->acquireTimer(Clock); unlikely(!Res)) {
1649
0
    Event.Valid = true;
1650
0
    Event.error = Res.error();
1651
0
    return;
1652
0
  } else {
1653
0
    Timers.emplace_back(std::move(*Res));
1654
0
  }
1655
1656
0
  auto &Timer = Timers.back();
1657
0
  if (auto Res = Timer.setTime(Timeout, Precision, Flags); unlikely(!Res)) {
1658
0
    Ctx->releaseTimer(std::move(Timer));
1659
0
    Timers.pop_back();
1660
0
    Event.Valid = true;
1661
0
    Event.error = Res.error();
1662
0
    return;
1663
0
  }
1664
1665
0
  assuming(Timer.Fd != Fd);
1666
0
  try {
1667
0
    auto [Iter, Added] = FdDatas.try_emplace(Timer.Fd);
1668
1669
0
    Iter->second.ReadEvent = &Event;
1670
0
    assuming(Added);
1671
1672
0
    epoll_event EPollEvent;
1673
0
    EPollEvent.events = EPOLLIN;
1674
0
#if defined(EPOLLRDHUP)
1675
0
    EPollEvent.events |= EPOLLRDHUP;
1676
0
#endif
1677
0
    EPollEvent.data.fd = Timer.Fd;
1678
1679
0
    if (auto Res = ::epoll_ctl(Fd, EPOLL_CTL_ADD, Timer.Fd, &EPollEvent);
1680
0
        unlikely(Res < 0)) {
1681
0
      FdDatas.erase(Iter);
1682
0
      Ctx->releaseTimer(std::move(Timer));
1683
0
      Timers.pop_back();
1684
0
      Event.Valid = true;
1685
0
      Event.error = fromErrNo(errno);
1686
0
      return;
1687
0
    }
1688
1689
0
    return;
1690
0
  } catch (std::bad_alloc &) {
1691
0
    Ctx->releaseTimer(std::move(Timer));
1692
0
    Timers.pop_back();
1693
0
    Event.Valid = true;
1694
0
    Event.error = __WASI_ERRNO_NOMEM;
1695
0
    return;
1696
0
  }
1697
0
}
1698
1699
0
void Poller::close(const INode &Node) noexcept {
1700
0
  FdDatas.erase(Node.Fd);
1701
0
  OldFdDatas.erase(Node.Fd);
1702
0
}
1703
1704
void Poller::read(const INode &Node, TriggerType Trigger,
1705
0
                  __wasi_userdata_t UserData) noexcept {
1706
0
  assuming(Events.size() < WasiEvents.size());
1707
0
  auto &Event = Events.emplace_back();
1708
0
  Event.Valid = false;
1709
0
  Event.userdata = UserData;
1710
0
  Event.type = __WASI_EVENTTYPE_FD_READ;
1711
1712
0
  assuming(Node.Fd != Fd);
1713
0
  try {
1714
0
    auto [Iter, Added] = FdDatas.try_emplace(Node.Fd);
1715
0
    const bool New = OldFdDatas.find(Node.Fd) == OldFdDatas.end();
1716
1717
0
    if (unlikely(!Added && Iter->second.ReadEvent != nullptr)) {
1718
0
      Event.Valid = true;
1719
0
      Event.error = __WASI_ERRNO_EXIST;
1720
0
      return;
1721
0
    }
1722
0
    Iter->second.ReadEvent = &Event;
1723
1724
0
    epoll_event EPollEvent;
1725
0
    EPollEvent.events = EPOLLIN;
1726
0
    if (!Added) {
1727
0
      assuming(Iter->second.WriteEvent != nullptr);
1728
0
      EPollEvent.events |= EPOLLOUT;
1729
0
    }
1730
0
    if (Trigger == TriggerType::Edge) {
1731
0
      EPollEvent.events |= EPOLLET;
1732
0
    }
1733
0
#if defined(EPOLLRDHUP)
1734
0
    EPollEvent.events |= EPOLLRDHUP;
1735
0
#endif
1736
0
    EPollEvent.data.fd = Node.Fd;
1737
1738
0
    if (likely(Added) && New) {
1739
0
      if (auto Res = ::epoll_ctl(Fd, EPOLL_CTL_ADD, Node.Fd, &EPollEvent);
1740
0
          unlikely(Res < 0)) {
1741
0
        FdDatas.erase(Iter);
1742
0
        Event.Valid = true;
1743
0
        Event.error = fromErrNo(errno);
1744
0
        return;
1745
0
      }
1746
0
    } else {
1747
0
      if (auto Res = ::epoll_ctl(Fd, EPOLL_CTL_MOD, Node.Fd, &EPollEvent);
1748
0
          unlikely(Res < 0)) {
1749
0
        Event.Valid = true;
1750
0
        Event.error = fromErrNo(errno);
1751
0
        return;
1752
0
      }
1753
0
    }
1754
0
  } catch (std::bad_alloc &) {
1755
0
    Event.Valid = true;
1756
0
    Event.error = __WASI_ERRNO_NOMEM;
1757
0
    return;
1758
0
  }
1759
0
}
1760
1761
void Poller::write(const INode &Node, TriggerType Trigger,
1762
0
                   __wasi_userdata_t UserData) noexcept {
1763
0
  assuming(Events.size() < WasiEvents.size());
1764
0
  auto &Event = Events.emplace_back();
1765
0
  Event.Valid = false;
1766
0
  Event.userdata = UserData;
1767
0
  Event.type = __WASI_EVENTTYPE_FD_WRITE;
1768
1769
0
  assuming(Node.Fd != Fd);
1770
0
  try {
1771
0
    auto [Iter, Added] = FdDatas.try_emplace(Node.Fd);
1772
0
    const bool New = OldFdDatas.find(Node.Fd) == OldFdDatas.end();
1773
1774
0
    if (unlikely(!Added && Iter->second.WriteEvent != nullptr)) {
1775
0
      Event.Valid = true;
1776
0
      Event.error = __WASI_ERRNO_EXIST;
1777
0
      return;
1778
0
    }
1779
0
    Iter->second.WriteEvent = &Event;
1780
1781
0
    epoll_event EPollEvent;
1782
0
    EPollEvent.events = EPOLLOUT;
1783
0
    if (!Added) {
1784
0
      assuming(Iter->second.ReadEvent != nullptr);
1785
0
      EPollEvent.events |= EPOLLIN;
1786
0
    }
1787
0
    if (Trigger == TriggerType::Edge) {
1788
0
      EPollEvent.events |= EPOLLET;
1789
0
    }
1790
0
#if defined(EPOLLRDHUP)
1791
0
    EPollEvent.events |= EPOLLRDHUP;
1792
0
#endif
1793
0
    EPollEvent.data.fd = Node.Fd;
1794
1795
0
    if (likely(Added) && New) {
1796
0
      if (auto Res = ::epoll_ctl(Fd, EPOLL_CTL_ADD, Node.Fd, &EPollEvent);
1797
0
          unlikely(Res < 0)) {
1798
0
        FdDatas.erase(Iter);
1799
0
        Event.Valid = true;
1800
0
        Event.error = fromErrNo(errno);
1801
0
        return;
1802
0
      }
1803
0
    } else {
1804
0
      if (auto Res = ::epoll_ctl(Fd, EPOLL_CTL_MOD, Node.Fd, &EPollEvent);
1805
0
          unlikely(Res < 0)) {
1806
0
        Event.Valid = true;
1807
0
        Event.error = fromErrNo(errno);
1808
0
        return;
1809
0
      }
1810
0
    }
1811
0
  } catch (std::bad_alloc &) {
1812
0
    Event.Valid = true;
1813
0
    Event.error = __WASI_ERRNO_NOMEM;
1814
0
    return;
1815
0
  }
1816
0
}
1817
1818
0
void Poller::wait() noexcept {
1819
0
  for (const auto &[NodeFd, FdData] : OldFdDatas) {
1820
0
    if (auto Iter = FdDatas.find(NodeFd); Iter == FdDatas.end()) {
1821
      // Remove unused event, ignore failed.
1822
      // In kernel before 2.6.9, EPOLL_CTL_DEL required a non-null pointer. Use
1823
      // `this` as the dummy parameter.
1824
0
      ::epoll_ctl(Fd, EPOLL_CTL_DEL, NodeFd,
1825
0
                  reinterpret_cast<struct epoll_event *>(this));
1826
0
    }
1827
0
  }
1828
1829
0
  EPollEvents.resize(Events.size());
1830
0
  const int Count =
1831
0
      ::epoll_wait(Fd, EPollEvents.data(), EPollEvents.size(), -1);
1832
0
  if (unlikely(Count < 0)) {
1833
0
    const auto Error = fromErrNo(errno);
1834
0
    for (auto &Event : Events) {
1835
0
      Event.Valid = true;
1836
0
      Event.error = Error;
1837
0
    }
1838
0
    return;
1839
0
  }
1840
1841
0
  auto ProcessEvent = [](const struct epoll_event &EPollEvent,
1842
0
                         OptionalEvent &Event) noexcept {
1843
0
    Event.Valid = true;
1844
0
    Event.error = __WASI_ERRNO_SUCCESS;
1845
0
    switch (Event.type) {
1846
0
    case __WASI_EVENTTYPE_CLOCK:
1847
0
      break;
1848
0
    case __WASI_EVENTTYPE_FD_READ: {
1849
0
      Event.fd_readwrite.flags = static_cast<__wasi_eventrwflags_t>(0);
1850
0
      if (EPollEvent.events & EPOLLHUP) {
1851
0
        Event.fd_readwrite.flags |= __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP;
1852
0
      }
1853
0
      bool UnknownNBytes = false;
1854
0
      int ReadBufUsed = 0;
1855
0
      if (auto Res = ::ioctl(EPollEvent.data.fd, FIONREAD, &ReadBufUsed);
1856
0
          unlikely(Res == 0)) {
1857
0
        UnknownNBytes = true;
1858
0
      }
1859
0
      if (UnknownNBytes) {
1860
0
        Event.fd_readwrite.nbytes = 1;
1861
0
      } else {
1862
0
        Event.fd_readwrite.nbytes = ReadBufUsed;
1863
0
      }
1864
0
      break;
1865
0
    }
1866
0
    case __WASI_EVENTTYPE_FD_WRITE: {
1867
0
      Event.fd_readwrite.flags = static_cast<__wasi_eventrwflags_t>(0);
1868
0
      if (EPollEvent.events & EPOLLHUP) {
1869
0
        Event.fd_readwrite.flags |= __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP;
1870
0
      }
1871
0
      bool UnknownNBytes = false;
1872
0
      int WriteBufSize = 0;
1873
0
      socklen_t IntSize = sizeof(WriteBufSize);
1874
0
      if (auto Res = ::getsockopt(EPollEvent.data.fd, SOL_SOCKET, SO_SNDBUF,
1875
0
                                  &WriteBufSize, &IntSize);
1876
0
          unlikely(Res != 0)) {
1877
0
        UnknownNBytes = true;
1878
0
      }
1879
0
      int WriteBufUsed = 0;
1880
0
      if (auto Res = ::ioctl(EPollEvent.data.fd, TIOCOUTQ, &WriteBufUsed);
1881
0
          unlikely(Res != 0)) {
1882
0
        UnknownNBytes = true;
1883
0
      }
1884
0
      if (UnknownNBytes) {
1885
0
        Event.fd_readwrite.nbytes = 1;
1886
0
      } else {
1887
0
        Event.fd_readwrite.nbytes = WriteBufSize - WriteBufUsed;
1888
0
      }
1889
0
      break;
1890
0
    }
1891
0
    }
1892
0
  };
1893
1894
0
  for (int I = 0; I < Count; ++I) {
1895
0
    const auto &EPollEvent = EPollEvents[I];
1896
0
    const auto Iter = FdDatas.find(EPollEvent.data.fd);
1897
0
    assuming(Iter != FdDatas.end());
1898
1899
0
    const bool NoInOut = !(EPollEvent.events & (EPOLLIN | EPOLLOUT));
1900
0
    if ((EPollEvent.events & EPOLLIN) ||
1901
0
        (NoInOut && EPollEvent.events & EPOLLHUP && Iter->second.ReadEvent)) {
1902
0
      assuming(Iter->second.ReadEvent);
1903
0
      assuming(Iter->second.ReadEvent->type == __WASI_EVENTTYPE_CLOCK ||
1904
0
               Iter->second.ReadEvent->type == __WASI_EVENTTYPE_FD_READ);
1905
0
      ProcessEvent(EPollEvent, *Iter->second.ReadEvent);
1906
0
    }
1907
0
    if (EPollEvent.events & EPOLLOUT ||
1908
0
        (NoInOut && EPollEvent.events & EPOLLHUP && Iter->second.WriteEvent)) {
1909
0
      assuming(Iter->second.WriteEvent);
1910
0
      assuming(Iter->second.WriteEvent->type == __WASI_EVENTTYPE_FD_WRITE);
1911
0
      ProcessEvent(EPollEvent, *Iter->second.WriteEvent);
1912
0
    }
1913
0
  }
1914
0
  for (auto &Timer : Timers) {
1915
    // Remove unused timer event, ignore failed.
1916
    // In kernel before 2.6.9, EPOLL_CTL_DEL required a non-null pointer. Use
1917
    // `this` as the dummy parameter.
1918
0
    ::epoll_ctl(Fd, EPOLL_CTL_DEL, Timer.Fd,
1919
0
                reinterpret_cast<struct epoll_event *>(this));
1920
0
    Ctx->releaseTimer(std::move(Timer));
1921
0
  }
1922
1923
0
  std::swap(FdDatas, OldFdDatas);
1924
0
  FdDatas.clear();
1925
0
  Timers.clear();
1926
0
  EPollEvents.clear();
1927
0
}
1928
1929
0
void Poller::reset() noexcept {
1930
0
  WasiEvents = {};
1931
0
  Events.clear();
1932
0
}
1933
1934
0
bool Poller::ok() noexcept { return FdHolder::ok(); }
1935
1936
} // namespace WASI
1937
} // namespace Host
1938
} // namespace WasmEdge
1939
1940
#endif