Permanent fallback to copy on hardlink error

This commit is contained in:
2025-01-07 03:39:05 +03:00
parent df4f2ebd99
commit 62daa023f5
3 changed files with 28 additions and 6 deletions

View File

@@ -30,7 +30,8 @@ sudo cp ${GOPATH}/bin/photocatalog /usr/local/bin/photocatalog
The tool supports the following organization modes: 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). - **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. - **move** — Moves files from the source to the target directory.
- **symlink** — Creates symbolic links at the target pointing to the source files. - **symlink** — Creates symbolic links at the target pointing to the source files.

View File

@@ -1,20 +1,36 @@
package modes package modes
import ( import (
"fmt" "log"
"os" "os"
"sync/atomic"
) )
var hardLinkNotSupported = atomic.Bool{}
type HardLink struct { type HardLink struct {
} }
func (h HardLink) PlaceIt(sourcePath, targetPath string, mode os.FileMode) error { 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 err := os.Link(sourcePath, targetPath); err != nil {
if os.IsExist(err) { if os.IsExist(err) {
return nil 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 return nil

View File

@@ -11,6 +11,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"github.com/fsnotify/fsnotify" "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 { if err := watcher.Close(); err != nil {
o.logErr(fmt.Errorf("close watcher: %w", err)) o.logErr(fmt.Errorf("close watcher: %w", err))
} }
syscall.Sync()
}() }()
go func() { go func() {
@@ -155,9 +158,11 @@ func (o *Organizer) Watch(ctx context.Context, wg *sync.WaitGroup) error {
continue continue
} }
if err := o.processFile(event.Name); err != nil { go func() {
o.logErr(fmt.Errorf("process file %s: %w", event.Name, err)) if err := o.processFile(event.Name); err != nil {
} o.logErr(fmt.Errorf("process file %s: %w", event.Name, err))
}
}()
} }
case <-ctx.Done(): case <-ctx.Done():