diff --git a/filesystem/fat12/directoryentry.go b/filesystem/fat12/directoryentry.go index 78a71cea..8d3735d3 100644 --- a/filesystem/fat12/directoryentry.go +++ b/filesystem/fat12/directoryentry.go @@ -55,9 +55,23 @@ func (de *directoryEntry) Info() (iofs.FileInfo, error) { shortName: de.fullShortName(), size: int64(de.fileSize), isDir: de.isSubdirectory, + sys: de.stat(), }, nil } +func (de *directoryEntry) stat() *StatT { + return &StatT{ + ReadOnly: de.isReadOnly, + Hidden: de.isHidden, + System: de.isSystem, + Archive: de.isArchiveDirty, + VolumeLabel: de.isVolumeLabel, + CreateTime: de.createTime, + AccessTime: de.accessTime, + Cluster: de.clusterLocation, + } +} + func (de *directoryEntry) nameMatches(name string) bool { return strings.EqualFold(de.filenameLong, name) || strings.EqualFold(de.fullShortName(), name) } diff --git a/filesystem/fat12/fat12_test.go b/filesystem/fat12/fat12_test.go index b2dab0c7..b7abd7d6 100644 --- a/filesystem/fat12/fat12_test.go +++ b/filesystem/fat12/fat12_test.go @@ -408,6 +408,32 @@ func TestFat12Stat(t *testing.T) { } } +func TestFat12StatSys(t *testing.T) { + _, fs := createFAT12(t, "SYSTEST") + writeFile(t, fs, "/sys.txt", []byte("sys")) + + info, err := fs.Stat("sys.txt") + if err != nil { + t.Fatalf("Stat: %v", err) + } + stat, ok := info.Sys().(*fat12.StatT) + if !ok { + t.Fatalf("Sys() did not return *fat12.StatT, got %T", info.Sys()) + } + if stat == nil { + t.Fatal("StatT is nil") + } + if stat.Cluster < 2 { + t.Errorf("Cluster = %d, expected >= 2 (data clusters start at 2)", stat.Cluster) + } + if stat.VolumeLabel { + t.Error("regular file should not have VolumeLabel set") + } + if stat.Hidden || stat.System || stat.ReadOnly { + t.Errorf("unexpected attribute bits set: Hidden=%v System=%v ReadOnly=%v", stat.Hidden, stat.System, stat.ReadOnly) + } +} + // ── ReadFile (fs.ReadFileFS) ────────────────────────────────────────────────── func TestFat12ReadFile(t *testing.T) { diff --git a/filesystem/fat12/file.go b/filesystem/fat12/file.go index fea4749e..6678c9ea 100644 --- a/filesystem/fat12/file.go +++ b/filesystem/fat12/file.go @@ -30,6 +30,7 @@ func (fl *File) Stat() (iofs.FileInfo, error) { shortName: fl.fullShortName(), size: int64(fl.fileSize), isDir: fl.isSubdirectory, + sys: fl.stat(), }, nil } diff --git a/filesystem/fat12/fileinfo.go b/filesystem/fat12/fileinfo.go index 8b64a0ae..150121dc 100644 --- a/filesystem/fat12/fileinfo.go +++ b/filesystem/fat12/fileinfo.go @@ -14,6 +14,21 @@ type FileInfo struct { shortName string size int64 isDir bool + sys *StatT +} + +// StatT carries FAT-specific metadata returned by FileInfo.Sys(). FAT12/16/32 +// have no inodes, uid/gid, or POSIX permissions, but they do carry attribute +// flags and additional timestamps that os.FileMode doesn't represent. +type StatT struct { + ReadOnly bool + Hidden bool + System bool + Archive bool + VolumeLabel bool + CreateTime time.Time + AccessTime time.Time + Cluster uint32 } // IsDir abbreviation for Mode().IsDir() @@ -63,9 +78,9 @@ func (fi FileInfo) Size() int64 { return fi.size } -// Sys underlying data source - not supported yet and so will return nil +// Sys returns *StatT with FAT-specific metadata. // //nolint:gocritic // we need this to comply with fs.FileInfo func (fi FileInfo) Sys() interface{} { - return nil + return fi.sys } diff --git a/filesystem/fat16/fat16.go b/filesystem/fat16/fat16.go index 4a93ec95..57854b70 100644 --- a/filesystem/fat16/fat16.go +++ b/filesystem/fat16/fat16.go @@ -20,6 +20,9 @@ type FileSystem struct { // interface guard var _ filesystem.FileSystem = (*FileSystem)(nil) +// StatT is an alias for fat12.StatT, the metadata returned by FileInfo.Sys(). +type StatT = fat12.StatT + // Type returns filesystem.TypeFat16, overriding fat12.FileSystem.Type(). func (fs *FileSystem) Type() filesystem.Type { return filesystem.TypeFat16 } diff --git a/filesystem/fat32/fat32.go b/filesystem/fat32/fat32.go index 05df6d72..32d235d3 100644 --- a/filesystem/fat32/fat32.go +++ b/filesystem/fat32/fat32.go @@ -28,6 +28,9 @@ const ( // is directly usable in fat12.Dos20BPB struct literals. type SectorSize = fat12.SectorSize +// StatT is an alias for fat12.StatT, the metadata returned by FileInfo.Sys(). +type StatT = fat12.StatT + const ( SectorSize512 SectorSize = 512 SectorSize4096 SectorSize = 4096