Module std::fs
Files as an effect: code reaches the file system only through an Fs capability in its row,
and only beneath the directory descriptors it is handed: every path is resolved beneath a
dir: &Fd, and an absolute one or a .. that would leave it fails EXDEV (18). The root
hands a program its first directory, cwd, and the program hands it on. Paths are bytes
(&[u8]). Errors are the kernel's errno values (Err(Int)).
A descriptor is a bare linear Fd: Fs::close consumes it and answers the error closing
reports (a write the disk refused); there is no File whose drop would have to lose it.
Install Blocking (one system call per op) or Evented (ops on the root's reactor);
std::net::Net's executors serve Fs too, and a test uses std::memfs::MemFs. A bare
with Fs is Fs portable migrates cross, the bound under which a function runs under each.
Source: std/fs.weir; committed interface: std/fs.iface. Index
- const
READ:Fs::openflag: open for reading. - const
WRITE:Fs::openflag: open for writing. - const
CREATE:Fs::openflag: create the file if missing (needsWRITE). - const
TRUNC:Fs::openflag: truncate the file to empty (needsWRITE). - const
EXCL:Fs::openflag: withCREATE, the file must not exist yet. - const
DIR:Fs::openflag: open a directory (with nothing else butREAD); flags not combined as these docs say areEINVAL(22). - effect
Fs: The file operations, each aonceop answeringErr(errno)on failure; an executor may park the task while the op runs. Every path is resolved beneath itsdir. - fn
cwd: The process's working directory, opened as a directory: the root's first directory, from which a program reaches every file it may. Needs the root'sSys, somaincalls it. - fn
preopen: The directory the root was handed undername(weir run --dir <path>:<name>, a native binary's--dir), opened anew. A name it was not handed isENOENT(2), as is every name in the simulated world. - handler
Blocking: The blocking executor: each op is one system call on the calling thread. Install it aswith Blocking { … }where the root'sSysis in scope (main). - handler
Evented: The completion executor: each op is submitted to the root's reactor and the body parks until it completes, so a file op never blocks a worker's loop. Install it aswith Evented(slots()) { … }(needsReactor);slots()is its table. Thewithbody is its one strand: there are no tasks, so at most one op is in flight. - fn
read_all: The whole of the filepathbeneathdir(with Allocfor the buffer). The read's error comes first, then the close's. - fn
write_all: Makespathbeneathdirhold exactlydata, creating it if missing. The write's error comes first, then the close's, which reports what the device refused after accepting the write; a write that makes no progress isEIO(5). - fn
names: The names in the directorydirbut.and.., sorted bytewise, each its ownBuf.
const READ
pub const READ: Int = 1;
Fs::open flag: open for reading.
const WRITE
pub const WRITE: Int = 2;
Fs::open flag: open for writing.
const CREATE
pub const CREATE: Int = 4;
Fs::open flag: create the file if missing (needs WRITE).
const TRUNC
pub const TRUNC: Int = 8;
Fs::open flag: truncate the file to empty (needs WRITE).
const EXCL
pub const EXCL: Int = 16;
Fs::open flag: with CREATE, the file must not exist yet.
const DIR
pub const DIR: Int = 32;
Fs::open flag: open a directory (with nothing else but READ); flags not combined as
these docs say are EINVAL (22).
effect Fs
pub effect Fs default portable migrates cross {
once fn open(dir: &Fd, path: &[u8], flags: Int) -> Result<Fd, Int>;
once fn read_at(f: &Fd, b: &mut [u8], off: usize) -> Result<usize, Int>;
once fn write_at(f: &Fd, b: &[u8], off: usize) -> Result<usize, Int>;
once fn sync(f: &Fd) -> Result<Unit, Int>;
once fn size(f: &Fd) -> Result<usize, Int>;
once fn mkdir(dir: &Fd, path: &[u8]) -> Result<Unit, Int>;
once fn remove(dir: &Fd, path: &[u8]) -> Result<Unit, Int>;
once fn rename(dir: &Fd, from: &[u8], to: &[u8]) -> Result<Unit, Int>;
once fn list(dir: &Fd, b: &mut [u8], at: usize) -> Result<usize, Int>;
linear fn close(fd: Fd) -> Result<Unit, Int>;
}
The file operations, each a once op answering Err(errno) on failure; an executor may park
the task while the op runs. Every path is resolved beneath its dir.
op open
once fn open(dir: &Fd, path: &[u8], flags: Int) -> Result<Fd, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
Opens or creates path beneath dir as flags say: a file, or a directory with DIR.
op read_at
once fn read_at(f: &Fd, b: &mut [u8], off: usize) -> Result<usize, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
Reads into b from offset off: the count read, 0 at and past the end.
op write_at
once fn write_at(f: &Fd, b: &[u8], off: usize) -> Result<usize, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
Writes b at offset off: the count written, which may be short.
op sync
once fn sync(f: &Fd) -> Result<Unit, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
Brings what was written to f to the device (fdatasync): once it answers Ok, a crash
keeps every write before it. Of a directory, the names made, removed or renamed in it,
which a crash may otherwise take back. After a failed sync, what it was to make durable
may still be read but is not on the disk, and a later sync that answers Ok does not put
it there (Linux since 4.13): treat the failure as fatal, not as a call to retry.
op size
once fn size(f: &Fd) -> Result<usize, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
A file's length in bytes.
op mkdir
once fn mkdir(dir: &Fd, path: &[u8]) -> Result<Unit, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
Makes the directory path beneath dir.
op remove
once fn remove(dir: &Fd, path: &[u8]) -> Result<Unit, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
Removes the file or empty directory path beneath dir.
op rename
once fn rename(dir: &Fd, from: &[u8], to: &[u8]) -> Result<Unit, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
Renames from to to, both beneath dir, replacing a file at to.
op list
once fn list(dir: &Fd, b: &mut [u8], at: usize) -> Result<usize, Int>;
Control kind once: the clause takes the continuation (cont k) and resumes or cancels it, so the perform is a suspension and an abort edge.
The names in dir but . and .., sorted bytewise, from the atth, into b: as many
whole names as fit, each followed by a NUL byte; the count, 0 past the last.
op close
linear fn close(fd: Fd) -> Result<Unit, Int>;
Control kind linear: the clause takes the continuation (cont k) and resumes it exactly once, so the perform is a suspension.
Closes fd, first bringing what was written to the device, and answers the first error.
A linear op, never cancelled, so a descriptor is not leaked by a cancel.
fn cwd
pub fn cwd() -> Result<Fd, Int> with Sys
The process's working directory, opened as a directory: the root's first directory, from
which a program reaches every file it may. Needs the root's Sys, so main calls it.
Committed interface (std/fs.iface):
bound Sys: Sys any
scheduling point: Sys not quiet
fn preopen
pub fn preopen(name: &[u8]) -> Result<Fd, Int> with Sys
The directory the root was handed under name (weir run --dir <path>:<name>, a native
binary's --dir), opened anew. A name it was not handed is ENOENT (2), as is every name in
the simulated world.
Committed interface (std/fs.iface):
bound Sys: Sys any
scheduling point: Sys not quiet
handler Blocking
pub handler Blocking: shared strict cross for Fs with Sys
The blocking executor: each op is one system call on the calling thread. Install it as
with Blocking { … } where the root's Sys is in scope (main).
Handles Fs, with clauses:
tail fn open(dir: &Fd, path: &[u8], flags: Int)tail fn read_at(f: &Fd, b: &mut [u8], off: usize)tail fn write_at(f: &Fd, b: &[u8], off: usize)tail fn sync(f: &Fd)tail fn size(f: &Fd)tail fn mkdir(dir: &Fd, path: &[u8])tail fn remove(dir: &Fd, path: &[u8])tail fn rename(dir: &Fd, from: &[u8], to: &[u8])tail fn list(dir: &Fd, b: &mut [u8], at: usize)tail fn close(fd: Fd)
handler Evented
pub handler Evented(s: Slots<Unit, once>): shared strict cross for Fs with Reactor
The completion executor: each op is submitted to the root's reactor and the body parks until
it completes, so a file op never blocks a worker's loop. Install it as
with Evented(slots()) { … } (needs Reactor); slots() is its table. The with body is
its one strand: there are no tasks, so at most one op is in flight.
Handles Fs, with clauses:
once fn open(dir: &Fd, path: &[u8], flags: Int) cont konce fn read_at(f: &Fd, b: &mut [u8], off: usize) cont konce fn write_at(f: &Fd, b: &[u8], off: usize) cont konce fn sync(f: &Fd) cont konce fn size(f: &Fd) cont konce fn mkdir(dir: &Fd, path: &[u8]) cont konce fn remove(dir: &Fd, path: &[u8]) cont konce fn rename(dir: &Fd, from: &[u8], to: &[u8]) cont konce fn list(dir: &Fd, b: &mut [u8], at: usize) cont klinear fn close(fd: Fd) cont k
fn read_all
pub fn read_all(dir: &Fd, path: &[u8]) -> Result<Buf<u8>, Int> with Fs, Alloc portable
The whole of the file path beneath dir (with Alloc for the buffer). The read's error
comes first, then the close's.
Committed interface (std/fs.iface):
bound Fs: std::fs::Fs portable cross
bound Alloc: Alloc any portable
may diverge
aborts: Fs.open, Fs.read_at, Fs.size, Alloc.alloc
scheduling point: always
holds dir across a migration: Fs.open holds its arguments across a migration
holds path across a migration: Fs.open holds its arguments across a migration
cleanup on an abort suspends on Fs
fn write_all
pub fn write_all(dir: &Fd, path: &[u8], data: &[u8]) -> Result<Unit, Int> with Fs
Makes path beneath dir hold exactly data, creating it if missing. The write's error comes
first, then the close's, which reports what the device refused after accepting the write; a
write that makes no progress is EIO (5).
Committed interface (std/fs.iface):
bound Fs: std::fs::Fs portable cross
may diverge
aborts: Fs.open, Fs.write_at
scheduling point: always
holds dir across a migration: Fs.open holds its arguments across a migration
holds path across a migration: Fs.open holds its arguments across a migration
holds data across a migration: Fs resumes elsewhere | Fs.write_at holds its arguments across a migration
cleanup on an abort suspends on Fs
fn names
pub fn names(dir: &Fd) -> Result<Buf<Buf<u8>>, Int> with Fs, Alloc portable
The names in the directory dir but . and .., sorted bytewise, each its own Buf.
Committed interface (std/fs.iface):
bound Fs: std::fs::Fs portable cross
bound Alloc: Alloc any portable
may diverge
aborts: Fs.list, Alloc.alloc
scheduling point: always
holds dir across a migration: Fs resumes elsewhere | Fs.list holds its arguments across a migration