Skip to content
Select theme

How to read file metadata

Short answer: std::fs::metadata("path") returns a Metadata value: .len() for the size in bytes, .modified() for the timestamp, .permissions() for the mode. One trap: the Permissions you get back are a copy. Calling set_mode on them changes your copy and nothing else — getting them onto the file takes fs::set_permissions.

From a fresh project — cargo new meta-demo, replace the contents of src/main.rs with the block below, and cargo run. The program writes the file it measures, so the size is exact; the age depends on when you ran it:

14 bytes
written: 364.929µs

len() is the size in bytes, already a u64. modified() returns a Result<SystemTime> because the std docs say the field “might not be available on all platforms” — that’s why the code has to expect it rather than use it directly. elapsed() is the distance from that timestamp to now; the duration it prints will differ from this one every time the program runs.

Two things worth knowing while you’re here. A path that doesn’t exist doesn’t give you a zero-size Metadata — fs::metadata returns an Err with kind NotFound, same Result treatment as any other file call. And fs::metadata follows symlinks: point it at a link to file.txt and you get file.txt’s metadata. fs::symlink_metadata stops at the link itself, which is the distinction the directory-walking code in How to walk a directory tree leans on.

mode now: 644
mode still on disk: 644
mode after set_permissions: 600

The first two lines are the trap. meta.permissions() hands you a fresh Permissions value, and set_mode mutates that copy — the file on disk never hears about it. fs::metadata("notes.txt")?.permissions().set_mode(0o600); compiles cleanly and does nothing. The call that actually writes is fs::set_permissions, and it takes the Permissions value you were already holding, which is why the code keeps perms alive until then.

Two details hide in that block. The remove_file line isn’t ceremony: fs::write creates a missing file with your umask’s default mode but leaves the mode of an existing file alone, so without it a rerun would print the 600 you set last time as the starting mode. And the & 0o777 mask is there because PermissionsExt::mode() returns the full st_mode from stat, whose high bits encode the file type — on this machine (macOS, Rust 1.99.0) a regular file comes back as 100644 and a directory as 40755. Mask it before printing or comparing. The starting 644 itself is this machine’s umask (022) at work; a different umask prints something else.

PermissionsExt is a Unix extension trait (std::os::unix::fs), so this block is Unix-only. Windows’ Permissions exposes readonly() and set_readonly() and nothing mode-shaped — the Unix rwx bits don’t map onto NTFS permissions. The size and timestamp code above runs unchanged on both.