# WASI Filesystem API WASI filesystem is a filesystem API primarily intended to let users run WASI programs that access their files on their existing filesystems, without significant overhead. It is intended to be roughly portable between Unix-family platforms and Windows, though it does not hide many of the major differences. Paths are passed as interface-type `string`s, meaning they must consist of a sequence of Unicode Scalar Values (USVs). Some filesystems may contain paths which are not accessible by this API. ## `size` ```wit /// Size of a range of bytes in memory. type size = u32 ``` ## `filesize` ```wit /// Non-negative file size or length of a region within a file. type filesize = u64 ``` ## `filedelta` ```wit /// Relative offset within a file. type filedelta = s64 ``` ## `datetime` ```wit /// Timestamp in seconds and nanoseconds. record datetime { seconds: u64, nanoseconds: u32, } ``` ## `descriptor-type` ```wit /// The type of a filesystem object referenced by a descriptor. /// /// Note: This was called `filetype` in earlier versions of WASI. enum descriptor-type { /// The type of the descriptor or file is unknown or is different from /// any of the other types specified. unknown, /// The descriptor refers to a block device inode. block-device, /// The descriptor refers to a character device inode. character-device, /// The descriptor refers to a directory inode. directory, /// The descriptor refers to a named pipe. fifo, /// The file refers to a symbolic link inode. symbolic-link, /// The descriptor refers to a regular file inode. regular-file, /// The descriptor refers to a socket. socket, } ``` ## `descriptor-flags` ```wit /// Descriptor flags. /// /// Note: This was called `fdflags` in earlier versions of WASI. flags descriptor-flags { /// Read mode: Data can be read. read, /// Write mode: Data can be written to. write, /// Append mode: Data written to the file is always appended to the file's /// end. append, /// Write according to synchronized I/O data integrity completion. Only the /// data stored in the file is synchronized. dsync, /// Non-blocking mode. nonblock, /// Synchronized read I/O operations. rsync, /// Write according to synchronized I/O file integrity completion. In /// addition to synchronizing the data stored in the file, the /// implementation may also synchronously update the file's metadata. sync, } ``` ## `descriptor-stat` ```wit /// File attributes. /// /// Note: This was called `filestat` in earlier versions of WASI. record descriptor-stat { /// Device ID of device containing the file. dev: device, /// File serial number. ino: inode, /// File type. %type: descriptor-type, /// Number of hard links to the file. nlink: linkcount, /// For regular files, the file size in bytes. For symbolic links, the length /// in bytes of the pathname contained in the symbolic link. size: filesize, /// Last data access timestamp. atim: datetime, /// Last data modification timestamp. mtim: datetime, /// Last file status change timestamp. ctim: datetime, } ``` ## `at-flags` ```wit /// Flags determining the method of how paths are resolved. flags at-flags { /// As long as the resolved path corresponds to a symbolic link, it is expanded. symlink-follow, } ``` ## `o-flags` ```wit /// Open flags used by `open-at`. flags o-flags { /// Create file if it does not exist. create, /// Fail if not a directory. directory, /// Fail if file already exists. excl, /// Truncate file to size 0. trunc, } ``` ## `mode` ```wit /// Permissions mode used by `open-at`, `change-file-permissions-at`, and /// similar. flags mode { /// True if the resource is considered readable by the containing /// filesystem. readable, /// True if the resource is considered writeable by the containing /// filesystem. writeable, /// True if the resource is considered executable by the containing /// filesystem. This does not apply to directories. executable, } ``` ## `linkcount` ```wit /// Number of hard links to an inode. type linkcount = u64 ``` ## `device` ```wit /// Identifier for a device containing a file system. Can be used in combination /// with `inode` to uniquely identify a file or directory in the filesystem. type device = u64 ``` ## `inode` ```wit /// Filesystem object serial number that is unique within its file system. type inode = u64 ``` ## `new-timestamp` ```wit /// When setting a timestamp, this gives the value to set it to. variant new-timestamp { /// Leave the timestamp set to its previous value. no-change, /// Set the timestamp to the current time of the system clock associated /// with the filesystem. now, /// Set the timestamp to the given value. timestamp(datetime), } ``` ## `dir-entry` ```wit /// A directory entry. record dir-entry { /// The serial number of the object referred to by this directory entry. /// May be none if the inode value is not known. /// /// When this is none, libc implementations might do an extra `stat-at` /// call to retrieve the inode number to fill their `d_ino` fields, so /// implementations which can set this to a non-none value should do so. ino: option<inode>, /// The type of the file referred to by this directory entry. %type: descriptor-type, /// The name of the object. name: string, } ``` ## `errno` ```wit /// Error codes returned by functions. /// Not all of these error codes are returned by the functions provided by this /// API; some are used in higher-level library layers, and others are provided /// merely for alignment with POSIX. enum errno { /// Permission denied. access, /// Resource unavailable, or operation would block. again, /// Connection already in progress. already, /// Bad descriptor. badf, /// Device or resource busy. busy, /// No child processes. child, /// Resource deadlock would occur. deadlk, /// Storage quota exceeded. dquot, /// File exists. exist, /// File too large. fbig, /// Illegal byte sequence. ilseq, /// Operation in progress. inprogress, /// Interrupted function. intr, /// Invalid argument. inval, /// I/O error. io, /// Is a directory. isdir, /// Too many levels of symbolic links. loop, /// Too many links. mlink, /// Message too large. msgsize, /// Filename too long. nametoolong, /// No such device. nodev, /// No such file or directory. noent, /// No locks available. nolck, /// Not enough space. nomem, /// No space left on device. nospc, /// Function not supported. nosys, /// Not a directory or a symbolic link to a directory. notdir, /// Directory not empty. notempty, /// State not recoverable. notrecoverable, /// Not supported, or operation not supported on socket. notsup, /// Inappropriate I/O control operation. notty, /// No such device or address. nxio, /// Value too large to be stored in data type. overflow, /// Operation not permitted. perm, /// Broken pipe. pipe, /// Read-only file system. rofs, /// Invalid seek. spipe, /// Text file busy. txtbsy, /// Cross-device link. xdev, } ``` ## `advice` ```wit /// File or memory access pattern advisory information. enum advice { /// The application has no advice to give on its behavior with respect to the specified data. normal, /// The application expects to access the specified data sequentially from lower offsets to higher offsets. sequential, /// The application expects to access the specified data in a random order. random, /// The application expects to access the specified data in the near future. will-need, /// The application expects that it will not access the specified data in the near future. dont-need, /// The application expects to access the specified data once and then not reuse it thereafter. no-reuse, } ``` ## `seek-from` ```wit /// The position relative to which to set the offset of the descriptor. variant seek-from { /// Seek relative to start-of-file. set(filesize), /// Seek relative to current position. cur(filedelta), /// Seek relative to end-of-file. end(filesize), } ``` ## `descriptor` ```wit /// A descriptor is a reference to a filesystem object, which may be a file, /// directory, named pipe, special file, or other object on which filesystem /// calls may be made. resource descriptor { ``` ## `fadvise` ```wit /// Provide file advisory information on a descriptor. /// /// This is similar to `posix_fadvise` in POSIX. fadvise: func( /// The offset within the file to which the advisory applies. offset: filesize, /// The length of the region to which the advisory applies. len: size, /// The advice. advice: advice ) -> result<_, errno> ``` ## `datasync` ```wit /// Synchronize the data of a file to disk. /// /// Note: This is similar to `fdatasync` in POSIX. datasync: func() -> result<_, errno> ``` ## `flags` ```wit /// Get flags associated with a descriptor. /// /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. %flags: func() -> result<descriptor-flags, errno> ``` ## `type` ```wit /// Get the dynamic type of a descriptor. /// /// Note: This returns the same value as the `type` field of the `fd-stat` /// returned by `stat`, `stat-at` and similar. /// /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided /// by `fstat` in POSIX. /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. %type: func() -> result<descriptor-type, errno> ``` ## `set-flags` ```wit /// Set flags associated with a descriptor. /// /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. /// /// Note: This was called `fd_fdstat_set_flags` in earlier versions of WASI. set-flags: func(%flags: descriptor-flags) -> result<_, errno> ``` ## `set-size` ```wit /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. set-size: func(size: filesize) -> result<_, errno> ``` ## `set-times` ```wit /// Adjust the timestamps of an open file or directory. /// /// Note: This is similar to `futimens` in POSIX. /// /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. set-times: func( /// The desired values of the data access timestamp. atim: new-timestamp, /// The desired values of the data modification timestamp. mtim: new-timestamp, ) -> result<_, errno> ``` ## `pread` ```wit /// Read from a descriptor, without using and updating the descriptor's offset. /// /// Note: This is similar to `pread` in POSIX. pread: func( /// The maximim number of bytes to read. len: size, /// The offset within the file at which to read. offset: filesize, ) -> stream<u8, errno> ``` ## `pwrite` ```wit /// Write to a descriptor, without using and updating the descriptor's offset. /// /// It is valid to write past the end of a file; the file is extended to the /// extent of the write, with bytes between the previous end and the start of /// the write set to zero. /// /// Note: This is similar to `pwrite` in POSIX. pwrite: func( /// Data to write buf: stream<u8>, /// The offset within the file at which to write. offset: filesize, ) -> future<result<size, errno>> ``` ## `readdir` ```wit /// Read directory entries from a directory. /// /// This always returns a new stream which starts at the beginning of the /// directory. readdir: func() -> stream<dir-entry, errno> ``` ## `seek` ```wit /// Move the offset of a file descriptor. /// /// If the descriptor refers to a directory, this function fails with /// `errno::spipe`. /// /// Returns new offset of the descriptor, relative to the start of the file. /// /// It is valid to seek past the end of a file. The file size is not modified /// until a write is performed, at which time the file is extended to the /// extent of the write, with bytes between the previous end and the start of /// the write set to zero. /// /// Note: This is similar to `lseek` in POSIX. seek: func( /// The method to compute the new offset. %from: seek-from, ) -> result<filesize, errno> ``` ## `sync` ```wit /// Synchronize the data and metadata of a file to disk. /// /// Note: This is similar to `fsync` in POSIX. sync: func() -> result<_, errno> ``` ## `tell` ```wit /// Return the current offset of a descriptor. /// /// If the descriptor refers to a directory, this function fails with /// `errno::spipe`. /// /// Returns the current offset of the descriptor, relative to the start of the file. /// /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. tell: func() -> result<filesize, errno> ``` ## `create-directory-at` ```wit /// Create a directory. /// /// Note: This is similar to `mkdirat` in POSIX. create-directory-at: func( /// The relative path at which to create the directory. path: string, ) -> result<_, errno> ``` ## `stat` ```wit /// Return the attributes of an open file or directory. /// /// Note: This is similar to `fstat` in POSIX. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. stat: func() -> result<descriptor-stat, errno> ``` ## `stat-at` ```wit /// Return the attributes of a file or directory. /// /// Note: This is similar to `fstatat` in POSIX. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. stat-at: func( /// Flags determining the method of how the path is resolved. at-flags: at-flags, /// The relative path of the file or directory to inspect. path: string, ) -> result<descriptor-stat, errno> ``` ## `set-times-at` ```wit /// Adjust the timestamps of a file or directory. /// /// Note: This is similar to `utimensat` in POSIX. /// /// Note: This was called `path_filestat_set_times` in earlier versions of WASI. set-times-at: func( /// Flags determining the method of how the path is resolved. at-flags: at-flags, /// The relative path of the file or directory to operate on. path: string, /// The desired values of the data access timestamp. atim: new-timestamp, /// The desired values of the data modification timestamp. mtim: new-timestamp, ) -> result<_, errno> ``` ## `link-at` ```wit /// Create a hard link. /// /// Note: This is similar to `linkat` in POSIX. link-at: func( /// Flags determining the method of how the path is resolved. old-at-flags: at-flags, /// The relative source path from which to link. old-path: string, /// The base directory for `new-path`. new-descriptor: handle descriptor, /// The relative destination path at which to create the hard link. new-path: string, ) -> result<_, errno> ``` ## `open-at` ```wit /// Open a file or directory. /// /// The returned descriptor is not guaranteed to be the lowest-numbered /// descriptor not currently open/ it is randomized to prevent applications /// from depending on making assumptions about indexes, since this is /// error-prone in multi-threaded contexts. The returned descriptor is /// guaranteed to be less than 2**31. /// /// Note: This is similar to `openat` in POSIX. open-at: func( /// Flags determining the method of how the path is resolved. at-flags: at-flags, /// The relative path of the object to open. path: string, /// The method by which to open the file. o-flags: o-flags, /// Flags to use for the resulting descriptor. %flags: descriptor-flags, /// Permissions to use when creating a new file. mode: mode ) -> result<descriptor, errno> ``` ## `readlink-at` ```wit /// Read the contents of a symbolic link. /// /// Note: This is similar to `readlinkat` in POSIX. readlink-at: func( /// The relative path of the symbolic link from which to read. path: string, ) -> result<string, errno> ``` ## `remove-directory-at` ```wit /// Remove a directory. /// /// Return `errno::notempty` if the directory is not empty. /// /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. remove-directory-at: func( /// The relative path to a directory to remove. path: string, ) -> result<_, errno> ``` ## `rename-at` ```wit /// Rename a filesystem object. /// /// Note: This is similar to `renameat` in POSIX. rename-at: func( /// The relative source path of the file or directory to rename. old-path: string, /// The base directory for `new-path`. new-descriptor: handle descriptor, /// The relative destination path to which to rename the file or directory. new-path: string, ) -> result<_, errno> ``` ## `symlink-at` ```wit /// Create a symbolic link. /// /// Note: This is similar to `symlinkat` in POSIX. symlink-at: func( /// The contents of the symbolic link. old-path: string, /// The relative destination path at which to create the symbolic link. new-path: string, ) -> result<_, errno> ``` ## `unlink-file-at` ```wit /// Unlink a filesystem object that is not a directory. /// /// Return `errno::isdir` if the path refers to a directory. /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. unlink-file-at: func( /// The relative path to a file to unlink. path: string, ) -> result<_, errno> ``` ## `change-file-permissions-at` ```wit /// Change the permissions of a filesystem object that is not a directory. /// /// Note that the ultimate meanings of these permissions is /// filesystem-specific. /// /// Note: This is similar to `fchmodat` in POSIX. change-file-permissions-at: func( /// Flags determining the method of how the path is resolved. at-flags: at-flags, /// The relative path to operate on. path: string, /// The new permissions for the filesystem object. mode: mode, ) -> result<_, errno> ``` ## `change-dir-permissions-at` ```wit /// Change the permissions of a directory. /// /// Note that the ultimate meanings of these permissions is /// filesystem-specific. /// /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" /// flag. `read` on a directory implies readability and searchability, and /// `execute` is not valid for directories. /// /// Note: This is similar to `fchmodat` in POSIX. change-directory-permissions-at: func( /// Flags determining the method of how the path is resolved. at-flags: at-flags, /// The relative path to operate on. path: string, /// The new permissions for the directory. mode: mode, ) -> result<_, errno> ``` ## `lock-shared` ```wit /// Request a shared advisory lock for an open file. /// /// This requests a *shared* lock; more than one shared lock can be held for /// a file at the same time. /// /// If the open file has an exclusive lock, this function downgrades the lock /// to a shared lock. If it has a shared lock, this function has no effect. /// /// This requests an *advisory* lock, meaning that the file could be accessed /// by other programs that don't hold the lock. /// /// It is unspecified how shared locks interact with locks acquired by /// non-WASI programs. /// /// This function blocks until the lock can be acquired. /// /// Not all filesystems support locking; on filesystems which don't support /// locking, this function returns `errno::notsup`. /// /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. lock-shared: func() -> result<_, errno> ``` ## `lock-exclusive` ```wit /// Request an exclusive advisory lock for an open file. /// /// This requests an *exclusive* lock; no other locks may be held for the /// file while an exclusive lock is held. /// /// If the open file has a shared lock and there are no exclusive locks held /// for the fhile, this function upgrades the lock to an exclusive lock. If the /// open file already has an exclusive lock, this function has no effect. /// /// This requests an *advisory* lock, meaning that the file could be accessed /// by other programs that don't hold the lock. /// /// It is unspecified whether this function succeeds if the file descriptor /// is not opened for writing. It is unspecified how exclusive locks interact /// with locks acquired by non-WASI programs. /// /// This function blocks until the lock can be acquired. /// /// Not all filesystems support locking; on filesystems which don't support /// locking, this function returns `errno::notsup`. /// /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. lock-exclusive: func() -> result<_, errno> ``` ## `try-lock-shared` ```wit /// Request a shared advisory lock for an open file. /// /// This requests a *shared* lock; more than one shared lock can be held for /// a file at the same time. /// /// If the open file has an exclusive lock, this function downgrades the lock /// to a shared lock. If it has a shared lock, this function has no effect. /// /// This requests an *advisory* lock, meaning that the file could be accessed /// by other programs that don't hold the lock. /// /// It is unspecified how shared locks interact with locks acquired by /// non-WASI programs. /// /// This function returns `errno::wouldblock` if the lock cannot be acquired. /// /// Not all filesystems support locking; on filesystems which don't support /// locking, this function returns `errno::notsup`. /// /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. try-lock-shared: func() -> result<_, errno> ``` ## `try-lock-exclusive` ```wit /// Request an exclusive advisory lock for an open file. /// /// This requests an *exclusive* lock; no other locks may be held for the /// file while an exclusive lock is held. /// /// If the open file has a shared lock and there are no exclusive locks held /// for the fhile, this function upgrades the lock to an exclusive lock. If the /// open file already has an exclusive lock, this function has no effect. /// /// This requests an *advisory* lock, meaning that the file could be accessed /// by other programs that don't hold the lock. /// /// It is unspecified whether this function succeeds if the file descriptor /// is not opened for writing. It is unspecified how exclusive locks interact /// with locks acquired by non-WASI programs. /// /// This function returns `errno::wouldblock` if the lock cannot be acquired. /// /// Not all filesystems support locking; on filesystems which don't support /// locking, this function returns `errno::notsup`. /// /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. try-lock-exclusive: func() -> result<_, errno> ``` ## `unlock` ```wit /// Release a shared or exclusive lock on an open file. /// /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. unlock: func() -> result<_, errno> ``` ```wit } ```