diff --git a/repo/repo.go b/repo/repo.go index e59cc37..1d1a565 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -604,7 +604,8 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) { return } - if err := tf.Sync(); err != nil { + syncNotSup, err := syncFile(tf) + if err != nil { _ = tf.Close() _ = os.Remove(tf.Name()) h.incrementRepoSpaceUsage(-written) @@ -626,10 +627,12 @@ func (h *Handler) saveBlob(w http.ResponseWriter, r *http.Request) { return } - if err := syncDir(filepath.Dir(path)); err != nil { - // Don't call os.Remove(path) as this is prone to race conditions with parallel upload retries - h.internalServerError(w, err) - return + if !syncNotSup { + if err := syncDir(filepath.Dir(path)); err != nil { + // Don't call os.Remove(path) as this is prone to race conditions with parallel upload retries + h.internalServerError(w, err) + return + } } h.sendMetric(objectType, BlobWrite, uint64(written)) @@ -648,6 +651,16 @@ func tempFile(fn string, perm os.FileMode) (f *os.File, err error) { return } +func syncFile(f *os.File) (bool, error) { + err := f.Sync() + // Ignore error if filesystem does not support fsync. + syncNotSup := err != nil && (errors.Is(err, syscall.ENOTSUP) || isMacENOTTY(err)) + if syncNotSup { + err = nil + } + return syncNotSup, err +} + func syncDir(dirname string) error { if runtime.GOOS == "windows" { // syncing a directory is not possible on windows @@ -659,6 +672,10 @@ func syncDir(dirname string) error { return err } err = dir.Sync() + // Ignore error if filesystem does not support fsync. + if errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOENT) || errors.Is(err, syscall.EINVAL) { + err = nil + } if err != nil { _ = dir.Close() return err diff --git a/repo/repo_unix.go b/repo/repo_unix.go new file mode 100644 index 0000000..c2efca9 --- /dev/null +++ b/repo/repo_unix.go @@ -0,0 +1,19 @@ +//go:build !windows +// +build !windows + +package repo + +import ( + "errors" + "runtime" + "syscall" +) + +// The ExFAT driver on some versions of macOS can return ENOTTY, +// "inappropriate ioctl for device", for fsync. +// +// https://github.com/restic/restic/issues/4016 +// https://github.com/realm/realm-core/issues/5789 +func isMacENOTTY(err error) bool { + return runtime.GOOS == "darwin" && errors.Is(err, syscall.ENOTTY) +} diff --git a/repo/repo_windows.go b/repo/repo_windows.go new file mode 100644 index 0000000..5091b11 --- /dev/null +++ b/repo/repo_windows.go @@ -0,0 +1,4 @@ +package repo + +// Windows is not macOS. +func isMacENOTTY(err error) bool { return false }