1package wasi:[email protected];
2
3/// WASI filesystem is a filesystem API primarily intended to let users run WASI
4/// programs that access their files on their existing filesystems, without
5/// significant overhead.
6///
7/// It is intended to be roughly portable between Unix-family platforms and
8/// Windows, though it does not hide many of the major differences.
9///
10/// Paths are passed as interface-type `string`s, meaning they must consist of
11/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain
12/// paths which are not accessible by this API.
13///
14/// The directory separator in WASI is always the forward-slash (`/`).
15///
16/// All paths in WASI are relative paths, and are interpreted relative to a
17/// `descriptor` referring to a base directory. If a `path` argument to any WASI
18/// function starts with `/`, or if any step of resolving a `path`, including
19/// `..` and symbolic link steps, reaches a directory outside of the base
20/// directory, or reaches a symlink to an absolute or rooted path in the
21/// underlying filesystem, the function fails with `error-code::not-permitted`.
22///
23/// For more information about WASI path resolution and sandboxing, see
24/// [WASI filesystem path resolution].
25///
26/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md
27@since(version = 0.2.0)
28interface types {
29  @since(version = 0.2.0)
30  use wasi:io/streams@0.2.6.{input-stream, output-stream, error};
31  @since(version = 0.2.0)
32  use wasi:clocks/wall-clock@0.2.6.{datetime};
33
34  /// File size or length of a region within a file.
35  @since(version = 0.2.0)
36  type filesize = u64;
37
38  /// The type of a filesystem object referenced by a descriptor.
39  ///
40  /// Note: This was called `filetype` in earlier versions of WASI.
41  @since(version = 0.2.0)
42  enum descriptor-type {
43    /// The type of the descriptor or file is unknown or is different from
44    /// any of the other types specified.
45    unknown,
46    /// The descriptor refers to a block device inode.
47    block-device,
48    /// The descriptor refers to a character device inode.
49    character-device,
50    /// The descriptor refers to a directory inode.
51    directory,
52    /// The descriptor refers to a named pipe.
53    fifo,
54    /// The file refers to a symbolic link inode.
55    symbolic-link,
56    /// The descriptor refers to a regular file inode.
57    regular-file,
58    /// The descriptor refers to a socket.
59    socket,
60  }
61
62  /// Descriptor flags.
63  ///
64  /// Note: This was called `fdflags` in earlier versions of WASI.
65  @since(version = 0.2.0)
66  flags descriptor-flags {
67    /// Read mode: Data can be read.
68    read,
69    /// Write mode: Data can be written to.
70    write,
71    /// Request that writes be performed according to synchronized I/O file
72    /// integrity completion. The data stored in the file and the file's
73    /// metadata are synchronized. This is similar to `O_SYNC` in POSIX.
74    ///
75    /// The precise semantics of this operation have not yet been defined for
76    /// WASI. At this time, it should be interpreted as a request, and not a
77    /// requirement.
78    file-integrity-sync,
79    /// Request that writes be performed according to synchronized I/O data
80    /// integrity completion. Only the data stored in the file is
81    /// synchronized. This is similar to `O_DSYNC` in POSIX.
82    ///
83    /// The precise semantics of this operation have not yet been defined for
84    /// WASI. At this time, it should be interpreted as a request, and not a
85    /// requirement.
86    data-integrity-sync,
87    /// Requests that reads be performed at the same level of integrity
88    /// requested for writes. This is similar to `O_RSYNC` in POSIX.
89    ///
90    /// The precise semantics of this operation have not yet been defined for
91    /// WASI. At this time, it should be interpreted as a request, and not a
92    /// requirement.
93    requested-write-sync,
94    /// Mutating directories mode: Directory contents may be mutated.
95    ///
96    /// When this flag is unset on a descriptor, operations using the
97    /// descriptor which would create, rename, delete, modify the data or
98    /// metadata of filesystem objects, or obtain another handle which
99    /// would permit any of those, shall fail with `error-code::read-only` if
100    /// they would otherwise succeed.
101    ///
102    /// This may only be set on directories.
103    mutate-directory,
104  }
105
106  /// Flags determining the method of how paths are resolved.
107  @since(version = 0.2.0)
108  flags path-flags {
109    /// As long as the resolved path corresponds to a symbolic link, it is
110    /// expanded.
111    symlink-follow,
112  }
113
114  /// Open flags used by `open-at`.
115  @since(version = 0.2.0)
116  flags open-flags {
117    /// Create file if it does not exist, similar to `O_CREAT` in POSIX.
118    create,
119    /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
120    directory,
121    /// Fail if file already exists, similar to `O_EXCL` in POSIX.
122    exclusive,
123    /// Truncate file to size 0, similar to `O_TRUNC` in POSIX.
124    truncate,
125  }
126
127  /// Number of hard links to an inode.
128  @since(version = 0.2.0)
129  type link-count = u64;
130
131  /// File attributes.
132  ///
133  /// Note: This was called `filestat` in earlier versions of WASI.
134  @since(version = 0.2.0)
135  record descriptor-stat {
136    /// File type.
137    %type: descriptor-type,
138    /// Number of hard links to the file.
139    link-count: link-count,
140    /// For regular files, the file size in bytes. For symbolic links, the
141    /// length in bytes of the pathname contained in the symbolic link.
142    size: filesize,
143    /// Last data access timestamp.
144    ///
145    /// If the `option` is none, the platform doesn't maintain an access
146    /// timestamp for this file.
147    data-access-timestamp: option<datetime>,
148    /// Last data modification timestamp.
149    ///
150    /// If the `option` is none, the platform doesn't maintain a
151    /// modification timestamp for this file.
152    data-modification-timestamp: option<datetime>,
153    /// Last file status-change timestamp.
154    ///
155    /// If the `option` is none, the platform doesn't maintain a
156    /// status-change timestamp for this file.
157    status-change-timestamp: option<datetime>,
158  }
159
160  /// When setting a timestamp, this gives the value to set it to.
161  @since(version = 0.2.0)
162  variant new-timestamp {
163    /// Leave the timestamp set to its previous value.
164    no-change,
165    /// Set the timestamp to the current time of the system clock associated
166    /// with the filesystem.
167    now,
168    /// Set the timestamp to the given value.
169    timestamp(datetime),
170  }
171
172  /// A directory entry.
173  record directory-entry {
174    /// The type of the file referred to by this directory entry.
175    %type: descriptor-type,
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(offset: filesize) -> result<input-stream, error-code>;
309    /// Return a stream for writing to a file, if available.
310    ///
311    /// May fail with an error-code describing why the file cannot be written.
312    ///
313    /// Note: This allows using `write-stream`, which is similar to `write` in
314    /// POSIX.
315    @since(version = 0.2.0)
316    write-via-stream: func(offset: filesize) -> result<output-stream, error-code>;
317    /// Return a stream for appending to a file, if available.
318    ///
319    /// May fail with an error-code describing why the file cannot be appended.
320    ///
321    /// Note: This allows using `write-stream`, which is similar to `write` with
322    /// `O_APPEND` in POSIX.
323    @since(version = 0.2.0)
324    append-via-stream: func() -> result<output-stream, error-code>;
325    /// Provide file advisory information on a descriptor.
326    ///
327    /// This is similar to `posix_fadvise` in POSIX.
328    @since(version = 0.2.0)
329    advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>;
330    /// Synchronize the data of a file to disk.
331    ///
332    /// This function succeeds with no effect if the file descriptor is not
333    /// opened for writing.
334    ///
335    /// Note: This is similar to `fdatasync` in POSIX.
336    @since(version = 0.2.0)
337    sync-data: func() -> result<_, error-code>;
338    /// Get flags associated with a descriptor.
339    ///
340    /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX.
341    ///
342    /// Note: This returns the value that was the `fs_flags` value returned
343    /// from `fdstat_get` in earlier versions of WASI.
344    @since(version = 0.2.0)
345    get-flags: func() -> result<descriptor-flags, error-code>;
346    /// Get the dynamic type of a descriptor.
347    ///
348    /// Note: This returns the same value as the `type` field of the `fd-stat`
349    /// returned by `stat`, `stat-at` and similar.
350    ///
351    /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided
352    /// by `fstat` in POSIX.
353    ///
354    /// Note: This returns the value that was the `fs_filetype` value returned
355    /// from `fdstat_get` in earlier versions of WASI.
356    @since(version = 0.2.0)
357    get-type: func() -> result<descriptor-type, error-code>;
358    /// Adjust the size of an open file. If this increases the file's size, the
359    /// extra bytes are filled with zeros.
360    ///
361    /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI.
362    @since(version = 0.2.0)
363    set-size: func(size: filesize) -> result<_, error-code>;
364    /// Adjust the timestamps of an open file or directory.
365    ///
366    /// Note: This is similar to `futimens` in POSIX.
367    ///
368    /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI.
369    @since(version = 0.2.0)
370    set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>;
371    /// Read from a descriptor, without using and updating the descriptor's offset.
372    ///
373    /// This function returns a list of bytes containing the data that was
374    /// read, along with a bool which, when true, indicates that the end of the
375    /// file was reached. The returned list will contain up to `length` bytes; it
376    /// may return fewer than requested, if the end of the file is reached or
377    /// if the I/O operation is interrupted.
378    ///
379    /// In the future, this may change to return a `stream<u8, error-code>`.
380    ///
381    /// Note: This is similar to `pread` in POSIX.
382    @since(version = 0.2.0)
383    read: func(length: filesize, offset: filesize) -> result<tuple<list<u8>, bool>, error-code>;
384    /// Write to a descriptor, without using and updating the descriptor's offset.
385    ///
386    /// It is valid to write past the end of a file; the file is extended to the
387    /// extent of the write, with bytes between the previous end and the start of
388    /// the write set to zero.
389    ///
390    /// In the future, this may change to take a `stream<u8, error-code>`.
391    ///
392    /// Note: This is similar to `pwrite` in POSIX.
393    @since(version = 0.2.0)
394    write: func(buffer: list<u8>, offset: filesize) -> result<filesize, error-code>;
395    /// Read directory entries from a directory.
396    ///
397    /// On filesystems where directories contain entries referring to themselves
398    /// and their parents, often named `.` and `..` respectively, these entries
399    /// are omitted.
400    ///
401    /// This always returns a new stream which starts at the beginning of the
402    /// directory. Multiple streams may be active on the same directory, and they
403    /// do not interfere with each other.
404    @since(version = 0.2.0)
405    read-directory: func() -> result<directory-entry-stream, error-code>;
406    /// Synchronize the data and metadata of a file to disk.
407    ///
408    /// This function succeeds with no effect if the file descriptor is not
409    /// opened for writing.
410    ///
411    /// Note: This is similar to `fsync` in POSIX.
412    @since(version = 0.2.0)
413    sync: func() -> result<_, error-code>;
414    /// Create a directory.
415    ///
416    /// Note: This is similar to `mkdirat` in POSIX.
417    @since(version = 0.2.0)
418    create-directory-at: func(path: string) -> result<_, error-code>;
419    /// Return the attributes of an open file or directory.
420    ///
421    /// Note: This is similar to `fstat` in POSIX, except that it does not return
422    /// device and inode information. For testing whether two descriptors refer to
423    /// the same underlying filesystem object, use `is-same-object`. To obtain
424    /// additional data that can be used do determine whether a file has been
425    /// modified, use `metadata-hash`.
426    ///
427    /// Note: This was called `fd_filestat_get` in earlier versions of WASI.
428    @since(version = 0.2.0)
429    stat: func() -> result<descriptor-stat, error-code>;
430    /// Return the attributes of a file or directory.
431    ///
432    /// Note: This is similar to `fstatat` in POSIX, except that it does not
433    /// return device and inode information. See the `stat` description for a
434    /// discussion of alternatives.
435    ///
436    /// Note: This was called `path_filestat_get` in earlier versions of WASI.
437    @since(version = 0.2.0)
438    stat-at: func(path-flags: path-flags, path: string) -> result<descriptor-stat, error-code>;
439    /// Adjust the timestamps of a file or directory.
440    ///
441    /// Note: This is similar to `utimensat` in POSIX.
442    ///
443    /// Note: This was called `path_filestat_set_times` in earlier versions of
444    /// WASI.
445    @since(version = 0.2.0)
446    set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>;
447    /// Create a hard link.
448    ///
449    /// Fails with `error-code::no-entry` if the old path does not exist,
450    /// with `error-code::exist` if the new path already exists, and
451    /// `error-code::not-permitted` if the old path is not a file.
452    ///
453    /// Note: This is similar to `linkat` in POSIX.
454    @since(version = 0.2.0)
455    link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow<descriptor>, new-path: string) -> result<_, error-code>;
456    /// Open a file or directory.
457    ///
458    /// If `flags` contains `descriptor-flags::mutate-directory`, and the base
459    /// descriptor doesn't have `descriptor-flags::mutate-directory` set,
460    /// `open-at` fails with `error-code::read-only`.
461    ///
462    /// If `flags` contains `write` or `mutate-directory`, or `open-flags`
463    /// contains `truncate` or `create`, and the base descriptor doesn't have
464    /// `descriptor-flags::mutate-directory` set, `open-at` fails with
465    /// `error-code::read-only`.
466    ///
467    /// Note: This is similar to `openat` in POSIX.
468    @since(version = 0.2.0)
469    open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result<descriptor, error-code>;
470    /// Read the contents of a symbolic link.
471    ///
472    /// If the contents contain an absolute or rooted path in the underlying
473    /// filesystem, this function fails with `error-code::not-permitted`.
474    ///
475    /// Note: This is similar to `readlinkat` in POSIX.
476    @since(version = 0.2.0)
477    readlink-at: func(path: string) -> result<string, error-code>;
478    /// Remove a directory.
479    ///
480    /// Return `error-code::not-empty` if the directory is not empty.
481    ///
482    /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
483    @since(version = 0.2.0)
484    remove-directory-at: func(path: string) -> result<_, error-code>;
485    /// Rename a filesystem object.
486    ///
487    /// Note: This is similar to `renameat` in POSIX.
488    @since(version = 0.2.0)
489    rename-at: func(old-path: string, new-descriptor: borrow<descriptor>, new-path: string) -> result<_, error-code>;
490    /// Create a symbolic link (also known as a "symlink").
491    ///
492    /// If `old-path` starts with `/`, the function fails with
493    /// `error-code::not-permitted`.
494    ///
495    /// Note: This is similar to `symlinkat` in POSIX.
496    @since(version = 0.2.0)
497    symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>;
498    /// Unlink a filesystem object that is not a directory.
499    ///
500    /// Return `error-code::is-directory` if the path refers to a directory.
501    /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
502    @since(version = 0.2.0)
503    unlink-file-at: func(path: string) -> result<_, error-code>;
504    /// Test whether two descriptors refer to the same filesystem object.
505    ///
506    /// In POSIX, this corresponds to testing whether the two descriptors have the
507    /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers.
508    /// wasi-filesystem does not expose device and inode numbers, so this function
509    /// may be used instead.
510    @since(version = 0.2.0)
511    is-same-object: func(other: borrow<descriptor>) -> bool;
512    /// Return a hash of the metadata associated with a filesystem object referred
513    /// to by a descriptor.
514    ///
515    /// This returns a hash of the last-modification timestamp and file size, and
516    /// may also include the inode number, device number, birth timestamp, and
517    /// other metadata fields that may change when the file is modified or
518    /// replaced. It may also include a secret value chosen by the
519    /// implementation and not otherwise exposed.
520    ///
521    /// Implementations are encouraged to provide the following properties:
522    ///
523    ///  - If the file is not modified or replaced, the computed hash value should
524    ///    usually not change.
525    ///  - If the object is modified or replaced, the computed hash value should
526    ///    usually change.
527    ///  - The inputs to the hash should not be easily computable from the
528    ///    computed hash.
529    ///
530    /// However, none of these is required.
531    @since(version = 0.2.0)
532    metadata-hash: func() -> result<metadata-hash-value, error-code>;
533    /// Return a hash of the metadata associated with a filesystem object referred
534    /// to by a directory descriptor and a relative path.
535    ///
536    /// This performs the same hash computation as `metadata-hash`.
537    @since(version = 0.2.0)
538    metadata-hash-at: func(path-flags: path-flags, path: string) -> result<metadata-hash-value, error-code>;
539  }
540
541  /// A stream of directory entries.
542  @since(version = 0.2.0)
543  resource directory-entry-stream {
544    /// Read a single directory entry from a `directory-entry-stream`.
545    @since(version = 0.2.0)
546    read-directory-entry: func() -> result<option<directory-entry>, error-code>;
547  }
548
549  /// Attempts to extract a filesystem-related `error-code` from the stream
550  /// `error` provided.
551  ///
552  /// Stream operations which return `stream-error::last-operation-failed`
553  /// have a payload with more information about the operation that failed.
554  /// This payload can be passed through to this function to see if there's
555  /// filesystem-related information about the error to return.
556  ///
557  /// Note that this function is fallible because not all stream-related
558  /// errors are filesystem-related errors.
559  @since(version = 0.2.0)
560  filesystem-error-code: func(err: borrow<error>) -> option<error-code>;
561}
562
563@since(version = 0.2.0)
564interface preopens {
565  @since(version = 0.2.0)
566  use types.{descriptor};
567
568  /// Return the set of preopened directories, and their paths.
569  @since(version = 0.2.0)
570  get-directories: func() -> list<tuple<descriptor, string>>;
571}
572
573@since(version = 0.2.0)
574world imports {
575  @since(version = 0.2.0)
576  import wasi:io/error@0.2.6;
577  @since(version = 0.2.0)
578  import wasi:io/poll@0.2.6;
579  @since(version = 0.2.0)
580  import wasi:io/streams@0.2.6;
581  @since(version = 0.2.0)
582  import wasi:clocks/wall-clock@0.2.6;
583  @since(version = 0.2.0)
584  import types;
585  @since(version = 0.2.0)
586  import preopens;
587}
588