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.
Size and modified time
Section titled “Size and modified time”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:
use std::fs;
fn main() { fs::write("notes.txt", b"hello metadata").expect("write failed");
let meta = fs::metadata("notes.txt").expect("notes.txt missing"); println!("{} bytes", meta.len());
let modified = meta.modified().expect("no modified time on this platform"); println!("written: {:?}", modified.elapsed().unwrap());}14 byteswritten: 364.929µslen() 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.
The permissions you cannot set
Section titled “The permissions you cannot set”use std::fs;use std::os::unix::fs::PermissionsExt;
fn main() { let _ = fs::remove_file("notes.txt"); // fs::write won't reset the mode of an existing file fs::write("notes.txt", b"hello metadata").expect("write failed");
let meta = fs::metadata("notes.txt").expect("notes.txt missing"); let mut perms = meta.permissions(); println!("mode now: {:o}", perms.mode() & 0o777);
perms.set_mode(0o600); println!("mode still on disk: {:o}", fs::metadata("notes.txt").unwrap().permissions().mode() & 0o777);
fs::set_permissions("notes.txt", perms).expect("set_permissions failed"); println!("mode after set_permissions: {:o}", fs::metadata("notes.txt").unwrap().permissions().mode() & 0o777);}mode now: 644mode still on disk: 644mode after set_permissions: 600The 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.
Related
Section titled “Related”- How to walk a directory tree — where
metadataversussymlink_metadatadecides whether a link gets followed - How to join paths without breaking on Windows — build the path you hand to
fs::metadata - Reading and writing files — the rest of the chapter