瀏覽代碼

lib/fs: Add bitmasks for Darwin to handle change to empty files (fixes #7731) (#7756)

Anur 4 年之前
父節點
當前提交
e7f8538e4d
共有 3 個文件被更改,包括 83 次插入2 次删除
  1. 17 0
      lib/fs/basicfs_watch_eventtypes_darwin.go
  2. 1 2
      lib/fs/basicfs_watch_eventtypes_other.go
  3. 65 0
      lib/fs/basicfs_watch_test.go

+ 17 - 0
lib/fs/basicfs_watch_eventtypes_darwin.go

@@ -0,0 +1,17 @@
+// Copyright (C) 2021 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// +build darwin,!kqueue,cgo
+
+package fs
+
+import "github.com/syncthing/notify"
+
+const (
+	subEventMask  = notify.Create | notify.Remove | notify.Write | notify.Rename | notify.FSEventsInodeMetaMod
+	permEventMask = 0
+	rmEventMask   = notify.Remove | notify.Rename
+)

+ 1 - 2
lib/fs/basicfs_watch_eventtypes_other.go

@@ -4,8 +4,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
-// +build !linux,!windows,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris
-// +build !darwin darwin,cgo
+// +build !linux,!windows,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!darwin,!cgo
 
 // Catch all platforms that are not specifically handled to use the generic
 // event types.

+ 65 - 0
lib/fs/basicfs_watch_test.go

@@ -12,6 +12,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -445,6 +446,61 @@ func TestWatchModTime(t *testing.T) {
 		}
 	}
 
+	testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false)
+}
+
+func TestModifyFile(t *testing.T) {
+	name := "modify"
+
+	old := createTestFile(name, "file")
+	modifyTestFile(name, old, "syncthing")
+
+	testCase := func() {
+		modifyTestFile(name, old, "modified")
+	}
+
+	expectedEvents := []Event{
+		{old, NonRemove},
+	}
+	allowedEvents := []Event{
+		{name, NonRemove},
+	}
+
+	sleepMs(1000)
+	testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, false)
+}
+
+func TestTruncateFileOnly(t *testing.T) {
+	name := "truncate"
+
+	file := createTestFile(name, "file")
+	modifyTestFile(name, file, "syncthing")
+
+	// modified the content to empty use os.WriteFile will first truncate the file
+	// (/os/file.go:696) then write nothing. This logic is also used in many editors,
+	// such as when emptying a file in VSCode or JetBrain
+	//
+	// darwin will only modified the inode's metadata, such us mtime, file size, etc.
+	// but would not modified the file directly, so FSEvent 'FSEventsModified' will not
+	// be received
+	//
+	// we should watch the FSEvent 'FSEventsInodeMetaMod' to watch the Inode metadata,
+	// and that should be considered as an NonRemove Event
+	//
+	// notify also considered FSEventsInodeMetaMod as Write Event
+	// /watcher_fsevents.go:89
+	testCase := func() {
+		modifyTestFile(name, file, "")
+	}
+
+	expectedEvents := []Event{
+		{file, NonRemove},
+	}
+	allowedEvents := []Event{
+		{file, NonRemove},
+	}
+
+	sleepMs(1000)
 	testScenario(t, name, testCase, expectedEvents, allowedEvents, fakeMatcher{}, true)
 }
 
@@ -470,6 +526,15 @@ func renameTestFile(name string, old string, new string) {
 	}
 }
 
+func modifyTestFile(name string, file string, content string) {
+	joined := filepath.Join(testDirAbs, name, file)
+
+	err := ioutil.WriteFile(joined, []byte(content), 0755)
+	if err != nil {
+		panic(fmt.Sprintf("Failed to modify test file %s: %s", joined, err))
+	}
+}
+
 func sleepMs(ms int) {
 	time.Sleep(time.Duration(ms) * time.Millisecond)
 }