From 259e9ef08e045a5df5bbec222e0592ec180a0323 Mon Sep 17 00:00:00 2001 From: yparitcher Date: Mon, 16 Jun 2025 11:12:33 -0400 Subject: [PATCH] fix(protocol): slightly loosen/correct ownership comparison criteria (fixes #9879) (#10176) Only Require either matching UID & GID OR matching Names. If the 2 devices have a different Name => UID mapping, they can never be totaly equal. Therefore when syncing we try matching the Name and fall back to the UID. However when scanning for changes we currently require both the Name & UID to match. This leads to forever having out of sync files back and forth, or local additions when receive only. This patch does not change the sending behavoir. It only change what we decide is equal for exisiting files with mismapped Name => UID, The added testcases show the change: Test 1,5,6 are the same as current. Test 2,3 Are what change with this patch (from false to true). Test 4 is a subset of test 2 they is currently special cased as true, which does not chnage. Co-authored-by: Jakob Borg --- lib/protocol/bep_fileinfo.go | 10 ++++++--- lib/protocol/bep_fileinfo_test.go | 36 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/lib/protocol/bep_fileinfo.go b/lib/protocol/bep_fileinfo.go index fa5f78d5d..a02cb6347 100644 --- a/lib/protocol/bep_fileinfo.go +++ b/lib/protocol/bep_fileinfo.go @@ -849,9 +849,13 @@ func unixOwnershipEqual(a, b *UnixData) bool { if a == nil || b == nil { return false } - ownerEqual := a.OwnerName == "" || b.OwnerName == "" || a.OwnerName == b.OwnerName - groupEqual := a.GroupName == "" || b.GroupName == "" || a.GroupName == b.GroupName - return a.UID == b.UID && a.GID == b.GID && ownerEqual && groupEqual + if a.UID == b.UID && a.GID == b.GID { + return true + } + if a.OwnerName == b.OwnerName && a.GroupName == b.GroupName { + return true + } + return false } func windowsOwnershipEqual(a, b *WindowsData) bool { diff --git a/lib/protocol/bep_fileinfo_test.go b/lib/protocol/bep_fileinfo_test.go index ec241db68..743624adf 100644 --- a/lib/protocol/bep_fileinfo_test.go +++ b/lib/protocol/bep_fileinfo_test.go @@ -196,6 +196,42 @@ func TestIsEquivalent(t *testing.T) { b: FileInfo{Type: FileInfoTypeFile, SymlinkTarget: []byte("b")}, eq: true, }, + // Unix Ownership should be the same + { + a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}}, + b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}}, + eq: true, + }, + // ... but matching ID is enough + { + a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}}, + b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "B", GroupName: "B", UID: 1000, GID: 1000}}}, + eq: true, + }, + // ... or matching name + { + a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}}, + b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1001, GID: 1001}}}, + eq: true, + }, + // ... or empty name + { + a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}}, + b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "", GroupName: "", UID: 1000, GID: 1000}}}, + eq: true, + }, + // ... but not different ownership + { + a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}}, + b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "B", GroupName: "B", UID: 1001, GID: 1001}}}, + eq: false, + }, + // or missing ownership + { + a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}}, + b: FileInfo{Platform: PlatformData{}}, + eq: false, + }, } if build.IsWindows {