From 75b58eb4805837d1af52a6d775a2769a56bfaa3a Mon Sep 17 00:00:00 2001 From: Simon Frei Date: Sun, 2 May 2021 16:45:44 +0200 Subject: [PATCH] lib/fs: Watch attrib. changes on inotify for mod. time (#7623) * lib/fs: Watch attrib. changes on inotify for mod. time * fix bsds (real) and darwin (test only) --- lib/fs/basicfs_watch_eventtypes_inotify.go | 5 ++- lib/fs/basicfs_watch_eventtypes_kqueue.go | 5 ++- lib/fs/basicfs_watch_test.go | 46 ++++++++++++++++++---- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/lib/fs/basicfs_watch_eventtypes_inotify.go b/lib/fs/basicfs_watch_eventtypes_inotify.go index eb58d2de7..75655918d 100644 --- a/lib/fs/basicfs_watch_eventtypes_inotify.go +++ b/lib/fs/basicfs_watch_eventtypes_inotify.go @@ -10,8 +10,9 @@ package fs import "github.com/syncthing/notify" +// notify.InAttrib is not only required for permissions, but also mod. time changes const ( - subEventMask = notify.InCreate | notify.InMovedTo | notify.InDelete | notify.InDeleteSelf | notify.InModify | notify.InMovedFrom | notify.InMoveSelf - permEventMask = notify.InAttrib + subEventMask = notify.InCreate | notify.InMovedTo | notify.InDelete | notify.InDeleteSelf | notify.InModify | notify.InMovedFrom | notify.InMoveSelf | notify.InAttrib + permEventMask = 0 rmEventMask = notify.InDelete | notify.InDeleteSelf | notify.InMovedFrom | notify.InMoveSelf ) diff --git a/lib/fs/basicfs_watch_eventtypes_kqueue.go b/lib/fs/basicfs_watch_eventtypes_kqueue.go index d1fe209de..a14930288 100644 --- a/lib/fs/basicfs_watch_eventtypes_kqueue.go +++ b/lib/fs/basicfs_watch_eventtypes_kqueue.go @@ -13,7 +13,8 @@ import "github.com/syncthing/notify" const ( // Platform independent notify.Create is required, as kqueue does not have // any event signalling file creation, but notify does generate those internally. - subEventMask = notify.NoteDelete | notify.NoteWrite | notify.NoteRename | notify.Create - permEventMask = notify.NoteAttrib | notify.NoteExtend + // NoteAttrib is not only required for permissions, but also mod. time changes + subEventMask = notify.NoteDelete | notify.NoteWrite | notify.NoteRename | notify.Create | notify.NoteAttrib | notify.NoteExtend + permEventMask = 0 rmEventMask = notify.NoteDelete | notify.NoteRename ) diff --git a/lib/fs/basicfs_watch_test.go b/lib/fs/basicfs_watch_test.go index 531de7819..ccf5e928a 100644 --- a/lib/fs/basicfs_watch_test.go +++ b/lib/fs/basicfs_watch_test.go @@ -87,7 +87,7 @@ func TestWatchIgnore(t *testing.T) { {name, NonRemove}, } - testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), skipIgnoredDirs: true}) + testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), skipIgnoredDirs: true}, false) } func TestWatchInclude(t *testing.T) { @@ -114,7 +114,7 @@ func TestWatchInclude(t *testing.T) { {name, NonRemove}, } - testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), include: filepath.Join(name, included)}) + testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{ignore: filepath.Join(name, ignored), include: filepath.Join(name, included)}, false) } func TestWatchRename(t *testing.T) { @@ -146,7 +146,7 @@ func TestWatchRename(t *testing.T) { // set the "allow others" flag because we might get the create of // "oldfile" initially - testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}) + testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false) } // TestWatchWinRoot checks that a watch at a drive letter does not panic due to @@ -308,7 +308,7 @@ func TestWatchOverflow(t *testing.T) { } } - testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}) + testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false) } func TestWatchErrorLinuxInterpretation(t *testing.T) { @@ -413,7 +413,39 @@ func TestWatchIssue4877(t *testing.T) { testFs = origTestFs }() - testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}) + testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false) +} + +func TestWatchModTime(t *testing.T) { + name := "modtime" + + file := createTestFile(name, "foo") + path := filepath.Join(name, file) + now := time.Now() + before := now.Add(-10 * time.Second) + if err := testFs.Chtimes(path, before, before); err != nil { + t.Fatal(err) + } + + testCase := func() { + if err := testFs.Chtimes(path, now, now); err != nil { + t.Error(err) + } + } + + expectedEvents := []Event{ + {file, NonRemove}, + } + + var allowedEvents []Event + // Apparently an event for the parent is also sent on mac + if runtime.GOOS == "darwin" { + allowedEvents = []Event{ + {name, NonRemove}, + } + } + + testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, true) } // path relative to folder root, also creates parent dirs if necessary @@ -442,7 +474,7 @@ func sleepMs(ms int) { time.Sleep(time.Duration(ms) * time.Millisecond) } -func testScenario(t *testing.T, name string, testCase func(), expectedEvents, allowedEvents []Event, fm fakeMatcher) { +func testScenario(t *testing.T, name string, testCase func(), expectedEvents, allowedEvents []Event, fm fakeMatcher, ignorePerms bool) { if err := testFs.MkdirAll(name, 0755); err != nil { panic(fmt.Sprintf("Failed to create directory %s: %s", name, err)) } @@ -451,7 +483,7 @@ func testScenario(t *testing.T, name string, testCase func(), expectedEvents, al ctx, cancel := context.WithCancel(context.Background()) defer cancel() - eventChan, errChan, err := testFs.Watch(name, fm, ctx, false) + eventChan, errChan, err := testFs.Watch(name, fm, ctx, ignorePerms) if err != nil { panic(err) }