Files
RedBear-OS/local/docs/archived/VFAT-IMPLEMENTATION-PLAN.md
vasilito e586a44be6 docs: second-pass boot audit (D-Bus honesty, shell quality, login), archive 4 stale docs
BOOT-PROCESS-SECOND-AUDIT-2026-05-03.md: deep dive into:
- D-Bus implementation honesty (15/19 login1 methods real, not stubs)
- ion shell quality matrix (vs bash/dash)
- Login prompt completeness (getty→login→ion chain)
- Per-subsystem hardware init status (storage/display/input/network/USB/audio/ACPI)
- Implementation plan Phases F1-F6

Archived 4 completed/deferred plans: GRUB, VFAT, USB-BOOT-INPUT, ZSH-PORTING
2026-05-03 09:37:09 +01:00

43 KiB
Raw Permalink Blame History

VFAT Implementation Plan — Red Bear OS

Date: 2026-04-17 Status: Implemented (Phase 13 complete, Phase 2b complete, Phase 4 deferred to runtime validation) Scope: FAT12/16/32 with LFN (VFAT) — data volumes and ESP only (NOT root filesystem) Reference Implementation: local/recipes/core/ext4d/ (ext4 scheme daemon)

1. Executive Summary

Implement full VFAT support in Red Bear OS: a FAT scheme daemon (fatd) for mounting FAT filesystems at runtime, management tools (mkfs, label, check), installer ESP integration, and runtime auto-mount for USB storage and SD cards.

FAT is not a root filesystem target — RedoxFS and ext4 remain the root options. FAT serves for: EFI System Partitions, USB mass storage, SD cards, and data exchange with other operating systems.

Recommended crate: fatfs v0.3.6 (MIT, 356 stars, already in dependency tree via installer). It provides FAT12/16/32, LFN, formatting, read/write, and no_std support.

Estimated effort: 610 weeks for a complete, tested implementation.

2. Current State

What Exists

Component Location Status
RedoxFS (default root FS) recipes/core/redoxfs/ Stable
ext4 (alternate root FS) local/recipes/core/ext4d/ Scheme daemon + mkfs + installer wired
fatfs crate in installer local/patches/installer/redox.patch Host-side EFI partition formatting only
redox-fatfs library recipes/libs/redox-fatfs/ Commented out, dead code
Bootloader FAT reading recipes/core/bootloader/ Reads RedoxFS only, no FAT
GRUB FAT reading GRUB EFI image GRUB fat module reads ESP
exfat-fuse recipes/wip/fuse/exfat-fuse/ WIP, not compiled

What Is Missing (the gaps this plan fills)

Gap Priority Description
VFAT scheme daemon Critical No fatd scheme for mounting FAT at runtime
FAT block device adapter Critical No adapter bridging Redox block I/O → fatfs traits
FAT management tools High No mkfs.fat, fatlabel, fsck.fat equivalents
Runtime auto-mount High No service to detect and mount FAT block devices
FAT filesystem checker Medium No verification or repair tool

Key Architectural Decision

The ext4d workspace at local/recipes/core/ext4d/source/ is the exact template for this implementation. It demonstrates:

  1. Block device adapterext4-blockdev/ with FileDisk (Linux) + RedoxDisk (Redox)
  2. Scheme daemonext4d/ with full FSScheme via redox_scheme::SchemeSync
  3. Management toolext4-mkfs/ as a standalone binary
  4. Workspace structure — Workspace Cargo.toml, resolver=3, edition=2024
  5. Feature flagsdefault = ["redox"], redox = ["dep:libredox", ...]
  6. Recipetemplate = "custom" with COOKBOOK_CARGO_PATH

3. Implementation Phases

Phase 1: FAT Scheme Daemon (fatd) — 34 weeks

Goal: A working VFAT scheme daemon that can mount and serve FAT filesystems.

1.1 Workspace Setup

Create local/recipes/core/fatd/ workspace mirroring ext4d structure:

local/recipes/core/fatd/
├── recipe.toml                    ← Custom build script
└── source/
    ├── Cargo.toml                 ← Workspace: fat-blockdev, fatd, fat-mkfs, fat-label, fat-check
    ├── fat-blockdev/
    │   ├── Cargo.toml
    │   └── src/
    │       ├── lib.rs             ← Re-exports + FatError type
    │       ├── file_disk.rs       ← FileDisk: std::fs backed (Linux host)
    │       └── redox_disk.rs      ← RedoxDisk: libredox backed (Redox target)
    ├── fatd/
    │   ├── Cargo.toml
    │   └── src/
    │       ├── main.rs            ← Daemon entry: fork, SIGTERM, dispatch
    │       ├── mount.rs           ← Scheme event loop (SchemeSync)
    │       ├── scheme.rs          ← FatScheme: full FSScheme impl
    │       └── handle.rs          ← FileHandle, DirHandle, Handle types
    ├── fat-mkfs/
    │   ├── Cargo.toml
    │   └── src/
    │       └── main.rs            ← Create FAT filesystems
    ├── fat-label/
    │   ├── Cargo.toml
    │   └── src/
    │       └── main.rs            ← Read/write volume labels
    └── fat-check/
        ├── Cargo.toml
        └── src/
            └── main.rs            ← Verify + repair FAT filesystems

Recipe (recipe.toml):

[source]
path = "source"

[build]
template = "custom"
script = """
COOKBOOK_CARGO_PATH=fatd cookbook_cargo
COOKBOOK_CARGO_PATH=fat-mkfs cookbook_cargo
COOKBOOK_CARGO_PATH=fat-label cookbook_cargo
COOKBOOK_CARGO_PATH=fat-check cookbook_cargo
"""

Workspace Cargo.toml:

[workspace]
members = ["fat-blockdev", "fatd", "fat-mkfs", "fat-label", "fat-check"]
resolver = "3"

[workspace.package]
version = "0.1.0"
edition = "2024"
license = "MIT"

[workspace.dependencies]
fatfs = "0.3.6"
fscommon = "0.1.1"
redox_syscall = "0.7.3"
redox-scheme = "0.11.0"
libredox = "0.1.13"
redox-path = "0.3.0"
log = "0.4"
env_logger = "0.11"
libc = "0.2"

Symlink: recipes/core/fatd → ../../local/recipes/core/fatd

1.2 Block Device Adapter (fat-blockdev)

The fatfs crate uses Read + Seek and Read + Write + Seek traits for block device access. We need adapters that wrap Redox's block I/O into these traits.

file_disk.rs (Linux host):

// Wraps std::fs::File to implement Read+Write+Seek
// Identical pattern to ext4-blockdev/src/file_disk.rs
// Uses fscommon::BufStream for caching
pub struct FileDisk { ... }
impl Read for FileDisk { ... }
impl Write for FileDisk { ... }
impl Seek for FileDisk { ... }

redox_disk.rs (Redox target, feature-gated):

// Wraps libredox fd to implement Read+Write+Seek
// Uses syscall::call::open/read/write/lseek/fstat
// Pattern from ext4-blockdev/src/redox_disk.rs
pub struct RedoxDisk {
    fd: usize,
    size: u64,  // from fstat
}
impl Read for RedoxDisk { ... }
impl Write for RedoxDisk { ... }
impl Seek for RedoxDisk { ... }

Critical detail: Wrap the disk in fscommon::BufStream for performance — fatfs does no internal caching and performs poorly without buffering.

let disk = RedoxDisk::open(disk_path)?;
let buf_disk = fscommon::BufStream::new(disk);
let fs = fatfs::FileSystem::new(buf_disk, fatfs::FsOptions::new())?;

1.3 VFAT Scheme Daemon (fatd)

Architecture: Single fatfs::FileSystem instance per daemon process. The fatfs crate is NOT safe for concurrent access from multiple FileSystem objects on the same device. One daemon = one mounted filesystem = one FileSystem instance.

handle.rs — Handle types:

pub enum Handle {
    File(FileHandle),
    Directory(DirectoryHandle),
    SchemeRoot,
}

pub struct FileHandle {
    path: String,
    offset: u64,
    flags: usize,
}

pub struct DirectoryHandle {
    path: String,
    entries: Vec<DirEntryInfo>,  // cached readdir results
    offset: usize,
    flags: usize,
}

Key difference from ext4d: fatfs does not have persistent file handles like rsext4's OpenFile. Files must be re-opened on each read/write operation. The FileHandle stores the path and offset, and the scheme re-opens the file on each read/write call.

scheme.rs — FatScheme implementing SchemeSync:

Required methods and their fatfs mapping:

SchemeSync method fatfs operation
scheme_root() Return SchemeRoot handle
openat() fs.root_dir().open_dir(path) or open_file(path)
read() Re-open file, seek to offset, file.read(buf)
write() Re-open file, seek to offset, file.write(buf)
fsize() Re-open file, file.len()
fstat() dir.iter().find() for entry, construct Stat
fstatvfs() fs.stats() for block/free counts
getdents() dir.iter() collect entries, serve from handle cache
ftruncate() Re-open file, file.truncate()
fsync() file.flush()
unlinkat() dir.remove(name) or dir.remove_dir(name)
fcntl() Return handle flags
fpath() Return mounted_path + handle path
on_close() Remove from handle map

Permission mapping: FAT has limited permissions (read-only, hidden, system, archive). Map to Unix permissions:

  • Read-only attribute → mode & !0o222
  • Otherwise → 0o644 for files, 0o755 for directories
  • Owner/group always 0 (FAT has no ownership concept)
  • Timestamps from FAT directory entry (2-second precision, date range 19802107)

Error mapping (fatfs error → syscall error):

fn fat_error(err: fatfs::Error<impl std::fmt::Debug>) -> syscall::error::Error {
    match err {
        fatfs::Error::NotFound => Error::new(ENOENT),
        fatfs::Error::AlreadyExists => Error::new(EEXIST),
        fatfs::Error::InvalidInput => Error::new(EINVAL),
        fatfs::Error::IsDirectory => Error::new(EISDIR),
        fatfs::Error::NotDirectory => Error::new(ENOTDIR),
        fatfs::Error::DirectoryNotEmpty => Error::new(ENOTEMPTY),
        fatfs::Error::WriteZero => Error::new(ENOSPC),
        fatfs::Error::UnexpectedEof => Error::new(EIO),
        _ => Error::new(EIO),
    }
}

main.rs — Daemon lifecycle:

  • Parse args: fatd [--no-daemon] <disk_path> <mountpoint>
  • Fork (optional daemonization)
  • Install SIGTERM handler for clean unmount
  • Open block device → create BufStream → fatfs::FileSystem::new()
  • Call mount::mount() to register scheme and enter event loop
  • On SIGTERM: fs.unmount() (or just drop — fatfs flushes on drop)

mount.rs — Event loop (identical pattern to ext4d mount.rs):

  • Socket::create()
  • register_sync_scheme(&socket, mountpoint, &mut scheme)
  • Loop: socket.next_request(SignalBehavior::Restart) → dispatch to scheme
  • On exit: scheme.cleanup() for clean unmount

1.4 LFN Support

The fatfs crate handles LFN transparently when the lfn feature is enabled:

fatfs = { version = "0.3.6", default-features = false, features = ["lfn", "alloc"] }

This provides:

  • Long filename read via DirEntry::file_name() (returns full long name)
  • Long filename write on Dir::create_file() and Dir::create_dir()
  • Automatic 8.3 short name generation (e.g., "MYLONG~1.TXT")
  • LFN checksum computation (handled internally)

No special LFN code needed in the scheme daemonfatfs abstracts it away. The scheme daemon just passes filenames through.

1.5 FAT12/16/32 Auto-Detection

fatfs::FileSystem::new() automatically detects FAT12, FAT16, or FAT32 based on the BPB (BIOS Parameter Block) in the first sector. No explicit type selection needed.

fatfs::format_volume() with FormatVolumeOptions::new() auto-selects FAT type based on volume size:

  • < 16 MB → FAT12 (or FAT16)
  • 16 MB 32 MB → FAT16
  • 32 MB → FAT32

Explicit type selection: FormatVolumeOptions::new().fat_type(FatType::Fat32).

Phase 2: Management Tools — 23 weeks

2.1 fat-mkfs — Create FAT Filesystems

Binary: fat-mkfs <device> [options]

Options:

  • -F <12|16|32> — Force FAT type (default: auto)
  • -n <label> — Volume label (max 11 chars)
  • -s <sectors_per_cluster> — Cluster size
  • -r <reserved_sectors> — Reserved sector count
  • -f <num_fats> — Number of FATs (default: 2)

Implementation:

let disk = FileDisk::open(device)?;
let options = fatfs::FormatVolumeOptions::new()
    .fat_type(fat_type)
    .volume_label(label);
fatfs::format_volume(&mut disk, options)?;

Also: fat-mkfs should be usable on the build host for creating test images and EFI System Partitions during development.

2.2 fat-label — Read/Write Volume Labels

Binary: fat-label <device> [new_label]

  • Without new_label: print current volume label
  • With -s "LABEL": set volume label (max 11 chars, uppercase)
  • With -s "": clear volume label

Current status: Read mode complete and tested. Write mode in progress (direct BPB modification since fatfs v0.3 lacks set_volume_label()).

Implementation for write:

// Read: fs.volume_label() returns String (works)
// Write: direct BPB modification at offset 43 (FAT12/16) or 71 (FAT32)
// FAT type detection: root_entry_count == 0 && fat_size_32 != 0 → FAT32
// Label padded to 11 bytes with 0x20, uppercased

2.3 fat-check — FAT Filesystem Checker

Phase 2a: Verifier (read-only) Complete

Checks performed (no modifications):

  1. BPB validation — sector size, cluster size, FAT size consistency
  2. Directory structure — valid entries, tree walking
  3. Cluster stats — total/free/used clusters via fatfs
  4. Boot sector signature — 0x55 0xAA check
  5. FAT type detection — FAT12/16/32 classification

Output: report of all issues found, severity (info/warning/error). Tested against clean and corrupt images.

Phase 2b: Safe Repairs Complete

Safe repairs (non-destructive, --repair flag):

  1. Dirty flag handling — clear dirty bit on FAT12/16/32 cluster 1 entries
  2. FSInfo repair — recount free clusters, update FSInfo sector
  3. Lost cluster recovery — reclaim lost clusters (mark free in FAT)
  4. Orphaned LFN cleanup — remove LFN entries without matching SFN

Exit codes: 0 = clean, 1 = errors remain, 2 = repairs were made.

Out of scope for initial version:

  • Cross-linked file repair
  • Directory entry reconstruction
  • Deep FAT table repair
  • File data recovery

Phase 3: Installer & Build Integration — 1 week

3.1 Installer ESP Access (already works)

The installer already uses fatfs to format and write the EFI partition. This is host-side and already functional. No changes needed for basic ESP creation.

3.2 Recipe Configuration

Add fatd and tools to relevant config files:

# config/desktop.toml or redbear-desktop.toml
fatd = {}
fat-mkfs = {}
fat-label = {}
fat-check = {}

3.3 Init Service

Create a Redox init service for auto-mounting FAT volumes. Follow the pattern in config/redbear-device-services.toml and config/redbear-netctl.toml: services are defined as [[files]] TOML blocks with paths under /usr/lib/init.d/, using the [unit] + [service] format with cmd, args, and type fields.

File: config/redbear-device-services.toml (append to existing file)

[[files]]
path = "/usr/lib/init.d/15_fatd.service"
data = """
[unit]
description = "FAT filesystem auto-mount daemon"
requires_weak = [
    "00_pcid-spawner.service",
]

[service]
cmd = "fatd"
args = ["disk/live-virtio", "fat-live"]
type = { scheme = "fat-live" }
"""

For runtime auto-mount of removable devices (USB, SD), a separate redbear-automount service would watch /scheme/disk/ for new block devices, probe for FAT signatures, and launch fatd instances dynamically. This follows the same [unit]/[service] TOML pattern. Reference implementation: config/redbear-device-services.toml lines 1426 (05_firmware-loader.service uses type = { scheme = "firmware" }).

Phase 4: Runtime Auto-Mount & Desktop Integration — 12 weeks

4.1 Block Device Discovery

When a block device appears (USB insertion, SD card detect), a service should:

  1. Detect new block device via /scheme/disk/ or equivalent
  2. Probe for FAT filesystem (read first sector, check for valid BPB signature)
  3. If FAT detected, launch fatd <device> <scheme_name>
  4. The FAT filesystem becomes accessible at /scheme/<scheme_name>/

4.2 Unmount Handling

On device removal or system shutdown:

  1. Send SIGTERM to fatd daemon
  2. Daemon flushes and drops fatfs::FileSystem (auto-flush on drop)
  3. Scheme is unregistered

4.3 Desktop File Manager Integration

For the KDE Plasma desktop path (Phases 34 of the desktop plan):

  • Solid/UDisks2 backend recognizes mounted FAT volumes
  • Volume labels displayed in file manager
  • "Safely remove" triggers clean unmount via SIGTERM to fatd

Phase 5: Testing & Hardening — 1 week

5.1 Unit Tests

Test against FAT images created with fat-mkfs:

  • Create/read/write/delete files with short names
  • Create/read/write/delete files with long names (LFN)
  • Create/remove directories
  • Rename files and directories
  • Read filesystem stats (fstatvfs)
  • Handle full filesystem (ENOSPC)
  • Handle read-only filesystem (EROFS)

5.2 Edge Cases

From the fatfs crate's bug history and FAT specification:

  • 0xE5 first byte: Short names starting with 0xE5 are stored as 0x05
  • FSInfo unreliability: Never trust FSInfo free count blindly
  • FAT32 upper 4 bits: Must be preserved when writing FAT entries
  • LFN checksum: Must verify against SFN to detect orphaned entries
  • Max path length: FAT LFN max is 255 characters
  • Case sensitivity: FAT is case-insensitive, must normalize lookups
  • Fragmentation: Large fragmented files should still read/write correctly
  • Timestamp precision: 2-second granularity, 19802107 date range

5.3 Compatibility Testing

Test with FAT images from:

  • Windows 10/11 formatted USB drives
  • Linux mkfs.fat created images
  • macOS formatted FAT32 SD cards
  • Digital camera FAT32 SD cards (often fragmented)
  • Large FAT32 volumes (128 GB+ SD cards)

4. Task Breakdown for Delegation

Wave 1: Foundation (Phase 1.11.2) — Parallel

Task Category Effort Dependencies QA
Create workspace structure, Cargo.toml, recipe.toml, symlinks quick 30 min None cargo check --target x86_64-unknown-redox succeeds from workspace root; ls -la recipes/core/fatd shows valid symlink
Implement fat-blockdev FileDisk (Linux) unspecified-low 2 hr Workspace Unit test: create 1 MB temp file, open via FileDisk, read 512 bytes at offset 0, verify zero-filled; seek to offset 1024, write pattern, read back, verify match
Implement fat-blockdev RedoxDisk (Redox, feature-gated) unspecified-low 2 hr Workspace cargo check --target x86_64-unknown-redox --features redox succeeds; cargo check (Linux, no redox feature) also succeeds

Wave 2: Scheme Daemon (Phase 1.31.5) — Sequential on Wave 1

Task Category Effort Dependencies QA
Implement handle.rs (FileHandle, DirHandle, Handle) unspecified-low 1 hr Wave 1 cargo check passes; handle.path() returns correct path; handle.flags() returns O_RDONLY/O_WRONLY/O_RDWR as set
Implement scheme.rs (FatScheme with SchemeSync) unspecified-high 23 days Wave 1 Integration test: create 10 MB FAT32 image via fatfs::format_volume(), mount via FatScheme, openat a file, write 100 bytes, read back 100 bytes, verify match; getdents on root dir returns "." and ".."; fstat returns st_mode with S_IFREG; fstatvfs returns non-zero f_blocks
Implement mount.rs (event loop) unspecified-low 2 hr scheme.rs cargo check passes; verify event loop compiles with register_sync_scheme and socket.next_request()
Implement main.rs (daemon lifecycle) unspecified-low 2 hr mount.rs Build fatd binary: cargo build --bin fatd; run fatd --help shows usage; run fatd test.img test-scheme with a FAT32 test image, verify scheme registered at /scheme/test-scheme/
LFN integration testing deep 1 day scheme.rs Create file named "This Is A Very Long Filename.txt" (33 chars), read it back, verify full name returned; create file with 200-char name, verify LFN entries; create file with Unicode name "café_日本語.txt", verify round-trip
FAT12/16/32 auto-detection testing deep 1 day scheme.rs Create three images (FAT12: 1 MB, FAT16: 16 MB, FAT32: 64 MB) via fat-mkfs, mount each via FatScheme, write and read a file on each, verify all three succeed

Wave 3: Management Tools (Phase 2) — Parallel after Wave 1

Task Category Effort Dependencies QA
Implement fat-mkfs binary unspecified-low 3 hr fat-blockdev Create 64 MB image: fat-mkfs /tmp/test.img; verify: fatfs::FileSystem::new() can mount it; verify: fat-mkfs -F 32 /tmp/test32.img creates FAT32; verify: fat-mkfs -n TESTVOL /tmp/test.img sets label
Implement fat-label binary unspecified-low 3 hr fat-blockdev After fat-mkfs -n TESTVOL /tmp/test.img: fat-label /tmp/test.img prints "TESTVOL"; fat-label /tmp/test.img NEWNAME succeeds; fat-label /tmp/test.img prints "NEWNAME"
Implement fat-check verifier (Phase 2a) unspecified-high 1 week fat-blockdev Run on clean image: exits 0, reports "filesystem clean"; corrupt FAT chain (write bad entry manually): fat-check detects and reports "cross-linked files" or "lost clusters"; run on image with orphaned LFN: reports "orphaned LFN entries"
Implement fat-check safe-repair (Phase 2b) unspecified-high 1 week Phase 2a Corrupt FSInfo free count: fat-check --repair fixes it, re-run verifier exits 0; set dirty bit: fat-check --repair clears it

Wave 4: Integration (Phase 34) — Sequential on Waves 23

Task Category Effort Dependencies QA
Add fatd to config TOMLs quick 15 min Wave 2 grep fatd config/redbear-desktop.toml shows fatd = {}; grep fatd config/redbear-full.toml shows fatd = {}
Create init service for FAT mounting unspecified-low 3 hr Wave 2 Service file exists at /usr/lib/init.d/15_fatd.service with [unit] and [service] sections; cmd = "fatd" present; type = { scheme = "..." } present; follows config/redbear-device-services.toml pattern exactly
Build + test full integration deep 2 days Waves 23 make all CONFIG_NAME=redbear-desktop succeeds; boot in QEMU: fatd --help runs; create FAT image on host, attach to QEMU VM, verify fatd can mount it at /scheme/fat-test/
Edge case + compatibility testing deep 3 days Wave 2 Test images: Windows-formatted FAT32 USB (4 GB), Linux mkfs.fat FAT16 (128 MB), macOS FAT32 SD (32 GB); all mount and read/write correctly via fatd

5. Dependency Graph

Phase 1.1 (workspace) ──┬──→ Phase 1.2 (blockdev) ──┬──→ Phase 1.3 (scheme daemon)
                         │                            │
                         │                            ├──→ Phase 2.1 (fat-mkfs)
                         │                            ├──→ Phase 2.2 (fat-label)
                         │                            └──→ Phase 2.3a (fat-check verify)
                         │                                       │
                         │                                       └──→ Phase 2.3b (fat-check repair)
                         │
Phase 1.3 ──────────────────────────────────────────→ Phase 3 (config/integration)
                                                          │
Phase 3 ──────────────────────────────────────────────→ Phase 4 (auto-mount)
                                                          │
Phase 4 + Phase 2 ───────────────────────────────────→ Phase 5 (testing)

Critical path: Phase 1.1 → 1.2 → 1.3 → Phase 3 → Phase 4 → Phase 5

Parallel opportunities: Phase 2 tools can start after Phase 1.2 (blockdev), overlapping with Phase 1.3 (scheme daemon).

6. Technical Notes

FAT Limitations in Unix Context

Since FAT is data/ESP only (not root), most Unix metadata issues are irrelevant:

FAT Limitation Impact for data volumes Mitigation
No Unix permissions Files appear as 0o644/0o755 Acceptable for data volumes
No symlinks Cannot store symlinks Data volumes don't need them
No device nodes Cannot store /dev entries Data volumes don't need them
No ownership All files appear uid=0/gid=0 Acceptable for data volumes
2s timestamp precision Some timestamps rounded Acceptable for data volumes
255 char filename max No path component > 255 chars Sufficient for data use
Case-insensitive Lookups must normalize Scheme daemon handles this
No sparse files Holes consume disk space Acceptable for data volumes
Max file size: 4 GB - 1 Large files may not fit Acceptable for most use

fatfs Crate Feature Configuration

[dependencies]
# For the scheme daemon (full features)
fatfs = { version = "0.3.6", default-features = false, features = ["lfn", "alloc", "log"] }

# For fat-mkfs (formatting support)
fatfs = { version = "0.3.6", default-features = false, features = ["lfn", "alloc"] }

# For fat-check (read-only)
fatfs = { version = "0.3.6", default-features = false, features = ["lfn", "alloc"] }

Features available:

  • lfn — VFAT long filename support (REQUIRED)
  • alloc — Use alloc crate for dynamic allocation (REQUIRED for no_std)
  • log — Logging via log crate (optional, useful for debugging)
  • chrono — Timestamp creation via chrono (optional, not needed with our time adapter)
  • std — Use std library (NOT used — we want no_std compatibility)

Block Caching Strategy

Without caching, fatfs performs one I/O operation per metadata read — extremely slow. The recommended approach:

use fscommon::BufStream;

// Wrap raw disk in buffered stream
let disk = RedoxDisk::open(disk_path)?;
let buf_disk = BufStream::new(disk);

// fatfs operates on the buffered stream
let fs = fatfs::FileSystem::new(buf_disk, fatfs::FsOptions::new())?;

BufStream provides a configurable read/write buffer (default 512 bytes, should be increased to 4096 or larger for better throughput on block devices).

Scheme Name Convention

Following the ext4d pattern:

  • fatd /scheme/disk/0 disk-fat-0 registers scheme disk-fat-0
  • Access at /scheme/disk-fat-0/path/to/file
  • Multiple FAT volumes: disk-fat-0, disk-fat-1, etc.

Alternative: Use a single fat scheme namespace and multiplex based on the device path embedded in the mount command.

Concurrency Model

fatfs::FileSystem is NOT thread-safe. The scheme daemon handles this by:

  1. Single-threaded event loop (same as ext4d)
  2. One FileSystem instance per daemon process
  3. Sequential request processing via socket.next_request()
  4. No internal mutability tricks needed

This matches the Redox scheme model — requests are serialized by the kernel.

7. Risks and Mitigations

Risk Probability Impact Mitigation
fatfs crate bug in LFN handling Low Medium v0.3.6 has known fixes; test thoroughly
Performance without caching High High BufStream wrapper is mandatory, not optional
FAT corruption on unsafe removal Medium High Write-fat-sync on flush; journal not possible on FAT
FAT32 max file size (4 GB) Low Low Document limitation; return EFBIG for oversized writes
fatfs API doesn't support needed operations Low Medium Fall back to direct BPB/FAT manipulation
Feature flag conflicts with no_std Low Medium Test both Linux and Redox builds in CI

8. Files to Create

local/recipes/core/fatd/
├── recipe.toml
└── source/
    ├── Cargo.toml                              ← Workspace root
    ├── fat-blockdev/
    │   ├── Cargo.toml
    │   └── src/
    │       ├── lib.rs
    │       ├── file_disk.rs
    │       └── redox_disk.rs
    ├── fatd/
    │   ├── Cargo.toml
    │   └── src/
    │       ├── main.rs
    │       ├── mount.rs
    │       ├── scheme.rs
    │       └── handle.rs
    ├── fat-mkfs/
    │   ├── Cargo.toml
    │   └── src/
    │       └── main.rs
    ├── fat-label/
    │   ├── Cargo.toml
    │   └── src/
    │       └── main.rs
    └── fat-check/
        ├── Cargo.toml
        └── src/
            └── main.rs

recipes/core/fatd → ../../local/recipes/core/fatd  (symlink, matching ext4d pattern)

config/redbear-desktop.toml  ← add fatd, fat-mkfs, fat-label, fat-check packages
config/redbear-full.toml     ← same
config/desktop.toml          ← add fatd (upstream or local override)

9. Estimated Timeline

Phase Duration Deliverable
Phase 1: FAT scheme daemon 34 weeks fatd binary, mount/unmount FAT volumes
Phase 2: Management tools 23 weeks fat-mkfs, fat-label, fat-check
Phase 3: Build integration 1 week Config entries, recipe symlinks
Phase 4: Auto-mount service 12 weeks Block device detection, auto-mount
Phase 5: Testing & hardening 1 week Edge cases, compatibility
Total 811 weeks Full VFAT support

Phase 2 can overlap with Phase 1.3, reducing wall-clock time to approximately 610 weeks with parallel execution.

10. Success Criteria

  • fatd mounts FAT12, FAT16, and FAT32 filesystems as Redox schemes (compiles, links on Redox target only)
  • Read/write files with both short (8.3) and long (LFN) filenames
  • Create/delete files and directories
  • Rename files and directories
  • Correctly report filesystem stats (fstatvfs)
  • fat-mkfs creates valid FAT filesystems usable by Windows/Linux/macOS
  • fat-label reads and writes volume labels (BPB + root-directory entry updated)
  • fat-check detects and reports FAT filesystem errors (verify + repair mode)
  • Integration with Redox config system (TOML)
  • (deferred: not on desktop critical path) Works on both Linux host (management tools ) and Redox target (fatd untested — requires runtime)
  • No unwrap()/expect() in library/driver code
  • (deferred: not on desktop critical path) Runtime auto-mount service (Phase 4 deferred to runtime validation)
  • (deferred: not on desktop critical path) Runtime validation of fatd on Redox target (requires QEMU/bare metal boot)

11. Test Results

Edge Case Testing (2026-04-17, Linux host)

Test Result Notes
Corrupt boot signature (0x00 0x00) Detected Exit 1, reports "invalid boot sector signature"
Zero bytes_per_sector Detected Exit 1, reports "invalid bytes per sector: 0"
Tiny FAT12 (512KB) Clean Auto-detected as FAT16 by fat-check (fatfs classifies small volumes)
Large FAT32 (256MB) Clean 516214 clusters, cluster size 512 bytes
Very large FAT32 (1GB) Clean 261631 clusters, cluster size 4096 bytes (auto-selected)
No volume label Reports "NO NAME"
Max length label (11 chars) "12345678901" round-trips correctly
Too-long label (12 chars) Rejected Exit 1, "volume label too long"
Auto-detect FAT type (32MB) Selected FAT16 automatically
Cross-platform (Linux mkfs.fat FAT32) ⚠️ Partial fatfs v0.3.6 rejects small mkfs.fat images (non-zero total_sectors_16 for FAT32 — fatfs strictness)
FAT12 (1MB) Clean mkfs + check pass
FAT16 (16MB) Clean mkfs + check pass
FAT32 (64MB) Clean mkfs + check pass
File creation on all FAT types 7 files + 1 dir created via fatfs on FAT12/16/32, all verified clean
Label write on populated image No data corruption after label change, files still accessible
FSInfo repair (FAT32) Detected mismatch (0xFFFFFFFF vs actual), repaired, re-check clean
Repair on clean image (FAT16) "Repaired: nothing needed", exit 0
Directory count accuracy Fixed: files: 7, directories: 1 (was 0/0 due to tuple borrowing bug)

Known limitation: fatfs v0.3.6 strictly requires total_sectors_16 == 0 for FAT32, but Linux's mkfs.fat may set it non-zero for small FAT32 images. This is a fatfs crate strictness issue, not a Red Bear code bug. Files created by fat-mkfs are always accepted.

12. Quality Assessment (2026-04-17)

12.1 Code Metrics

Crate Lines Files unwrap() expect() TODO/FIXME #[cfg(test)]
fat-blockdev 134 3 0 0 0 0
fatd 1376 4 0 0 0 25 tests
fat-mkfs 158 1 0 0 0 0
fat-label 436 1 0 0 0 7 tests
fat-check 1399 1 0 0 0 28 tests
Total 3503 10 0 0 0 60 tests

12.2 Anti-Patterns Found

Severity File Line Issue
Medium fat-blockdev/src/file_disk.rs 17 Fixed: logs warning
Medium fat-blockdev/src/redox_disk.rs 26,32,38,50 Fixed: preserves error details
Medium fat-label/src/main.rs 281-291 Fixed: warns on full root dir
Low fatd/src/scheme.rs 633 handle.flags().unwrap_or(O_RDONLY) silently defaults to read-only
Low fatd/src/scheme.rs 214-220 Fixed: dead code removed
Low fatd/src/main.rs 98,106,113 let _ = pipe.write_all(...) silently ignores status pipe errors
Low fat-check/src/main.rs 484 Fixed: FAT12 dirty flag implemented
Low fat-mkfs/src/main.rs 72-82 Fixed: pre-zero with 64K chunks

12.3 Functional Gaps vs Reference (ext4d)

Operation ext4d fatd Notes
linkat (hard links) FAT doesn't support hard links — gap is by design
renameat frename via fatfs Dir::rename() — cross-directory rename supported
symlinkat/readlinkat FAT doesn't support symlinks — gap is by design
refresh_file_handle ext4d re-opens after truncate; fatd just seeks
Directory non-empty check unlinkat checks for entries before AT_REMOVEDIR
Real inode numbers ⚠️ fatd uses synthetic hash-based inodes
st_nlink ⚠️ Hardcoded to 1 (files) or 2 (dirs)
fsync scope Full FS Single file ext4d syncs entire filesystem

12.4 Error Handling Quality

Pattern: CLI tools use unwrap_or_else(\|e\| { eprintln!(...); process::exit(1) }) consistently. Daemon code uses ? operator and map_err(fat_error) for syscall error conversion.

Issue: fat_error() in scheme.rs:811-834 uses string matching on io::Error descriptions to map to syscall error codes. This is fragile — error message changes in fatfs would break it. ext4d's ext4_error() is simpler and more robust.

12.5 Missing Features vs Standard Linux Tools

fat-mkfs vs mkfs.fat

Option mkfs.fat fat-mkfs Notes
Cluster size (-s) -c <sectors> option, power-of-2 validation
Reserved sectors (-f)
Number of FATs Hardcoded to 2
Bytes per sector (-S) Hardcoded to 512
Drive number
Backup boot sector
Media descriptor Uses fatfs default (0xF8)
Bad cluster check (-c)
Invariant mode (-I)
Pre-zeroing of image 64K-chunk zero-fill

fat-check vs fsck.fat

Check fsck.fat fat-check Severity
Media descriptor byte (BPB:21) Medium
FAT type string (BPB:54-61) Low
Cross-linked files Medium
Duplicate directory entries Medium
Invalid volume label chars Low
Timestamp validation Low
FSInfo reserved bits Medium
FAT32 fs_version field Medium
Automatic repair (-a) Low
FAT12 dirty flag Bits 11:10 of cluster 1 entry

12.6 Style Consistency

  • Follows ext4d reference patterns closely (workspace layout, scheme structure, handle types)
  • Consistent naming: snake_case functions, PascalCase types
  • Error messages prefixed with binary name (fat-label:, fat-check:, etc.)
  • rustfmt.toml at workspace root: max_width=100, brace_style=SameLineWhere
  • 60 unit tests across 3 crates (25 scheme + 7 label + 28 check) + 13+ integration edge cases

12.7 Build Integration Assessment

Check Status Notes
recipe.toml correctness Custom template, COOKBOOK_CARGO_PATH for all 4 binaries
Symlink recipes/core/fatd Points to ../../local/recipes/core/fatd
redbear-device-services.toml Packages + init service at /usr/lib/init.d/15_fatd.service
Included in redbear-desktop.toml Via include chain
Included in redbear-full.toml Via include chain
Included in redbear-minimal.toml Via include chain
Included in redbear-full.toml Via include chain
Included in redbear-wayland.toml Does NOT include redbear-device-services.toml
cargo check passes All crates check clean
cargo build --release (tools) fat-mkfs, fat-label, fat-check build on Linux
cargo build --release (fatd) ⚠️ Compiles but links only on Redox target (expected)

12.8 Documentation Assessment

Document Accurate Notes
VFAT-IMPLEMENTATION-PLAN.md Status, success criteria, and test results all accurate
local/AGENTS.md FAT section Workspace layout, tool status, limitations documented
Success criteria checkboxes Done items checked, deferred items unchecked
Test results table 13+ edge cases documented with outcomes

12.9 Maturity Rating

Dimension Rating (1-5) Notes
Code correctness 4 Clean error handling, no unwrap/expect in daemon code
Feature completeness 4 Rename + rmdir check + cluster size now implemented
Test coverage 4 60 unit tests + 13+ integration edge cases (helper-level, not end-to-end scheme tests)
Code style 4 Consistent with ext4d reference, clean formatting
Documentation 4 Comprehensive plan, accurate status, known limitations
Build integration 5 Wired into 5/5 configs via redbear-device-services.toml include chain
Error resilience 3 fatfs re-opens on each file access (no persistent handles)
Production readiness 2 Not runtime-tested on Redox; Phase 4 auto-mount deferred

Overall: 3.6/5 (provisional — pending runtime validation on Redox/QEMU). Solid implementation with good test coverage at the helper and tool level. fatd scheme daemon has not been runtime-tested.

12.10 Cleanup Status

# Cleanup Status Detail
1 redox_disk.rs error discarding Done 3 read/write/flush .map_err(|_|...) replaced with .map_err(|e| format!("redox {op}: {e:?}")); seek already had detail
2 file_disk.rs:17 silent failure Done Logs warning instead of silently returning 0
3 fat-label full-root-dir warning Done Both FAT32 and FAT12/16 paths warn when root dir full
4 scheme.rs:214-220 dead code Done Redundant uid==0 check removed
5 Pre-zero image in fat-mkfs Done 64K-chunk zero-fill before format, no sparse files
6 FAT12 dirty flag detection Done Bits 11:10 of cluster 1 entry; detect + repair verified
7 frename support Done Dir::rename() for cross-directory rename, handle path updated post-rename
8 Rmdir non-empty check Done unlinkat checks directory entries before AT_REMOVEDIR
9 Cluster size option in fat-mkfs Done -c <sectors> with power-of-2 validation
10 Unit test suite Done 60 tests across 3 crates (25 scheme + 7 label + 28 check)
11 lfn_checksum overflow fix Done wrapping_add for u8 arithmetic, regression test added

12.11 Remaining Improvements (Deferred)

  1. Runtime validate fatd on QEMU — Boot Red Bear OS, mount a FAT image, perform read/write/rename ops
  2. Evaluate redbear-wayland.toml inclusion — Verified: wayland.toml includes redbear-device-services.toml, so FAT tools are in all 5 configs
  3. handle.flags().unwrap_or(O_RDONLY) — Low severity silent default in fcntl
  4. let _ = pipe.write_all(...) in main.rs — Low severity, hides daemon startup status pipe errors
  5. fsync only flushes single file — Doesn't sync filesystem metadata (by design: fatfs has no journal)
  6. fat_error() string matching — Medium severity; depends on exact fatfs error message text. Low risk on stable fatfs 0.3.6 but fragile across versions

12.12 Independent Audit Results (2026-04-17, 3rd pass)

Three parallel explore agents audited: (A) scheme daemon code quality vs ext4d reference, (B) management tools quality, (C) build integration and documentation accuracy.

Scheme daemon audit (A):

  • fevent error codes: Verified identical to ext4d — NOT a bug (EPERM = operation not supported, EBADF = bad fd)
  • frename permission checks: lookup_parent already enforces PERM_EXEC | PERM_WRITE on both source and destination parents
  • fat_error string matching: Known, documented, low risk on stable fatfs 0.3.6
  • fsync scope: By design — fatfs has no journal, single-file flush is appropriate
  • Handle path update after frename: Correctly implemented with update_path()
  • unlinkat non-empty check: Correct — iterates entries, returns ENOTEMPTY if any non-dot entry found
  • Match arm completeness: All SchemeSync trait methods fully implemented

Management tools audit (B):

  • fat-mkfs: Argument parsing complete (-F, -n, -s, -c), validation correct, pre-zeroing works
  • fat-label: BPB offset calculation correct (43 for FAT12/16, 71 for FAT32), root-dir entry creation verified
  • fat-check: BPB validation thorough, FAT chain walking correct, dirty flag logic correct for all FAT types
  • lfn_checksum: Wrapping arithmetic verified correct with known test vectors
  • Exit codes: 0=clean, 1=errors, 2=repaired — matches fsck conventions
  • Unit test vectors: All verified correct (FAT12/16/32 encoding, round-trip, classification)

Build integration audit (C):

  • All 5/5 redbear configs include redbear-device-services.toml via include chain (including redbear-wayland via wayland.toml)
  • Recipe symlink correct: recipes/core/fatd → ../../local/recipes/core/fatd
  • Workspace Cargo.toml: All 5 crates correctly configured (fixed stale chrono reference)
  • Init service at /usr/lib/init.d/15_fatd.service correct
  • AGENTS.md FAT section: Accurate
  • VFAT-IMPLEMENTATION-PLAN.md Sections 10/12: Accurate

Audit conclusion: No critical or high-severity issues found in implementation code. One medium doc accuracy issue corrected (redox_disk.rs error detail fix was claimed but not persisted — now actually applied). All code spot-checks passed. Remaining items are low severity and documented in Section 12.10.