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