Skip to content

Commit

Permalink
Add lazy mode for fat32 to speed up writes
Browse files Browse the repository at this point in the history
For example, it reduces time to create ~5GB file system from 7m40s
to 1m30s on my laptop.
  • Loading branch information
Frostman committed Sep 28, 2024
1 parent 6ad9db3 commit e6fcaed
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 0 deletions.
35 changes: 35 additions & 0 deletions filesystem/fat32/fat32.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type FileSystem struct {
size int64
start int64
file util.File
lazy bool
}

// Equal compare if two filesystems are equal
Expand Down Expand Up @@ -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))
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions filesystem/fat32/fat32_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
}

0 comments on commit e6fcaed

Please sign in to comment.