Coverage Report

Created: 2025-08-25 06:58

/src/WasmEdge/lib/host/wasi/environ.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 "host/wasi/environ.h"
5
#include "common/errcode.h"
6
#include "common/spdlog.h"
7
#include "host/wasi/vfs.h"
8
#include "host/wasi/vinode.h"
9
10
using namespace std::literals;
11
12
namespace WasmEdge {
13
namespace Host {
14
namespace WASI {
15
16
namespace {
17
static inline constexpr const __wasi_rights_t kPreOpenBaseRightsReadOnly =
18
    __WASI_RIGHTS_PATH_OPEN | __WASI_RIGHTS_FD_READDIR |
19
    __WASI_RIGHTS_PATH_READLINK | __WASI_RIGHTS_PATH_FILESTAT_GET |
20
    __WASI_RIGHTS_FD_FILESTAT_GET;
21
static inline constexpr const __wasi_rights_t kPreOpenInheritingRightsReadOnly =
22
    __WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_SEEK |
23
    __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS | __WASI_RIGHTS_FD_SYNC |
24
    __WASI_RIGHTS_FD_TELL | __WASI_RIGHTS_FD_ADVISE |
25
    __WASI_RIGHTS_PATH_CREATE_DIRECTORY | __WASI_RIGHTS_PATH_CREATE_FILE |
26
    __WASI_RIGHTS_PATH_LINK_SOURCE | __WASI_RIGHTS_PATH_LINK_TARGET |
27
    __WASI_RIGHTS_PATH_OPEN | __WASI_RIGHTS_FD_READDIR |
28
    __WASI_RIGHTS_PATH_READLINK | __WASI_RIGHTS_PATH_RENAME_SOURCE |
29
    __WASI_RIGHTS_PATH_RENAME_TARGET | __WASI_RIGHTS_PATH_FILESTAT_GET |
30
    __WASI_RIGHTS_FD_FILESTAT_GET | __WASI_RIGHTS_FD_FILESTAT_SET_TIMES |
31
    __WASI_RIGHTS_PATH_SYMLINK | __WASI_RIGHTS_PATH_REMOVE_DIRECTORY |
32
    __WASI_RIGHTS_PATH_UNLINK_FILE | __WASI_RIGHTS_POLL_FD_READWRITE;
33
static inline constexpr const __wasi_rights_t kPreOpenBaseRights =
34
    __WASI_RIGHTS_PATH_CREATE_DIRECTORY | __WASI_RIGHTS_PATH_CREATE_FILE |
35
    __WASI_RIGHTS_PATH_LINK_SOURCE | __WASI_RIGHTS_PATH_LINK_TARGET |
36
    __WASI_RIGHTS_PATH_OPEN | __WASI_RIGHTS_FD_READDIR |
37
    __WASI_RIGHTS_PATH_READLINK | __WASI_RIGHTS_PATH_RENAME_SOURCE |
38
    __WASI_RIGHTS_PATH_RENAME_TARGET | __WASI_RIGHTS_PATH_FILESTAT_GET |
39
    __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES | __WASI_RIGHTS_FD_FILESTAT_GET |
40
    __WASI_RIGHTS_FD_FILESTAT_SET_TIMES | __WASI_RIGHTS_PATH_SYMLINK |
41
    __WASI_RIGHTS_PATH_REMOVE_DIRECTORY | __WASI_RIGHTS_PATH_UNLINK_FILE |
42
    __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
43
static inline constexpr const __wasi_rights_t kPreOpenInheritingRights =
44
    __WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_SEEK |
45
    __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS | __WASI_RIGHTS_FD_SYNC |
46
    __WASI_RIGHTS_FD_TELL | __WASI_RIGHTS_FD_WRITE | __WASI_RIGHTS_FD_ADVISE |
47
    __WASI_RIGHTS_FD_ALLOCATE | __WASI_RIGHTS_PATH_CREATE_DIRECTORY |
48
    __WASI_RIGHTS_PATH_CREATE_FILE | __WASI_RIGHTS_PATH_LINK_SOURCE |
49
    __WASI_RIGHTS_PATH_LINK_TARGET | __WASI_RIGHTS_PATH_OPEN |
50
    __WASI_RIGHTS_FD_READDIR | __WASI_RIGHTS_PATH_READLINK |
51
    __WASI_RIGHTS_PATH_RENAME_SOURCE | __WASI_RIGHTS_PATH_RENAME_TARGET |
52
    __WASI_RIGHTS_PATH_FILESTAT_GET | __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE |
53
    __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES | __WASI_RIGHTS_FD_FILESTAT_GET |
54
    __WASI_RIGHTS_FD_FILESTAT_SET_SIZE | __WASI_RIGHTS_FD_FILESTAT_SET_TIMES |
55
    __WASI_RIGHTS_PATH_SYMLINK | __WASI_RIGHTS_PATH_REMOVE_DIRECTORY |
56
    __WASI_RIGHTS_PATH_UNLINK_FILE | __WASI_RIGHTS_POLL_FD_READWRITE |
57
    __WASI_RIGHTS_SOCK_SHUTDOWN;
58
static inline constexpr const __wasi_rights_t kStdInDefaultRights =
59
    __WASI_RIGHTS_FD_ADVISE | __WASI_RIGHTS_FD_FILESTAT_GET |
60
    __WASI_RIGHTS_FD_READ | __WASI_RIGHTS_POLL_FD_READWRITE;
61
static inline constexpr const __wasi_rights_t kStdOutDefaultRights =
62
    __WASI_RIGHTS_FD_ADVISE | __WASI_RIGHTS_FD_DATASYNC |
63
    __WASI_RIGHTS_FD_FILESTAT_GET | __WASI_RIGHTS_FD_SYNC |
64
    __WASI_RIGHTS_FD_WRITE | __WASI_RIGHTS_POLL_FD_READWRITE;
65
static inline constexpr const __wasi_rights_t kStdErrDefaultRights =
66
    kStdOutDefaultRights;
67
static inline constexpr const __wasi_rights_t kNoInheritingRights =
68
    static_cast<__wasi_rights_t>(0);
69
static inline constexpr const auto kReadOnly = "readonly"sv;
70
71
} // namespace
72
73
void Environ::init(Span<const std::string> Dirs, std::string ProgramName,
74
0
                   Span<const std::string> Args, Span<const std::string> Envs) {
75
0
  {
76
    // Open dir for WASI environment.
77
0
    std::vector<std::shared_ptr<VINode>> PreopenedDirs;
78
0
    PreopenedDirs.reserve(Dirs.size());
79
0
    for (const auto &Dir : Dirs) {
80
0
      const auto Pos = Dir.find(':');
81
0
      std::string HostDir =
82
0
          (Pos == std::string::npos) ? Dir : Dir.substr(Pos + 1);
83
      // Handle the readonly flag
84
0
      bool ReadOnly = false;
85
0
      if (const auto ROPos = HostDir.find(':'); ROPos != std::string::npos) {
86
0
        const auto Mode = HostDir.substr(ROPos + 1);
87
0
        HostDir = HostDir.substr(0, ROPos);
88
0
        if (kReadOnly == Mode) {
89
0
          ReadOnly = true;
90
0
        }
91
0
      }
92
0
      std::string GuestDir = VINode::canonicalGuest(
93
0
          (Pos == std::string::npos) ? std::string_view(Dir)
94
0
                                     : std::string_view(Dir).substr(0, Pos));
95
0
      if (GuestDir.size() == 0) {
96
0
        GuestDir = '/';
97
0
      }
98
0
      const auto BaseRights =
99
0
          ReadOnly ? kPreOpenBaseRightsReadOnly : kPreOpenBaseRights;
100
0
      const auto InheritingRights = ReadOnly ? kPreOpenInheritingRightsReadOnly
101
0
                                             : kPreOpenInheritingRights;
102
0
      if (auto Res = VINode::bind(BaseRights, InheritingRights,
103
0
                                  std::move(GuestDir), std::move(HostDir));
104
0
          unlikely(!Res)) {
105
0
        spdlog::error("Bind guest directory failed:{}"sv, Res.error());
106
0
        continue;
107
0
      } else {
108
0
        PreopenedDirs.emplace_back(std::move(*Res));
109
0
      }
110
0
    }
111
112
0
    std::sort(PreopenedDirs.begin(), PreopenedDirs.end());
113
114
0
    FdMap.emplace(0, VINode::stdIn(kStdInDefaultRights, kNoInheritingRights));
115
0
    FdMap.emplace(1, VINode::stdOut(kStdOutDefaultRights, kNoInheritingRights));
116
0
    FdMap.emplace(2, VINode::stdErr(kStdErrDefaultRights, kNoInheritingRights));
117
118
0
    int NewFd = 3;
119
0
    for (auto &PreopenedDir : PreopenedDirs) {
120
0
      FdMap.emplace(NewFd++, std::move(PreopenedDir));
121
0
    }
122
0
  }
123
124
0
  Arguments.resize(Args.size() + 1);
125
0
  Arguments.front() = std::move(ProgramName);
126
0
  std::copy(Args.begin(), Args.end(), Arguments.begin() + 1);
127
0
  Arguments.shrink_to_fit();
128
129
0
  EnvironVariables.resize(Envs.size());
130
0
  std::copy(Envs.begin(), Envs.end(), EnvironVariables.begin());
131
0
  EnvironVariables.shrink_to_fit();
132
133
0
  ExitCode = 0;
134
0
}
135
136
WasiExpect<void>
137
Environ::initWithFds(Span<const std::string> Dirs, std::string ProgramName,
138
                     Span<const std::string> Args, Span<const std::string> Envs,
139
0
                     int32_t StdInFd, int32_t StdOutFd, int32_t StdErrFd) {
140
0
  {
141
0
    auto StdInVNode =
142
0
        VINode::fromFd(StdInFd, kStdInDefaultRights, kNoInheritingRights);
143
0
    if (!StdInVNode) {
144
0
      return WasiUnexpect(StdInVNode.error());
145
0
    }
146
0
    FdMap.emplace(0, std::move(*StdInVNode));
147
0
    auto StdOutVNode =
148
0
        VINode::fromFd(StdOutFd, kStdOutDefaultRights, kNoInheritingRights);
149
0
    if (!StdOutVNode) {
150
0
      return WasiUnexpect(StdOutVNode.error());
151
0
    }
152
0
    FdMap.emplace(1, std::move(*StdOutVNode));
153
0
    auto StdErrVNode =
154
0
        VINode::fromFd(StdErrFd, kStdErrDefaultRights, kNoInheritingRights);
155
0
    if (!StdErrVNode) {
156
0
      return WasiUnexpect(StdErrVNode.error());
157
0
    }
158
0
    FdMap.emplace(2, std::move(*StdErrVNode));
159
160
    // Open dir for WASI environment.
161
0
    std::vector<std::shared_ptr<VINode>> PreopenedDirs;
162
0
    PreopenedDirs.reserve(Dirs.size());
163
0
    for (const auto &Dir : Dirs) {
164
0
      const auto Pos = Dir.find(':');
165
0
      std::string HostDir =
166
0
          (Pos == std::string::npos) ? Dir : Dir.substr(Pos + 1);
167
      // Handle the readonly flag
168
0
      bool ReadOnly = false;
169
0
      if (const auto ROPos = HostDir.find(':'); ROPos != std::string::npos) {
170
0
        const auto Mode = HostDir.substr(ROPos + 1);
171
0
        HostDir = HostDir.substr(0, ROPos);
172
0
        if (kReadOnly == Mode) {
173
0
          ReadOnly = true;
174
0
        }
175
0
      }
176
0
      std::string GuestDir = VINode::canonicalGuest(
177
0
          (Pos == std::string::npos) ? std::string_view(Dir)
178
0
                                     : std::string_view(Dir).substr(0, Pos));
179
0
      if (GuestDir.size() == 0) {
180
0
        GuestDir = '/';
181
0
      }
182
0
      const auto BaseRights =
183
0
          ReadOnly ? kPreOpenBaseRightsReadOnly : kPreOpenBaseRights;
184
0
      const auto InheritingRights = ReadOnly ? kPreOpenInheritingRightsReadOnly
185
0
                                             : kPreOpenInheritingRights;
186
0
      if (auto Res = VINode::bind(BaseRights, InheritingRights,
187
0
                                  std::move(GuestDir), std::move(HostDir));
188
0
          unlikely(!Res)) {
189
0
        spdlog::error("Bind guest directory failed:{}"sv, Res.error());
190
0
        continue;
191
0
      } else {
192
0
        PreopenedDirs.emplace_back(std::move(*Res));
193
0
      }
194
0
    }
195
196
0
    std::sort(PreopenedDirs.begin(), PreopenedDirs.end());
197
198
0
    int NewFd = 3;
199
0
    for (auto &PreopenedDir : PreopenedDirs) {
200
0
      FdMap.emplace(NewFd++, std::move(PreopenedDir));
201
0
    }
202
0
  }
203
204
0
  Arguments.resize(Args.size() + 1);
205
0
  Arguments.front() = std::move(ProgramName);
206
0
  std::copy(Args.begin(), Args.end(), Arguments.begin() + 1);
207
0
  Arguments.shrink_to_fit();
208
209
0
  EnvironVariables.resize(Envs.size());
210
0
  std::copy(Envs.begin(), Envs.end(), EnvironVariables.begin());
211
0
  EnvironVariables.shrink_to_fit();
212
213
0
  ExitCode = 0;
214
0
  return {};
215
0
}
216
217
0
void Environ::fini() noexcept {
218
0
  EnvironVariables.clear();
219
0
  Arguments.clear();
220
0
  FdMap.clear();
221
0
}
222
223
0
Environ::~Environ() noexcept { fini(); }
224
225
} // namespace WASI
226
} // namespace Host
227
} // namespace WasmEdge