AmigaOS supports two native filesystem types: **OFS** (Old File System, OS 1.x) and **FFS** (Fast File System, OS 2.0+). Both use a block-based layout with 512-byte blocks and work on floppies and hard disks alike. FFS improves throughput by stripping per-block headers from data blocks. Both are limited to 4 GB partitions (32-bit signed arithmetic), 30-character filenames (107 with DOS\6/\7 on OS 3.5+), and offer no journaling — a crash forces a full validation pass that rebuilds the bitmap, sometimes losing files. Third-party replacements (**PFS3**, **SFS**, **FFS2** in OS 3.5+) address these limits with atomic commits, >100 GB volumes, and extent-based allocation. Understanding the on-disk layout is essential for FPGA core developers implementing virtual filesystems, HDF support, and ADF image handling.
| **Directory caching** | No | DOS\4 | Caches directory block lookups in RAM |
| **Crash resilience** | None | None | Both require full validation after unclean shutdown; no journaling, no atomic writes |
| **Metadata checksums** | Yes | Yes | Every block (root, file header, directory, data) has a checksum at offset $014 |
| **Self-describing blocks** | Yes | No | OFS data blocks contain back-pointers to file header; FFS data blocks are raw — unrecoverable if lost |
¹ **Directory file count in practice:** A directory's hash table has 72 slots (DD floppy) or 128+ (hard disk partitions). Each slot starts a collision chain. With a good hash distribution, 72-entry tables comfortably handle ~500 files before chain-walking overhead becomes noticeable. The theoretical limit is the number of free blocks on the volume — every file/directory entry consumes at least one block.
Hard disks use the **RDB (Rigid Disk Block)** partitioning scheme — a flexible linked-list structure far more capable than the PC MBR of the same era. There is no distinction between "primary" and "logical" partitions; all partitions are peers in a singly-linked list.
```
Block 0: RDB (Rigid Disk Block) — "RDSK" signature
Block 1–N: Partition blocks (linked list, one per partition)
├─ Partition 0: DH0 (Workbench)
├─ Partition 1: DH1 (Work)
├─ Partition 2: DH2 (Games)
└─ Partition N: ...
Block N+1: First partition data begins
(may be offset for cylinder alignment)
```
### RDB (Rigid Disk Block) — Block 0
The RDB is the master header for the entire physical disk. Its 4-byte signature is `RDSK` ($5244534B):
$44 4 rdb_LoCyl Low cylinder (start of partitionable area)
$48 4 rdb_HiCyl High cylinder (end of partitionable area)
$4C 4 rdb_CylBlocks Blocks per cylinder = Heads × Sectors
$50 4 rdb_AutoParkSeconds Auto-park timeout
$54 4 rdb_HighRSVDBlock Highest reserved block for RDB area
```
> [!NOTE]
> Solaris fields `rdb_LoCyl` and `rdb_HiCyl` define the partitionable region. The RDB and partition blocks occupy the reserved area *before* `rdb_LoCyl`. Partition data starts at `rdb_LoCyl`.
### PartitionBlock — Linked List
Each partition is described by a PartitionBlock chained via `de_Next` pointers. TheRDB's `rdb_PartitionList` field points to the first one:
$50 4 de_Next ➜ Next PartitionBlock in linked list (0 = end)
$54 4 de_SizeBlock Block size in bytes (usually 512, PFS3 can use 1024+)
$58 4 de_FileSysHdr ➜ Filesystem header for this partition (0 = use ROM FS)
```
### Partition Flags (de_Flags)
| Bit | Constant | Meaning |
|---|---|---|
| 0 | `PBF_BOOTABLE` | Partition can be selected for boot |
| 1 | `PBF_AUTOMOUNT` | Automatically mount on boot (off = not mounted) |
> [!WARNING]
> A partition with `PBF_AUTOMOUNT`=0 still exists on disk but will **not appear** as a DOS device until manually mounted. The Kickstart ignores non-automount partitions during the boot device scan. This is how "hidden" partitions for dual-boot are configured.
### Filesystem Header in RDB
The RDB can store **complete filesystem binaries** inline — this is how PFS3 and SFS work without patches to the Kickstart ROM. The `rdb_FileSysHdrList` field chains FileSysHdr blocks, each containing:
$0C 4 fh_DosType Filesystem DosType this binary handles
$10 4 fh_Version Filesystem version
$14 4 fh_Size Binary code size in bytes
$18 4 fh_Next ➜ Next FileSysHdr in linked list
$1C ... fh_Code[] Filesystem binary code
```
Each PartitionBlock's `de_DosType` encoding matches a FileSysHdr's `fh_DosType`. When the Kickstart boots, it looks up the filesystem code from the RDB, loads it into RAM, and uses it instead of (or alongside) the ROM filesystem.
### Partition Limits
| Limit | Value | Notes |
|---|---|---|
| **Max partitions** | Unlimited (RDB space limited) | Practical: 8–16 typical; ~30+ on large disks with expanded RDB area |
| **Min partition size** | 1 cylinder (~1–8 MB typical) | Depends on geometry; smaller is possible with logical sizes |
- **Highest BootPri wins** — If DH0 has BootPri=5 and DF0 has BootPri=3, the hard disk boots first
- **Floppy override at ≥ BootPri 15**: Setting a hard disk partition's BootPri ≥ 15 overrides the floppy even when a floppy is inserted (normal behavior = floppy boots before HDD at default priorities)
- **Tie-breaking**: If two partitions have the same BootPri, the **first** in the RDB partition linked list wins
- **Custom boot blocks**: On floppies, the boot block can contain custom code that takes over before the normal DOS boot. Hard disks ignore boot blocks — they always use the RDB-internal boot path
### Phase 3: Loading the Filesystem
For the chosen partition:
1. Follow `de_FileSysHdr` pointer to the FileSysHdr block
2. If non-zero: copy the filesystem binary into RAM and jump to its Init routine (this is how PFS3/SFS load from RDB without being in Kickstart ROM)
3. If zero: use the built-in ROM filesystem (FFS at `L:FastFileSystem` from Kickstart)
4. The filesystem mounts the volume, reads the root block, and locates `S:Startup-Sequence`
Holding both mouse buttons at power-on interrupts the boot and displays the **Early Startup Menu**. This lets the user:
- Select a specific partition to boot (overrides BootPri)
- Disable individual partitions (useful for incompatible setups or RAM conservation)
- Toggle CPU caches on/off
- Display expansion board diagnostics
This is the primary way to dual-boot: you install multiple OS components on different partitions, set one as default (high BootPri), and use Early Startup to select alternatives. No bootloader software needed.
### Valid Multi-Boot Configurations
| Scenario | Setup | How to Boot |
|---|---|---|
| **Dual OS (3.1 + 3.9)** | Two bootable partitions: DH0=3.1 (BootPri 5), DH1=3.9 (BootPri 4) | Default boots 3.1; Early Startup selects DH1 for 3.9 |
| **OS 4.x Classic + 3.x** | Requires separate Kickstart ROMs (OS4 uses its own) | Typically hardware Kickstart switcher + Early Startup |
| **MorphOS + AmigaOS** | Separate partitions; MorphOS uses SFS-based fixing | Early Startup or MorphOS boot loader |
| **Recovery + Main** | Small emergency partition (OFS/FFS, BootPri 10) + Main partition (PFS3, BootPri 5) | Recovery auto-boots if main FS absent; Early Startup overrides |
| **Games-only + Workbench** | Games partition (BootPri 0, or Automount off) + Workbench partition (BootPri 5) | Early Startup to select games; otherwise invisible |
> [!NOTE]
> There is **no bootloader discovery mechanism** on classic Amigas — each OS install is independent. You can't chain-load from one partition to another (like GRUB). The Early Startup Menu or BootPri remapping tools (`BootPri`, `ChangeBootPri`) are the standard methods.
/* table_size = 72 for DD floppy, 128 for HD floppy */
```
### Hash Collision Resolution
Collisions are resolved by **chaining**: each file/directory header has a `hash_chain` pointer (at offset `$1F0`) linking to the next entry that hashed to the same slot. The chain ends with 0.
> **Reverse order**: Data block pointers in `data_blocks[]` are stored **last-to-first**. Index 71 points to the first data block, index 70 to the second, etc. This is a BCPL heritage quirk.
OFS and FFS were designed for floppy-sized media in the mid-1980s. By the mid-1990s, hard drives exceeded 4 GB routinely, and FFS's lack of crash resilience made it a liability. Three major replacements emerged, each tackling different pain points.
### PFS3 (Professional File System 3)
Developed by Michiel Pelt (Greed Developments), PFS3 is the gold standard for modern Amiga setups. Originally commercial (GBP 36), now open-source and freely available on Aminet (`PFS3_53.lha`).
**How it works:** PFS3 uses **atomic commits** — metadata changes are written to a new location first, then a single pointer update makes them visible. If power fails mid-write, the old state remains intact. The filesystem is always consistent; there is no validation pass, ever.
**Key features:**
- **Delayed writes are safe** — dirty buffers commit atomically; unlike FFS, where a crash during delayed writes corrupts the bitmap
- **Extent-based allocation** — contiguous runs of blocks reduce fragmentation and improve sequential read speed
- **Configurable block sizes** — 512, 1024, 2048, or 4096 bytes; larger blocks reduce metadata overhead for big files
- **Directory indexing** — internal B-tree-style structure ("anode" tree) eliminates hash chain walking for large directories
- **Long filenames** — 107 characters, same as DOS\6/\7
- **Multi-user variant** — MuFS version integrates with Geert Uytterhoeven's multi-user filesystem for UNIX-style permissions
**Two variants:**
| Variant | Driver Interface | When to Use |
|---|---|---|
| **TD64** | TrackDisk64 commands | Modern IDE/SCSI controllers that support TD64 (PowerFlyer, FastATA, etc.) |
| **DirectSCSI** | Raw SCSI commands, bypasses device layer | A1200/A4000 internal IDE port; older controllers without TD64 support |
> [!WARNING]
> PFS3 is **hard disk only**. It cannot be used on floppy disks — its metadata structures require more space than an 880 KB floppy provides.
**Pros:**
- Zero validation time — reboot is instant after any crash
- 2–5× faster than FFS on accelerated machines (68030+), especially with large directories
- No fragmentation problems with extent-based allocation
- Open source, actively maintained (Aminet `PFS3_53`)
- Block size tuning for workload (512 for many small files, 4096 for large media files)
**Cons:**
- No floppy support — floppy-only workflows must stay on OFS/FFS
- Manual installation — must be copied into RDB and each partition reconfigured
- Version confusion — PFS2 CD + PFS3 update disk was the original distribution; modern users should get PFS3_53 directly from Aminet
- Older-era sizing quirks — 68000 version exists but is slow; real benefits only on 68020+
### SFS (Smart File System)
Developed by John Hendrikx, SFS takes a journaling approach rather than PFS3's atomic-commit model. It records pending changes to a journal before applying them; after a crash, the journal is replayed.
**Key features:**
- **Journaling** —changes logged before application; replay takes seconds, not minutes
- **Large block support** — up to 32 KB blocks, reducing metadata overhead for media files
- **Extent-based allocation** — similar to PFS3; contiguous runs reduce fragmentation
- **Defragmentation tools** — SFS includes built-in defrag utilities (PFS3 largely doesn't need them)
- **127 GB max volume** — slightly larger than PFS3's 104 GB limit
**Pros:**
- Larger max volume than PFS3 (127 GB vs 104 GB)
- Journal replay is fast (seconds vs FFS minutes)
- Large block support good for media-heavy workloads
- Built-in defragmentation
**Cons:**
- Requires 68020+ CPU minimum (no 68000 version)
- Journal replay still takes time (PFS3 is instant)
- 4 GB max file size (same as FFS)
- Less widely adopted than PFS3 in the modern Amiga community
- HDD only — same as PFS3
### FFS2 — Fast File System 2 (OS 3.5 / 3.9)
AmigaOS 3.5 and 3.9 ship an updated FFS that adds 64-bit support via NSD (New Style Device) or TD64. This is the "official" large-disk solution, but carries forward FFS's fundamental validation problem.
**Key features:**
- **64-bit arithmetic** — partitions and files >4 GB (requires NSD patching at boot via `SetPatch`)
- **Long filenames** — 107 characters (DOS\6/\7)
- **Backward compatible** — still works on floppies; still uses bitmap allocation
- **NO journaling or atomic commits** — crashes still trigger full validation
**Pros:**
- Official Commodore/Haage & Partner solution
- Works on floppies (unlike PFS3/SFS)
- Backward compatible with all old tools
- No third-party installation needed on OS 3.5+
**Cons:**
- Still validates after every crash — can take minutes on large drives and sometimes fails
- NSD requires boot-time patching (`NSDPatch.cfg` in `DEVS:`)
- Old disk tools (DiskSalv, Quarterback, ReOrg) break on >4 GB partitions
- Some older buffered IDE interfaces incompatible with NSD
- Still 512-byte blocks, still bitmap allocation, still no atomicity
### Which Filesystem Should I Use?
```mermaid
graph TD
A[Need floppy support?] -->|Yes| B[Use OFS or FFS]
A -->|No| C[What CPU?]
C -->|68000| D[PFS3 \n68000 version]
C -->|68020+| E[PFS3 \nor SFS]
E --> F[Priority: instant crash recovery?]
F -->|Yes| G[PFS3 \natomic commits]
F -->|No| H[Need >104 GB?]
H -->|Yes| I[SFS \n127 GB max]
H -->|No| G
```
---
## Crash Resilience — Validation vs Atomic Commits vs Journaling
### FFS/OFS: Validation
When an Amiga with FFS suffers an unclean shutdown (power loss, guru meditation, reset), the bitmap — which tracks free vs allocated blocks — may be out of sync with actual block usage. On next boot, DOS triggers **validation**:
1. Scan every block on the partition
2. Rebuild the bitmap from scratch by checking which blocks are referenced
3. Mark all unreferenced blocks as free
4. DOS generates "checksum error on block N" for any corrupt metadata
**Cost:**
| Drive Size | Typical Validation Time (68030/50) |
|---|---|
| 100 MB | ~10–15 seconds |
| 500 MB | ~45–90 seconds |
| 2 GB | ~3–6 minutes |
| 4 GB | ~6–12 minutes |
If validation fails (corrupt root block, broken hash chain), the volume remains inaccessible until DiskSalv or Quarterback repairs it — and files are often lost in the process.
### PFS3: Atomic Commits
PFS3 never validates. Every metadata operation follows a write-twice protocol:
1. Write new metadata to a free block (the "shadow copy")
2. Update a single root pointer to point to the new metadata
3. Mark the old block as free
If power fails at step 1 — old pointer still points to valid old data. If power fails at step 2 — the pointer update either completes (new state) or doesn't (old state). There is no intermediate corrupt state. The filesystem is **always consistent**.
### SFS: Journaling
SFS writes pending changes to a journal (a reserved area on disk) before modifying the main structures. After a crash:
1. On mount, check journal for uncommitted entries
2. Replay them in order (redo log)
3. Mark journal clean
Replay is fast (seconds) but not instant like PFS3. The journal itself can be corrupted by a crash during journal writes — a well-known journaling vulnerability that PFS3's atomic-pointer approach avoids entirely.
| Power loss during file write | Validation required | Validation required | Instant recovery | Journal replay (~seconds) |
| Power loss during directory create | Validation required | Validation required | Instant recovery | Journal replay |
| Power loss during rename | Validation required | Validation required | Instant recovery | Journal replay |
| Power loss during format | Volume likely lost | Volume likely lost | Volume consistent (atomic) | Journal replay |
| Risk of file loss | Moderate to high | Moderate to high | **Zero** (by design) | Very low |
| Best for... | Floppy-only, 68000 | OS 3.5+ with floppy needs | **Any hard disk Amiga** | Large volumes >104 GB |
> [!WARNING]
> FFS "delayed writes" (buffered writes that flush later) are dangerous. If a crash occurs before the flush, the bitmap is corrupt. PFS3's delayed writes are safe because the atomic-commit protocol guarantees either the full write or nothing. **On FFS, always disable delayed writes** by not using `ACTION_WRITE` with buffering — or use `ACTION_FLUSH` after every critical write.
---
## Fragmentation & Defragmentation
### How Bitmap Allocation Causes Fragmentation
OFS and FFS allocate blocks from a bitmap — a flat, per-partition table where each bit represents one block (1 = free, 0 = allocated). The allocator scans the bitmap linearly and takes the first available block:
File blocks end up: [0][1][2][3] [4] [5][6] [7][8][9]
scattered across the bitmap
```
After files are created, modified, and deleted over weeks or months, the free blocks become a Swiss-cheese pattern of isolated gaps. A new file that needs 50 contiguous blocks will be split across dozens of non-contiguous groups — **fragmentation**.
### Quantified Impact
Fragmentation affects different media differently:
| Media | Seek Time | Fragmentation Penalty |
|---|---|---|
| **DD Floppy** (880 KB) | ~30 ms track-to-track | Negligible — small total blocks; everything is ~seeks away regardless |
| **Mechanical HDD** (1990s) | ~15–20 ms avg | **Severe** — fragmented file can be 3–10× slower to read sequentially |
| **Mechanical HDD** (late 1990s) | ~8–12 ms avg | Moderate — faster seeks help, but still 2–4× penalty |
| **CF Card / SSD** (IDE adapter) | ~0.1 ms | **Negligible** — no mechanical seek; random access is nearly as fast as sequential |
> [!NOTE]
> On CF cards and SSDs connected via IDE adapter, fragmentation has effectively zero performance impact. The seek time is ~0.1 ms regardless of block location. This makes PFS3's anti-fragmentation features less critical on solid-state — the main reason to use PFS3 on a CF setup is **crash resilience**, not fragmentation avoidance.
Since OFS/FFS has no built-in defragmentation, third-party tools filled the gap:
| Tool | Author | How It Works | Limitations |
|---|---|---|---|
| **ReOrg** | Dirk Stoecker | Reads all files, sorts by directory, rewrites contiguously; rebuilds bitmap | OS 3.1 only; breaks on >4 GB partitions (FFS2/NSD) |
| **DiskSafe** | — | Block-level defrag with safety checks; can resume after interruption | Slower than ReOrg; limited to FFS partitions |
| **Quarterback Tools** | — | Full suite: defrag, backup, repair, undelete | Commercial; breaks on >4 GB partitions; last update ~1997 |
| **SFS Defrag** | John Hendrikx | Built into SFS; moves extents to consolidate free space | SFS only; not needed for PFS3 |
**How ReOrg works:**
1. Scan the entire partition, building a file list sorted by directory path
2. For each file: read all data blocks into a buffer, then write them to a new contiguous region
3. Update the file header with new block pointers
4. Mark old blocks as free in a new bitmap
5. Relocate directory headers so files in the same directory are physically close
> [!WARNING]
> ReOrg and Quarterback rewrite every block on the partition. A power failure during defragmentation **destroys the filesystem** — all files become inaccessible. Always back up before defragmenting. PFS3 and SFS make this at-risk operation unnecessary.
### PFS3: Why It Rarely Fragments
PFS3 uses **extent-based allocation** instead of a bitmap. When a file grows, PFS3:
1. Reserves a contiguous run of blocks (an "extent")
2. If the file outgrows the extent, allocates a second extent — but each extent is internally contiguous
3. On delete, free extents can be merged with adjacent free space
The result: files are always stored in large contiguous chunks. Even "fragmented" PFS3 files are far less scattered than typical FFS files. In practice, PFS3 volumes rarely need defragmentation — the extent allocator's anti-fragmentation behavior is built-in.
### SFS: Extent + Defrag
SFS also uses extent-based allocation (like PFS3) but includes a user-invoked defragmenter. After heavy create/delete cycles, SFS volumes can develop free-space fragmentation (many small free extents rather than few large ones). Running `SFSdefrag` consolidates free space into fewer, larger extents.
### Best Practices to Avoid Fragmentation
1.**Use PFS3 or SFS** — extent-based allocation eliminates the bitmap scatter problem entirely
2.**Keep Workbench on a dedicated partition** — the boot partition sees frequent small writes (prefs files, env variables); isolating it prevents fragmenting your data partition
3.**Avoid filling partitions past 85%** — near-full volumes force the allocator to use every scattered free block
4.**On FFS: defragment quarterly** if using a mechanical hard drive; never defragment without a backup
5.**Large files first** — when creating a new FFS volume, copy large static files (archives, disk images) first so they get the big contiguous runs
6.**On CF/SD/SSD: don't bother defragmenting** — the seek time advantage is zero; the write-cycle cost of rewriting every block outweighs any benefit
The following exercise runs on a modern computer (macOS, Linux, or Windows) using standard Python 3 with zero dependencies. We open a raw `.adf` floppy disk image — the bit-exact dump of an Amiga DD disk — and walk its on-disk structures from first principles: identify the filesystem type from the boot block signature, locate the root block at the partition midpoint, extract the volume name from its BSTR field, and enumerate every file and directory by traversing the hash table. By the end, you'll have a functional filesystem reader that understands OFS/FFS block layout without any Amiga runtime or emulator.
## Partition IDs, Signatures & On-Disk Data Layout
### Two-Layer Identification: RDB DosType vs On-Disk Boot Block
Understanding what you see in a disk editor requires knowing the **two-layer identification system**:
1.**RDB DosType** (`de_DosType` in PartitionBlock): Tells the Kickstart **which filesystem handler** to load from the RDB or ROM. This is the identifier you configure in HDToolBox when you "Change Filesystem" on a partition.
2.**On-Disk Boot Block Signature** (first 4 bytes of block 0/1 within the partition): The filesystem handler verifies this at **mount time** to confirm the partition was actually formatted with this filesystem and hasn't been corrupted.
> [!NOTE]
> For OFS and FFS, these two identifiers are **identical** — the RDB DosType IS the on-disk signature. For PFS3, **they are different** — the RDB uses `PFS\3` but the on-disk boot block uses `PFS\1` or `PFS\2`. This distinction is critical when analyzing raw disk images: the hex you see at the partition's first block may not match the hex you expect from HDToolBox's dropdown.
| `DOS\7` | `$444F5337` | `44 4F 53 37` | FFS Intl + DirCache + Long Filenames (107 chars); = FFS2 on OS 3.5+ with NSD/TD64 64-bit support |
> [!NOTE]
> The byte at offset 3 increments with each feature tier: bit 0 = FFS (vs OFS), bit 1 = International, bit 2 = DirCache. `DOS\7` has bits 0+1+2 set (7 = 0b0111). This bitmask design means a simpler handler can read a more-capable partition's metadata, though only a matching handler can use all features.
#### PFS3 (Professional File System III)
| Use | Identifier | Hex (BE uint32) | Raw Bytes |
² Reserved blocksize > 1024 bytes, or non-512-byte sectors (format introduced in PFS3 v17.4).
> [!WARNING]
> If you see `PFS\1` at block 0 of a PFS3 partition, that's **correct behavior** — it's the mount-time signature, not the RDB DosType. Do NOT change the RDB DosType expecting to match the hex boot block bytes. PFS3 intentionally separates these so that different versions can share the same RDB identifier while changing internal on-disk structures.
#### SFS (Smart File System)
| Use | Identifier | Hex (BE uint32) | Raw Bytes |
### On-Disk Data Layout — Where Does the FAT Live?
A common question from users familiar with FAT/NTFS/ext4: **"Where is the allocation table?"** The answer differs radically per filesystem, and knowing where metadata lives is essential for forensic analysis and emulator development.
**Key insight:** There is no FAT at the beginning. Metadata is at the **midpoint**:
- **Root block** at `⌈total_blocks / 2⌉` (e.g., block 880 on a DD floppy of 1760 blocks)
- **Bitmap** blocks are scattered — the root block points to them via extension fields; they track free/used blocks (1 = free, 0 = used)
- **File header blocks** are everywhere — they contain the data block pointer list (last-to-first ordering), file size, name, and protection bits
- **Data blocks** are everywhere — in FFS, 100% payload (512 bytes); in OFS, 488 bytes of payload + 24 bytes of self-describing header
This midpoint placement was inherited from early Unix filesystem design (the superblock was placed at the center to minimize average seek distance). It makes OFS/FFS distinctive: in a hex editor, the first interesting metadata after the boot block doesn't appear until halfway through the image.
**Key insight:** Unlike OFS/FFS, PFS3 stores **all metadata at the beginning**:
- **Boot block** at block 0 (512 bytes; second copy at block 1 for redundancy)
- **Root block** immediately after boot block in the reserved zone. Its `disktype` field matches the boot block (`PFS\1` or `PFS\2`)
- **Reserved bitmap** tracks free blocks within the metadata zone itself
- **Anode index blocks** — an anode is an "allocation node" that describes a contiguous extent of data blocks. Index blocks are indirect pointers for large numbers of anodes
- **Directory blocks** (signature `DB` at UWORD offset 0): contain directory entries (filenames, protection bits, anode numbers) in variable-length records
- **Anode blocks** (signature `AB`): contain the extent descriptors — `blocknr` (start), `clustersize` (length in blocks), `next` (pointer to next anode for file)
- **Data blocks** begin after the metadata zone — allocated as extents (contiguous runs), tracked by anodes, with no per-block metadata overhead
This "metadata-first" design has two advantages: (a) the root is always at a known location (block ~1), no midpoint calculation needed; (b) the metadata zone is read once at mount time and cached, so all subsequent I/O goes directly to data blocks with no metadata interleaving.
- **Free space bitmaps** for allocation bookkeeping
- SFS supports much larger block sizes (up to 32 KB), which can reduce the metadata-to-data ratio for large files
### Quick Comparison: Where Is Everything?
| Question | OFS/FFS | PFS3 | SFS |
|---|---|---|---|
| Where is the root block? | **Midpoint** (total/2 up) | **Block ~1** (after boot) | **Block 2** (MDB) |
| Where is the free-space map? | Bitmap blocks, pointed to by root | Reserved bitmap (in metadata zone) | Free space bitmaps + Extent B-tree |
| Where are directory entries? | Hash table in root/dir header blocks → file header blocks | Directory blocks (`DB`) in metadata zone | Directory B-tree (in metadata zone) |
| Where are file data blocks? | Everywhere, linked via singly-linked lists | After metadata zone, allocated as extents | After metadata zone, allocated as extents |
| Is metadata interleaved with data? | Yes — OFS embeds headers; FFS separates but scatters | No — metadata zone is contiguous at beginning | No — metadata is at beginning, separate from data |
| What identifies a file's data blocks? | Last-to-first pointer list in file header block | Anode chain (contiguous extent descriptors) | Extent B-tree entries |
> [!NOTE]
> The "root block at midpoint" vs "metadata at beginning" distinction is the single most important difference when scanning a raw disk image. If you see meaningful structured data at block 0 and then nothing recognizable for thousands of blocks, you're looking at FFS/OFS — the root block lives at the midpoint. If you see structured metadata immediately after the boot block, you're looking at PFS3 or SFS.
### 1. "The Big-Endian Blindfold"
**What fails:** reading on-disk structures with native-endian assumptions:
```python
# BROKEN: interprets multi-byte fields in host byte order
**Why it fails:** The Amiga is Big-Endian (Motorola byte order). Every multi-byte value on disk (type, header_key, checksum, byte_size) is big-endian. Modern x86 and ARM are little-endian. Using native byte order produces swapped field values that fail checksums and point to garbage blocks.
**Correct:**
```python
# Always use big-endian unpack format:
type_id = struct.unpack('>I', block[0:4])[0]
```
### 2. "The Checksum Cheater"
**What fails:** reading a block without validating its checksum:
**Why it fails:** A single bit-flip passes the type check but corrupts all subsequent fields. OFS/FFS uses a checksum (sum of 128 longs must be 0). Always verify before trusting field values.
**Correct:**
```c
LONG sum = 0;
for (int i = 0; i <128;i++)
sum += ((ULONG *)block)[i];
if (sum != 0) return ERROR_CHECKSUM_FAIL;
ULONG fileSize = ((ULONG *)header)[0x148 / 4];
```
### 3. "The Forward Hash Walker"
**What fails:** walking data_blocks in natural index order:
**Why it fails:** Data block pointers are stored last-to-first (BCPL heritage). Index 71 = first data block, index 0 = last. Reading in index order assembles the file backwards.
**What fails:** treating BSTR as a null-terminated C string:
```c
/* BROKEN: assumes null-terminated */
printf("Volume: %s\n", rootBlock[0x1B0]);
/* Prints garbage past the name */
```
**Why it fails:** AmigaDOS uses BSTR format: first byte = length, followed by characters, NO null terminator. Passing to printf reads random memory until a null appears.
**Correct:**
```c
UBYTE len = rootBlock[0x1B0];
char name[32];
memcpy(name, &rootBlock[0x1B1], len);
name[len] = '\0';
printf("Volume: %s\n", name);
```
### 5. "The Partition Table Skipper"
**What fails:** assuming every image starts with a boot block:
```python
# BROKEN: checks for DOS at offset 0
fs_type = data[0:4]
if fs_type not in [b'DOS\x00', b'DOS\x01']:
raise ValueError("Bad filesystem")
# Fails on hard disk images with RDB
```
**Why it fails:** HDF images start with RDB (Rigid Disk Block, "RDSK" signature) at block 0, followed by partition table. The filesystem starts at the partition's first block, not at image offset 0.
**Correct:**
```python
sig = data[0:4]
if sig == b'RDSK':
# Parse partition table, find DOS partition
for part in parse_partitions(data):
handle_filesystem(data, part.start_block)
elif sig.startswith(b'DOS'):
handle_filesystem(data, 0) # Direct ADF
```
---
## Pitfalls
### 1. Checksum Omission
Every block has a checksum at offset $014. Failing to verify means trusting corrupt data. The parser works on clean images but silently produces garbage from degraded disks.
### 2. Endianness Errors
AmigaDOS is Big-Endian everywhere: types, block numbers, sizes, hash entries, protection bits, dates. Cross-platform tools on x86/ARM must explicitly byte-swap every multi-byte field.
### 3. BSTR Overflow Risks
BSTR fields have fixed max sizes, but the length byte controls content. A corrupt length byte exceeding the field size causes buffer over-reads. Always clamp: min(length_byte, MAX_FIELD_SIZE).
### 4. Root Block Hardcoding
Root block is at total_blocks/2, not a hardcoded 880. Code assuming 880 fails on HD floppies (root at 1216) and every hard disk partition.
---
## Best Practices
1.**Always validate checksums before trusting any block field.**
2.**Use big-endian byte order for ALL multi-byte reads/writes.**
3.**Compute root_block as total_blocks/2, never hardcode 880.**
4.**Clamp BSTR length to the field maximum size.**
5.**Read data_blocks in REVERSE index order (71 to 0).**
6.**Check for RDB signature before assuming direct FS.**
7.**Use hash_chain for collision resolution, not linear scan.**
8.**Write tools that handle both OFS and FFS paths separately.**
---
## When to Use / When NOT to Use
### Filesystem Selection
| Scenario | Use | Avoid | Why |
|---|---|---|---|
| Bootable floppy for any Amiga | OFS (DOS\0) — universal boot | FFS needs KS 2.0+ | OFS boots on 68000 through 68060, all Kickstart versions |
| Daily-use hard disk (stock) | FFS + Intl + DirCache (DOS\5) | OFS loses 5% to headers | DirCache reduces directory scan overhead on mechanical drives |
| Daily-use hard disk (expanded) | **PFS3** — atomic, fast, zero validation | FFS — validation on every crash | 68030+ with PFS3 is 2–5× faster; instant recovery after power loss |
| Large media files (>50 MB) | PFS3 (block=4096) or SFS (block=32768) | FFS (block=512 fixed) | Larger blocks reduce metadata overhead and fragmentation for big files |
| Data recovery / forensics | OFS — self-describing blocks | FFS — no per-block back-pointers | OFS data blocks point back to file headers; FFS data blocks are anonymous |
| FPGA core implementation | OFS — easier to bootstrap | FFS — harder to test edge cases | OFS block headers provide validation points; FFS needs external context |
| OS 3.5+ with floppy needs | OFS/FFS DOS\6/\7 (long filenames) | PFS3/SFS (no floppy support) | Long filenames on floppies require DOS\6 (OFS) or DOS\7 (FFS) |
### Partition Layout Strategy
| Goal | Partition Layout | Best Practices |
|---|---|---|
| Single OS, single user | DH0=Workbench (1–2 GB), DH1=Work (rest of disk) | Separate Work keeps fragmentation off boot partition |
| Single OS, multi-purpose | DH0=Workbench (1 GB), DH1=Work, DH2=Games, DH3=Media | Each workload gets its own volume; failure of one partition leaves others intact |
| Dual-boot (3.1 + 3.9) | DH0=3.1 (BootPri 5), DH1=3.9 (BootPri 4) | Both bootable; Early Startup switches; share DH2 for data |
| Game archive (WHDLoad) | DH0=Workbench (small), DH1=Games (large, PFS3) | PFS3 block=1024 good balance for games; Automount games partition off for fastest Workbench boot |
| Development system | DH0=Workbench, DH1=Source, DH2=Build artifacts | Separates OS crashes from source data; builds benefit from PFS3's extent alloc |
| FPGA development target | DH0=Boot (FFS, 500 MB), DH1=Transfer/Data (FFS, small) | Keep boot partition FFS for ROM compatibility; small sizes for fast validation if needed |
| Large modern SSD | DH0=System (2 GB PFS3 blk=1024), DH1=Data (rest, PFS3 blk=4096) | Boot partition <4GBforKSROMcompatibility;largeblocksizeondataformediathroughput|
| **FFS2** (1999) | Official (OS 3.5+) | >4 GB² | >4 GB² | 107 | ❌ | ✅ Block | ✅ | 64-bit via NSD/TD64; still validates on crash; floppy-compatible |
¹ 107-character filenames with DOS\6 (OFS) or DOS\7 (FFS), requires OS 3.5+.
² Requires NSD (New Style Device) or TD64 driver support; old tools break on >4 GB partitions.
**Key Amiga innovations:** Checksummed metadata (neither FAT nor HFS had it), hash-based O(1) directory lookup, BSTR format immune to null-byte injection. Commodore anticipated validation as a safety measure for a primarily floppy-based world — it became a liability when hard drives grew.
No. A partition is either OFS or FFS. Multiple partitions on one disk can use different types, but each partition is one and only one format.
### Q: Why is the root block at the midpoint?
Minimizes average seek distance on mechanical drives. Half the data is before the root, half after. Inherited from early Unix superblock placement.
### Q: Does AmigaOS suffer from fragmentation?
Yes. OFS/FFS allocates from the bitmap and never defragments. Impact is minimal on floppies but noticeable on hard drives over time. Third-party tools (ReOrg, DiskSafe) existed.
### Q: Can I convert OFS to FFS in-place?
Not safely. Requires backup-format-restore cycle. In-place tools existed but were fragile and not recommended.
### Q: What happens when a checksum fails?
The filesystem handler rejects the block with ERROR_DISK_NOT_VALIDATED. The operation fails with no attempted recovery. Self-healing was not designed into AmigaOS.
### Q: How do PFS3, SFS, and FFS2 differ from FFS?
See the [Third-Party Filesystems](#third-party-filesystems--deep-dive) section above for detailed pros/cons. In brief: PFS3 uses atomic commits (instant recovery, always consistent), SFS uses journaling (replay takes seconds), and FFS2 adds 64-bit support but still requires full validation after crashes. PFS3 is the recommended choice for any hard disk Amiga today.
### Q: What is the maximum hard disk size AmigaOS can handle?
Depends on the filesystem: stock FFS = 4 GB per partition, FFS2 (OS 3.5+) = >4 GB with NSD/TD64, PFS3 = 104 GB, SFS = 127 GB. The boot partition must be entirely within the first 4 GB of the drive regardless of filesystem (a Kickstart ROM limitation, not a filesystem one).
### Q: How many files can a single directory hold?
No hard limit — directory entries are chained through hash collision lists. With a 72-slot hash table (DD floppy), ~500 files is comfortable; with 128+ slots (hard disk partitions), thousands work fine. The real limit is block exhaustion on the volume. PFS3's B-tree-style anode tree handles very large directories (10,000+ files) far better than FFS's linear hash chains.
### Q: Does a crash always require disk validation?
Only on OFS/FFS/FFS2. PFS3 uses atomic commits and is always consistent — zero validation time after any crash. SFS replays its journal in seconds. This is one of the strongest reasons to move off FFS: on a 4 GB partition, FFS validation takes 6–12 minutes on a 68030; PFS3 takes zero seconds.
### Q: Do PFS3 or SFS work on floppy disks?
No. Both require hard disk storage. PFS3's atomic commit structures alone exceed the 880 KB capacity of a DD floppy. For floppy-only workflows, use OFS (universal boot) or FFS (KS 2.0+). For cross-media workflows, use FFS on floppies and PFS3 on hard disk partitions.
### Q: How many partitions can I create on one disk?
There is no hard limit — all partitions are peers in a singly-linked list within the RDB. The only practical limit is the reserved RDB area size (`rdb_HighRSVDBlock`). Typical configurations support 8–16 partitions comfortably; expanding the RDB reserved area allows 30+. Unlike MBR, there are no "primary" vs "logical" distinctions.
### Q: Can I mix different filesystems on one disk?
Yes. Each partition independently specifies its filesystem via `de_DosType`. A common setup: DH0=FFS (bootable, for ROM compatibility), DH1=PFS3 (Work, fast recovery), DH2=SFS (Games, large blocks). The Kickstart loads the correct filesystem binary from the RDB for each partition at mount time.
### Q: How does dual-boot work without a bootloader?
The Amiga's **Early Startup Menu** (hold both mouse buttons at power-on) lets you select any bootable partition regardless of its BootPri. To set up dual-boot: create two bootable partitions with different OS installs, assign the preferred default a higher BootPri, and use Early Startup to select the alternative when needed. Toolbox tools like `ChangeBootPri` can also remap BootPri from software.
### Q: Does the Kickstart ROM limit which filesystem I can boot from?
For stock Kickstart 3.1: the boot partition must use a filesystem the ROM knows how to read — FFS (DOS\1) or OFS (DOS\0). However, if the filesystem binary is stored in the RDB (the standard for PFS3/SFS), the Kickstart loads it before mounting, so the boot partition can use PFS3 or SFS even on KS 3.1. The real constraint: the **boot partition must be within the first 4 GB of the drive** (a Kickstart scsi.device limitation, not a filesystem limitation).