diff --git a/filesystem/fat32/fat32.go b/filesystem/fat32/fat32.go index 240b6632..8262e0d7 100644 --- a/filesystem/fat32/fat32.go +++ b/filesystem/fat32/fat32.go @@ -60,6 +60,7 @@ type FileSystem struct { size int64 start int64 file util.File + lazy bool } // Equal compare if two filesystems are equal @@ -433,6 +434,10 @@ func (fs *FileSystem) writeBootSector() error { } func (fs *FileSystem) writeFsis() error { + if fs.lazy { + return nil + } + fsInformationSector := fs.bootSector.biosParameterBlock.fsInformationSector backupBootSector := fs.bootSector.biosParameterBlock.backupBootSector fsisPrimary := int64(fsInformationSector * uint16(SectorSize512)) @@ -453,6 +458,10 @@ func (fs *FileSystem) writeFsis() error { } func (fs *FileSystem) writeFat() error { + if fs.lazy { + return nil + } + reservedSectors := fs.bootSector.biosParameterBlock.dos331BPB.dos20BPB.reservedSectors fatPrimaryStart := uint64(reservedSectors) * uint64(SectorSize512) fatSecondaryStart := fatPrimaryStart + uint64(fs.table.size) @@ -688,6 +697,32 @@ func (fs *FileSystem) SetLabel(volumeLabel string) error { return nil } +// SetLazy sets the lazy flag for the filesystem. If lazy is true, then the filesystem will not write FAT tables and +// other metadata to the disk when creating/writing files or directories. After all changes to file system are done +// Commit() must be called to write the changes to the disk. +func (fs *FileSystem) SetLazy(lazy bool) { + fs.lazy = lazy +} + +// Commit writes the FAT tables and other metadata to the disk. This is only necessary if lazy is set to true. +func (fs *FileSystem) Commit() error { + curr := fs.lazy + fs.lazy = false + defer func() { + fs.lazy = curr + }() + + if err := fs.writeFsis(); err != nil { + return fmt.Errorf("failed to write the file system information sector: %w", err) + } + + if err := fs.writeFat(); err != nil { + return fmt.Errorf("failed to write the file allocation table: %w", err) + } + + return nil +} + // read directory entries for a given cluster func (fs *FileSystem) getClusterList(firstCluster uint32) ([]uint32, error) { // first, get the chain of clusters diff --git a/filesystem/fat32/fat32_test.go b/filesystem/fat32/fat32_test.go index f4450242..2d3c9f2b 100644 --- a/filesystem/fat32/fat32_test.go +++ b/filesystem/fat32/fat32_test.go @@ -1085,6 +1085,16 @@ func testMkFile(fs filesystem.FileSystem, p string, size int) error { } func TestCreateFileTree(t *testing.T) { + testCreateFileTree(t, false) +} + +func TestCreateFileTreeLazy(t *testing.T) { + testCreateFileTree(t, true) +} + +func testCreateFileTree(t *testing.T, lazy bool) { + t.Helper() + filename := "fat32_test" tmpDir := t.TempDir() tmpImgPath := filepath.Join(tmpDir, filename) @@ -1104,6 +1114,7 @@ func TestCreateFileTree(t *testing.T) { if err != nil { t.Fatalf("error creating filesystem: %v", err) } + fs.(*fat32.FileSystem).SetLazy(lazy) if err := fs.Mkdir("/A"); err != nil { t.Errorf("Error making dir /A in root: %v", err) @@ -1150,4 +1161,8 @@ func TestCreateFileTree(t *testing.T) { if err := testMkFile(fs, file, gb); err != nil { t.Errorf("Error making gigfile1 %s: %v", file, err) } + + if err := fs.(*fat32.FileSystem).Commit(); err != nil { + t.Errorf("Error committing filesystem: %v", err) + } }