nsfwapp/backend/file_ops_windows.go
2026-02-06 10:28:46 +01:00

91 lines
1.7 KiB
Go

package main
import (
"errors"
"io"
"os"
"runtime"
"strings"
"syscall"
"time"
)
func moveFile(src, dst string) error {
// zuerst Rename (schnell)
if err := os.Rename(src, dst); err == nil {
return nil
} else {
// Fallback: Copy+Remove (z.B. bei EXDEV)
in, err2 := os.Open(src)
if err2 != nil {
return err
}
defer in.Close()
out, err2 := os.Create(dst)
if err2 != nil {
return err
}
if _, err2 := io.Copy(out, in); err2 != nil {
out.Close()
return err2
}
if err2 := out.Close(); err2 != nil {
return err2
}
return os.Remove(src)
}
}
const windowsSharingViolation syscall.Errno = 32 // ERROR_SHARING_VIOLATION
func isSharingViolation(err error) bool {
if runtime.GOOS != "windows" {
return false
}
// Windows: ERROR_SHARING_VIOLATION = 32, ERROR_LOCK_VIOLATION = 33
var pe *os.PathError
if errors.As(err, &pe) {
if errno, ok := pe.Err.(syscall.Errno); ok {
return errno == syscall.Errno(32) || errno == syscall.Errno(33)
}
}
// Fallback über Text
s := strings.ToLower(err.Error())
return strings.Contains(s, "sharing violation") ||
strings.Contains(s, "used by another process") ||
strings.Contains(s, "wird von einem anderen prozess verwendet")
}
func removeWithRetry(path string) error {
var err error
for i := 0; i < 40; i++ { // ~4s bei 100ms
err = os.Remove(path)
if err == nil {
return nil
}
if isSharingViolation(err) {
time.Sleep(100 * time.Millisecond)
continue
}
return err
}
return err
}
func renameWithRetry(oldPath, newPath string) error {
var err error
for i := 0; i < 40; i++ {
err = os.Rename(oldPath, newPath)
if err == nil {
return nil
}
if isSharingViolation(err) {
time.Sleep(100 * time.Millisecond)
continue
}
return err
}
return err
}