Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions file/dirops.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@ import (
"os"
"path"
"path/filepath"
"time"
)

// Set of allowed file extensions for the safety check before clearing the objects folder
var allowedExtensions = map[string]struct{}{
".json": {},
".gmnotes": {},
".luascriptstate": {},
".ttslua": {},
".xml": {},
}

// DirCreator abstracts folder creation
type DirCreator interface {
CreateDir(relpath string, suggestion string) (string, error)
Clear() error
}

// DirExplorer allows files and folders to be enumerated
Expand Down Expand Up @@ -51,6 +62,59 @@ func (d *DirOps) CreateDir(relpath, suggestion string) (string, error) {
return dirname, nil
}

// preClearCheck walks the directory and ensures all files have an allowed extension.
// It returns an error if a file with a disallowed extension is found.
func (d *DirOps) preClearCheck() error {
walkErr := filepath.Walk(d.base, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

// Skip directories
if info.IsDir() {
return nil
}

ext := filepath.Ext(info.Name())
if _, isAllowed := allowedExtensions[ext]; !isAllowed {
return fmt.Errorf("unsafe file type found: %s", path)
}
return nil
})

// If the directory doesn't exist, Walk returns an error. We treat this as "safe".
if os.IsNotExist(walkErr) {
return nil
}
return walkErr
}

// Clear removes all contents from the base directory and recreates it.
func (d *DirOps) Clear() error {
log.Println("Performing safety check...")
startTime := time.Now()

if err := d.preClearCheck(); err != nil {
return fmt.Errorf("pre-clear safety check failed, operation aborted: %w", err)
}

duration := time.Since(startTime)
log.Printf("Safety check passed in %v. Proceeding with clear.", duration)

// Remove the directory and all its contents
if err := os.RemoveAll(d.base); err != nil {
return fmt.Errorf("error clearing directory %s: %w", d.base, err)
}

// Recreate the empty directory
if err := os.MkdirAll(d.base, 0755); err != nil {
return fmt.Errorf("error recreating directory %s: %w", d.base, err)
}

log.Printf("Cleared and recreated directory: %s", d.base)
return nil
}

// ListFilesAndFolders allows for file exploration. returns relateive file or folder names
func (d *DirOps) ListFilesAndFolders(relpath string) ([]string, []string, error) {
p := filepath.Join(d.base, relpath)
Expand Down
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@ func main() {
if *objin != "" {
*modfile = *objin
objs = file.NewJSONOps(filepath.Dir(*objout))
} else {
// clear the objects directory to avoid orphaned files (by removing and recreating)
if err := objdir.Clear(); err != nil {
log.Fatalf("Failed to clear objects directory before writing: %v", err)
}
}

raw, err := prepForReverse(*moddir, *modfile)
if err != nil {
log.Fatalf("prepForReverse (%s) failed : %v", *modfile, err)
Expand Down
5 changes: 5 additions & 0 deletions tests/fakefiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ func (f *FakeFiles) CreateDir(a, b string) (string, error) {
return b, nil
}

// Clear satisfies DirCreator
func (f *FakeFiles) Clear() error {
return nil
}

// ListFilesAndFolders satisfies DirExplorer
func (f *FakeFiles) ListFilesAndFolders(relpath string) ([]string, []string, error) {
// ignore non json files. i don't think they Matter
Expand Down