Syncthing not syncing modification time

You shouldn’t need those (even though it should of course work). Either don’t provide any arguments if the config is in the default location, or point at the non-default location with --home.

Well, it seemed to be needed, because it threw:

“unexpected HTTP status returned: 403 Forbidden”

when used without it.

Best regards Matthias

That’s weird - I just tried with --gui-address and --gui-apikey and it works for me. Did you try using the --home param instead?

OK - the config(s) contained ‘debugging false’ and changing that did the trick.

When I run the upper command on the ‘source’ and on the ‘target’ side, I’ll get the two attached outputs: file-debug-source.txt (1.3 KB) and file-debug-target.txt (1.3 KB)

Does that give you a hint?

Best regards Matthias

It does indeed: The target has an entry in db for modification times:

  "mtime": {
    "err": null,
    "value": {
      "real": "2021-02-05T10:25:54+01:00",
      "virtual": "2021-04-28T19:25:03.6818776+02:00"
    }
  }

Which means when syncing Syncthing tried to set the correct modification time, the filesystem pretended it worked, but when checking the filesystem reports a different time than what was set. In that case Syncthing remembers the incorrect time, and when encountering that incorrect time, replaces it with the correct time. That explanation is probably too confusing to follow. Anyway your filesystem on the target side is misbehaving with regards to modification time and Syncthing is covering up for it (as there’s lots of filesystems with weird timestamp behaviour, e.g. 2s intervals on FAT). What filesystem is it here?

I never though we serialised the timezone? The offsets look weird.

Also, the real timestamp is so far in the past comparing with the virtual one, feels something gone horribly wrong.

I assumed that’s the modification time of the previous version, and theorized that the Chmod call had no effect but still returned a nil error → mapping gets saved to db. The different timezones might be because the time from the FileInfo seconds through time.Unix uses a different timezone than what the filesystem returns - that shouldn’t be problematic though, or is it?

I guess I am surprised that it’s not normalised.

Without having looked at the code I assumed we stored 2xlongs to represent the time (epoch seconds, nanos into the second), so I was puzzled how you’d end up with timezone information as part of that.

Regardless of that fact, OPs filesystem does not preserve (or permit us to modify) the timestamps, given we even go through this code path, so case closed from OPs perspective I guess.

It’s ext4 with mount options ‘defaults,user_xattr,acl,noatime’ on both sides.

That’s not supposed to be a filesystem that doesn’t preserve or permit to modifiy file timestamps. You agree?

Best regards Matthias

Definitely wouldn’t expect anything like that from ext4.

Actually looking at the code it doesn’t react to errors when setting mod time, which seems obviously bad. However there’s a comment that explicitly says that we don’t care about the error:

func (f *mtimeFS) Chtimes(name string, atime, mtime time.Time) error {
	// Do a normal Chtimes call, don't care if it succeeds or not.
	f.chtimes(name, atime, mtime)

	// Stat the file to see what happened. Here we *do* return an error,
	// because it might be "does not exist" or similar.
	info, err := f.Filesystem.Lstat(name)
	if err != nil {
		return err
	}

	f.save(name, info.ModTime(), mtime)
	return nil
}

Sure I would expect that if Chtimes fails, Stat does too, but that’s hardly guaranteed and anything we should depend on. @AudriusButkevicius do you know what the reasoning for ignoring the error there is?

Edit:

I found something on that in an old PR by Jakob (lib/db, lib/fs, lib/model: Introduce fs.MtimeFS, remove VirtualMtimeRepo by calmh · Pull Request #3479 · syncthing/syncthing · GitHub):

This changes the mtime handling to be a layer that intercepts Lstat and Chtimes to always present a consistent view to the application. It’s a bit more ambitious in getting it right than the previous implementation as it will always check what the resulting mtime was after Chtimes, regardless of if an error was returned or not, and record any difference.

And when Chtimes is used there are related comments:

	f.mtimefs.Chtimes(file.Name, file.ModTime(), file.ModTime()) // never fails

So it seems like the intention is that Chtimes failures/errors never surface, i.e. they are completely ignored and handled by mtimeFS. Not entirely sure why though.

I don’t know for sure, but I am not sure we care given we store virtual times?

My uneducated guess would be that it’s hard to reason what the error means, given its supposed to change two values, it could be a permissions error. noatime error etc only setting atime (but did mtime succeed? Was it even attempted?), so the best way to understand what was actually done is to check the state via lstat.

I guess we care in this case, as its the users complaint, but I think implementation wise we care whether it works to decide if we need a corrective measure.

I suspect it will be down to something simple like user running syncthing is not the owner of the files in question, or the files are readonly/somehow mispermissioned.

Yeah, right makes sense. As in we try to set the correct mod. time, but only care about internal consistency if it fails.
Until now my understanding was, that mtimeFS handles cases where the filesystem does accept changing mod times, but doesn’t set it to exactly the value we told it to. Now it looks like it does that, plus it also handles the case where the times weren’t changed at all.
As you said, that makes a lot of things a lot simpler, but it’s an important gotcha and might even be worth mentioning in the FAQ about what we sync (as in mod. time is synced on a best effort basis, any errors are ignored).

@Matzmann Did you check if the user Syncthing is running as can set the modification time on the affected item?

We could part of the debug endpoint try setting mtime to the real value we store in the db, and return the error along side.

1 Like

I’m sorry for coming back late.

When I touch the file as the user syncthing runs as, the time gets updated.

But …! : When I touch it again with -r to reset the time to the value it’s been before using another file, I get an ‘Operation not permitted’.

What’s that supposed to be?

(Of course it works as the user the file belongs to.)

Best regards Matthias

Well, that’s precisely the behavior Syncthing tries and succeeds in working around here, so that’s working as intended. As for why the FS is returning EPERM, who knows. You have an acl option, perhaps there’s funky restrictions due to ACLs being applied?

1 Like

OK, I tried with and without the acl option but to no avail (even with adding an ACL for the Syncthing user).

The linux permission system gives read/write permissions to the group (if configured) but not the permission to (only) change the mtime (nor does it give the permission to change the owner).

Of course a group member can change the mtime to the current time by changing something in a file (and then maybe reverse the change), but he/she cannot change the time to a defined value - and that’s what would be necessary here.

Syncthing just can’t work around that restriction.

Ticket can be closed.

Thanks very much for your input and your efforts! :slight_smile:

Best regards Matthias

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.