Repository: syncthing/syncthing (language: go)

Issue:
Sync from Linux to Mac with ownership - Local additions after rescan
Syncthing v1.23.6 on Mac (Monterey 12.6.2 and Big Sur 11.7.9) and Linux (Debian GNU/Linux 12 (bookworm))

We have a syncthing folder setup as a “send only” folder on a linux system and sync that with a “receive only” folder on a couple of Macs. 
Syncthing on the Macs is started by a user with elevated priviledges. 
The synced folder on the Macs is located under /Users/Shared/foldername. 
The “sync ownership” option is enabled. Both systems are members of a domain. 
The sync completes and all looks good, there are no errors or warnings in the log. Ownership and permissions of user and group get synced totally fine.

But after a rescan ALL Files are marked as local additions. If i disable “sync ownership” all works as expected and we do not have that problem. 

Tested that with a new testfolder with only a few files in it and was able to replicate the behaviour.

I excluded the mac-folder from spotilight search and experimented with some settings like setting “modTimeWindowS” but that did not solve it.

I dont see a change in the files, there are no extended attributes set and the timestamp of the files does not change after the sync.

Corresponding Syncthing forum post: 
https://forum.syncthing.net/t/sync-from-linux-to-mac-with-ownership-local-additions-after-rescan/20673/1 


Discussion / hints:
This one clearly slipped through the cracks, sorry.

Could you enable debugging (in the UI actions > advanced > GUI > debugging) and then run `syncthing cli debug file <folder-id> <rel-file-path>` on an affected file both on the mac and linux, and post the output here.
Sounds like a similar problem I encountered. Syncing from Linux to Linux where one PC is `send&receive` and the other one is `receive-only`. Both are using the `SyncOwnership` folder config.

Every time I revert the local changes on the receiving PC nearly all files pop up again as local changes.

Maybe that's because of this logic:

```
func unixOwnershipEqual(a, b *UnixData) bool {
	if a == nil && b == nil {
		return true
	}
	if a == nil || b == nil {
		return false
	}
	return a.UID == b.UID && a.GID == b.GID && a.OwnerName == b.OwnerName && a.GroupName == b.GroupName
}
```

In my case the receiver hasn't the same users as it is just a server and it would be sufficient for me if it just stores the `UID` and the `GID`.

The output of `syncthing cli debug file SHARE FILEPATH` seems to support this:

Sender:

```
"unix": {
    "gid": 1234,
    "groupName": "sven",
    "ownerName": "sven",
    "uid": 1234
},
```

Receiver:

```
"unix": {
    "gid": 1234,
    "groupName": "",
    "ownerName": "",
    "uid": 1234
},
```

Because the `ownerName` and `groupName` can't be synchronized the current behavior led to an endless sync loop especially when `receive-only` is set to `send&receive`.

A proposed fix would be:

```
return a.UID == b.UID && a.GID == b.GID
```

Would this be ok?
I think the check should probably be "equal or either side is empty" for the name values...
Wouldn't that fail if the UID or GID is assigned to a different user/group on the target?

I think there is a use-case for both problems:
1. The user wants to make sure the username matches on both systems regardless of the UID.
2. The user wants to make sure the UID matches on both systems because the target system is just used as distributor between machines.

So the better solution would be having a switch to sync either the UID or the username between the hosts. But that sounds like a future thing :-)

> Syncthing records both the numerical UID and GID for a file and the corresponding user and group names, when they are known. When applying ownership Syncthing will first attempt to look up a local user or group with the given name, and if that fails it will fall back to the numerical UID and GID.

You can propose another mechanism, but then that's a separate feature issue I think.

Fix patch:
diff --git a/lib/protocol/bep_extensions.go b/lib/protocol/bep_extensions.go
index 4ee23adc5a9..628353637b2 100644
--- a/lib/protocol/bep_extensions.go
+++ b/lib/protocol/bep_extensions.go
@@ -568,7 +568,9 @@ func unixOwnershipEqual(a, b *UnixData) bool {
 	if a == nil || b == nil {
 		return false
 	}
-	return a.UID == b.UID && a.GID == b.GID && a.OwnerName == b.OwnerName && a.GroupName == b.GroupName
+	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
 }
 
 func windowsOwnershipEqual(a, b *WindowsData) bool {


Test patch: