From 2e55209b347008f3ead8bb3cb07bfc91ad199256 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Thu, 15 Aug 2024 21:17:49 +0200 Subject: [PATCH 1/3] restic: restore timestamps after extended attributes restoring the xattr containing resource forks on macOS apparently modifies the file modification timestamps. Thus, restore the timestamp after xattrs. --- changelog/unreleased/issue-4969 | 7 +++++++ internal/restic/node.go | 14 +++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 changelog/unreleased/issue-4969 diff --git a/changelog/unreleased/issue-4969 b/changelog/unreleased/issue-4969 new file mode 100644 index 000000000..ce76a7389 --- /dev/null +++ b/changelog/unreleased/issue-4969 @@ -0,0 +1,7 @@ +Bugfix: Correctly restore timestamp for files with resource forks on macOS + +On macOS, timestamps were incorrectly restored for files with resource forks. +This has been fixed. + +https://github.com/restic/restic/issues/4969 +https://github.com/restic/restic/pull/5006 diff --git a/internal/restic/node.go b/internal/restic/node.go index 7c1988227..6afdff64a 100644 --- a/internal/restic/node.go +++ b/internal/restic/node.go @@ -249,13 +249,6 @@ func (node Node) restoreMetadata(path string, warn func(msg string)) error { firsterr = errors.WithStack(err) } - if err := node.RestoreTimestamps(path); err != nil { - debug.Log("error restoring timestamps for dir %v: %v", path, err) - if firsterr == nil { - firsterr = err - } - } - if err := node.restoreExtendedAttributes(path); err != nil { debug.Log("error restoring extended attributes for %v: %v", path, err) if firsterr == nil { @@ -270,6 +263,13 @@ func (node Node) restoreMetadata(path string, warn func(msg string)) error { } } + if err := node.RestoreTimestamps(path); err != nil { + debug.Log("error restoring timestamps for %v: %v", path, err) + if firsterr == nil { + firsterr = err + } + } + // Moving RestoreTimestamps and restoreExtendedAttributes calls above as for readonly files in windows // calling Chmod below will no longer allow any modifications to be made on the file and the // calls above would fail. From 43b36ad2b0215cf29be54b9aa7e093528de13949 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 25 Aug 2024 23:13:54 +0200 Subject: [PATCH 2/3] restore: test timestamps for macOS resource forks are restored correctly --- internal/restic/node_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/internal/restic/node_test.go b/internal/restic/node_test.go index 7991d33e0..642beadc5 100644 --- a/internal/restic/node_test.go +++ b/internal/restic/node_test.go @@ -197,6 +197,20 @@ var nodeTests = []Node{ {"user.foo", []byte("bar")}, }, }, + { + Name: "testXattrFileMacOSResourceFork", + Type: "file", + Content: IDs{}, + UID: uint32(os.Getuid()), + GID: uint32(os.Getgid()), + Mode: 0604, + ModTime: parseTime("2005-05-14 21:07:03.111"), + AccessTime: parseTime("2005-05-14 21:07:04.222"), + ChangeTime: parseTime("2005-05-14 21:07:05.333"), + ExtendedAttributes: []ExtendedAttribute{ + {"com.apple.ResourceFork", []byte("bar")}, + }, + }, } func TestNodeRestoreAt(t *testing.T) { @@ -216,6 +230,11 @@ func TestNodeRestoreAt(t *testing.T) { extAttrArr[i].Name = strings.ToUpper(extAttrArr[i].Name) } } + for _, attr := range test.ExtendedAttributes { + if strings.HasPrefix(attr.Name, "com.apple.") && runtime.GOOS != "darwin" { + t.Skipf("attr %v only relevant on macOS", attr.Name) + } + } // tempdir might be backed by a filesystem that does not support // extended attributes From 311b27ced8a3af600de9d89a21017ff59eece106 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 25 Aug 2024 23:14:39 +0200 Subject: [PATCH 3/3] restic: cleanup redundant code in test case --- internal/restic/node_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/restic/node_test.go b/internal/restic/node_test.go index 642beadc5..ab7f66e5b 100644 --- a/internal/restic/node_test.go +++ b/internal/restic/node_test.go @@ -248,10 +248,6 @@ func TestNodeRestoreAt(t *testing.T) { rtest.OK(t, test.CreateAt(context.TODO(), nodePath, nil)) rtest.OK(t, test.RestoreMetadata(nodePath, func(msg string) { rtest.OK(t, fmt.Errorf("Warning triggered for path: %s: %s", nodePath, msg)) })) - if test.Type == "dir" { - rtest.OK(t, test.RestoreTimestamps(nodePath)) - } - fi, err := os.Lstat(nodePath) rtest.OK(t, err)