diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 8e047ad59..30fdc473d 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -239,8 +239,9 @@ func main() { IgnoreFile: ".stignore", FollowSymlinks: cfg.Options.FollowSymlinks, BlockSize: BlockSize, - Suppressor: sup, TempNamer: defTempNamer, + Suppressor: sup, + CurrentFiler: m, } updateLocalModel(m, w) diff --git a/cmd/syncthing/model.go b/cmd/syncthing/model.go index dc8590a89..acab979cc 100644 --- a/cmd/syncthing/model.go +++ b/cmd/syncthing/model.go @@ -397,7 +397,7 @@ func (m *Model) Request(nodeID, repo, name string, offset int64, size int) ([]by warnf("SECURITY (nonexistent file) REQ(in): %s: %q o=%d s=%d", nodeID, name, offset, size) return nil, ErrNoSuchFile } - if lf.Flags&protocol.FlagInvalid != 0 { + if lf.Suppressed { return nil, ErrInvalid } @@ -480,6 +480,14 @@ func (m *Model) SeedLocal(fs []protocol.FileInfo) { m.recomputeNeedForGlobal() } +// Implements scanner.CurrentFiler +func (m *Model) CurrentFile(file string) scanner.File { + m.lmut.RLock() + f := m.local[file] + m.lmut.RUnlock() + return f +} + // ConnectedTo returns true if we are connected to the named node. func (m *Model) ConnectedTo(nodeID string) bool { m.pmut.RLock() @@ -810,7 +818,7 @@ func (m *Model) recomputeNeedForFile(gf scanner.File, toAdd []addOrder, toDelete m.lmut.RUnlock() if !ok || gf.NewerThan(lf) { - if gf.Flags&protocol.FlagInvalid != 0 { + if gf.Suppressed { // Never attempt to sync invalid files return toAdd, toDelete } @@ -889,12 +897,13 @@ func fileFromFileInfo(f protocol.FileInfo) scanner.File { offset += int64(b.Size) } return scanner.File{ - Name: f.Name, - Size: offset, - Flags: f.Flags, - Modified: f.Modified, - Version: f.Version, - Blocks: blocks, + Name: f.Name, + Size: offset, + Flags: f.Flags &^ protocol.FlagInvalid, + Modified: f.Modified, + Version: f.Version, + Blocks: blocks, + Suppressed: f.Flags&protocol.FlagInvalid != 0, } } @@ -906,11 +915,15 @@ func fileInfoFromFile(f scanner.File) protocol.FileInfo { Hash: b.Hash, } } - return protocol.FileInfo{ + pf := protocol.FileInfo{ Name: f.Name, Flags: f.Flags, Modified: f.Modified, Version: f.Version, Blocks: blocks, } + if f.Suppressed { + pf.Flags |= protocol.FlagInvalid + } + return pf } diff --git a/protocol/protocol.go b/protocol/protocol.go index c375b501f..0c5a0c0be 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -26,8 +26,8 @@ const ( ) const ( - FlagDeleted = 1 << 12 - FlagInvalid = 1 << 13 + FlagDeleted uint32 = 1 << 12 + FlagInvalid = 1 << 13 ) var ( diff --git a/scanner/file.go b/scanner/file.go index 17ff2295e..66355af3c 100644 --- a/scanner/file.go +++ b/scanner/file.go @@ -3,12 +3,13 @@ package scanner import "fmt" type File struct { - Name string - Flags uint32 - Modified int64 - Version uint32 - Size int64 - Blocks []Block + Name string + Flags uint32 + Modified int64 + Version uint32 + Size int64 + Blocks []Block + Suppressed bool } func (f File) String() string { diff --git a/scanner/walk.go b/scanner/walk.go index 2ebab3f9f..e32cd0830 100644 --- a/scanner/walk.go +++ b/scanner/walk.go @@ -9,8 +9,6 @@ import ( "path/filepath" "strings" "time" - - "github.com/calmh/syncthing/protocol" ) type Walker struct { @@ -25,10 +23,13 @@ type Walker struct { IgnoreFile string // If TempNamer is not nil, it is used to ignore tempory files when walking. TempNamer TempNamer + // If CurrentFiler is not nil, it is queried for the current file before rescanning. + CurrentFiler CurrentFiler // If Suppressor is not nil, it is queried for supression of modified files. + // Suppressed files will be returned with empty metadata and the Suppressed flag set. + // Requires CurrentFiler to be set. Suppressor Suppressor - previous map[string]File // file name -> last seen file state suppressed map[string]bool // file name -> suppression status } @@ -44,6 +45,11 @@ type Suppressor interface { Suppress(name string, fi os.FileInfo) bool } +type CurrentFiler interface { + // CurrentFile returns the file as seen at last scan. + CurrentFile(name string) File +} + // Walk returns the list of files found in the local repository by scanning the // file system. Files are blockwise hashed. func (w *Walker) Walk() (files []File, ignore map[string][]string) { @@ -95,8 +101,7 @@ func (w *Walker) CleanTempFiles() { } func (w *Walker) lazyInit() { - if w.previous == nil { - w.previous = make(map[string]File) + if w.suppressed == nil { w.suppressed = make(map[string]bool) } } @@ -171,40 +176,30 @@ func (w *Walker) walkAndHashFiles(res *[]File, ign map[string][]string) filepath } if info.Mode()&os.ModeType == 0 { - modified := info.ModTime().Unix() - pf := w.previous[rn] - - if pf.Modified == modified { - if nf := uint32(info.Mode()); nf != pf.Flags { + if w.CurrentFiler != nil { + cf := w.CurrentFiler.CurrentFile(rn) + if cf.Modified == info.ModTime().Unix() { if debug { - dlog.Println("new flags:", rn) + dlog.Println("unchanged:", rn) } - pf.Flags = nf - pf.Version++ - w.previous[rn] = pf - } else if debug { - dlog.Println("unchanged:", rn) + *res = append(*res, cf) + return nil } - *res = append(*res, pf) - return nil - } - if w.Suppressor != nil && w.Suppressor.Suppress(rn, info) { - if debug { - dlog.Println("suppressed:", rn) + if w.Suppressor != nil && w.Suppressor.Suppress(rn, info) { + if debug { + dlog.Println("suppressed:", rn) + } + if !w.suppressed[rn] { + w.suppressed[rn] = true + log.Printf("INFO: Changes to %q are being temporarily suppressed because it changes too frequently.", p) + } + cf.Suppressed = true + *res = append(*res, cf) + } else if w.suppressed[rn] { + log.Printf("INFO: Changes to %q are no longer suppressed.", p) + delete(w.suppressed, rn) } - if !w.suppressed[rn] { - w.suppressed[rn] = true - log.Printf("INFO: Changes to %q are being temporarily suppressed because it changes too frequently.", p) - } - f := pf - f.Flags = protocol.FlagInvalid - f.Blocks = nil - *res = append(*res, f) - return nil - } else if w.suppressed[rn] { - log.Printf("INFO: Changes to %q are no longer suppressed.", p) - delete(w.suppressed, rn) } fd, err := os.Open(p) @@ -232,10 +227,9 @@ func (w *Walker) walkAndHashFiles(res *[]File, ign map[string][]string) filepath Name: rn, Size: info.Size(), Flags: uint32(info.Mode()), - Modified: modified, + Modified: info.ModTime().Unix(), Blocks: blocks, } - w.previous[rn] = f *res = append(*res, f) }