From 62daa023f5798b3b382211414d2ff698d57f7351 Mon Sep 17 00:00:00 2001 From: derfenix Date: Tue, 7 Jan 2025 03:39:05 +0300 Subject: [PATCH] Permanent fallback to copy on hardlink error --- README.md | 3 ++- internal/organizer/modes/hardlink.go | 20 ++++++++++++++++++-- internal/organizer/organizer.go | 11 ++++++++--- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c8d3b4b..eb1bfa0 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ sudo cp ${GOPATH}/bin/photocatalog /usr/local/bin/photocatalog The tool supports the following organization modes: - **copy** — Copies files to the target directory. If the filesystem supports it, uses Copy-on-Write (COW) for efficiency (via FICLONE ioctl call). -- **hardlink** — Creates hardlinks to the source files, saving disk space. Ideal if the source and target are on the same partition, though file permissions remain linked to the original. +- **hardlink** — Creates hardlinks to the source files, saving disk space. Ideal (and usable only) if the source and target are on the same partition, +though file permissions remain linked to the original. Fallback to copy on fail. - **move** — Moves files from the source to the target directory. - **symlink** — Creates symbolic links at the target pointing to the source files. diff --git a/internal/organizer/modes/hardlink.go b/internal/organizer/modes/hardlink.go index 3e2ca86..c6a77d1 100644 --- a/internal/organizer/modes/hardlink.go +++ b/internal/organizer/modes/hardlink.go @@ -1,20 +1,36 @@ package modes import ( - "fmt" + "log" "os" + "sync/atomic" ) +var hardLinkNotSupported = atomic.Bool{} + type HardLink struct { } func (h HardLink) PlaceIt(sourcePath, targetPath string, mode os.FileMode) error { + if hardLinkNotSupported.Load() { + if copyErr := (Copy{}).PlaceIt(sourcePath, targetPath, mode); copyErr != nil { + return copyErr + } + } + if err := os.Link(sourcePath, targetPath); err != nil { if os.IsExist(err) { return nil } - return fmt.Errorf("create hard link: %w", err) + log.Println("Create hardlink failed:", err.Error()) + hardLinkNotSupported.Store(true) + + if copyErr := (Copy{}).PlaceIt(sourcePath, targetPath, mode); copyErr != nil { + return copyErr + } + + return nil } return nil diff --git a/internal/organizer/organizer.go b/internal/organizer/organizer.go index 63f181f..6827383 100644 --- a/internal/organizer/organizer.go +++ b/internal/organizer/organizer.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "sync" + "syscall" "github.com/fsnotify/fsnotify" @@ -130,6 +131,8 @@ func (o *Organizer) Watch(ctx context.Context, wg *sync.WaitGroup) error { if err := watcher.Close(); err != nil { o.logErr(fmt.Errorf("close watcher: %w", err)) } + + syscall.Sync() }() go func() { @@ -155,9 +158,11 @@ func (o *Organizer) Watch(ctx context.Context, wg *sync.WaitGroup) error { continue } - if err := o.processFile(event.Name); err != nil { - o.logErr(fmt.Errorf("process file %s: %w", event.Name, err)) - } + go func() { + if err := o.processFile(event.Name); err != nil { + o.logErr(fmt.Errorf("process file %s: %w", event.Name, err)) + } + }() } case <-ctx.Done():