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