From 348e966daa0c73a2ba9a905c903f86f2cd37601a Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Fri, 11 Nov 2022 14:53:42 +0100 Subject: [PATCH] backend/local: Ignore ENOTTY for fsync on Mac Fixes #4016. --- changelog/unreleased/issue-4016 | 8 ++++++++ internal/backend/local/local.go | 2 +- internal/backend/local/local_unix.go | 14 +++++++++++++- internal/backend/local/local_windows.go | 3 +++ 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 changelog/unreleased/issue-4016 diff --git a/changelog/unreleased/issue-4016 b/changelog/unreleased/issue-4016 new file mode 100644 index 000000000..bf0b86409 --- /dev/null +++ b/changelog/unreleased/issue-4016 @@ -0,0 +1,8 @@ +Bugfix: Support ExFAT-formatted local backends on macOS Ventura + +ExFAT-formatted disks could not be used as local backends starting from macOS +Ventura. Restic commands would fail with "inappropriate ioctl for device". This +has been fixed. + +https://github.com/restic/restic/issues/4016 +https://github.com/restic/restic/pull/4021 diff --git a/internal/backend/local/local.go b/internal/backend/local/local.go index bce83d768..71f341dfa 100644 --- a/internal/backend/local/local.go +++ b/internal/backend/local/local.go @@ -177,7 +177,7 @@ func (b *Local) Save(ctx context.Context, h restic.Handle, rd restic.RewindReade // Ignore error if filesystem does not support fsync. err = f.Sync() - syncNotSup := errors.Is(err, syscall.ENOTSUP) + syncNotSup := err != nil && (errors.Is(err, syscall.ENOTSUP) || isMacENOTTY(err)) if err != nil && !syncNotSup { return errors.WithStack(err) } diff --git a/internal/backend/local/local_unix.go b/internal/backend/local/local_unix.go index 3dde753a8..e3256ed7a 100644 --- a/internal/backend/local/local_unix.go +++ b/internal/backend/local/local_unix.go @@ -6,6 +6,7 @@ package local import ( "errors" "os" + "runtime" "syscall" "github.com/restic/restic/internal/fs" @@ -19,7 +20,9 @@ func fsyncDir(dir string) error { } err = d.Sync() - if errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOENT) || errors.Is(err, syscall.EINVAL) { + if err != nil && + (errors.Is(err, syscall.ENOTSUP) || errors.Is(err, syscall.ENOENT) || + errors.Is(err, syscall.EINVAL) || isMacENOTTY(err)) { err = nil } @@ -31,6 +34,15 @@ func fsyncDir(dir string) error { return err } +// 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) +} + // set file to readonly func setFileReadonly(f string, mode os.FileMode) error { return fs.Chmod(f, mode&^0222) diff --git a/internal/backend/local/local_windows.go b/internal/backend/local/local_windows.go index 72ced630c..d69b9eec8 100644 --- a/internal/backend/local/local_windows.go +++ b/internal/backend/local/local_windows.go @@ -7,6 +7,9 @@ import ( // Can't explicitly flush directory changes on Windows. func fsyncDir(dir string) error { return nil } +// Windows is not macOS. +func isMacENOTTY(err error) bool { return false } + // We don't modify read-only on windows, // since it will make us unable to delete the file, // and this isn't common practice on this platform.