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