A resource which represents some error information.
19 |
The only method provided by this resource is to-debug-string,
20 | which provides some human-readable information about the error.
21 |
In the wasi:io package, this resource is returned through the
22 | wasi:io/streams/stream-error type.
23 |
To provide more specific error information, other interfaces may
24 | offer functions to "downcast" this error into more specific types. For example,
25 | errors returned from streams derived from filesystem types can be described using
26 | the filesystem's own error-code type. This is done using the function
27 | wasi:filesystem/types/filesystem-error-code, which takes a borrow<error>
28 | parameter and returns an option<wasi:filesystem/types/error-code>.
29 |
The set of functions which can "downcast" an error into a more
30 | concrete type is open.
31 |
Functions
32 |
[method]error.to-debug-string: func
33 |
Returns a string that is suitable to assist humans in debugging
34 | this error.
35 |
WARNING: The returned string should not be consumed mechanically!
36 | It may change across platforms, hosts, or other implementation
37 | details. Parsing this string is a major platform-compatibility
38 | hazard.
This function takes a list of pollables, which identify I/O sources of
78 | interest, and waits until one or more of the events is ready for I/O.
79 |
The result list<u32> contains one or more indices of handles in the
80 | argument list that is ready for I/O.
81 |
This function traps if either:
82 |
83 |
the list is empty, or:
84 |
the list contains more elements than can be indexed with a u32 value.
85 |
86 |
A timeout can be implemented by adding a pollable from the
87 | wasi-clocks API to the list.
88 |
This function does not return a result; polling in itself does not
89 | do any I/O so it doesn't fail. If any of the I/O sources identified by
90 | the pollables has an error, it is indicated by marking the source as
91 | being ready for I/O.
The last operation (a write or flush) failed before completion.
120 |
More information is available in the error payload.
121 |
After this, the stream will be closed. All future operations return
122 | stream-error::closed.
123 |
124 |
125 |
closed
126 |
The stream is closed: no more input will be accepted by the
127 | stream. A closed output-stream will return this error on all
128 | future operations.
129 |
130 |
131 |
resource input-stream
132 |
An input bytestream.
133 |
input-streams are non-blocking to the extent practical on underlying
134 | platforms. I/O operations always return promptly; if fewer bytes are
135 | promptly available than requested, they return the number of bytes promptly
136 | available, which could even be zero. To wait for data to be available,
137 | use the subscribe function to obtain a pollable which can be polled
138 | for using wasi:io/poll.
139 |
resource output-stream
140 |
An output bytestream.
141 |
output-streams are non-blocking to the extent practical on
142 | underlying platforms. Except where specified otherwise, I/O operations also
143 | always return promptly, after the number of bytes that can be written
144 | promptly, which could even be zero. To wait for the stream to be ready to
145 | accept data, the subscribe function to obtain a pollable which can be
146 | polled for using wasi:io/poll.
147 |
Dropping an output-stream while there's still an active write in
148 | progress may result in the data being lost. Before dropping the stream,
149 | be sure to fully flush your writes.
150 |
Functions
151 |
[method]input-stream.read: func
152 |
Perform a non-blocking read from the stream.
153 |
When the source of a read is binary data, the bytes from the source
154 | are returned verbatim. When the source of a read is known to the
155 | implementation to be text, bytes containing the UTF-8 encoding of the
156 | text are returned.
157 |
This function returns a list of bytes containing the read data,
158 | when successful. The returned list will contain up to len bytes;
159 | it may return fewer than requested, but not more. The list is
160 | empty when no bytes are available for reading at this time. The
161 | pollable given by subscribe will be ready when more bytes are
162 | available.
163 |
This function fails with a stream-error when the operation
164 | encounters an error, giving last-operation-failed, or when the
165 | stream is closed, giving closed.
166 |
When the caller gives a len of 0, it represents a request to
167 | read 0 bytes. If the stream is still open, this call should
168 | succeed and return an empty list, or otherwise fail with closed.
169 |
The len parameter is a u64, which could represent a list of u8 which
170 | is not possible to allocate in wasm32, or not desirable to allocate as
171 | as a return value by the callee. The callee may return a list of bytes
172 | less than len in size while more bytes are available for reading.
Create a pollable which will resolve once either the specified stream
221 | has bytes available to read or the other end of the stream has been
222 | closed.
223 | The created pollable is a child resource of the input-stream.
224 | Implementations may trap if the input-stream is dropped before
225 | all derived pollables created with this function are dropped.
Check readiness for writing. This function never blocks.
236 |
Returns the number of bytes permitted for the next call to write,
237 | or an error. Calling write with more bytes than this function has
238 | permitted will trap.
239 |
When this function returns 0 bytes, the subscribe pollable will
240 | become ready when this function will report at least 1 byte, or an
241 | error.
When the destination of a write is binary data, the bytes from
253 | contents are written verbatim. When the destination of a write is
254 | known to the implementation to be text, the bytes of contents are
255 | transcoded from UTF-8 into the encoding of the destination and then
256 | written.
257 |
Precondition: check-write gave permit of Ok(n) and contents has a
258 | length of less than or equal to n. Otherwise, this function will trap.
259 |
returns Err(closed) without writing if the stream has closed since
260 | the last call to check-write provided a permit.
Perform a write of up to 4096 bytes, and then flush the stream. Block
272 | until all of these operations are complete, or an error occurs.
273 |
This is a convenience wrapper around the use of check-write,
274 | subscribe, write, and flush, and is implemented with the
275 | following pseudo-code:
276 |
let pollable = this.subscribe();
277 | while !contents.is_empty() {
278 | // Wait for the stream to become writable
279 | pollable.block();
280 | let Ok(n) = this.check-write(); // eliding error handling
281 | let len = min(n, contents.len());
282 | let (chunk, rest) = contents.split_at(len);
283 | this.write(chunk ); // eliding error handling
284 | contents = rest;
285 | }
286 | this.flush();
287 | // Wait for completion of `flush`
288 | pollable.block();
289 | // Check for any errors that arose during `flush`
290 | let _ = this.check-write(); // eliding error handling
291 |
Request to flush buffered output. This function never blocks.
303 |
This tells the output-stream that the caller intends any buffered
304 | output to be flushed. the output which is expected to be flushed
305 | is all that has been passed to write prior to this call.
306 |
Upon calling this function, the output-stream will not accept any
307 | writes (check-write will return ok(0)) until the flush has
308 | completed. The subscribe pollable will become ready when the
309 | flush has completed and the stream can accept more writes.
Create a pollable which will resolve once the output-stream
331 | is ready for more writing, or an error has occurred. When this
332 | pollable is ready, check-write will return ok(n) with n>0, or an
333 | error.
334 |
If the stream is closed, this pollable is always ready immediately.
335 |
The created pollable is a child resource of the output-stream.
336 | Implementations may trap if the output-stream is dropped before
337 | all derived pollables created with this function are dropped.
This should be used precisely like write with the exact same
349 | preconditions (must use check-write first), but instead of
350 | passing a list of bytes, you simply pass the number of zero-bytes
351 | that should be written.
Perform a write of up to 4096 zeroes, and then flush the stream.
363 | Block until all of these operations are complete, or an error
364 | occurs.
365 |
This is a convenience wrapper around the use of check-write,
366 | subscribe, write-zeroes, and flush, and is implemented with
367 | the following pseudo-code:
368 |
let pollable = this.subscribe();
369 | while num_zeroes != 0 {
370 | // Wait for the stream to become writable
371 | pollable.block();
372 | let Ok(n) = this.check-write(); // eliding error handling
373 | let len = min(n, num_zeroes);
374 | this.write-zeroes(len); // eliding error handling
375 | num_zeroes -= len;
376 | }
377 | this.flush();
378 | // Wait for completion of `flush`
379 | pollable.block();
380 | // Check for any errors that arose during `flush`
381 | let _ = this.check-write(); // eliding error handling
382 |
Read from one stream and write to another, with blocking.
417 |
This is similar to splice, except that it blocks until the
418 | output-stream is ready for writing, and the input-stream
419 | is ready for reading, before performing the splice.
WASI Wall Clock is a clock API intended to let users query the current
432 | time. The name "wall" makes an analogy to a "clock on the wall", which
433 | is not necessarily monotonic as it may be reset.
434 |
It is intended to be portable at least between Unix-family platforms and
435 | Windows.
436 |
A wall clock is a clock which measures the date and time according to
437 | some external reference.
438 |
External references may be reset, so this clock is not necessarily
439 | monotonic, making it unsuitable for measuring elapsed time.
440 |
It is intended for reporting the current date and time for humans.
441 |
442 |
Types
443 |
record datetime
444 |
A time and date in seconds plus nanoseconds.
445 |
Record Fields
446 |
447 |
seconds: u64
448 |
nanoseconds: u32
449 |
450 |
451 |
Functions
452 |
now: func
453 |
Read the current value of the clock.
454 |
This clock is not monotonic, therefore calling this function repeatedly
455 | will not necessarily produce a sequence of non-decreasing values.
456 |
The returned timestamps represent the number of seconds since
457 | 1970-01-01T00:00:00Z, also known as POSIX's Seconds Since the Epoch,
458 | also known as Unix Time.
459 |
The nanoseconds field of the output is always less than 1000000000.
WASI filesystem is a filesystem API primarily intended to let users run WASI
473 | programs that access their files on their existing filesystems, without
474 | significant overhead.
475 |
It is intended to be roughly portable between Unix-family platforms and
476 | Windows, though it does not hide many of the major differences.
477 |
Paths are passed as interface-type strings, meaning they must consist of
478 | a sequence of Unicode Scalar Values (USVs). Some filesystems may contain
479 | paths which are not accessible by this API.
480 |
The directory separator in WASI is always the forward-slash (/).
481 |
All paths in WASI are relative paths, and are interpreted relative to a
482 | descriptor referring to a base directory. If a path argument to any WASI
483 | function starts with /, or if any step of resolving a path, including
484 | .. and symbolic link steps, reaches a directory outside of the base
485 | directory, or reaches a symlink to an absolute or rooted path in the
486 | underlying filesystem, the function fails with error-code::not-permitted.
File size or length of a region within a file.
506 |
enum descriptor-type
507 |
The type of a filesystem object referenced by a descriptor.
508 |
Note: This was called filetype in earlier versions of WASI.
509 |
Enum Cases
510 |
511 |
512 |
unknown
513 |
The type of the descriptor or file is unknown or is different from
514 | any of the other types specified.
515 |
516 |
517 |
block-device
518 |
The descriptor refers to a block device inode.
519 |
520 |
521 |
character-device
522 |
The descriptor refers to a character device inode.
523 |
524 |
525 |
directory
526 |
The descriptor refers to a directory inode.
527 |
528 |
529 |
fifo
530 |
The descriptor refers to a named pipe.
531 |
532 |
533 |
symbolic-link
534 |
The file refers to a symbolic link inode.
535 |
536 |
537 |
regular-file
538 |
The descriptor refers to a regular file inode.
539 |
540 |
541 |
socket
542 |
The descriptor refers to a socket.
543 |
544 |
545 |
flags descriptor-flags
546 |
Descriptor flags.
547 |
Note: This was called fdflags in earlier versions of WASI.
548 |
Flags members
549 |
550 |
551 |
read:
552 |
Read mode: Data can be read.
553 |
554 |
555 |
write:
556 |
Write mode: Data can be written to.
557 |
558 |
559 |
file-integrity-sync:
560 |
Request that writes be performed according to synchronized I/O file
561 | integrity completion. The data stored in the file and the file's
562 | metadata are synchronized. This is similar to `O_SYNC` in POSIX.
563 |
The precise semantics of this operation have not yet been defined for
564 | WASI. At this time, it should be interpreted as a request, and not a
565 | requirement.
566 |
567 |
568 |
data-integrity-sync:
569 |
Request that writes be performed according to synchronized I/O data
570 | integrity completion. Only the data stored in the file is
571 | synchronized. This is similar to `O_DSYNC` in POSIX.
572 |
The precise semantics of this operation have not yet been defined for
573 | WASI. At this time, it should be interpreted as a request, and not a
574 | requirement.
575 |
576 |
577 |
requested-write-sync:
578 |
Requests that reads be performed at the same level of integrity
579 | requested for writes. This is similar to `O_RSYNC` in POSIX.
580 |
The precise semantics of this operation have not yet been defined for
581 | WASI. At this time, it should be interpreted as a request, and not a
582 | requirement.
583 |
584 |
585 |
mutate-directory:
586 |
Mutating directories mode: Directory contents may be mutated.
587 |
When this flag is unset on a descriptor, operations using the
588 | descriptor which would create, rename, delete, modify the data or
589 | metadata of filesystem objects, or obtain another handle which
590 | would permit any of those, shall fail with error-code::read-only if
591 | they would otherwise succeed.
592 |
This may only be set on directories.
593 |
594 |
595 |
flags path-flags
596 |
Flags determining the method of how paths are resolved.
597 |
Flags members
598 |
599 |
symlink-follow:
As long as the resolved path corresponds to a symbolic link, it is
600 | expanded.
601 |
602 |
603 |
flags open-flags
604 |
Open flags used by open-at.
605 |
Flags members
606 |
607 |
608 |
create:
609 |
Create file if it does not exist, similar to `O_CREAT` in POSIX.
610 |
611 |
612 |
directory:
613 |
Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
614 |
615 |
616 |
exclusive:
617 |
Fail if file already exists, similar to `O_EXCL` in POSIX.
618 |
619 |
620 |
truncate:
621 |
Truncate file to size 0, similar to `O_TRUNC` in POSIX.
622 |
623 |
624 |
type link-count
625 |
u64
626 |
Number of hard links to an inode.
627 |
record descriptor-stat
628 |
File attributes.
629 |
Note: This was called filestat in earlier versions of WASI.
The type of the file referred to by this directory entry.
689 |
690 |
691 |
name: string
692 |
The name of the object.
693 |
694 |
695 |
enum error-code
696 |
Error codes returned by functions, similar to errno in POSIX.
697 | Not all of these error codes are returned by the functions provided by this
698 | API; some are used in higher-level library layers, and others are provided
699 | merely for alignment with POSIX.
700 |
Enum Cases
701 |
702 |
703 |
access
704 |
Permission denied, similar to `EACCES` in POSIX.
705 |
706 |
707 |
would-block
708 |
Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX.
709 |
710 |
711 |
already
712 |
Connection already in progress, similar to `EALREADY` in POSIX.
713 |
714 |
715 |
bad-descriptor
716 |
Bad descriptor, similar to `EBADF` in POSIX.
717 |
718 |
719 |
busy
720 |
Device or resource busy, similar to `EBUSY` in POSIX.
721 |
722 |
723 |
deadlock
724 |
Resource deadlock would occur, similar to `EDEADLK` in POSIX.
725 |
726 |
727 |
quota
728 |
Storage quota exceeded, similar to `EDQUOT` in POSIX.
729 |
730 |
731 |
exist
732 |
File exists, similar to `EEXIST` in POSIX.
733 |
734 |
735 |
file-too-large
736 |
File too large, similar to `EFBIG` in POSIX.
737 |
738 |
739 |
illegal-byte-sequence
740 |
Illegal byte sequence, similar to `EILSEQ` in POSIX.
741 |
742 |
743 |
in-progress
744 |
Operation in progress, similar to `EINPROGRESS` in POSIX.
745 |
746 |
747 |
interrupted
748 |
Interrupted function, similar to `EINTR` in POSIX.
749 |
750 |
751 |
invalid
752 |
Invalid argument, similar to `EINVAL` in POSIX.
753 |
754 |
755 |
io
756 |
I/O error, similar to `EIO` in POSIX.
757 |
758 |
759 |
is-directory
760 |
Is a directory, similar to `EISDIR` in POSIX.
761 |
762 |
763 |
loop
764 |
Too many levels of symbolic links, similar to `ELOOP` in POSIX.
765 |
766 |
767 |
too-many-links
768 |
Too many links, similar to `EMLINK` in POSIX.
769 |
770 |
771 |
message-size
772 |
Message too large, similar to `EMSGSIZE` in POSIX.
773 |
774 |
775 |
name-too-long
776 |
Filename too long, similar to `ENAMETOOLONG` in POSIX.
777 |
778 |
779 |
no-device
780 |
No such device, similar to `ENODEV` in POSIX.
781 |
782 |
783 |
no-entry
784 |
No such file or directory, similar to `ENOENT` in POSIX.
785 |
786 |
787 |
no-lock
788 |
No locks available, similar to `ENOLCK` in POSIX.
789 |
790 |
791 |
insufficient-memory
792 |
Not enough space, similar to `ENOMEM` in POSIX.
793 |
794 |
795 |
insufficient-space
796 |
No space left on device, similar to `ENOSPC` in POSIX.
797 |
798 |
799 |
not-directory
800 |
Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX.
801 |
802 |
803 |
not-empty
804 |
Directory not empty, similar to `ENOTEMPTY` in POSIX.
805 |
806 |
807 |
not-recoverable
808 |
State not recoverable, similar to `ENOTRECOVERABLE` in POSIX.
809 |
810 |
811 |
unsupported
812 |
Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX.
813 |
814 |
815 |
no-tty
816 |
Inappropriate I/O control operation, similar to `ENOTTY` in POSIX.
817 |
818 |
819 |
no-such-device
820 |
No such device or address, similar to `ENXIO` in POSIX.
821 |
822 |
823 |
overflow
824 |
Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX.
825 |
826 |
827 |
not-permitted
828 |
Operation not permitted, similar to `EPERM` in POSIX.
829 |
830 |
831 |
pipe
832 |
Broken pipe, similar to `EPIPE` in POSIX.
833 |
834 |
835 |
read-only
836 |
Read-only file system, similar to `EROFS` in POSIX.
837 |
838 |
839 |
invalid-seek
840 |
Invalid seek, similar to `ESPIPE` in POSIX.
841 |
842 |
843 |
text-file-busy
844 |
Text file busy, similar to `ETXTBSY` in POSIX.
845 |
846 |
847 |
cross-device
848 |
Cross-device link, similar to `EXDEV` in POSIX.
849 |
850 |
851 |
enum advice
852 |
File or memory access pattern advisory information.
853 |
Enum Cases
854 |
855 |
856 |
normal
857 |
The application has no advice to give on its behavior with respect
858 | to the specified data.
859 |
860 |
861 |
sequential
862 |
The application expects to access the specified data sequentially
863 | from lower offsets to higher offsets.
864 |
865 |
866 |
random
867 |
The application expects to access the specified data in a random
868 | order.
869 |
870 |
871 |
will-need
872 |
The application expects to access the specified data in the near
873 | future.
874 |
875 |
876 |
dont-need
877 |
The application expects that it will not access the specified data
878 | in the near future.
879 |
880 |
881 |
no-reuse
882 |
The application expects to access the specified data once and then
883 | not reuse it thereafter.
884 |
885 |
886 |
record metadata-hash-value
887 |
A 128-bit hash value, split into parts because wasm doesn't have a
888 | 128-bit integer type.
889 |
Record Fields
890 |
891 |
892 |
lower: u64
893 |
64 bits of a 128-bit hash value.
894 |
895 |
896 |
upper: u64
897 |
Another 64 bits of a 128-bit hash value.
898 |
899 |
900 |
resource descriptor
901 |
A descriptor is a reference to a filesystem object, which may be a file,
902 | directory, named pipe, special file, or other object on which filesystem
903 | calls may be made.
904 |
resource directory-entry-stream
905 |
A stream of directory entries.
906 |
Functions
907 |
[method]descriptor.read-via-stream: func
908 |
Return a stream for reading from a file, if available.
909 |
May fail with an error-code describing why the file cannot be read.
910 |
Multiple read, write, and append streams may be active on the same open
911 | file and they do not interfere with each other.
912 |
Note: This allows using read-stream, which is similar to read in POSIX.
Read from a descriptor, without using and updating the descriptor's offset.
1034 |
This function returns a list of bytes containing the data that was
1035 | read, along with a bool which, when true, indicates that the end of the
1036 | file was reached. The returned list will contain up to length bytes; it
1037 | may return fewer than requested, if the end of the file is reached or
1038 | if the I/O operation is interrupted.
1039 |
In the future, this may change to return a stream<u8, error-code>.
Write to a descriptor, without using and updating the descriptor's offset.
1053 |
It is valid to write past the end of a file; the file is extended to the
1054 | extent of the write, with bytes between the previous end and the start of
1055 | the write set to zero.
1056 |
In the future, this may change to take a stream<u8, error-code>.
On filesystems where directories contain entries referring to themselves
1071 | and their parents, often named . and .. respectively, these entries
1072 | are omitted.
1073 |
This always returns a new stream which starts at the beginning of the
1074 | directory. Multiple streams may be active on the same directory, and they
1075 | do not interfere with each other.
Return the attributes of an open file or directory.
1111 |
Note: This is similar to fstat in POSIX, except that it does not return
1112 | device and inode information. For testing whether two descriptors refer to
1113 | the same underlying filesystem object, use is-same-object. To obtain
1114 | additional data that can be used do determine whether a file has been
1115 | modified, use metadata-hash.
1116 |
Note: This was called fd_filestat_get in earlier versions of WASI.
Note: This is similar to fstatat in POSIX, except that it does not
1128 | return device and inode information. See the stat description for a
1129 | discussion of alternatives.
1130 |
Note: This was called path_filestat_get in earlier versions of WASI.
Test whether two descriptors refer to the same filesystem object.
1269 |
In POSIX, this corresponds to testing whether the two descriptors have the
1270 | same device (st_dev) and inode (st_ino or d_ino) numbers.
1271 | wasi-filesystem does not expose device and inode numbers, so this function
1272 | may be used instead.
Return a hash of the metadata associated with a filesystem object referred
1284 | to by a descriptor.
1285 |
This returns a hash of the last-modification timestamp and file size, and
1286 | may also include the inode number, device number, birth timestamp, and
1287 | other metadata fields that may change when the file is modified or
1288 | replaced. It may also include a secret value chosen by the
1289 | implementation and not otherwise exposed.
1290 |
Implementations are encouraged to provide the following properties:
1291 |
1292 |
If the file is not modified or replaced, the computed hash value should
1293 | usually not change.
1294 |
If the object is modified or replaced, the computed hash value should
1295 | usually change.
1296 |
The inputs to the hash should not be easily computable from the
1297 | computed hash.
Attempts to extract a filesystem-related error-code from the stream
1334 | error provided.
1335 |
Stream operations which return stream-error::last-operation-failed
1336 | have a payload with more information about the operation that failed.
1337 | This payload can be passed through to this function to see if there's
1338 | filesystem-related information about the error to return.
1339 |
Note that this function is fallible because not all stream-related
1340 | errors are filesystem-related errors.
1363 |
--------------------------------------------------------------------------------
/path-resolution.md:
--------------------------------------------------------------------------------
1 | # WASI filesystem path resolution
2 |
3 | wasi-filesystem uses a filesystem path sandboxing scheme modeled after the
4 | system used in [CloudABI], which is also similar to the system used in
5 | [Capsicum].
6 |
7 | On Linux, it corresponds to the `RESOLVE_BENEATH` behavior in
8 | [Linux's `openat2`]. In FreeBSD, it corresponds to the `O_RESOLVE_BENEATH`
9 | behavior in [FreeBSD's `open`]. However, path resolution can also be
10 | implemented manually using `openat` and `readlinkat` or similar primitives.
11 |
12 | ## Sandboxing overview
13 |
14 | All functions in wasi-filesystem which operate on filesystem paths take
15 | a pair of values: a base directory handle, and a relative path. Absolute
16 | paths are not permitted, and there is no global namespace. All path
17 | accesses are relative to a base directory handle.
18 |
19 | Path resolution is constrained to occur within the sub-filesystem referenced
20 | by the base handle. Information about the filesystem outside of the base
21 | directory handles is not visible. In particular, it's not permitted to use
22 | paths that temporarily step outside the sandbox with something like
23 | "../../../stuff/here", even if the final resolved path is back inside the
24 | sandbox, because that would leak information about the existence of
25 | directories outside the sandbox.
26 |
27 | Importantly, the sandboxing is designed to be implementable even in the presence
28 | of outside processes accessing the same filesystem, including renaming,
29 | unlinking, and creating new files and directories.
30 |
31 | ## Symlinks
32 |
33 | Creating a symlink with an absolute path string fails with a "not permitted"
34 | error.
35 |
36 | Other than that, symlinks may be created with any string, provided the
37 | underlying filesystem implementation supports it.
38 |
39 | Sandboxing for symlink strings is performed at the time of an access, when a
40 | path is being resolved, and not at the time that the symlink is created or
41 | moved. This ensures that the sandbox is respected even if there are symlinks
42 | created or renamed by other entities with access to the filesystem.
43 |
44 | ## Host Implementation
45 |
46 | ### Implementing path resolution manually
47 |
48 | Plain `openat` doesn't perform any sandboxing; it will readily open paths
49 | containing ".." or starting with "/", or symlinks to paths containing ".."
50 | or starting with "/". It has an `O_NOFOLLOW` flag, however this flag only
51 | applies to the last component of the path (eg. the "c" in "a/b/c"). So
52 | the strategy for using `openat` to implement sandboxing is to split paths
53 | into components (eg. "a", "b", "c") and open them one component at a time,
54 | so that each component can be opened with `O_NOFOLLOW`.
55 |
56 | If the `openat` call fails, and the OS error code indicates that it *was*
57 | a symlink (eg. `ELOOP`), then call `readlinkat` to read the link contents,
58 | split the contents into components, and prepend these new components to the
59 | component list. If it starts with an absolute path, that's an attempt to
60 | jump outside the sandbox, so path resolution should fail with an
61 | "access denied" error message.
62 |
63 | If a path component is "..", instead of opening it, pop an item off of the
64 | component list. If the list was empty, that represents an attempt to use
65 | ".." to step outside the sandbox, so path resolution should fail with an
66 | "access denied" error message.
67 |
68 | ### Implementation notes
69 |
70 | On Linux, `openat2` with `RESOLVE_BENEATH` may be used as an optimization to
71 | implement many system calls other than just "open" by utilizing Linux's
72 | `O_PATH` and "/proc/self/fd" features.
73 |
74 | On Windows, the [`NtCreateFile`] function can accept a directory handle and
75 | can behave like an `openat` function, which can be used in the
76 | [manual algorithm](implementing-path-resolution-manually).
77 |
78 | The Rust library [cap-std] implements WASI's filesystem sandboxing semantics,
79 | but is otherwise independent of WASI or Wasm, so it can be reused in other
80 | settings. It uses `openat2` and `NtCreateFile` and other optimizations.
81 |
82 | cloudabi-utils has an [implementation of the manual technique in C], though
83 | that repository is no longer maintained.
84 |
85 | [implementation of the manual technique in C]: https://github.com/NuxiNL/cloudabi-utils/blob/master/src/libemulator/posix.c#L1205
86 | [cap-std]: https://github.com/bytecodealliance/cap-std
87 | [Linux's `openat2`]: https://man7.org/linux/man-pages/man2/openat2.2.html
88 | [CloudABI]: https://github.com/NuxiNL/cloudabi
89 | [Capsicum]: https://wiki.freebsd.org/Capsicum
90 | [FreeBSD's `open`]: https://man.freebsd.org/cgi/man.cgi?sektion=2&query=open
91 | [`NtCreateFile`]: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
92 |
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | # Testing guidelines
2 |
3 | TK fill in testing guidelines
4 |
5 | ## Installing the tools
6 |
7 | TK fill in instructions
8 |
9 | ## Running the tests
10 |
11 | TK fill in instructions
12 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/deps.lock:
--------------------------------------------------------------------------------
1 | [clocks]
2 | url = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz"
3 | subdir = "wit-0.3.0-draft"
4 | sha256 = "26e315db0d371495f8834edfc0e479042f94152ce677d96d54d3623d0e4ffb1e"
5 | sha512 = "e1c76f499435841316f9287b88d8173558e64f277c321ff390556de8707a0b18dd6c1749bbb17bbbba8d523da246ef6eb05c990ceddb762e03efb2ae30cacc76"
6 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/deps.toml:
--------------------------------------------------------------------------------
1 | clocks = { url = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz", subdir = "wit-0.3.0-draft" }
2 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/deps/clocks/monotonic-clock.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.3.0;
2 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed
3 | /// time.
4 | ///
5 | /// It is intended to be portable at least between Unix-family platforms and
6 | /// Windows.
7 | ///
8 | /// A monotonic clock is a clock which has an unspecified initial value, and
9 | /// successive reads of the clock will produce non-decreasing values.
10 | @since(version = 0.3.0)
11 | interface monotonic-clock {
12 | /// An instant in time, in nanoseconds. An instant is relative to an
13 | /// unspecified initial value, and can only be compared to instances from
14 | /// the same monotonic-clock.
15 | @since(version = 0.3.0)
16 | type instant = u64;
17 |
18 | /// A duration of time, in nanoseconds.
19 | @since(version = 0.3.0)
20 | type duration = u64;
21 |
22 | /// Read the current value of the clock.
23 | ///
24 | /// The clock is monotonic, therefore calling this function repeatedly will
25 | /// produce a sequence of non-decreasing values.
26 | @since(version = 0.3.0)
27 | now: func() -> instant;
28 |
29 | /// Query the resolution of the clock. Returns the duration of time
30 | /// corresponding to a clock tick.
31 | @since(version = 0.3.0)
32 | resolution: func() -> duration;
33 |
34 | /// Wait until the specified instant has occurred.
35 | @since(version = 0.3.0)
36 | wait-until: func(
37 | when: instant,
38 | );
39 |
40 | /// Wait for the specified duration has elapsed.
41 | @since(version = 0.3.0)
42 | wait-for: func(
43 | how-long: duration,
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/deps/clocks/timezone.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.3.0;
2 |
3 | @unstable(feature = clocks-timezone)
4 | interface timezone {
5 | @unstable(feature = clocks-timezone)
6 | use wall-clock.{datetime};
7 |
8 | /// Return information needed to display the given `datetime`. This includes
9 | /// the UTC offset, the time zone name, and a flag indicating whether
10 | /// daylight saving time is active.
11 | ///
12 | /// If the timezone cannot be determined for the given `datetime`, return a
13 | /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight
14 | /// saving time.
15 | @unstable(feature = clocks-timezone)
16 | display: func(when: datetime) -> timezone-display;
17 |
18 | /// The same as `display`, but only return the UTC offset.
19 | @unstable(feature = clocks-timezone)
20 | utc-offset: func(when: datetime) -> s32;
21 |
22 | /// Information useful for displaying the timezone of a specific `datetime`.
23 | ///
24 | /// This information may vary within a single `timezone` to reflect daylight
25 | /// saving time adjustments.
26 | @unstable(feature = clocks-timezone)
27 | record timezone-display {
28 | /// The number of seconds difference between UTC time and the local
29 | /// time of the timezone.
30 | ///
31 | /// The returned value will always be less than 86400 which is the
32 | /// number of seconds in a day (24*60*60).
33 | ///
34 | /// In implementations that do not expose an actual time zone, this
35 | /// should return 0.
36 | utc-offset: s32,
37 |
38 | /// The abbreviated name of the timezone to display to a user. The name
39 | /// `UTC` indicates Coordinated Universal Time. Otherwise, this should
40 | /// reference local standards for the name of the time zone.
41 | ///
42 | /// In implementations that do not expose an actual time zone, this
43 | /// should be the string `UTC`.
44 | ///
45 | /// In time zones that do not have an applicable name, a formatted
46 | /// representation of the UTC offset may be returned, such as `-04:00`.
47 | name: string,
48 |
49 | /// Whether daylight saving time is active.
50 | ///
51 | /// In implementations that do not expose an actual time zone, this
52 | /// should return false.
53 | in-daylight-saving-time: bool,
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/deps/clocks/wall-clock.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.3.0;
2 | /// WASI Wall Clock is a clock API intended to let users query the current
3 | /// time. The name "wall" makes an analogy to a "clock on the wall", which
4 | /// is not necessarily monotonic as it may be reset.
5 | ///
6 | /// It is intended to be portable at least between Unix-family platforms and
7 | /// Windows.
8 | ///
9 | /// A wall clock is a clock which measures the date and time according to
10 | /// some external reference.
11 | ///
12 | /// External references may be reset, so this clock is not necessarily
13 | /// monotonic, making it unsuitable for measuring elapsed time.
14 | ///
15 | /// It is intended for reporting the current date and time for humans.
16 | @since(version = 0.3.0)
17 | interface wall-clock {
18 | /// A time and date in seconds plus nanoseconds.
19 | @since(version = 0.3.0)
20 | record datetime {
21 | seconds: u64,
22 | nanoseconds: u32,
23 | }
24 |
25 | /// Read the current value of the clock.
26 | ///
27 | /// This clock is not monotonic, therefore calling this function repeatedly
28 | /// will not necessarily produce a sequence of non-decreasing values.
29 | ///
30 | /// The returned timestamps represent the number of seconds since
31 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch],
32 | /// also known as [Unix Time].
33 | ///
34 | /// The nanoseconds field of the output is always less than 1000000000.
35 | ///
36 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16
37 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time
38 | @since(version = 0.3.0)
39 | now: func() -> datetime;
40 |
41 | /// Query the resolution of the clock.
42 | ///
43 | /// The nanoseconds field of the output is always less than 1000000000.
44 | @since(version = 0.3.0)
45 | resolution: func() -> datetime;
46 | }
47 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/deps/clocks/world.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.3.0;
2 |
3 | @since(version = 0.3.0)
4 | world imports {
5 | @since(version = 0.3.0)
6 | import monotonic-clock;
7 | @since(version = 0.3.0)
8 | import wall-clock;
9 | @unstable(feature = clocks-timezone)
10 | import timezone;
11 | }
12 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/preopens.wit:
--------------------------------------------------------------------------------
1 | package wasi:filesystem@0.3.0;
2 |
3 | @since(version = 0.3.0)
4 | interface preopens {
5 | @since(version = 0.3.0)
6 | use types.{descriptor};
7 |
8 | /// Return the set of preopened directories, and their paths.
9 | @since(version = 0.3.0)
10 | get-directories: func() -> list>;
11 | }
12 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/types.wit:
--------------------------------------------------------------------------------
1 | package wasi:filesystem@0.3.0;
2 | /// WASI filesystem is a filesystem API primarily intended to let users run WASI
3 | /// programs that access their files on their existing filesystems, without
4 | /// significant overhead.
5 | ///
6 | /// It is intended to be roughly portable between Unix-family platforms and
7 | /// Windows, though it does not hide many of the major differences.
8 | ///
9 | /// Paths are passed as interface-type `string`s, meaning they must consist of
10 | /// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain
11 | /// paths which are not accessible by this API.
12 | ///
13 | /// The directory separator in WASI is always the forward-slash (`/`).
14 | ///
15 | /// All paths in WASI are relative paths, and are interpreted relative to a
16 | /// `descriptor` referring to a base directory. If a `path` argument to any WASI
17 | /// function starts with `/`, or if any step of resolving a `path`, including
18 | /// `..` and symbolic link steps, reaches a directory outside of the base
19 | /// directory, or reaches a symlink to an absolute or rooted path in the
20 | /// underlying filesystem, the function fails with `error-code::not-permitted`.
21 | ///
22 | /// For more information about WASI path resolution and sandboxing, see
23 | /// [WASI filesystem path resolution].
24 | ///
25 | /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md
26 | @since(version = 0.3.0)
27 | interface types {
28 | @since(version = 0.3.0)
29 | use wasi:clocks/wall-clock@0.3.0.{datetime};
30 |
31 | /// File size or length of a region within a file.
32 | @since(version = 0.3.0)
33 | type filesize = u64;
34 |
35 | /// The type of a filesystem object referenced by a descriptor.
36 | ///
37 | /// Note: This was called `filetype` in earlier versions of WASI.
38 | @since(version = 0.3.0)
39 | enum descriptor-type {
40 | /// The type of the descriptor or file is unknown or is different from
41 | /// any of the other types specified.
42 | unknown,
43 | /// The descriptor refers to a block device inode.
44 | block-device,
45 | /// The descriptor refers to a character device inode.
46 | character-device,
47 | /// The descriptor refers to a directory inode.
48 | directory,
49 | /// The descriptor refers to a named pipe.
50 | fifo,
51 | /// The file refers to a symbolic link inode.
52 | symbolic-link,
53 | /// The descriptor refers to a regular file inode.
54 | regular-file,
55 | /// The descriptor refers to a socket.
56 | socket,
57 | }
58 |
59 | /// Descriptor flags.
60 | ///
61 | /// Note: This was called `fdflags` in earlier versions of WASI.
62 | @since(version = 0.3.0)
63 | flags descriptor-flags {
64 | /// Read mode: Data can be read.
65 | read,
66 | /// Write mode: Data can be written to.
67 | write,
68 | /// Request that writes be performed according to synchronized I/O file
69 | /// integrity completion. The data stored in the file and the file's
70 | /// metadata are synchronized. This is similar to `O_SYNC` in POSIX.
71 | ///
72 | /// The precise semantics of this operation have not yet been defined for
73 | /// WASI. At this time, it should be interpreted as a request, and not a
74 | /// requirement.
75 | file-integrity-sync,
76 | /// Request that writes be performed according to synchronized I/O data
77 | /// integrity completion. Only the data stored in the file is
78 | /// synchronized. This is similar to `O_DSYNC` in POSIX.
79 | ///
80 | /// The precise semantics of this operation have not yet been defined for
81 | /// WASI. At this time, it should be interpreted as a request, and not a
82 | /// requirement.
83 | data-integrity-sync,
84 | /// Requests that reads be performed at the same level of integrity
85 | /// requested for writes. This is similar to `O_RSYNC` in POSIX.
86 | ///
87 | /// The precise semantics of this operation have not yet been defined for
88 | /// WASI. At this time, it should be interpreted as a request, and not a
89 | /// requirement.
90 | requested-write-sync,
91 | /// Mutating directories mode: Directory contents may be mutated.
92 | ///
93 | /// When this flag is unset on a descriptor, operations using the
94 | /// descriptor which would create, rename, delete, modify the data or
95 | /// metadata of filesystem objects, or obtain another handle which
96 | /// would permit any of those, shall fail with `error-code::read-only` if
97 | /// they would otherwise succeed.
98 | ///
99 | /// This may only be set on directories.
100 | mutate-directory,
101 | }
102 |
103 | /// File attributes.
104 | ///
105 | /// Note: This was called `filestat` in earlier versions of WASI.
106 | @since(version = 0.3.0)
107 | record descriptor-stat {
108 | /// File type.
109 | %type: descriptor-type,
110 | /// Number of hard links to the file.
111 | link-count: link-count,
112 | /// For regular files, the file size in bytes. For symbolic links, the
113 | /// length in bytes of the pathname contained in the symbolic link.
114 | size: filesize,
115 | /// Last data access timestamp.
116 | ///
117 | /// If the `option` is none, the platform doesn't maintain an access
118 | /// timestamp for this file.
119 | data-access-timestamp: option,
120 | /// Last data modification timestamp.
121 | ///
122 | /// If the `option` is none, the platform doesn't maintain a
123 | /// modification timestamp for this file.
124 | data-modification-timestamp: option,
125 | /// Last file status-change timestamp.
126 | ///
127 | /// If the `option` is none, the platform doesn't maintain a
128 | /// status-change timestamp for this file.
129 | status-change-timestamp: option,
130 | }
131 |
132 | /// Flags determining the method of how paths are resolved.
133 | @since(version = 0.3.0)
134 | flags path-flags {
135 | /// As long as the resolved path corresponds to a symbolic link, it is
136 | /// expanded.
137 | symlink-follow,
138 | }
139 |
140 | /// Open flags used by `open-at`.
141 | @since(version = 0.3.0)
142 | flags open-flags {
143 | /// Create file if it does not exist, similar to `O_CREAT` in POSIX.
144 | create,
145 | /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
146 | directory,
147 | /// Fail if file already exists, similar to `O_EXCL` in POSIX.
148 | exclusive,
149 | /// Truncate file to size 0, similar to `O_TRUNC` in POSIX.
150 | truncate,
151 | }
152 |
153 | /// Number of hard links to an inode.
154 | @since(version = 0.3.0)
155 | type link-count = u64;
156 |
157 | /// When setting a timestamp, this gives the value to set it to.
158 | @since(version = 0.3.0)
159 | variant new-timestamp {
160 | /// Leave the timestamp set to its previous value.
161 | no-change,
162 | /// Set the timestamp to the current time of the system clock associated
163 | /// with the filesystem.
164 | now,
165 | /// Set the timestamp to the given value.
166 | timestamp(datetime),
167 | }
168 |
169 | /// A directory entry.
170 | record directory-entry {
171 | /// The type of the file referred to by this directory entry.
172 | %type: descriptor-type,
173 |
174 | /// The name of the object.
175 | name: string,
176 | }
177 |
178 | /// Error codes returned by functions, similar to `errno` in POSIX.
179 | /// Not all of these error codes are returned by the functions provided by this
180 | /// API; some are used in higher-level library layers, and others are provided
181 | /// merely for alignment with POSIX.
182 | enum error-code {
183 | /// Permission denied, similar to `EACCES` in POSIX.
184 | access,
185 | /// Connection already in progress, similar to `EALREADY` in POSIX.
186 | already,
187 | /// Bad descriptor, similar to `EBADF` in POSIX.
188 | bad-descriptor,
189 | /// Device or resource busy, similar to `EBUSY` in POSIX.
190 | busy,
191 | /// Resource deadlock would occur, similar to `EDEADLK` in POSIX.
192 | deadlock,
193 | /// Storage quota exceeded, similar to `EDQUOT` in POSIX.
194 | quota,
195 | /// File exists, similar to `EEXIST` in POSIX.
196 | exist,
197 | /// File too large, similar to `EFBIG` in POSIX.
198 | file-too-large,
199 | /// Illegal byte sequence, similar to `EILSEQ` in POSIX.
200 | illegal-byte-sequence,
201 | /// Operation in progress, similar to `EINPROGRESS` in POSIX.
202 | in-progress,
203 | /// Interrupted function, similar to `EINTR` in POSIX.
204 | interrupted,
205 | /// Invalid argument, similar to `EINVAL` in POSIX.
206 | invalid,
207 | /// I/O error, similar to `EIO` in POSIX.
208 | io,
209 | /// Is a directory, similar to `EISDIR` in POSIX.
210 | is-directory,
211 | /// Too many levels of symbolic links, similar to `ELOOP` in POSIX.
212 | loop,
213 | /// Too many links, similar to `EMLINK` in POSIX.
214 | too-many-links,
215 | /// Message too large, similar to `EMSGSIZE` in POSIX.
216 | message-size,
217 | /// Filename too long, similar to `ENAMETOOLONG` in POSIX.
218 | name-too-long,
219 | /// No such device, similar to `ENODEV` in POSIX.
220 | no-device,
221 | /// No such file or directory, similar to `ENOENT` in POSIX.
222 | no-entry,
223 | /// No locks available, similar to `ENOLCK` in POSIX.
224 | no-lock,
225 | /// Not enough space, similar to `ENOMEM` in POSIX.
226 | insufficient-memory,
227 | /// No space left on device, similar to `ENOSPC` in POSIX.
228 | insufficient-space,
229 | /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX.
230 | not-directory,
231 | /// Directory not empty, similar to `ENOTEMPTY` in POSIX.
232 | not-empty,
233 | /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX.
234 | not-recoverable,
235 | /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX.
236 | unsupported,
237 | /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX.
238 | no-tty,
239 | /// No such device or address, similar to `ENXIO` in POSIX.
240 | no-such-device,
241 | /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX.
242 | overflow,
243 | /// Operation not permitted, similar to `EPERM` in POSIX.
244 | not-permitted,
245 | /// Broken pipe, similar to `EPIPE` in POSIX.
246 | pipe,
247 | /// Read-only file system, similar to `EROFS` in POSIX.
248 | read-only,
249 | /// Invalid seek, similar to `ESPIPE` in POSIX.
250 | invalid-seek,
251 | /// Text file busy, similar to `ETXTBSY` in POSIX.
252 | text-file-busy,
253 | /// Cross-device link, similar to `EXDEV` in POSIX.
254 | cross-device,
255 | }
256 |
257 | /// File or memory access pattern advisory information.
258 | @since(version = 0.3.0)
259 | enum advice {
260 | /// The application has no advice to give on its behavior with respect
261 | /// to the specified data.
262 | normal,
263 | /// The application expects to access the specified data sequentially
264 | /// from lower offsets to higher offsets.
265 | sequential,
266 | /// The application expects to access the specified data in a random
267 | /// order.
268 | random,
269 | /// The application expects to access the specified data in the near
270 | /// future.
271 | will-need,
272 | /// The application expects that it will not access the specified data
273 | /// in the near future.
274 | dont-need,
275 | /// The application expects to access the specified data once and then
276 | /// not reuse it thereafter.
277 | no-reuse,
278 | }
279 |
280 | /// A 128-bit hash value, split into parts because wasm doesn't have a
281 | /// 128-bit integer type.
282 | @since(version = 0.3.0)
283 | record metadata-hash-value {
284 | /// 64 bits of a 128-bit hash value.
285 | lower: u64,
286 | /// Another 64 bits of a 128-bit hash value.
287 | upper: u64,
288 | }
289 |
290 | /// A descriptor is a reference to a filesystem object, which may be a file,
291 | /// directory, named pipe, special file, or other object on which filesystem
292 | /// calls may be made.
293 | @since(version = 0.3.0)
294 | resource descriptor {
295 | /// Return a stream for reading from a file.
296 | ///
297 | /// Multiple read, write, and append streams may be active on the same open
298 | /// file and they do not interfere with each other.
299 | ///
300 | /// This function returns a future, which will resolve to an error code if
301 | /// reading full contents of the file fails.
302 | ///
303 | /// Note: This is similar to `pread` in POSIX.
304 | @since(version = 0.3.0)
305 | read-via-stream: func(
306 | /// The offset within the file at which to start reading.
307 | offset: filesize,
308 | ) -> tuple, future>>;
309 |
310 | /// Return a stream for writing to a file, if available.
311 | ///
312 | /// May fail with an error-code describing why the file cannot be written.
313 | ///
314 | /// It is valid to write past the end of a file; the file is extended to the
315 | /// extent of the write, with bytes between the previous end and the start of
316 | /// the write set to zero.
317 | ///
318 | /// This function returns once either full contents of the stream are
319 | /// written or an error is encountered.
320 | ///
321 | /// Note: This is similar to `pwrite` in POSIX.
322 | @since(version = 0.3.0)
323 | write-via-stream: func(
324 | /// Data to write
325 | data: stream,
326 | /// The offset within the file at which to start writing.
327 | offset: filesize,
328 | ) -> result<_, error-code>;
329 |
330 | /// Return a stream for appending to a file, if available.
331 | ///
332 | /// May fail with an error-code describing why the file cannot be appended.
333 | ///
334 | /// This function returns once either full contents of the stream are
335 | /// written or an error is encountered.
336 | ///
337 | /// Note: This is similar to `write` with `O_APPEND` in POSIX.
338 | @since(version = 0.3.0)
339 | append-via-stream: func(data: stream) -> result<_, error-code>;
340 |
341 | /// Provide file advisory information on a descriptor.
342 | ///
343 | /// This is similar to `posix_fadvise` in POSIX.
344 | @since(version = 0.3.0)
345 | advise: func(
346 | /// The offset within the file to which the advisory applies.
347 | offset: filesize,
348 | /// The length of the region to which the advisory applies.
349 | length: filesize,
350 | /// The advice.
351 | advice: advice
352 | ) -> result<_, error-code>;
353 |
354 | /// Synchronize the data of a file to disk.
355 | ///
356 | /// This function succeeds with no effect if the file descriptor is not
357 | /// opened for writing.
358 | ///
359 | /// Note: This is similar to `fdatasync` in POSIX.
360 | @since(version = 0.3.0)
361 | sync-data: func() -> result<_, error-code>;
362 |
363 | /// Get flags associated with a descriptor.
364 | ///
365 | /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX.
366 | ///
367 | /// Note: This returns the value that was the `fs_flags` value returned
368 | /// from `fdstat_get` in earlier versions of WASI.
369 | @since(version = 0.3.0)
370 | get-flags: func() -> result;
371 |
372 | /// Get the dynamic type of a descriptor.
373 | ///
374 | /// Note: This returns the same value as the `type` field of the `fd-stat`
375 | /// returned by `stat`, `stat-at` and similar.
376 | ///
377 | /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided
378 | /// by `fstat` in POSIX.
379 | ///
380 | /// Note: This returns the value that was the `fs_filetype` value returned
381 | /// from `fdstat_get` in earlier versions of WASI.
382 | @since(version = 0.3.0)
383 | get-type: func() -> result;
384 |
385 | /// Adjust the size of an open file. If this increases the file's size, the
386 | /// extra bytes are filled with zeros.
387 | ///
388 | /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI.
389 | @since(version = 0.3.0)
390 | set-size: func(size: filesize) -> result<_, error-code>;
391 |
392 | /// Adjust the timestamps of an open file or directory.
393 | ///
394 | /// Note: This is similar to `futimens` in POSIX.
395 | ///
396 | /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI.
397 | @since(version = 0.3.0)
398 | set-times: func(
399 | /// The desired values of the data access timestamp.
400 | data-access-timestamp: new-timestamp,
401 | /// The desired values of the data modification timestamp.
402 | data-modification-timestamp: new-timestamp,
403 | ) -> result<_, error-code>;
404 |
405 | /// Read directory entries from a directory.
406 | ///
407 | /// On filesystems where directories contain entries referring to themselves
408 | /// and their parents, often named `.` and `..` respectively, these entries
409 | /// are omitted.
410 | ///
411 | /// This always returns a new stream which starts at the beginning of the
412 | /// directory. Multiple streams may be active on the same directory, and they
413 | /// do not interfere with each other.
414 | ///
415 | /// This function returns a future, which will resolve to an error code if
416 | /// reading full contents of the directory fails.
417 | @since(version = 0.3.0)
418 | read-directory: func() -> tuple, future>>;
419 |
420 | /// Synchronize the data and metadata of a file to disk.
421 | ///
422 | /// This function succeeds with no effect if the file descriptor is not
423 | /// opened for writing.
424 | ///
425 | /// Note: This is similar to `fsync` in POSIX.
426 | @since(version = 0.3.0)
427 | sync: func() -> result<_, error-code>;
428 |
429 | /// Create a directory.
430 | ///
431 | /// Note: This is similar to `mkdirat` in POSIX.
432 | @since(version = 0.3.0)
433 | create-directory-at: func(
434 | /// The relative path at which to create the directory.
435 | path: string,
436 | ) -> result<_, error-code>;
437 |
438 | /// Return the attributes of an open file or directory.
439 | ///
440 | /// Note: This is similar to `fstat` in POSIX, except that it does not return
441 | /// device and inode information. For testing whether two descriptors refer to
442 | /// the same underlying filesystem object, use `is-same-object`. To obtain
443 | /// additional data that can be used do determine whether a file has been
444 | /// modified, use `metadata-hash`.
445 | ///
446 | /// Note: This was called `fd_filestat_get` in earlier versions of WASI.
447 | @since(version = 0.3.0)
448 | stat: func() -> result;
449 |
450 | /// Return the attributes of a file or directory.
451 | ///
452 | /// Note: This is similar to `fstatat` in POSIX, except that it does not
453 | /// return device and inode information. See the `stat` description for a
454 | /// discussion of alternatives.
455 | ///
456 | /// Note: This was called `path_filestat_get` in earlier versions of WASI.
457 | @since(version = 0.3.0)
458 | stat-at: func(
459 | /// Flags determining the method of how the path is resolved.
460 | path-flags: path-flags,
461 | /// The relative path of the file or directory to inspect.
462 | path: string,
463 | ) -> result;
464 |
465 | /// Adjust the timestamps of a file or directory.
466 | ///
467 | /// Note: This is similar to `utimensat` in POSIX.
468 | ///
469 | /// Note: This was called `path_filestat_set_times` in earlier versions of
470 | /// WASI.
471 | @since(version = 0.3.0)
472 | set-times-at: func(
473 | /// Flags determining the method of how the path is resolved.
474 | path-flags: path-flags,
475 | /// The relative path of the file or directory to operate on.
476 | path: string,
477 | /// The desired values of the data access timestamp.
478 | data-access-timestamp: new-timestamp,
479 | /// The desired values of the data modification timestamp.
480 | data-modification-timestamp: new-timestamp,
481 | ) -> result<_, error-code>;
482 |
483 | /// Create a hard link.
484 | ///
485 | /// Fails with `error-code::no-entry` if the old path does not exist,
486 | /// with `error-code::exist` if the new path already exists, and
487 | /// `error-code::not-permitted` if the old path is not a file.
488 | ///
489 | /// Note: This is similar to `linkat` in POSIX.
490 | @since(version = 0.3.0)
491 | link-at: func(
492 | /// Flags determining the method of how the path is resolved.
493 | old-path-flags: path-flags,
494 | /// The relative source path from which to link.
495 | old-path: string,
496 | /// The base directory for `new-path`.
497 | new-descriptor: borrow,
498 | /// The relative destination path at which to create the hard link.
499 | new-path: string,
500 | ) -> result<_, error-code>;
501 |
502 | /// Open a file or directory.
503 | ///
504 | /// If `flags` contains `descriptor-flags::mutate-directory`, and the base
505 | /// descriptor doesn't have `descriptor-flags::mutate-directory` set,
506 | /// `open-at` fails with `error-code::read-only`.
507 | ///
508 | /// If `flags` contains `write` or `mutate-directory`, or `open-flags`
509 | /// contains `truncate` or `create`, and the base descriptor doesn't have
510 | /// `descriptor-flags::mutate-directory` set, `open-at` fails with
511 | /// `error-code::read-only`.
512 | ///
513 | /// Note: This is similar to `openat` in POSIX.
514 | @since(version = 0.3.0)
515 | open-at: func(
516 | /// Flags determining the method of how the path is resolved.
517 | path-flags: path-flags,
518 | /// The relative path of the object to open.
519 | path: string,
520 | /// The method by which to open the file.
521 | open-flags: open-flags,
522 | /// Flags to use for the resulting descriptor.
523 | %flags: descriptor-flags,
524 | ) -> result;
525 |
526 | /// Read the contents of a symbolic link.
527 | ///
528 | /// If the contents contain an absolute or rooted path in the underlying
529 | /// filesystem, this function fails with `error-code::not-permitted`.
530 | ///
531 | /// Note: This is similar to `readlinkat` in POSIX.
532 | @since(version = 0.3.0)
533 | readlink-at: func(
534 | /// The relative path of the symbolic link from which to read.
535 | path: string,
536 | ) -> result;
537 |
538 | /// Remove a directory.
539 | ///
540 | /// Return `error-code::not-empty` if the directory is not empty.
541 | ///
542 | /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
543 | @since(version = 0.3.0)
544 | remove-directory-at: func(
545 | /// The relative path to a directory to remove.
546 | path: string,
547 | ) -> result<_, error-code>;
548 |
549 | /// Rename a filesystem object.
550 | ///
551 | /// Note: This is similar to `renameat` in POSIX.
552 | @since(version = 0.3.0)
553 | rename-at: func(
554 | /// The relative source path of the file or directory to rename.
555 | old-path: string,
556 | /// The base directory for `new-path`.
557 | new-descriptor: borrow,
558 | /// The relative destination path to which to rename the file or directory.
559 | new-path: string,
560 | ) -> result<_, error-code>;
561 |
562 | /// Create a symbolic link (also known as a "symlink").
563 | ///
564 | /// If `old-path` starts with `/`, the function fails with
565 | /// `error-code::not-permitted`.
566 | ///
567 | /// Note: This is similar to `symlinkat` in POSIX.
568 | @since(version = 0.3.0)
569 | symlink-at: func(
570 | /// The contents of the symbolic link.
571 | old-path: string,
572 | /// The relative destination path at which to create the symbolic link.
573 | new-path: string,
574 | ) -> result<_, error-code>;
575 |
576 | /// Unlink a filesystem object that is not a directory.
577 | ///
578 | /// Return `error-code::is-directory` if the path refers to a directory.
579 | /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
580 | @since(version = 0.3.0)
581 | unlink-file-at: func(
582 | /// The relative path to a file to unlink.
583 | path: string,
584 | ) -> result<_, error-code>;
585 |
586 | /// Test whether two descriptors refer to the same filesystem object.
587 | ///
588 | /// In POSIX, this corresponds to testing whether the two descriptors have the
589 | /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers.
590 | /// wasi-filesystem does not expose device and inode numbers, so this function
591 | /// may be used instead.
592 | @since(version = 0.3.0)
593 | is-same-object: func(other: borrow) -> bool;
594 |
595 | /// Return a hash of the metadata associated with a filesystem object referred
596 | /// to by a descriptor.
597 | ///
598 | /// This returns a hash of the last-modification timestamp and file size, and
599 | /// may also include the inode number, device number, birth timestamp, and
600 | /// other metadata fields that may change when the file is modified or
601 | /// replaced. It may also include a secret value chosen by the
602 | /// implementation and not otherwise exposed.
603 | ///
604 | /// Implementations are encouraged to provide the following properties:
605 | ///
606 | /// - If the file is not modified or replaced, the computed hash value should
607 | /// usually not change.
608 | /// - If the object is modified or replaced, the computed hash value should
609 | /// usually change.
610 | /// - The inputs to the hash should not be easily computable from the
611 | /// computed hash.
612 | ///
613 | /// However, none of these is required.
614 | @since(version = 0.3.0)
615 | metadata-hash: func() -> result;
616 |
617 | /// Return a hash of the metadata associated with a filesystem object referred
618 | /// to by a directory descriptor and a relative path.
619 | ///
620 | /// This performs the same hash computation as `metadata-hash`.
621 | @since(version = 0.3.0)
622 | metadata-hash-at: func(
623 | /// Flags determining the method of how the path is resolved.
624 | path-flags: path-flags,
625 | /// The relative path of the file or directory to inspect.
626 | path: string,
627 | ) -> result;
628 | }
629 | }
630 |
--------------------------------------------------------------------------------
/wit-0.3.0-draft/world.wit:
--------------------------------------------------------------------------------
1 | package wasi:filesystem@0.3.0;
2 |
3 | @since(version = 0.3.0)
4 | world imports {
5 | @since(version = 0.3.0)
6 | import types;
7 | @since(version = 0.3.0)
8 | import preopens;
9 | }
10 |
--------------------------------------------------------------------------------
/wit/deps.lock:
--------------------------------------------------------------------------------
1 | [clocks]
2 | url = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz"
3 | sha256 = "f1c53079469e20167e2cd4300651f13f34a6fd508b5c7331bb7c8c95fb8857fb"
4 | sha512 = "2a9281d0edcabad3bfef0dcfa7a9712d6beb6d6bb07b29fbf24235706d149672901ffc32192404a1ac511c512eb775daf9716ad3a0514bdf6a5c93a5fed19450"
5 |
6 | [io]
7 | url = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz"
8 | sha256 = "749de54c05f7118dc118999ad69c5b63890ea1dd86f1be69270062195c7b79c2"
9 | sha512 = "b5096d518a7931c100594b9216bb4a98cd64533166bc7fdbb7465b48effc345863bd3ce2ec84fef59b273bae507edab08ec08975d56240ccef68f2494964e80a"
10 |
--------------------------------------------------------------------------------
/wit/deps.toml:
--------------------------------------------------------------------------------
1 | io = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz"
2 | clocks = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz"
3 |
--------------------------------------------------------------------------------
/wit/deps/clocks/monotonic-clock.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.2.5;
2 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed
3 | /// time.
4 | ///
5 | /// It is intended to be portable at least between Unix-family platforms and
6 | /// Windows.
7 | ///
8 | /// A monotonic clock is a clock which has an unspecified initial value, and
9 | /// successive reads of the clock will produce non-decreasing values.
10 | @since(version = 0.2.0)
11 | interface monotonic-clock {
12 | @since(version = 0.2.0)
13 | use wasi:io/poll@0.2.5.{pollable};
14 |
15 | /// An instant in time, in nanoseconds. An instant is relative to an
16 | /// unspecified initial value, and can only be compared to instances from
17 | /// the same monotonic-clock.
18 | @since(version = 0.2.0)
19 | type instant = u64;
20 |
21 | /// A duration of time, in nanoseconds.
22 | @since(version = 0.2.0)
23 | type duration = u64;
24 |
25 | /// Read the current value of the clock.
26 | ///
27 | /// The clock is monotonic, therefore calling this function repeatedly will
28 | /// produce a sequence of non-decreasing values.
29 | @since(version = 0.2.0)
30 | now: func() -> instant;
31 |
32 | /// Query the resolution of the clock. Returns the duration of time
33 | /// corresponding to a clock tick.
34 | @since(version = 0.2.0)
35 | resolution: func() -> duration;
36 |
37 | /// Create a `pollable` which will resolve once the specified instant
38 | /// has occurred.
39 | @since(version = 0.2.0)
40 | subscribe-instant: func(
41 | when: instant,
42 | ) -> pollable;
43 |
44 | /// Create a `pollable` that will resolve after the specified duration has
45 | /// elapsed from the time this function is invoked.
46 | @since(version = 0.2.0)
47 | subscribe-duration: func(
48 | when: duration,
49 | ) -> pollable;
50 | }
51 |
--------------------------------------------------------------------------------
/wit/deps/clocks/timezone.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.2.5;
2 |
3 | @unstable(feature = clocks-timezone)
4 | interface timezone {
5 | @unstable(feature = clocks-timezone)
6 | use wall-clock.{datetime};
7 |
8 | /// Return information needed to display the given `datetime`. This includes
9 | /// the UTC offset, the time zone name, and a flag indicating whether
10 | /// daylight saving time is active.
11 | ///
12 | /// If the timezone cannot be determined for the given `datetime`, return a
13 | /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight
14 | /// saving time.
15 | @unstable(feature = clocks-timezone)
16 | display: func(when: datetime) -> timezone-display;
17 |
18 | /// The same as `display`, but only return the UTC offset.
19 | @unstable(feature = clocks-timezone)
20 | utc-offset: func(when: datetime) -> s32;
21 |
22 | /// Information useful for displaying the timezone of a specific `datetime`.
23 | ///
24 | /// This information may vary within a single `timezone` to reflect daylight
25 | /// saving time adjustments.
26 | @unstable(feature = clocks-timezone)
27 | record timezone-display {
28 | /// The number of seconds difference between UTC time and the local
29 | /// time of the timezone.
30 | ///
31 | /// The returned value will always be less than 86400 which is the
32 | /// number of seconds in a day (24*60*60).
33 | ///
34 | /// In implementations that do not expose an actual time zone, this
35 | /// should return 0.
36 | utc-offset: s32,
37 |
38 | /// The abbreviated name of the timezone to display to a user. The name
39 | /// `UTC` indicates Coordinated Universal Time. Otherwise, this should
40 | /// reference local standards for the name of the time zone.
41 | ///
42 | /// In implementations that do not expose an actual time zone, this
43 | /// should be the string `UTC`.
44 | ///
45 | /// In time zones that do not have an applicable name, a formatted
46 | /// representation of the UTC offset may be returned, such as `-04:00`.
47 | name: string,
48 |
49 | /// Whether daylight saving time is active.
50 | ///
51 | /// In implementations that do not expose an actual time zone, this
52 | /// should return false.
53 | in-daylight-saving-time: bool,
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/wit/deps/clocks/wall-clock.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.2.5;
2 | /// WASI Wall Clock is a clock API intended to let users query the current
3 | /// time. The name "wall" makes an analogy to a "clock on the wall", which
4 | /// is not necessarily monotonic as it may be reset.
5 | ///
6 | /// It is intended to be portable at least between Unix-family platforms and
7 | /// Windows.
8 | ///
9 | /// A wall clock is a clock which measures the date and time according to
10 | /// some external reference.
11 | ///
12 | /// External references may be reset, so this clock is not necessarily
13 | /// monotonic, making it unsuitable for measuring elapsed time.
14 | ///
15 | /// It is intended for reporting the current date and time for humans.
16 | @since(version = 0.2.0)
17 | interface wall-clock {
18 | /// A time and date in seconds plus nanoseconds.
19 | @since(version = 0.2.0)
20 | record datetime {
21 | seconds: u64,
22 | nanoseconds: u32,
23 | }
24 |
25 | /// Read the current value of the clock.
26 | ///
27 | /// This clock is not monotonic, therefore calling this function repeatedly
28 | /// will not necessarily produce a sequence of non-decreasing values.
29 | ///
30 | /// The returned timestamps represent the number of seconds since
31 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch],
32 | /// also known as [Unix Time].
33 | ///
34 | /// The nanoseconds field of the output is always less than 1000000000.
35 | ///
36 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16
37 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time
38 | @since(version = 0.2.0)
39 | now: func() -> datetime;
40 |
41 | /// Query the resolution of the clock.
42 | ///
43 | /// The nanoseconds field of the output is always less than 1000000000.
44 | @since(version = 0.2.0)
45 | resolution: func() -> datetime;
46 | }
47 |
--------------------------------------------------------------------------------
/wit/deps/clocks/world.wit:
--------------------------------------------------------------------------------
1 | package wasi:clocks@0.2.5;
2 |
3 | @since(version = 0.2.0)
4 | world imports {
5 | @since(version = 0.2.0)
6 | import monotonic-clock;
7 | @since(version = 0.2.0)
8 | import wall-clock;
9 | @unstable(feature = clocks-timezone)
10 | import timezone;
11 | }
12 |
--------------------------------------------------------------------------------
/wit/deps/io/error.wit:
--------------------------------------------------------------------------------
1 | package wasi:io@0.2.5;
2 |
3 | @since(version = 0.2.0)
4 | interface error {
5 | /// A resource which represents some error information.
6 | ///
7 | /// The only method provided by this resource is `to-debug-string`,
8 | /// which provides some human-readable information about the error.
9 | ///
10 | /// In the `wasi:io` package, this resource is returned through the
11 | /// `wasi:io/streams/stream-error` type.
12 | ///
13 | /// To provide more specific error information, other interfaces may
14 | /// offer functions to "downcast" this error into more specific types. For example,
15 | /// errors returned from streams derived from filesystem types can be described using
16 | /// the filesystem's own error-code type. This is done using the function
17 | /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow`
18 | /// parameter and returns an `option`.
19 | ///
20 | /// The set of functions which can "downcast" an `error` into a more
21 | /// concrete type is open.
22 | @since(version = 0.2.0)
23 | resource error {
24 | /// Returns a string that is suitable to assist humans in debugging
25 | /// this error.
26 | ///
27 | /// WARNING: The returned string should not be consumed mechanically!
28 | /// It may change across platforms, hosts, or other implementation
29 | /// details. Parsing this string is a major platform-compatibility
30 | /// hazard.
31 | @since(version = 0.2.0)
32 | to-debug-string: func() -> string;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/wit/deps/io/poll.wit:
--------------------------------------------------------------------------------
1 | package wasi:io@0.2.5;
2 |
3 | /// A poll API intended to let users wait for I/O events on multiple handles
4 | /// at once.
5 | @since(version = 0.2.0)
6 | interface poll {
7 | /// `pollable` represents a single I/O event which may be ready, or not.
8 | @since(version = 0.2.0)
9 | resource pollable {
10 |
11 | /// Return the readiness of a pollable. This function never blocks.
12 | ///
13 | /// Returns `true` when the pollable is ready, and `false` otherwise.
14 | @since(version = 0.2.0)
15 | ready: func() -> bool;
16 |
17 | /// `block` returns immediately if the pollable is ready, and otherwise
18 | /// blocks until ready.
19 | ///
20 | /// This function is equivalent to calling `poll.poll` on a list
21 | /// containing only this pollable.
22 | @since(version = 0.2.0)
23 | block: func();
24 | }
25 |
26 | /// Poll for completion on a set of pollables.
27 | ///
28 | /// This function takes a list of pollables, which identify I/O sources of
29 | /// interest, and waits until one or more of the events is ready for I/O.
30 | ///
31 | /// The result `list` contains one or more indices of handles in the
32 | /// argument list that is ready for I/O.
33 | ///
34 | /// This function traps if either:
35 | /// - the list is empty, or:
36 | /// - the list contains more elements than can be indexed with a `u32` value.
37 | ///
38 | /// A timeout can be implemented by adding a pollable from the
39 | /// wasi-clocks API to the list.
40 | ///
41 | /// This function does not return a `result`; polling in itself does not
42 | /// do any I/O so it doesn't fail. If any of the I/O sources identified by
43 | /// the pollables has an error, it is indicated by marking the source as
44 | /// being ready for I/O.
45 | @since(version = 0.2.0)
46 | poll: func(in: list>) -> list;
47 | }
48 |
--------------------------------------------------------------------------------
/wit/deps/io/streams.wit:
--------------------------------------------------------------------------------
1 | package wasi:io@0.2.5;
2 |
3 | /// WASI I/O is an I/O abstraction API which is currently focused on providing
4 | /// stream types.
5 | ///
6 | /// In the future, the component model is expected to add built-in stream types;
7 | /// when it does, they are expected to subsume this API.
8 | @since(version = 0.2.0)
9 | interface streams {
10 | @since(version = 0.2.0)
11 | use error.{error};
12 | @since(version = 0.2.0)
13 | use poll.{pollable};
14 |
15 | /// An error for input-stream and output-stream operations.
16 | @since(version = 0.2.0)
17 | variant stream-error {
18 | /// The last operation (a write or flush) failed before completion.
19 | ///
20 | /// More information is available in the `error` payload.
21 | ///
22 | /// After this, the stream will be closed. All future operations return
23 | /// `stream-error::closed`.
24 | last-operation-failed(error),
25 | /// The stream is closed: no more input will be accepted by the
26 | /// stream. A closed output-stream will return this error on all
27 | /// future operations.
28 | closed
29 | }
30 |
31 | /// An input bytestream.
32 | ///
33 | /// `input-stream`s are *non-blocking* to the extent practical on underlying
34 | /// platforms. I/O operations always return promptly; if fewer bytes are
35 | /// promptly available than requested, they return the number of bytes promptly
36 | /// available, which could even be zero. To wait for data to be available,
37 | /// use the `subscribe` function to obtain a `pollable` which can be polled
38 | /// for using `wasi:io/poll`.
39 | @since(version = 0.2.0)
40 | resource input-stream {
41 | /// Perform a non-blocking read from the stream.
42 | ///
43 | /// When the source of a `read` is binary data, the bytes from the source
44 | /// are returned verbatim. When the source of a `read` is known to the
45 | /// implementation to be text, bytes containing the UTF-8 encoding of the
46 | /// text are returned.
47 | ///
48 | /// This function returns a list of bytes containing the read data,
49 | /// when successful. The returned list will contain up to `len` bytes;
50 | /// it may return fewer than requested, but not more. The list is
51 | /// empty when no bytes are available for reading at this time. The
52 | /// pollable given by `subscribe` will be ready when more bytes are
53 | /// available.
54 | ///
55 | /// This function fails with a `stream-error` when the operation
56 | /// encounters an error, giving `last-operation-failed`, or when the
57 | /// stream is closed, giving `closed`.
58 | ///
59 | /// When the caller gives a `len` of 0, it represents a request to
60 | /// read 0 bytes. If the stream is still open, this call should
61 | /// succeed and return an empty list, or otherwise fail with `closed`.
62 | ///
63 | /// The `len` parameter is a `u64`, which could represent a list of u8 which
64 | /// is not possible to allocate in wasm32, or not desirable to allocate as
65 | /// as a return value by the callee. The callee may return a list of bytes
66 | /// less than `len` in size while more bytes are available for reading.
67 | @since(version = 0.2.0)
68 | read: func(
69 | /// The maximum number of bytes to read
70 | len: u64
71 | ) -> result, stream-error>;
72 |
73 | /// Read bytes from a stream, after blocking until at least one byte can
74 | /// be read. Except for blocking, behavior is identical to `read`.
75 | @since(version = 0.2.0)
76 | blocking-read: func(
77 | /// The maximum number of bytes to read
78 | len: u64
79 | ) -> result, stream-error>;
80 |
81 | /// Skip bytes from a stream. Returns number of bytes skipped.
82 | ///
83 | /// Behaves identical to `read`, except instead of returning a list
84 | /// of bytes, returns the number of bytes consumed from the stream.
85 | @since(version = 0.2.0)
86 | skip: func(
87 | /// The maximum number of bytes to skip.
88 | len: u64,
89 | ) -> result;
90 |
91 | /// Skip bytes from a stream, after blocking until at least one byte
92 | /// can be skipped. Except for blocking behavior, identical to `skip`.
93 | @since(version = 0.2.0)
94 | blocking-skip: func(
95 | /// The maximum number of bytes to skip.
96 | len: u64,
97 | ) -> result;
98 |
99 | /// Create a `pollable` which will resolve once either the specified stream
100 | /// has bytes available to read or the other end of the stream has been
101 | /// closed.
102 | /// The created `pollable` is a child resource of the `input-stream`.
103 | /// Implementations may trap if the `input-stream` is dropped before
104 | /// all derived `pollable`s created with this function are dropped.
105 | @since(version = 0.2.0)
106 | subscribe: func() -> pollable;
107 | }
108 |
109 |
110 | /// An output bytestream.
111 | ///
112 | /// `output-stream`s are *non-blocking* to the extent practical on
113 | /// underlying platforms. Except where specified otherwise, I/O operations also
114 | /// always return promptly, after the number of bytes that can be written
115 | /// promptly, which could even be zero. To wait for the stream to be ready to
116 | /// accept data, the `subscribe` function to obtain a `pollable` which can be
117 | /// polled for using `wasi:io/poll`.
118 | ///
119 | /// Dropping an `output-stream` while there's still an active write in
120 | /// progress may result in the data being lost. Before dropping the stream,
121 | /// be sure to fully flush your writes.
122 | @since(version = 0.2.0)
123 | resource output-stream {
124 | /// Check readiness for writing. This function never blocks.
125 | ///
126 | /// Returns the number of bytes permitted for the next call to `write`,
127 | /// or an error. Calling `write` with more bytes than this function has
128 | /// permitted will trap.
129 | ///
130 | /// When this function returns 0 bytes, the `subscribe` pollable will
131 | /// become ready when this function will report at least 1 byte, or an
132 | /// error.
133 | @since(version = 0.2.0)
134 | check-write: func() -> result;
135 |
136 | /// Perform a write. This function never blocks.
137 | ///
138 | /// When the destination of a `write` is binary data, the bytes from
139 | /// `contents` are written verbatim. When the destination of a `write` is
140 | /// known to the implementation to be text, the bytes of `contents` are
141 | /// transcoded from UTF-8 into the encoding of the destination and then
142 | /// written.
143 | ///
144 | /// Precondition: check-write gave permit of Ok(n) and contents has a
145 | /// length of less than or equal to n. Otherwise, this function will trap.
146 | ///
147 | /// returns Err(closed) without writing if the stream has closed since
148 | /// the last call to check-write provided a permit.
149 | @since(version = 0.2.0)
150 | write: func(
151 | contents: list
152 | ) -> result<_, stream-error>;
153 |
154 | /// Perform a write of up to 4096 bytes, and then flush the stream. Block
155 | /// until all of these operations are complete, or an error occurs.
156 | ///
157 | /// This is a convenience wrapper around the use of `check-write`,
158 | /// `subscribe`, `write`, and `flush`, and is implemented with the
159 | /// following pseudo-code:
160 | ///
161 | /// ```text
162 | /// let pollable = this.subscribe();
163 | /// while !contents.is_empty() {
164 | /// // Wait for the stream to become writable
165 | /// pollable.block();
166 | /// let Ok(n) = this.check-write(); // eliding error handling
167 | /// let len = min(n, contents.len());
168 | /// let (chunk, rest) = contents.split_at(len);
169 | /// this.write(chunk ); // eliding error handling
170 | /// contents = rest;
171 | /// }
172 | /// this.flush();
173 | /// // Wait for completion of `flush`
174 | /// pollable.block();
175 | /// // Check for any errors that arose during `flush`
176 | /// let _ = this.check-write(); // eliding error handling
177 | /// ```
178 | @since(version = 0.2.0)
179 | blocking-write-and-flush: func(
180 | contents: list
181 | ) -> result<_, stream-error>;
182 |
183 | /// Request to flush buffered output. This function never blocks.
184 | ///
185 | /// This tells the output-stream that the caller intends any buffered
186 | /// output to be flushed. the output which is expected to be flushed
187 | /// is all that has been passed to `write` prior to this call.
188 | ///
189 | /// Upon calling this function, the `output-stream` will not accept any
190 | /// writes (`check-write` will return `ok(0)`) until the flush has
191 | /// completed. The `subscribe` pollable will become ready when the
192 | /// flush has completed and the stream can accept more writes.
193 | @since(version = 0.2.0)
194 | flush: func() -> result<_, stream-error>;
195 |
196 | /// Request to flush buffered output, and block until flush completes
197 | /// and stream is ready for writing again.
198 | @since(version = 0.2.0)
199 | blocking-flush: func() -> result<_, stream-error>;
200 |
201 | /// Create a `pollable` which will resolve once the output-stream
202 | /// is ready for more writing, or an error has occurred. When this
203 | /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an
204 | /// error.
205 | ///
206 | /// If the stream is closed, this pollable is always ready immediately.
207 | ///
208 | /// The created `pollable` is a child resource of the `output-stream`.
209 | /// Implementations may trap if the `output-stream` is dropped before
210 | /// all derived `pollable`s created with this function are dropped.
211 | @since(version = 0.2.0)
212 | subscribe: func() -> pollable;
213 |
214 | /// Write zeroes to a stream.
215 | ///
216 | /// This should be used precisely like `write` with the exact same
217 | /// preconditions (must use check-write first), but instead of
218 | /// passing a list of bytes, you simply pass the number of zero-bytes
219 | /// that should be written.
220 | @since(version = 0.2.0)
221 | write-zeroes: func(
222 | /// The number of zero-bytes to write
223 | len: u64
224 | ) -> result<_, stream-error>;
225 |
226 | /// Perform a write of up to 4096 zeroes, and then flush the stream.
227 | /// Block until all of these operations are complete, or an error
228 | /// occurs.
229 | ///
230 | /// This is a convenience wrapper around the use of `check-write`,
231 | /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with
232 | /// the following pseudo-code:
233 | ///
234 | /// ```text
235 | /// let pollable = this.subscribe();
236 | /// while num_zeroes != 0 {
237 | /// // Wait for the stream to become writable
238 | /// pollable.block();
239 | /// let Ok(n) = this.check-write(); // eliding error handling
240 | /// let len = min(n, num_zeroes);
241 | /// this.write-zeroes(len); // eliding error handling
242 | /// num_zeroes -= len;
243 | /// }
244 | /// this.flush();
245 | /// // Wait for completion of `flush`
246 | /// pollable.block();
247 | /// // Check for any errors that arose during `flush`
248 | /// let _ = this.check-write(); // eliding error handling
249 | /// ```
250 | @since(version = 0.2.0)
251 | blocking-write-zeroes-and-flush: func(
252 | /// The number of zero-bytes to write
253 | len: u64
254 | ) -> result<_, stream-error>;
255 |
256 | /// Read from one stream and write to another.
257 | ///
258 | /// The behavior of splice is equivalent to:
259 | /// 1. calling `check-write` on the `output-stream`
260 | /// 2. calling `read` on the `input-stream` with the smaller of the
261 | /// `check-write` permitted length and the `len` provided to `splice`
262 | /// 3. calling `write` on the `output-stream` with that read data.
263 | ///
264 | /// Any error reported by the call to `check-write`, `read`, or
265 | /// `write` ends the splice and reports that error.
266 | ///
267 | /// This function returns the number of bytes transferred; it may be less
268 | /// than `len`.
269 | @since(version = 0.2.0)
270 | splice: func(
271 | /// The stream to read from
272 | src: borrow,
273 | /// The number of bytes to splice
274 | len: u64,
275 | ) -> result;
276 |
277 | /// Read from one stream and write to another, with blocking.
278 | ///
279 | /// This is similar to `splice`, except that it blocks until the
280 | /// `output-stream` is ready for writing, and the `input-stream`
281 | /// is ready for reading, before performing the `splice`.
282 | @since(version = 0.2.0)
283 | blocking-splice: func(
284 | /// The stream to read from
285 | src: borrow,
286 | /// The number of bytes to splice
287 | len: u64,
288 | ) -> result;
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/wit/deps/io/world.wit:
--------------------------------------------------------------------------------
1 | package wasi:io@0.2.5;
2 |
3 | @since(version = 0.2.0)
4 | world imports {
5 | @since(version = 0.2.0)
6 | import streams;
7 |
8 | @since(version = 0.2.0)
9 | import poll;
10 | }
11 |
--------------------------------------------------------------------------------
/wit/preopens.wit:
--------------------------------------------------------------------------------
1 | package wasi:filesystem@0.2.5;
2 |
3 | @since(version = 0.2.0)
4 | interface preopens {
5 | @since(version = 0.2.0)
6 | use types.{descriptor};
7 |
8 | /// Return the set of preopened directories, and their paths.
9 | @since(version = 0.2.0)
10 | get-directories: func() -> list>;
11 | }
12 |
--------------------------------------------------------------------------------
/wit/types.wit:
--------------------------------------------------------------------------------
1 | package wasi:filesystem@0.2.5;
2 | /// WASI filesystem is a filesystem API primarily intended to let users run WASI
3 | /// programs that access their files on their existing filesystems, without
4 | /// significant overhead.
5 | ///
6 | /// It is intended to be roughly portable between Unix-family platforms and
7 | /// Windows, though it does not hide many of the major differences.
8 | ///
9 | /// Paths are passed as interface-type `string`s, meaning they must consist of
10 | /// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain
11 | /// paths which are not accessible by this API.
12 | ///
13 | /// The directory separator in WASI is always the forward-slash (`/`).
14 | ///
15 | /// All paths in WASI are relative paths, and are interpreted relative to a
16 | /// `descriptor` referring to a base directory. If a `path` argument to any WASI
17 | /// function starts with `/`, or if any step of resolving a `path`, including
18 | /// `..` and symbolic link steps, reaches a directory outside of the base
19 | /// directory, or reaches a symlink to an absolute or rooted path in the
20 | /// underlying filesystem, the function fails with `error-code::not-permitted`.
21 | ///
22 | /// For more information about WASI path resolution and sandboxing, see
23 | /// [WASI filesystem path resolution].
24 | ///
25 | /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md
26 | @since(version = 0.2.0)
27 | interface types {
28 | @since(version = 0.2.0)
29 | use wasi:io/streams@0.2.5.{input-stream, output-stream, error};
30 | @since(version = 0.2.0)
31 | use wasi:clocks/wall-clock@0.2.5.{datetime};
32 |
33 | /// File size or length of a region within a file.
34 | @since(version = 0.2.0)
35 | type filesize = u64;
36 |
37 | /// The type of a filesystem object referenced by a descriptor.
38 | ///
39 | /// Note: This was called `filetype` in earlier versions of WASI.
40 | @since(version = 0.2.0)
41 | enum descriptor-type {
42 | /// The type of the descriptor or file is unknown or is different from
43 | /// any of the other types specified.
44 | unknown,
45 | /// The descriptor refers to a block device inode.
46 | block-device,
47 | /// The descriptor refers to a character device inode.
48 | character-device,
49 | /// The descriptor refers to a directory inode.
50 | directory,
51 | /// The descriptor refers to a named pipe.
52 | fifo,
53 | /// The file refers to a symbolic link inode.
54 | symbolic-link,
55 | /// The descriptor refers to a regular file inode.
56 | regular-file,
57 | /// The descriptor refers to a socket.
58 | socket,
59 | }
60 |
61 | /// Descriptor flags.
62 | ///
63 | /// Note: This was called `fdflags` in earlier versions of WASI.
64 | @since(version = 0.2.0)
65 | flags descriptor-flags {
66 | /// Read mode: Data can be read.
67 | read,
68 | /// Write mode: Data can be written to.
69 | write,
70 | /// Request that writes be performed according to synchronized I/O file
71 | /// integrity completion. The data stored in the file and the file's
72 | /// metadata are synchronized. This is similar to `O_SYNC` in POSIX.
73 | ///
74 | /// The precise semantics of this operation have not yet been defined for
75 | /// WASI. At this time, it should be interpreted as a request, and not a
76 | /// requirement.
77 | file-integrity-sync,
78 | /// Request that writes be performed according to synchronized I/O data
79 | /// integrity completion. Only the data stored in the file is
80 | /// synchronized. This is similar to `O_DSYNC` in POSIX.
81 | ///
82 | /// The precise semantics of this operation have not yet been defined for
83 | /// WASI. At this time, it should be interpreted as a request, and not a
84 | /// requirement.
85 | data-integrity-sync,
86 | /// Requests that reads be performed at the same level of integrity
87 | /// requested for writes. This is similar to `O_RSYNC` in POSIX.
88 | ///
89 | /// The precise semantics of this operation have not yet been defined for
90 | /// WASI. At this time, it should be interpreted as a request, and not a
91 | /// requirement.
92 | requested-write-sync,
93 | /// Mutating directories mode: Directory contents may be mutated.
94 | ///
95 | /// When this flag is unset on a descriptor, operations using the
96 | /// descriptor which would create, rename, delete, modify the data or
97 | /// metadata of filesystem objects, or obtain another handle which
98 | /// would permit any of those, shall fail with `error-code::read-only` if
99 | /// they would otherwise succeed.
100 | ///
101 | /// This may only be set on directories.
102 | mutate-directory,
103 | }
104 |
105 | /// File attributes.
106 | ///
107 | /// Note: This was called `filestat` in earlier versions of WASI.
108 | @since(version = 0.2.0)
109 | record descriptor-stat {
110 | /// File type.
111 | %type: descriptor-type,
112 | /// Number of hard links to the file.
113 | link-count: link-count,
114 | /// For regular files, the file size in bytes. For symbolic links, the
115 | /// length in bytes of the pathname contained in the symbolic link.
116 | size: filesize,
117 | /// Last data access timestamp.
118 | ///
119 | /// If the `option` is none, the platform doesn't maintain an access
120 | /// timestamp for this file.
121 | data-access-timestamp: option,
122 | /// Last data modification timestamp.
123 | ///
124 | /// If the `option` is none, the platform doesn't maintain a
125 | /// modification timestamp for this file.
126 | data-modification-timestamp: option,
127 | /// Last file status-change timestamp.
128 | ///
129 | /// If the `option` is none, the platform doesn't maintain a
130 | /// status-change timestamp for this file.
131 | status-change-timestamp: option,
132 | }
133 |
134 | /// Flags determining the method of how paths are resolved.
135 | @since(version = 0.2.0)
136 | flags path-flags {
137 | /// As long as the resolved path corresponds to a symbolic link, it is
138 | /// expanded.
139 | symlink-follow,
140 | }
141 |
142 | /// Open flags used by `open-at`.
143 | @since(version = 0.2.0)
144 | flags open-flags {
145 | /// Create file if it does not exist, similar to `O_CREAT` in POSIX.
146 | create,
147 | /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
148 | directory,
149 | /// Fail if file already exists, similar to `O_EXCL` in POSIX.
150 | exclusive,
151 | /// Truncate file to size 0, similar to `O_TRUNC` in POSIX.
152 | truncate,
153 | }
154 |
155 | /// Number of hard links to an inode.
156 | @since(version = 0.2.0)
157 | type link-count = u64;
158 |
159 | /// When setting a timestamp, this gives the value to set it to.
160 | @since(version = 0.2.0)
161 | variant new-timestamp {
162 | /// Leave the timestamp set to its previous value.
163 | no-change,
164 | /// Set the timestamp to the current time of the system clock associated
165 | /// with the filesystem.
166 | now,
167 | /// Set the timestamp to the given value.
168 | timestamp(datetime),
169 | }
170 |
171 | /// A directory entry.
172 | record directory-entry {
173 | /// The type of the file referred to by this directory entry.
174 | %type: descriptor-type,
175 |
176 | /// The name of the object.
177 | name: string,
178 | }
179 |
180 | /// Error codes returned by functions, similar to `errno` in POSIX.
181 | /// Not all of these error codes are returned by the functions provided by this
182 | /// API; some are used in higher-level library layers, and others are provided
183 | /// merely for alignment with POSIX.
184 | enum error-code {
185 | /// Permission denied, similar to `EACCES` in POSIX.
186 | access,
187 | /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX.
188 | would-block,
189 | /// Connection already in progress, similar to `EALREADY` in POSIX.
190 | already,
191 | /// Bad descriptor, similar to `EBADF` in POSIX.
192 | bad-descriptor,
193 | /// Device or resource busy, similar to `EBUSY` in POSIX.
194 | busy,
195 | /// Resource deadlock would occur, similar to `EDEADLK` in POSIX.
196 | deadlock,
197 | /// Storage quota exceeded, similar to `EDQUOT` in POSIX.
198 | quota,
199 | /// File exists, similar to `EEXIST` in POSIX.
200 | exist,
201 | /// File too large, similar to `EFBIG` in POSIX.
202 | file-too-large,
203 | /// Illegal byte sequence, similar to `EILSEQ` in POSIX.
204 | illegal-byte-sequence,
205 | /// Operation in progress, similar to `EINPROGRESS` in POSIX.
206 | in-progress,
207 | /// Interrupted function, similar to `EINTR` in POSIX.
208 | interrupted,
209 | /// Invalid argument, similar to `EINVAL` in POSIX.
210 | invalid,
211 | /// I/O error, similar to `EIO` in POSIX.
212 | io,
213 | /// Is a directory, similar to `EISDIR` in POSIX.
214 | is-directory,
215 | /// Too many levels of symbolic links, similar to `ELOOP` in POSIX.
216 | loop,
217 | /// Too many links, similar to `EMLINK` in POSIX.
218 | too-many-links,
219 | /// Message too large, similar to `EMSGSIZE` in POSIX.
220 | message-size,
221 | /// Filename too long, similar to `ENAMETOOLONG` in POSIX.
222 | name-too-long,
223 | /// No such device, similar to `ENODEV` in POSIX.
224 | no-device,
225 | /// No such file or directory, similar to `ENOENT` in POSIX.
226 | no-entry,
227 | /// No locks available, similar to `ENOLCK` in POSIX.
228 | no-lock,
229 | /// Not enough space, similar to `ENOMEM` in POSIX.
230 | insufficient-memory,
231 | /// No space left on device, similar to `ENOSPC` in POSIX.
232 | insufficient-space,
233 | /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX.
234 | not-directory,
235 | /// Directory not empty, similar to `ENOTEMPTY` in POSIX.
236 | not-empty,
237 | /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX.
238 | not-recoverable,
239 | /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX.
240 | unsupported,
241 | /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX.
242 | no-tty,
243 | /// No such device or address, similar to `ENXIO` in POSIX.
244 | no-such-device,
245 | /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX.
246 | overflow,
247 | /// Operation not permitted, similar to `EPERM` in POSIX.
248 | not-permitted,
249 | /// Broken pipe, similar to `EPIPE` in POSIX.
250 | pipe,
251 | /// Read-only file system, similar to `EROFS` in POSIX.
252 | read-only,
253 | /// Invalid seek, similar to `ESPIPE` in POSIX.
254 | invalid-seek,
255 | /// Text file busy, similar to `ETXTBSY` in POSIX.
256 | text-file-busy,
257 | /// Cross-device link, similar to `EXDEV` in POSIX.
258 | cross-device,
259 | }
260 |
261 | /// File or memory access pattern advisory information.
262 | @since(version = 0.2.0)
263 | enum advice {
264 | /// The application has no advice to give on its behavior with respect
265 | /// to the specified data.
266 | normal,
267 | /// The application expects to access the specified data sequentially
268 | /// from lower offsets to higher offsets.
269 | sequential,
270 | /// The application expects to access the specified data in a random
271 | /// order.
272 | random,
273 | /// The application expects to access the specified data in the near
274 | /// future.
275 | will-need,
276 | /// The application expects that it will not access the specified data
277 | /// in the near future.
278 | dont-need,
279 | /// The application expects to access the specified data once and then
280 | /// not reuse it thereafter.
281 | no-reuse,
282 | }
283 |
284 | /// A 128-bit hash value, split into parts because wasm doesn't have a
285 | /// 128-bit integer type.
286 | @since(version = 0.2.0)
287 | record metadata-hash-value {
288 | /// 64 bits of a 128-bit hash value.
289 | lower: u64,
290 | /// Another 64 bits of a 128-bit hash value.
291 | upper: u64,
292 | }
293 |
294 | /// A descriptor is a reference to a filesystem object, which may be a file,
295 | /// directory, named pipe, special file, or other object on which filesystem
296 | /// calls may be made.
297 | @since(version = 0.2.0)
298 | resource descriptor {
299 | /// Return a stream for reading from a file, if available.
300 | ///
301 | /// May fail with an error-code describing why the file cannot be read.
302 | ///
303 | /// Multiple read, write, and append streams may be active on the same open
304 | /// file and they do not interfere with each other.
305 | ///
306 | /// Note: This allows using `read-stream`, which is similar to `read` in POSIX.
307 | @since(version = 0.2.0)
308 | read-via-stream: func(
309 | /// The offset within the file at which to start reading.
310 | offset: filesize,
311 | ) -> result;
312 |
313 | /// Return a stream for writing to a file, if available.
314 | ///
315 | /// May fail with an error-code describing why the file cannot be written.
316 | ///
317 | /// Note: This allows using `write-stream`, which is similar to `write` in
318 | /// POSIX.
319 | @since(version = 0.2.0)
320 | write-via-stream: func(
321 | /// The offset within the file at which to start writing.
322 | offset: filesize,
323 | ) -> result;
324 |
325 | /// Return a stream for appending to a file, if available.
326 | ///
327 | /// May fail with an error-code describing why the file cannot be appended.
328 | ///
329 | /// Note: This allows using `write-stream`, which is similar to `write` with
330 | /// `O_APPEND` in POSIX.
331 | @since(version = 0.2.0)
332 | append-via-stream: func() -> result;
333 |
334 | /// Provide file advisory information on a descriptor.
335 | ///
336 | /// This is similar to `posix_fadvise` in POSIX.
337 | @since(version = 0.2.0)
338 | advise: func(
339 | /// The offset within the file to which the advisory applies.
340 | offset: filesize,
341 | /// The length of the region to which the advisory applies.
342 | length: filesize,
343 | /// The advice.
344 | advice: advice
345 | ) -> result<_, error-code>;
346 |
347 | /// Synchronize the data of a file to disk.
348 | ///
349 | /// This function succeeds with no effect if the file descriptor is not
350 | /// opened for writing.
351 | ///
352 | /// Note: This is similar to `fdatasync` in POSIX.
353 | @since(version = 0.2.0)
354 | sync-data: func() -> result<_, error-code>;
355 |
356 | /// Get flags associated with a descriptor.
357 | ///
358 | /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX.
359 | ///
360 | /// Note: This returns the value that was the `fs_flags` value returned
361 | /// from `fdstat_get` in earlier versions of WASI.
362 | @since(version = 0.2.0)
363 | get-flags: func() -> result;
364 |
365 | /// Get the dynamic type of a descriptor.
366 | ///
367 | /// Note: This returns the same value as the `type` field of the `fd-stat`
368 | /// returned by `stat`, `stat-at` and similar.
369 | ///
370 | /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided
371 | /// by `fstat` in POSIX.
372 | ///
373 | /// Note: This returns the value that was the `fs_filetype` value returned
374 | /// from `fdstat_get` in earlier versions of WASI.
375 | @since(version = 0.2.0)
376 | get-type: func() -> result;
377 |
378 | /// Adjust the size of an open file. If this increases the file's size, the
379 | /// extra bytes are filled with zeros.
380 | ///
381 | /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI.
382 | @since(version = 0.2.0)
383 | set-size: func(size: filesize) -> result<_, error-code>;
384 |
385 | /// Adjust the timestamps of an open file or directory.
386 | ///
387 | /// Note: This is similar to `futimens` in POSIX.
388 | ///
389 | /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI.
390 | @since(version = 0.2.0)
391 | set-times: func(
392 | /// The desired values of the data access timestamp.
393 | data-access-timestamp: new-timestamp,
394 | /// The desired values of the data modification timestamp.
395 | data-modification-timestamp: new-timestamp,
396 | ) -> result<_, error-code>;
397 |
398 | /// Read from a descriptor, without using and updating the descriptor's offset.
399 | ///
400 | /// This function returns a list of bytes containing the data that was
401 | /// read, along with a bool which, when true, indicates that the end of the
402 | /// file was reached. The returned list will contain up to `length` bytes; it
403 | /// may return fewer than requested, if the end of the file is reached or
404 | /// if the I/O operation is interrupted.
405 | ///
406 | /// In the future, this may change to return a `stream`.
407 | ///
408 | /// Note: This is similar to `pread` in POSIX.
409 | @since(version = 0.2.0)
410 | read: func(
411 | /// The maximum number of bytes to read.
412 | length: filesize,
413 | /// The offset within the file at which to read.
414 | offset: filesize,
415 | ) -> result, bool>, error-code>;
416 |
417 | /// Write to a descriptor, without using and updating the descriptor's offset.
418 | ///
419 | /// It is valid to write past the end of a file; the file is extended to the
420 | /// extent of the write, with bytes between the previous end and the start of
421 | /// the write set to zero.
422 | ///
423 | /// In the future, this may change to take a `stream`.
424 | ///
425 | /// Note: This is similar to `pwrite` in POSIX.
426 | @since(version = 0.2.0)
427 | write: func(
428 | /// Data to write
429 | buffer: list,
430 | /// The offset within the file at which to write.
431 | offset: filesize,
432 | ) -> result;
433 |
434 | /// Read directory entries from a directory.
435 | ///
436 | /// On filesystems where directories contain entries referring to themselves
437 | /// and their parents, often named `.` and `..` respectively, these entries
438 | /// are omitted.
439 | ///
440 | /// This always returns a new stream which starts at the beginning of the
441 | /// directory. Multiple streams may be active on the same directory, and they
442 | /// do not interfere with each other.
443 | @since(version = 0.2.0)
444 | read-directory: func() -> result;
445 |
446 | /// Synchronize the data and metadata of a file to disk.
447 | ///
448 | /// This function succeeds with no effect if the file descriptor is not
449 | /// opened for writing.
450 | ///
451 | /// Note: This is similar to `fsync` in POSIX.
452 | @since(version = 0.2.0)
453 | sync: func() -> result<_, error-code>;
454 |
455 | /// Create a directory.
456 | ///
457 | /// Note: This is similar to `mkdirat` in POSIX.
458 | @since(version = 0.2.0)
459 | create-directory-at: func(
460 | /// The relative path at which to create the directory.
461 | path: string,
462 | ) -> result<_, error-code>;
463 |
464 | /// Return the attributes of an open file or directory.
465 | ///
466 | /// Note: This is similar to `fstat` in POSIX, except that it does not return
467 | /// device and inode information. For testing whether two descriptors refer to
468 | /// the same underlying filesystem object, use `is-same-object`. To obtain
469 | /// additional data that can be used do determine whether a file has been
470 | /// modified, use `metadata-hash`.
471 | ///
472 | /// Note: This was called `fd_filestat_get` in earlier versions of WASI.
473 | @since(version = 0.2.0)
474 | stat: func() -> result;
475 |
476 | /// Return the attributes of a file or directory.
477 | ///
478 | /// Note: This is similar to `fstatat` in POSIX, except that it does not
479 | /// return device and inode information. See the `stat` description for a
480 | /// discussion of alternatives.
481 | ///
482 | /// Note: This was called `path_filestat_get` in earlier versions of WASI.
483 | @since(version = 0.2.0)
484 | stat-at: func(
485 | /// Flags determining the method of how the path is resolved.
486 | path-flags: path-flags,
487 | /// The relative path of the file or directory to inspect.
488 | path: string,
489 | ) -> result;
490 |
491 | /// Adjust the timestamps of a file or directory.
492 | ///
493 | /// Note: This is similar to `utimensat` in POSIX.
494 | ///
495 | /// Note: This was called `path_filestat_set_times` in earlier versions of
496 | /// WASI.
497 | @since(version = 0.2.0)
498 | set-times-at: func(
499 | /// Flags determining the method of how the path is resolved.
500 | path-flags: path-flags,
501 | /// The relative path of the file or directory to operate on.
502 | path: string,
503 | /// The desired values of the data access timestamp.
504 | data-access-timestamp: new-timestamp,
505 | /// The desired values of the data modification timestamp.
506 | data-modification-timestamp: new-timestamp,
507 | ) -> result<_, error-code>;
508 |
509 | /// Create a hard link.
510 | ///
511 | /// Fails with `error-code::no-entry` if the old path does not exist,
512 | /// with `error-code::exist` if the new path already exists, and
513 | /// `error-code::not-permitted` if the old path is not a file.
514 | ///
515 | /// Note: This is similar to `linkat` in POSIX.
516 | @since(version = 0.2.0)
517 | link-at: func(
518 | /// Flags determining the method of how the path is resolved.
519 | old-path-flags: path-flags,
520 | /// The relative source path from which to link.
521 | old-path: string,
522 | /// The base directory for `new-path`.
523 | new-descriptor: borrow,
524 | /// The relative destination path at which to create the hard link.
525 | new-path: string,
526 | ) -> result<_, error-code>;
527 |
528 | /// Open a file or directory.
529 | ///
530 | /// If `flags` contains `descriptor-flags::mutate-directory`, and the base
531 | /// descriptor doesn't have `descriptor-flags::mutate-directory` set,
532 | /// `open-at` fails with `error-code::read-only`.
533 | ///
534 | /// If `flags` contains `write` or `mutate-directory`, or `open-flags`
535 | /// contains `truncate` or `create`, and the base descriptor doesn't have
536 | /// `descriptor-flags::mutate-directory` set, `open-at` fails with
537 | /// `error-code::read-only`.
538 | ///
539 | /// Note: This is similar to `openat` in POSIX.
540 | @since(version = 0.2.0)
541 | open-at: func(
542 | /// Flags determining the method of how the path is resolved.
543 | path-flags: path-flags,
544 | /// The relative path of the object to open.
545 | path: string,
546 | /// The method by which to open the file.
547 | open-flags: open-flags,
548 | /// Flags to use for the resulting descriptor.
549 | %flags: descriptor-flags,
550 | ) -> result;
551 |
552 | /// Read the contents of a symbolic link.
553 | ///
554 | /// If the contents contain an absolute or rooted path in the underlying
555 | /// filesystem, this function fails with `error-code::not-permitted`.
556 | ///
557 | /// Note: This is similar to `readlinkat` in POSIX.
558 | @since(version = 0.2.0)
559 | readlink-at: func(
560 | /// The relative path of the symbolic link from which to read.
561 | path: string,
562 | ) -> result;
563 |
564 | /// Remove a directory.
565 | ///
566 | /// Return `error-code::not-empty` if the directory is not empty.
567 | ///
568 | /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
569 | @since(version = 0.2.0)
570 | remove-directory-at: func(
571 | /// The relative path to a directory to remove.
572 | path: string,
573 | ) -> result<_, error-code>;
574 |
575 | /// Rename a filesystem object.
576 | ///
577 | /// Note: This is similar to `renameat` in POSIX.
578 | @since(version = 0.2.0)
579 | rename-at: func(
580 | /// The relative source path of the file or directory to rename.
581 | old-path: string,
582 | /// The base directory for `new-path`.
583 | new-descriptor: borrow,
584 | /// The relative destination path to which to rename the file or directory.
585 | new-path: string,
586 | ) -> result<_, error-code>;
587 |
588 | /// Create a symbolic link (also known as a "symlink").
589 | ///
590 | /// If `old-path` starts with `/`, the function fails with
591 | /// `error-code::not-permitted`.
592 | ///
593 | /// Note: This is similar to `symlinkat` in POSIX.
594 | @since(version = 0.2.0)
595 | symlink-at: func(
596 | /// The contents of the symbolic link.
597 | old-path: string,
598 | /// The relative destination path at which to create the symbolic link.
599 | new-path: string,
600 | ) -> result<_, error-code>;
601 |
602 | /// Unlink a filesystem object that is not a directory.
603 | ///
604 | /// Return `error-code::is-directory` if the path refers to a directory.
605 | /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
606 | @since(version = 0.2.0)
607 | unlink-file-at: func(
608 | /// The relative path to a file to unlink.
609 | path: string,
610 | ) -> result<_, error-code>;
611 |
612 | /// Test whether two descriptors refer to the same filesystem object.
613 | ///
614 | /// In POSIX, this corresponds to testing whether the two descriptors have the
615 | /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers.
616 | /// wasi-filesystem does not expose device and inode numbers, so this function
617 | /// may be used instead.
618 | @since(version = 0.2.0)
619 | is-same-object: func(other: borrow) -> bool;
620 |
621 | /// Return a hash of the metadata associated with a filesystem object referred
622 | /// to by a descriptor.
623 | ///
624 | /// This returns a hash of the last-modification timestamp and file size, and
625 | /// may also include the inode number, device number, birth timestamp, and
626 | /// other metadata fields that may change when the file is modified or
627 | /// replaced. It may also include a secret value chosen by the
628 | /// implementation and not otherwise exposed.
629 | ///
630 | /// Implementations are encouraged to provide the following properties:
631 | ///
632 | /// - If the file is not modified or replaced, the computed hash value should
633 | /// usually not change.
634 | /// - If the object is modified or replaced, the computed hash value should
635 | /// usually change.
636 | /// - The inputs to the hash should not be easily computable from the
637 | /// computed hash.
638 | ///
639 | /// However, none of these is required.
640 | @since(version = 0.2.0)
641 | metadata-hash: func() -> result;
642 |
643 | /// Return a hash of the metadata associated with a filesystem object referred
644 | /// to by a directory descriptor and a relative path.
645 | ///
646 | /// This performs the same hash computation as `metadata-hash`.
647 | @since(version = 0.2.0)
648 | metadata-hash-at: func(
649 | /// Flags determining the method of how the path is resolved.
650 | path-flags: path-flags,
651 | /// The relative path of the file or directory to inspect.
652 | path: string,
653 | ) -> result;
654 | }
655 |
656 | /// A stream of directory entries.
657 | @since(version = 0.2.0)
658 | resource directory-entry-stream {
659 | /// Read a single directory entry from a `directory-entry-stream`.
660 | @since(version = 0.2.0)
661 | read-directory-entry: func() -> result