Skip to content

Commit

Permalink
implement remove and rename feature for fat32 (#233)
Browse files Browse the repository at this point in the history
* feat: implement remove for fat32, rename in same dir feature for fat32, add new unit-tests
  • Loading branch information
ludelion5 authored Nov 17, 2024
1 parent 42bfcf6 commit 0aefa87
Show file tree
Hide file tree
Showing 3 changed files with 531 additions and 11 deletions.
57 changes: 56 additions & 1 deletion filesystem/fat32/directory.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fat32

import (
"fmt"
"time"
)

Expand Down Expand Up @@ -41,7 +42,7 @@ func (d *Directory) entriesToBytes(bytesPerCluster int) ([]byte, error) {
func (d *Directory) createEntry(name string, cluster uint32, dir bool) (*directoryEntry, error) {
// is it a long filename or a short filename?
var isLFN bool
// TODO: convertLfnSfn does not calculate if the short name conflicts and thus shoukld increment the last character
// TODO: convertLfnSfn does not calculate if the short name conflicts and thus should increment the last character
// that should happen here, once we can look in the directory entry
shortName, extension, isLFN, _ := convertLfnSfn(name)
lfn := ""
Expand Down Expand Up @@ -70,6 +71,60 @@ func (d *Directory) createEntry(name string, cluster uint32, dir bool) (*directo
return &entry, nil
}

// removeEntry removes an entry in the given directory
func (d *Directory) removeEntry(name string) error {
// TODO implement check for long/short filename after increment of sfn is correctly implemented

removeEntryIndex := -1
for i, entry := range d.entries {
if entry.filenameLong == name { // || entry.filenameShort == shortName do not compare SFN, since it is not incremented correctly
removeEntryIndex = i
}
}

if removeEntryIndex == -1 {
return fmt.Errorf("cannot find entry for name %s", name)
}

// remove the entry from the list
d.entries = append(d.entries[:removeEntryIndex], d.entries[removeEntryIndex+1:]...)

return nil
}

// renameEntry renames an entry in the given directory, and returns the handle to it
func (d *Directory) renameEntry(oldFileName, newFileName string) error {
// TODO implement check for long/short filename after increment of sfn is correctly implemented

newEntries := make([]*directoryEntry, 0, len(d.entries))
var isReplaced = false
for _, entry := range d.entries {
if entry.filenameLong == newFileName {
continue // skip adding already existing file, will be overwritten
}
if entry.filenameLong == oldFileName { // || entry.filenameShort == shortName do not compare SFN, since it is not incremented correctly
var lfn string
shortName, extension, isLFN, _ := convertLfnSfn(newFileName)
if isLFN {
lfn = newFileName
}
entry.filenameLong = lfn
entry.filenameShort = shortName
entry.fileExtension = extension
entry.modifyTime = time.Now()
isReplaced = true
}
newEntries = append(newEntries, entry)
}
if !isReplaced {
return fmt.Errorf("cannot find file entry for %s", oldFileName)
}

d.entries = newEntries

return nil
}

// createVolumeLabel create a volume label entry in the given directory, and return the handle to it
func (d *Directory) createVolumeLabel(name string) (*directoryEntry, error) {
// allocate a slot for the new filename in the existing directory
Expand Down
130 changes: 120 additions & 10 deletions filesystem/fat32/fat32.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,18 +637,128 @@ func (fs *FileSystem) OpenFile(p string, flag int) (filesystem.File, error) {
}, nil
}

// removes the named file or (empty) directory.
func (fs *FileSystem) Remove(pathname string) error {
// get the path
dir := path.Dir(pathname)
filename := path.Base(pathname)
// if the dir == filename, then it is just /
if dir == filename {
return fmt.Errorf("cannot remove directory %s as file", pathname)
}
// get the directory entries
parentDir, entries, err := fs.readDirWithMkdir(dir, false)
if err != nil {
return fmt.Errorf("could not read directory entries for %s", dir)
}
// we now know that the directory exists, see if the file exists
var targetEntry *directoryEntry
for _, e := range entries {
shortName := e.filenameShort
if e.fileExtension != "" {
shortName += "." + e.fileExtension
}
if e.filenameLong != filename && shortName != filename {
continue
}
// cannot do anything with directories
if e.isSubdirectory {
content, err := fs.ReadDir(pathname)
if err != nil {
return fmt.Errorf("error while checking if file to delete is empty: %+v", err)
}
// '.' & '..' are always present in directory
if len(content) > 2 {
return fmt.Errorf("cannot remove non-empty directory %s", pathname)
}
}
// if we got this far, we have found the file
targetEntry = e
}

// see if the file exists
// if the file does not exist, and is not opened for os.O_CREATE, return an error
if targetEntry == nil {
return fmt.Errorf("target file %s does not exist", pathname)
}
err = parentDir.removeEntry(filename)
if err != nil {
return fmt.Errorf("failed to remove file %s: %v", pathname, err)
}

// we need to make sure that clusters are removed which may not be used anymore
_, err = fs.allocateSpace(uint64(parentDir.fileSize), parentDir.clusterLocation)
if err != nil {
return fmt.Errorf("failed to allocate clusters: %v", err)
}

// write the directory entries to disk
err = fs.writeDirectoryEntries(parentDir)
if err != nil {
return fmt.Errorf("error writing directory file %s to disk: %v", pathname, err)
}

return nil
}

// Rename renames (moves) oldpath to newpath. If newpath already exists and is not a directory, Rename replaces it.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Rename(oldpath, newpath string) error {
return filesystem.ErrNotImplemented
}
// get the path
dir := path.Dir(oldpath)
filename := path.Base(oldpath)

// removes the named file or (empty) directory.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Remove(pathname string) error {
return filesystem.ErrNotImplemented
newDir := path.Dir(newpath)
newname := path.Base(newpath)
if dir != newDir {
return errors.New("can only rename files within the same directory")
}

// if the dir == filename, then it is just /
if dir == filename {
return fmt.Errorf("cannot rename directory %s as file", oldpath)
}
// get the directory entries
parentDir, entries, err := fs.readDirWithMkdir(dir, false)
if err != nil {
return fmt.Errorf("could not read directory entries for %s", dir)
}
// we now know that the directory exists, see if the file exists
var targetEntry *directoryEntry
for _, e := range entries {
shortName := e.filenameShort
if e.fileExtension != "" {
shortName += "." + e.fileExtension
}
if e.filenameLong != filename && shortName != filename {
continue
}
// if we got this far, we have found the file
targetEntry = e
}

// see if the file exists
// if the file does not exist, and is not opened for os.O_CREATE, return an error
if targetEntry == nil {
return fmt.Errorf("target file %s does not exist", oldpath)
}
err = parentDir.renameEntry(filename, newname)
if err != nil {
return fmt.Errorf("failed to rename file %s: %v", oldpath, err)
}

// we need to make sure that clusters are removed which may not be used anymore
_, err = fs.allocateSpace(uint64(parentDir.fileSize), parentDir.clusterLocation)
if err != nil {
return fmt.Errorf("failed to allocate clusters: %v", err)
}

// write the directory entries to disk
err = fs.writeDirectoryEntries(parentDir)
if err != nil {
return fmt.Errorf("error writing directory file %s to disk: %v", oldpath, err)
}

return nil
}

// Label get the label of the filesystem from the secial file in the root directory.
Expand Down Expand Up @@ -801,7 +911,7 @@ func (fs *FileSystem) mkSubdir(parent *Directory, name string) (*directoryEntry,
}

func (fs *FileSystem) writeDirectoryEntries(dir *Directory) error {
// we need to save the entries of theparent
// we need to save the entries of the parent
b, err := dir.entriesToBytes(fs.bytesPerCluster)
if err != nil {
return fmt.Errorf("could not create a valid byte stream for a FAT32 Entries: %w", err)
Expand Down
Loading

0 comments on commit 0aefa87

Please sign in to comment.