Skip to content

fat12/16/32: expose attribute flags and timestamps via FileInfo.Sys()#396

Open
luthermonson wants to merge 1 commit into
diskfs:masterfrom
luthermonson:fat-expose-metadata
Open

fat12/16/32: expose attribute flags and timestamps via FileInfo.Sys()#396
luthermonson wants to merge 1 commit into
diskfs:masterfrom
luthermonson:fat-expose-metadata

Conversation

@luthermonson
Copy link
Copy Markdown
Contributor

Refs #301. Sibling of #393 (ext4) — same pattern, applied to all three FAT variants in a single change by leveraging the recent FAT12/16 refactor.

What this does

FileInfo.Sys() previously returned nil for fat12, fat16, and fat32. FAT has no inodes / uid / gid / POSIX permissions — that's a format limit, not a code one — but the directory entry does carry metadata that doesn't fit os.FileMode:

  • Attribute bits: read-only, hidden, system, archive (dirty), volume-label
  • Two extra timestamps: creation (full resolution) and last access (date-only)
  • The starting cluster of the file's chain

This PR introduces fat12.StatT carrying those fields, wires fat12.FileInfo.Sys() to return it, and exposes the same type from fat32 and fat16 via type aliases.

Why this lives in fat12 (not three separate copies)

The upstream FAT12/16 support PR (#358) restructured the FAT codebase: the implementation lives in filesystem/fat12, and filesystem/fat32 + filesystem/fat16 are thin wrappers that embed *fat12.FileSystem and override Type(). Public API parity with the old fat32 surface is preserved through type aliases — type SectorSize = fat12.SectorSize already exists in fat32.go.

This PR follows that same pattern for StatT:

// filesystem/fat12/fileinfo.go — single source of truth
type StatT struct { ReadOnly, Hidden, System, Archive, VolumeLabel bool; CreateTime, AccessTime time.Time; Cluster uint32 }

// filesystem/fat32/fat32.go — re-export
type StatT = fat12.StatT

// filesystem/fat16/fat16.go — re-export
type StatT = fat12.StatT

Concrete benefits:

  1. One implementation, three packages covered. The directoryEntry.stat() helper that builds the StatT is defined once in fat12. fat16 and fat32 inherit the populated Sys() automatically.
  2. Callers keep their natural import. Someone using fat32 writes fi.Sys().(*fat32.StatT) and it works; someone using fat12 writes fi.Sys().(*fat12.StatT) and it works. They get the same *StatT value because the types are aliases (not separate types with the same field shape) — so a downstream library that wants to handle any FAT variant can type-assert once against *fat12.StatT and it'll match all three.
  3. No drift risk. If FAT16 later needs a new attribute exposed, it goes in fat12.StatT and the other two get it for free. The alternative — three separate StatT structs — would inevitably diverge.
  4. Mirrors the upstream architectural choice. fat16/fat32 are already wrappers around fat12.FileSystem; their public types are already aliases. Adding StatT as an alias is consistent with SectorSize, not a new pattern.

Fields

All populated from data parseDirEntries already extracts today; no new parsing.

Field Source
ReadOnly bool directoryEntry.isReadOnly
Hidden bool directoryEntry.isHidden
System bool directoryEntry.isSystem
Archive bool directoryEntry.isArchiveDirty
VolumeLabel bool directoryEntry.isVolumeLabel
CreateTime time.Time directoryEntry.createTime
AccessTime time.Time directoryEntry.accessTime (date-only per FAT spec)
Cluster uint32 directoryEntry.clusterLocation

Tests

New TestFat12StatSys writes a file to a freshly-created FAT12 image, asserts Sys() returns *fat12.StatT, Cluster >= 2 (data clusters start at 2), VolumeLabel == false, and that no unexpected attribute bits are set on a regular file. Coverage extends to fat16/fat32 automatically because they reuse the same directoryEntry.Info() and File.Stat() code paths.

FAT FileInfo.Sys() previously returned nil. Introduce fat12.StatT
carrying the FAT directory-entry metadata that has no analogue in
os.FileMode: read-only/hidden/system/archive/volume-label attribute
bits, creation and access timestamps, and the starting cluster of the
file's chain. Wire fat12.FileInfo.Sys() to populate and return a *StatT
from data already parsed in parseDirEntries.

Because fat12 is the shared base for FAT12, FAT16, and FAT32, this
single change is reflected through type aliases in the fat16 and fat32
packages: fat32.StatT = fat12.StatT and fat16.StatT = fat12.StatT.

Refs diskfs#301.
@luthermonson luthermonson force-pushed the fat-expose-metadata branch from 9c3bfc5 to e50b9c1 Compare May 14, 2026 05:30
Copy link
Copy Markdown
Collaborator

@deitch deitch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Needs to be removed from "Draft" to go ahead.

@luthermonson luthermonson marked this pull request as ready for review May 14, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants