A pure Rust implementation of the Professional File System III (PFS3) for Amiga disk images. Read, write, format, check, and mount PFS3 volumes on modern systems.
- Read access — list directories, read files, extract entire volumes, follow softlinks
- Write support — create files, directories, delete entries, rename, format new volumes
- Filesystem checker — validate on-disk consistency with optional repair
- FUSE driver — mount PFS3 images as native filesystems (macOS via macFUSE, Linux via FUSE)
- Amiga protection bits — full HSPARWED support via CLI,
chmod, and extended attributes - RDB auto-detection — automatically finds PFS3 partitions in Rigid Disk Block images
- Deldir / .Trashcan — browse and recover deleted files
- Real device support — works on raw partitions (
/dev/sdX,/dev/rdiskN) - SUPERINDEX support — handles large volumes (multi-GB) with superindex anode trees
- Pure Rust — no C dependencies (except FUSE for mounting)
This project provides two packages:
pfs3— CLI tools (no dependencies, static binary)pfs3-fuse— FUSE driver (requires libfuse3 on Linux or macFUSE/FUSE-T on macOS)
Download from GitHub Releases. Available for Linux (x86_64, aarch64) and macOS (Intel, Apple Silicon).
brew tap metaneutrons/tap
brew install pfs3# CLI tools
yay -S pfs3
# FUSE driver
yay -S pfs3-fuseDownload .deb packages from GitHub Releases:
sudo dpkg -i pfs3_*.deb
sudo dpkg -i pfs3-fuse_*.deb # optional, pulls in libfuse3# CLI tools only
cargo install --path .
# FUSE driver (requires libfuse3-dev on Linux or macFUSE on macOS)
cargo install --path crates/pfs3-fuseRequires Rust 1.85+ (edition 2024).
pfs3 info disk.hdf
pfs3 info amiga_drive.hdf -p DH0 # select partition by name
pfs3 partitions amiga_drive.hdf # list all RDB partitionspfs3 ls disk.hdf # root directory
pfs3 ls disk.hdf S # subdirectory
pfs3 ls disk.hdf -p DH0 C # partition + pathOutput includes Amiga protection bits:
Type Protect Size Date Name
------------------------------------------------------------------------
FILE ----r-ed 976 1978-01-01 00:05:08 Wait
FILE -s--rwed 14 2021-06-16 19:27:30 WOMyFiles
DIR ----rwed 2023-07-30 18:05:40 C
pfs3 cat disk.hdf S/Startup-Sequence # print to stdout
pfs3 extract disk.hdf / -o ./output # extract everything
pfs3 extract disk.hdf Libs -o ./libs # extract a directorypfs3 write disk.img localfile.txt RemoteName.txt
pfs3 mkdir disk.img NewDir
pfs3 write disk.img data.bin NewDir/data.bin
pfs3 rm disk.img OldFile.txtpfs3 protect disk.img myfile.txt rwed # absolute: set exactly these bits
pfs3 protect disk.img myfile.txt "+sp" # add script + pure flags
pfs3 protect disk.img myfile.txt -- "-wd" # remove write + delete
pfs3 protect disk.img myfile.txt hsparwed # set all 8 bitsProtection bits follow Amiga conventions:
| Bit | Name | Meaning |
|---|---|---|
| H | Hidden | File is hidden from directory listings |
| S | Script | File is an executable script |
| P | Pure | Program can be made resident (re-entrant) |
| A | Archive | File has been modified since last backup |
| R | Read | File can be read |
| W | Write | File can be written |
| E | Execute | File can be executed |
| D | Delete | File can be deleted |
Note: RWED use inverted logic on disk (bit set = denied). The CLI and xattr interface abstract this away — lowercase letters mean "granted".
pfs3 check disk.hdf
pfs3 check disk.hdf --repairpfs3 mkfs new.img --name "MyDisk" --size-mb 100
pfs3 mkfs /dev/sda3 --name "WorkDisk"pfs3 tune disk.hdf --name "NewName" # rename volume
pfs3 deldir disk.hdf # list deleted filesMount any PFS3 image as a native filesystem:
mkdir -p /tmp/pfs3
pfs3-fuse disk.hdf /tmp/pfs3 --auto-unmount
# Use it like any filesystem
ls /tmp/pfs3/
cat /tmp/pfs3/S/Startup-Sequence
cp /tmp/pfs3/C/Dir ~/amiga-dir
umount /tmp/pfs3pfs3-fuse disk.hdf /tmp/pfs3 --auto-unmount --write
echo "hello" > /tmp/pfs3/test.txt
mkdir /tmp/pfs3/NewDir
mv /tmp/pfs3/old.txt /tmp/pfs3/new.txt
rm /tmp/pfs3/unwanted.txt
⚠️ Read-write mode is experimental. PFS3 does not support journaling or atomic updates — this is a limitation of the on-disk format, same as the original AmigaOS driver. Use only on copies of disk images, never on originals.
# Auto-detect first PFS3 partition
pfs3-fuse amiga_drive.hdf /tmp/pfs3
# Manual byte offset
pfs3-fuse drive.img /tmp/pfs3 --offset 258048When mounted via FUSE, Amiga protection bits are accessible through the user.amiga.protection extended attribute. This preserves all 8 bits (HSPARWED) losslessly — unlike chmod which can only express RWED.
# Read protection bits
getfattr -n user.amiga.protection /tmp/pfs3/myfile
# user.amiga.protection="--p-rwed"
# Set protection bits (same syntax as CLI)
setfattr -n user.amiga.protection -v "hsparwed" /tmp/pfs3/myfile
setfattr -n user.amiga.protection -v "+sp" /tmp/pfs3/myfile
setfattr -n user.amiga.protection -v "-wd" /tmp/pfs3/myfile
# List all xattrs
getfattr -d /tmp/pfs3/myfilechmod also works and maps to RWED bits, preserving any HSPA flags that were previously set:
chmod 644 /tmp/pfs3/myfile # sets rw-d, preserves hspa
chmod 755 /tmp/pfs3/myfile # sets rwed, preserves hspaIf the volume has a deldir (PFS3 trash), it appears as a virtual .Trashcan directory at the mount root:
ls /tmp/pfs3/.Trashcan/
cat /tmp/pfs3/.Trashcan/deleted_file.txtpfs3/
├── crates/
│ ├── libpfs3/ ← Core library (no OS dependencies)
│ │ ├── src/
│ │ │ ├── ondisk/ — On-disk structures (rootblock, direntry), BE parsing
│ │ │ ├── volume.rs — Volume open, RDB detection, file/dir access
│ │ │ ├── anode.rs — Anode lookup, chain traversal, SUPERINDEX
│ │ │ ├── dir.rs — Directory entry parsing
│ │ │ ├── bitmap.rs — Block bitmap allocation
│ │ │ ├── format.rs — Filesystem formatter (mkfs)
│ │ │ ├── writer.rs — File/dir write, delete, rename, protection
│ │ │ ├── rdb.rs — Rigid Disk Block partition table parser
│ │ │ ├── cache.rs — LRU block cache
│ │ │ ├── io.rs — BlockDevice trait + file/partition impl
│ │ │ ├── error.rs — Error types
│ │ │ └── util.rs — Datestamp, charset, protection bit conversion
│ │ └── tests/
│ │ ├── integration.rs — 76+ tests against real PFS3 images
│ │ └── fixtures/
│ │ ├── small.hdf — Generated test image (320KB)
│ │ └── pfs.7z — Real PFS3 image from AmiFUSE (8MB)
│ └── pfs3-fuse/ ← FUSE driver binary
│ └── src/
│ ├── main.rs — FUSE filesystem implementation
│ └── types.rs — Inode management, volume access
├── src/ ← CLI tool binary
│ ├── main.rs
│ ├── bin/
│ │ └── gen_fixtures.rs — Test fixture generator
│ └── cmd/
│ ├── info.rs, ls.rs, cat.rs, extract.rs
│ ├── check.rs, mkfs.rs, write.rs
│ ├── protect.rs, tune.rs
│ └── mod.rs
├── Cargo.toml
└── LICENSE — LGPL-3.0-or-later
small.hdf(320KB) — Generated bygen_fixturesusing the PFS3 formatter. Contains files, directories, and various edge cases.pfs.7z→pfs.hdf(8MB) — Created by the real PFS3 driver (pfs3aio) running under m68k emulation. Sourced from the AmiFUSE project. Automatically extracted at test time viasevenz-rust.
All core tests run against both images to ensure compatibility with real PFS3 on-disk structures.
Tested against:
- Volumes created by the original pfs3aio driver
- Coffin OS R65 32GB disk images (multi-partition RDB, SUPERINDEX, deldir)
- Volumes with MODE_SUPERINDEX, MODE_SUPERDELDIR, MODE_DELDIR, MODE_LARGEFILE
- pfs3aio — Original PFS3 AmigaOS driver source by Michiel Pelt
- AmiFUSE — FUSE driver using m68k emulation of real Amiga FS handlers
LGPL-3.0-or-later