Просмотр исходного кода

lib/model: Move stale scan check info finisher (ref #4305, fix #3742)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4317
Audrius Butkevicius 8 лет назад
Родитель
Сommit
b8c249cddc
2 измененных файлов с 36 добавлено и 25 удалено
  1. 34 24
      lib/model/rwfolder.go
  2. 2 1
      lib/model/sharedpullerstate.go

+ 34 - 24
lib/model/rwfolder.go

@@ -1002,27 +1002,6 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- c
 
 	tempName := ignore.TempName(file.Name)
 
-	if hasCurFile && !curFile.IsDirectory() && !curFile.IsSymlink() {
-		// Check that the file on disk is what we expect it to be according to
-		// the database. If there's a mismatch here, there might be local
-		// changes that we don't know about yet and we should scan before
-		// touching the file. If we can't stat the file we'll just pull it.
-		if info, err := f.fs.Lstat(file.Name); err == nil {
-			if !info.ModTime().Equal(curFile.ModTime()) || info.Size() != curFile.Size {
-				l.Debugln("file modified but not rescanned; not pulling:", file.Name)
-				// Scan() is synchronous (i.e. blocks until the scan is
-				// completed and returns an error), but a scan can't happen
-				// while we're in the puller routine. Request the scan in the
-				// background and it'll be handled when the current pulling
-				// sweep is complete. As we do retries, we'll queue the scan
-				// for this file up to ten times, but the last nine of those
-				// scans will be cheap...
-				go f.Scan([]string{file.Name})
-				return
-			}
-		}
-	}
-
 	scanner.PopulateOffsets(file.Blocks)
 
 	var blocks []protocol.BlockInfo
@@ -1101,7 +1080,8 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- c
 		available:        reused,
 		availableUpdated: time.Now(),
 		ignorePerms:      f.ignorePermissions(file),
-		version:          curFile.Version,
+		hasCurFile:       hasCurFile,
+		curFile:          curFile,
 		mut:              sync.NewRWMutex(),
 		sparse:           !f.DisableSparseFiles,
 		created:          time.Now(),
@@ -1386,6 +1366,36 @@ func (f *sendReceiveFolder) performFinish(state *sharedPullerState) error {
 		// There is an old file or directory already in place. We need to
 		// handle that.
 
+		curMode := uint32(stat.Mode())
+		if runtime.GOOS == "windows" && osutil.IsWindowsExecutable(state.file.Name) {
+			curMode |= 0111
+		}
+
+		// Check that the file on disk is what we expect it to be according to
+		// the database. If there's a mismatch here, there might be local
+		// changes that we don't know about yet and we should scan before
+		// touching the file.
+		// There is also a case where we think the file should be there, but
+		// it was removed, which is a conflict, yet creations always wins when
+		// competing with a deletion, so no need to handle that specially.
+		switch {
+		// The file reappeared from nowhere, or mtime/size has changed, fallthrough -> rescan.
+		case !state.hasCurFile || !stat.ModTime().Equal(state.curFile.ModTime()) || stat.Size() != state.curFile.Size:
+			fallthrough
+		// Permissions have changed, means the file has changed, rescan.
+		case !f.ignorePermissions(state.curFile) && state.curFile.HasPermissionBits() && !scanner.PermsEqual(state.curFile.Permissions, curMode):
+			l.Debugln("file modified but not rescanned; not finishing:", state.curFile.Name)
+			// Scan() is synchronous (i.e. blocks until the scan is
+			// completed and returns an error), but a scan can't happen
+			// while we're in the puller routine. Request the scan in the
+			// background and it'll be handled when the current pulling
+			// sweep is complete. As we do retries, we'll queue the scan
+			// for this file up to ten times, but the last nine of those
+			// scans will be cheap...
+			go f.Scan([]string{state.curFile.Name})
+			return nil
+		}
+
 		switch {
 		case stat.IsDir() || stat.IsSymlink():
 			// It's a directory or a symlink. These are not versioned or
@@ -1400,13 +1410,13 @@ func (f *sendReceiveFolder) performFinish(state *sharedPullerState) error {
 				return err
 			}
 
-		case f.inConflict(state.version, state.file.Version):
+		case f.inConflict(state.curFile.Version, state.file.Version):
 			// The new file has been changed in conflict with the existing one. We
 			// should file it away as a conflict instead of just removing or
 			// archiving. Also merge with the version vector we had, to indicate
 			// we have resolved the conflict.
 
-			state.file.Version = state.file.Version.Merge(state.version)
+			state.file.Version = state.file.Version.Merge(state.curFile.Version)
 			err = osutil.InWritableDir(func(name string) error {
 				return f.moveForConflict(name, state.file.ModifiedBy.String())
 			}, f.fs, state.file.Name)

+ 2 - 1
lib/model/sharedpullerstate.go

@@ -27,7 +27,8 @@ type sharedPullerState struct {
 	realName    string
 	reused      int // Number of blocks reused from temporary file
 	ignorePerms bool
-	version     protocol.Vector // The current (old) version
+	hasCurFile  bool              // Whether curFile is set
+	curFile     protocol.FileInfo // The file as it exists now in our database
 	sparse      bool
 	created     time.Time