mirror of
https://github.com/derfenix/photocatalog.git
synced 2026-03-12 07:07:39 +03:00
66 lines
1.3 KiB
Go
66 lines
1.3 KiB
Go
package modes
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"runtime"
|
|
"sync/atomic"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
var cowDisabled = atomic.Bool{}
|
|
|
|
type Copy struct{}
|
|
|
|
func (c Copy) PlaceIt(sourcePath, targetPath string, mode os.FileMode) error {
|
|
targetFile, err := os.OpenFile(targetPath, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, mode)
|
|
if err != nil {
|
|
return fmt.Errorf("open target file: %w", err)
|
|
}
|
|
|
|
defer func() {
|
|
_ = targetFile.Close()
|
|
}()
|
|
|
|
sourceFile, err := os.OpenFile(sourcePath, os.O_RDONLY, os.ModePerm)
|
|
if err != nil {
|
|
return fmt.Errorf("open source file: %w", err)
|
|
}
|
|
|
|
defer func() {
|
|
_ = sourceFile.Close()
|
|
}()
|
|
|
|
// Try to do a COW.
|
|
if runtime.GOOS == "linux" && !cowDisabled.Load() {
|
|
if err := unix.IoctlFileClone(int(targetFile.Fd()), int(sourceFile.Fd())); err == nil {
|
|
return nil
|
|
} else {
|
|
log.Println(fmt.Errorf("COW attempt for %s failed: %w", targetPath, err))
|
|
log.Println("Disabling COW until restart")
|
|
cowDisabled.Store(true)
|
|
}
|
|
}
|
|
|
|
copySize, err := io.Copy(targetFile, sourceFile)
|
|
if err != nil {
|
|
return fmt.Errorf("copy source file: %w", err)
|
|
}
|
|
|
|
stat, err := sourceFile.Stat()
|
|
if err != nil {
|
|
log.Println("stat source file failed:", err)
|
|
|
|
return nil
|
|
}
|
|
|
|
if stat.Size() != copySize {
|
|
log.Printf("copy source file size not equal target file size: source %d != %d copied\n", stat.Size(), copySize)
|
|
}
|
|
|
|
return nil
|
|
}
|