aboutsummaryrefslogtreecommitdiffstats
path: root/errors.go
diff options
context:
space:
mode:
Diffstat (limited to 'errors.go')
-rw-r--r--errors.go82
1 files changed, 82 insertions, 0 deletions
diff --git a/errors.go b/errors.go
new file mode 100644
index 0000000..282a08b
--- /dev/null
+++ b/errors.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+ "errors"
+ "io/fs"
+ "log"
+ "os"
+ "syscall"
+ "time"
+)
+
+const maxRetries = 3
+const retryDelay = 100 * time.Millisecond
+
+type SyncError struct {
+ Path string
+ Op string
+ Err error
+}
+
+func (e *SyncError) Error() string {
+ return e.Op + " " + e.Path + ": " + e.Err.Error()
+}
+
+func (e *SyncError) Unwrap() error {
+ return e.Err
+}
+
+func retry(name string, fn func() error) error {
+ var lastErr error
+ for i := 0; i < maxRetries; i++ {
+ lastErr = fn()
+ if lastErr == nil {
+ return nil
+ }
+ if !isRetryable(lastErr) {
+ return lastErr
+ }
+ time.Sleep(retryDelay * time.Duration(i+1))
+ }
+ return lastErr
+}
+
+func isRetryable(err error) bool {
+ if errors.Is(err, fs.ErrNotExist) {
+ return true
+ }
+ if errors.Is(err, syscall.ENOENT) {
+ return true
+ }
+ return false
+}
+
+func logError(op, relPath string, err error) {
+ if err == nil {
+ return
+ }
+ if errors.Is(err, fs.ErrNotExist) {
+ return
+ }
+ var pathErr *fs.PathError
+ if errors.As(err, &pathErr) {
+ err = pathErr.Err
+ }
+
+ switch {
+ case errors.Is(err, syscall.ENOSPC):
+ log.Printf("DISK FULL: %s %s failed - no space left on device", op, relPath)
+ case errors.Is(err, syscall.EACCES), errors.Is(err, syscall.EPERM):
+ log.Printf("PERMISSION DENIED: %s %s - check volume mount permissions", op, relPath)
+ case errors.Is(err, syscall.ELOOP):
+ log.Printf("SYMLINK LOOP: %s %s - too many symbolic links", op, relPath)
+ default:
+ log.Printf("ERROR: %s %s: %v", op, relPath, err)
+ }
+}
+
+func copyFileWithRetry(src, dst string, mode os.FileMode) error {
+ return retry("copy", func() error {
+ return copyFile(src, dst, mode)
+ })
+}