Module fs/metadata

fs/metadata
Stability: unstable — the timestamp surface is one accessor short and the missing one changes this module's shape. There is no creation time (`btime`): `Statx` requests `STATX_BASIC_STATS`, which excludes `STATX_BTIME`, and `std/sys/statx.yo` declares no `btime_sec()` even though the Linux, macOS and Windows runtimes already implement `__yo_statx_btime_sec` (wasm does not). Adding it is additive, but it also settles what `status_changed()` should mean on Windows, where there is no POSIX ctime and the value is currently the creation time with the write time's nanoseconds (`issues/stddoc-io-windows-status-changed-mixes-two-timestamps.md`). Freezing follows a real `created()` and a decided Windows `ctime`, not a release count. — stable modules only change additively; this one may still change.

File metadata via statx with ergonomic accessors. Uses the Exception effect for error handling.

Example

{ metadata } :: import "std/fs/metadata";

main :: (fn(io : Io, exn : Exception) -> unit)({
  m := io.await(metadata(Path.new(`/tmp/test.txt`), io), { io, exn });
  printf("size: %lld\n", m.size());
  printf("is file: %d\n", i32(m.is_file()));
});

Platform coverage

The backend is statx on Linux, stat/fstat on macOS and wasm, and _wstat64 plus GetFileAttributesExW/GetFileInformationByHandle on Windows (src/codegen/async/runtime_io_*.yo). Everything a POSIX stat carries is available everywhere; the accessors that have no Windows equivalent say so at their own definition (uid, gid, ino, status_changed*).

Stability

unstable — the timestamp surface is one accessor short and the missing one changes this module's shape. There is no creation time (btime): Statx requests STATX_BASIC_STATS, which excludes STATX_BTIME, and std/sys/statx.yo declares no btime_sec() even though the Linux, macOS and Windows runtimes already implement __yo_statx_btime_sec (wasm does not). Adding it is additive, but it also settles what status_changed() should mean on Windows, where there is no POSIX ctime and the value is currently the creation time with the write time's nanoseconds (issues/stddoc-io-windows-status-changed-mixes-two-timestamps.md). Freezing follows a real created() and a decided Windows ctime, not a release count.

Types

Metadata object
Metadata

File metadata wrapper around Statx.

Fields

NameTypeDescription
_statxStatx
_buf_ptr*(u8)

Trait Implementations

impl(Metadata, ...)
size : (Metadata) fn(self : Metadata) -> i64

Size in bytes — Rust's Metadata::len. For a directory this is the filesystem's own bookkeeping size, not the number of entries; for a symlink read through symlink_metadata it is the length of the target string.

Parameters

NameTypeNotes
selfMetadata

Returns: i64

mode : (Metadata) fn(self : Metadata) -> u32

The raw st_mode word — permission bits AND the file-type bits (S_IFMT) in one integer, so mask before comparing. Prefer is_file/is_dir/is_symlink for the type and is_readonly for writability. On Windows the CRT synthesises it from the file attributes and the filename extension, so a real POSIX mode is only available on Linux, macOS and wasm.

Parameters

NameTypeNotes
selfMetadata

Returns: u32

is_file : (Metadata) fn(self : Metadata) -> bool

True for a regular file — Rust's Metadata::is_file. Through metadata (which follows symlinks) a link to a file answers true; through symlink_metadata it does not.

Parameters

NameTypeNotes
selfMetadata

Returns: bool

is_dir : (Metadata) fn(self : Metadata) -> bool

True for a directory — Rust's Metadata::is_dir.

Parameters

NameTypeNotes
selfMetadata

Returns: bool

modified : (Metadata) fn(self : Metadata) -> SystemTime

Last content-modification time as a wall-clock SystemTime — Rust's Metadata::modified. Full nanosecond resolution: statx reports mtime_nsec and it was being discarded by the seconds-only accessor below, which is why this is the one to reach for.

A filesystem timestamp and SystemTime.now()'s CLOCK_REALTIME are DIFFERENT CLOCK DOMAINS, so an mtime may sit a hair ahead of a now() read afterwards (Emscripten's MEMFS measured 64 ns ahead). Nothing promises the ordering — only that the two readings are close — which is why SystemTime.duration_since hands back a Result rather than clamping to zero.

Parameters

NameTypeNotes
selfMetadata

Returns: SystemTime

accessed : (Metadata) fn(self : Metadata) -> SystemTime

Last access time as a SystemTime — Rust's Metadata::accessed. Read this expecting it to be stale: relatime/noatime mounts are the default on Linux, so atime often only moves when the file is also written.

Parameters

NameTypeNotes
selfMetadata

Returns: SystemTime

status_changed : (Metadata) fn(self : Metadata) -> SystemTime

POSIX ctime — the last INODE change — as a SystemTime. NOT a creation time; see status_changed_time below for why, and for what Windows reports here instead.

Parameters

NameTypeNotes
selfMetadata

Returns: SystemTime

modified_time : (Metadata) fn(self : Metadata) -> i64

Last modification time in WHOLE SECONDS since the Unix epoch.

Kept for callers that want the raw field, but modified() above is the one to use: it carries the nanoseconds this drops, and a SystemTime can be compared and subtracted without every call site re-deriving what the integer means. The two agree by construction — modified_time() == modified().as_unix_secs() is asserted by tests/fs/metadata.test.yo.

Parameters

NameTypeNotes
selfMetadata

Returns: i64

accessed_time : (Metadata) fn(self : Metadata) -> i64

Last access time in whole seconds since the Unix epoch. See accessed() for the nanosecond-carrying form and the staleness caveat.

Parameters

NameTypeNotes
selfMetadata

Returns: i64

status_changed_time : (Metadata) fn(self : Metadata) -> i64

Status change time in whole seconds since the Unix epoch — POSIX ctime.

This is NOT the file's creation time: ctime moves whenever the inode's metadata changes (chmod, rename, link count), so it can be LATER than the modification time. A real creation time (statx btime / st_birthtime / Windows CreationTime) is not exposed yet — Statx requests STATX_BASIC_STATS (0x7ff), which does not include STATX_BTIME (0x800), and there is no btime_sec() accessor. See plans/archive/STD_API_AUDIT.md §5.

Windows has no ctime at all: the CRT's st_ctime is the file's CREATION time (and only on NTFS), so on that platform this answers a different question from the POSIX one — treat it as "some timestamp about the file" rather than as a change marker.

Parameters

NameTypeNotes
selfMetadata

Returns: i64

is_readonly : (Metadata) fn(self : Metadata) -> bool

True when NO write bit is set for owner, group or other — Rust's Permissions::readonly. This is a question about the mode bits, not about whether THIS process can write: root ignores them, and an ACL, a read-only mount or an immutable flag can deny a write that the bits permit. Attempt the write if the answer has to be authoritative.

Parameters

NameTypeNotes
selfMetadata

Returns: bool

uid : (Metadata) fn(self : Metadata) -> u32

Owning user id. POSIX only — on Windows the CRT's _wstat64 leaves st_uid at 0, so this reports 0 for every file there rather than mapping the security descriptor's owner SID.

Parameters

NameTypeNotes
selfMetadata

Returns: u32

gid : (Metadata) fn(self : Metadata) -> u32

Owning group id. POSIX only — 0 on Windows, for the same reason as uid.

Parameters

NameTypeNotes
selfMetadata

Returns: u32

ino : (Metadata) fn(self : Metadata) -> u64

Inode number — with the device id, the filesystem's identity for this file, which is how two paths are recognised as the same file. On Windows this is the NTFS file index from GetFileInformationByHandle, which serves the same purpose but is 0 when that call fails (no open permission, or a filesystem that has no index). The device id itself is not exposed yet, so an identity comparison is only sound within one filesystem.

Parameters

NameTypeNotes
selfMetadata

Returns: u64

impl(Metadata, Dispose(...))
dispose : (Metadata) fn(self : Metadata) -> unit

Release the resources self owns — a file descriptor, a socket, a lock, a buffer the allocator handed out. Called automatically when the last reference to the value goes away, so an implementor never calls it directly and must tolerate being the only one who ever does.

It must be safe to run exactly once: the runtime calls it at refcount zero, and a type that also exposes an explicit close/release is responsible for making the second call a no-op.

Parameters

NameTypeNotes
selfMetadata

Returns: unit

Functions

metadata function
fn(path : Path, io : Io) -> Impl(Future(Metadata, IoExn))

Get metadata for a path (follows symlinks).

Parameters

NameTypeNotes
pathPath
ioIo

Returns: Impl(Future(Metadata, IoExn))

metadata_str function
fn(path : str, io : Io) -> Impl(Future(Metadata, IoExn))

Get metadata for a path given as a raw string (follows symlinks).

Parameters

NameTypeNotes
pathstr
ioIo

Returns: Impl(Future(Metadata, IoExn))

metadata_fd function
fn(fd : i32, io : Io) -> Impl(Future(Metadata, IoExn))

Get metadata for an ALREADY-OPEN descriptor (fstat) — Rust's File::metadata. This is the only correct answer for a handle whose path is unknown (File.from_fd) or has been renamed, replaced or unlinked since the open: the DESCRIPTOR names the inode, a path does not. It also costs no path resolution.

There is no follow/nofollow variant: a descriptor is already an open inode, so there is no final symlink left to follow.

Parameters

NameTypeNotes
fdi32
ioIo

Returns: Impl(Future(Metadata, IoExn))