aboutsummaryrefslogtreecommitdiffstats
path: root/errors.go
blob: 2cc0b4bb4ba7fc98508af44ddc131679e6966184 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package main

import (
	"errors"
	"io/fs"
	"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 logSyncError(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):
		logErrorf("DISK FULL: %s %s failed - no space left on device", op, relPath)
	case errors.Is(err, syscall.EACCES), errors.Is(err, syscall.EPERM):
		logErrorf("PERMISSION DENIED: %s %s - check volume mount permissions", op, relPath)
	case errors.Is(err, syscall.ELOOP):
		logErrorf("SYMLINK LOOP: %s %s - too many symbolic links", op, relPath)
	default:
		logErrorf("%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)
	})
}