Expand description
ufs-core — a pure-Rust, from-scratch UFS/FFS filesystem reader.
UFS = the Unix File System, a.k.a. the Berkeley Fast File System (FFS).
Parses the on-disk UFS structures a forensic tool needs — superblock and
geometry, cylinder-group headers and allocation bitmaps, inodes,
directories, and file content — over any byte source. The reader targets
both UFS1 (4.4BSD/FreeBSD legacy, 128-byte inodes, 32-bit block pointers,
superblock at byte 8192, magic 0x00011954) and UFS2 (FreeBSD 5+,
256-byte inodes, 64-bit block pointers, superblock at byte 65536, magic
0x19540119).
Import path is ufs (see [lib] name): use ufs::Superblock;.
UFS is endianness-agnostic on disk — the byte order is that of the host that
created the filesystem, and the superblock magic disambiguates it. The
reader supports both little- and big-endian images, selecting the order by
which interpretation makes the magic match (see Endian).
§Safety and robustness
This crate parses untrusted, attacker-controllable disk images. It is
#![forbid(unsafe_code)] and every integer is read through bounds-checked
readers that yield 0/None out of range rather than panic (the Paranoid
Gatekeeper standard).
Re-exports§
pub use bytes::Endian;
Modules§
- bytes
- Bounds-checked readers for both byte orders (the Paranoid Gatekeeper standard).
Structs§
- Cylinder
Group - Parsed cylinder-group header — the per-group allocation map.
- DirEntry
- One decoded directory entry (
struct direct). - Inode
- A decoded UFS inode — the metadata and block-pointer arrays a forensic tool
needs. Carries the union (superset) of the UFS1 and UFS2 dinode fields;
UFS1-absent fields (birthtime) are
None.#[non_exhaustive]so later phases add fields without a breaking change. - Superblock
- Parsed UFS superblock — geometry and addressing fields the cylinder-group and inode decode (P1) need.
- Timespec
- A UFS timestamp: whole seconds since the Unix epoch plus a nanosecond fraction. UFS2 stores seconds as a signed 64-bit value; UFS1 as 32-bit (widened here). The nanosecond field is a signed 32-bit count.
Enums§
- DirEntry
Type - A directory-entry file type (
d_type,DT_*indir.h). - File
Type - The file type decoded from
di_mode & IFMT. - UfsError
- Errors surfaced while parsing UFS/FFS on-disk structures.
- UfsVersion
- The on-disk UFS version, resolved from the superblock magic.
Constants§
- CG_
MAGIC - The cylinder-group header magic (
CG_MAGIC), at offset 4 of the header. - DIRBLKSIZ
- The directory block size (
DIRBLKSIZ) — a directory’s data is a sequence of these atomically-written blocks. - DIR_
ROUNDUP - The directory-entry name roundup (
DIR_ROUNDUP): names are padded to a 4-byte boundary. - FS_
UFS1_ MAGIC - UFS1 superblock magic (
FS_UFS1_MAGIC), read atfs_magic(offset 1372). - FS_
UFS2_ MAGIC - UFS2 superblock magic (
FS_UFS2_MAGIC), read atfs_magic(offset 1372). - SBLOCK_
UFS1 - Byte offset of the primary UFS1 superblock from the filesystem start
(
SBLOCK_UFS1). - SBLOCK_
UFS2 - Byte offset of the primary UFS2 superblock from the filesystem start
(
SBLOCK_UFS2). - UFS1_
DINODE_ SIZE - Size in bytes of a UFS1 dinode (
sizeof(struct ufs1_dinode)). - UFS2_
DINODE_ SIZE - Size in bytes of a UFS2 dinode (
sizeof(struct ufs2_dinode)). - UFS_
NDADDR - Number of direct block pointers in a dinode (
UFS_NDADDR). - UFS_
NIADDR - Number of indirect block pointers in a dinode (
UFS_NIADDR): single, double, and triple indirect. - UFS_
ROOTINO - The UFS root inode number (
UFS_ROOTINO).
Functions§
- list_
dir - Decode the directory entries of the directory inode
dir_ino, returning the live entries (skippingd_ino == 0free/deleted slots). Seelist_dir_allto also surface the deleted slots. - list_
dir_ all - Decode the directory entries of
dir_ino, includingd_ino == 0free/deleted slots (flaggeddeleted). The forensic-relevant superset oflist_dir— a deleted slot’s residuald_namebytes are preserved so an analyzer can recover them. - read_
block - Read
lenbytes of a file’s blockaddrfrompartition(the filesystem partition bytes, filesystem byte 0).addris a fragment address as stored in an inode’sdi_db[]/di_ib[]; the byte offset isaddr * fs_fsize. - read_
by_ path - Resolve an absolute path (e.g.
"/a/b/c") to its(inode number, inode), descending from the root inode (UFS_ROOTINO= 2) and matching each component against the live directory entries at each level. - read_
file - Assemble the full byte content of the file inode
ino, walking its block map (directdi_db[0..12], then single/double/triple indirect viadi_ib[0..3]) up todi_size. A hole (a0pointer, at any level of the tree) reads as zeros; the last block is sized to the remainingdi_size(fragment tail). - read_
inode - Locate and decode the inode numbered
inofrompartition, the filesystem partition bytes (filesystem byte 0 — a caller holding a whole disk image slices past the BSD-disklabel partition base first). - read_
inode_ file - Assemble the byte content of an already-decoded
inode(the block-map walkread_fileperforms after locating the inode). Exposed so callers holding anInode(e.g. afterread_by_path) need not re-locate it. - read_
path_ content - Resolve an absolute path to its file content:
read_by_paththenread_file. ReturnsOk(None)when the path does not resolve (likeread_by_path);Ok(Some(bytes))with the file’sdi_sizebytes otherwise. - read_
symlink_ target - The target of a symbolic-link inode. For a fast (inline) symlink
(
di_size <= fs_maxsymlinklen) the target lives in the block-pointer bytes of the dinode and is returned directly (P1 already decoded it — seeInode::symlink_target). For a slow symlink (di_size > fs_maxsymlinklen) the target is stored in the file’s data block(s), so it is read via the block map like any file’s content and truncated todi_size.