Coverage Report

Created: 2025-07-01 06:18

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