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