lib/db, lib/fs, lib/model: Introduce fs.MtimeFS, remove VirtualMtimeRepo

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3479
This commit is contained in:
Jakob Borg
2016-08-05 17:45:45 +00:00
committed by Audrius Butkevicius
parent f368d2278f
commit 0655991a19
11 changed files with 348 additions and 242 deletions

View File

@@ -57,9 +57,8 @@ type Config struct {
TempLifetime time.Duration
// If CurrentFiler is not nil, it is queried for the current file before rescanning.
CurrentFiler CurrentFiler
// If MtimeRepo is not nil, it is used to provide mtimes on systems that
// don't support setting arbitrary mtimes.
MtimeRepo MtimeRepo
// The Lstater provides reliable mtimes on top of the regular filesystem.
Lstater Lstater
// If IgnorePerms is true, changes to permission bits will not be
// detected. Scanned files will get zero permission bits and the
// NoPermissionBits flag set.
@@ -88,10 +87,8 @@ type CurrentFiler interface {
CurrentFile(name string) (protocol.FileInfo, bool)
}
type MtimeRepo interface {
// GetMtime returns a (possibly modified) actual mtime given a file name
// and its on disk mtime.
GetMtime(relPath string, mtime time.Time) time.Time
type Lstater interface {
Lstat(name string) (os.FileInfo, error)
}
func Walk(cfg Config) (chan protocol.FileInfo, error) {
@@ -103,8 +100,8 @@ func Walk(cfg Config) (chan protocol.FileInfo, error) {
if w.TempNamer == nil {
w.TempNamer = noTempNamer{}
}
if w.MtimeRepo == nil {
w.MtimeRepo = noMtimeRepo{}
if w.Lstater == nil {
w.Lstater = defaultLstater{}
}
return w.walk()
@@ -119,8 +116,7 @@ type walker struct {
func (w *walker) walk() (chan protocol.FileInfo, error) {
l.Debugln("Walk", w.Dir, w.Subs, w.BlockSize, w.Matcher)
err := checkDir(w.Dir)
if err != nil {
if err := w.checkDir(); err != nil {
return nil, err
}
@@ -245,14 +241,18 @@ func (w *walker) walkAndHashFiles(fchan, dchan chan protocol.FileInfo) filepath.
return nil
}
mtime := w.MtimeRepo.GetMtime(relPath, info.ModTime())
info, err = w.Lstater.Lstat(absPath)
// An error here would be weird as we've already gotten to this point, but act on it ninetheless
if err != nil {
return skip
}
if w.TempNamer.IsTemporary(relPath) {
// A temporary file
l.Debugln("temporary:", relPath)
if info.Mode().IsRegular() && mtime.Add(w.TempLifetime).Before(now) {
if info.Mode().IsRegular() && info.ModTime().Add(w.TempLifetime).Before(now) {
os.Remove(absPath)
l.Debugln("removing temporary:", relPath, mtime)
l.Debugln("removing temporary:", relPath, info.ModTime())
}
return nil
}
@@ -283,17 +283,17 @@ func (w *walker) walkAndHashFiles(fchan, dchan chan protocol.FileInfo) filepath.
}
case info.Mode().IsDir():
err = w.walkDir(relPath, info, mtime, dchan)
err = w.walkDir(relPath, info, dchan)
case info.Mode().IsRegular():
err = w.walkRegular(relPath, info, mtime, fchan)
err = w.walkRegular(relPath, info, fchan)
}
return err
}
}
func (w *walker) walkRegular(relPath string, info os.FileInfo, mtime time.Time, fchan chan protocol.FileInfo) error {
func (w *walker) walkRegular(relPath string, info os.FileInfo, fchan chan protocol.FileInfo) error {
curMode := uint32(info.Mode())
if runtime.GOOS == "windows" && osutil.IsWindowsExecutable(relPath) {
curMode |= 0111
@@ -310,12 +310,12 @@ func (w *walker) walkRegular(relPath string, info os.FileInfo, mtime time.Time,
// - has the same size as previously
cf, ok := w.CurrentFiler.CurrentFile(relPath)
permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Permissions, curMode)
if ok && permUnchanged && !cf.IsDeleted() && cf.Modified == mtime.Unix() && !cf.IsDirectory() &&
if ok && permUnchanged && !cf.IsDeleted() && cf.Modified == info.ModTime().Unix() && !cf.IsDirectory() &&
!cf.IsSymlink() && !cf.IsInvalid() && cf.Size == info.Size() {
return nil
}
l.Debugln("rescan:", cf, mtime.Unix(), info.Mode()&os.ModePerm)
l.Debugln("rescan:", cf, info.ModTime().Unix(), info.Mode()&os.ModePerm)
f := protocol.FileInfo{
Name: relPath,
@@ -323,7 +323,7 @@ func (w *walker) walkRegular(relPath string, info os.FileInfo, mtime time.Time,
Version: cf.Version.Update(w.ShortID),
Permissions: curMode & uint32(maskModePerm),
NoPermissions: w.IgnorePerms,
Modified: mtime.Unix(),
Modified: info.ModTime().Unix(),
Size: info.Size(),
}
l.Debugln("to hash:", relPath, f)
@@ -337,7 +337,7 @@ func (w *walker) walkRegular(relPath string, info os.FileInfo, mtime time.Time,
return nil
}
func (w *walker) walkDir(relPath string, info os.FileInfo, mtime time.Time, dchan chan protocol.FileInfo) error {
func (w *walker) walkDir(relPath string, info os.FileInfo, dchan chan protocol.FileInfo) error {
// A directory is "unchanged", if it
// - exists
// - has the same permissions as previously, unless we are ignoring permissions
@@ -357,7 +357,7 @@ func (w *walker) walkDir(relPath string, info os.FileInfo, mtime time.Time, dcha
Version: cf.Version.Update(w.ShortID),
Permissions: uint32(info.Mode() & maskModePerm),
NoPermissions: w.IgnorePerms,
Modified: mtime.Unix(),
Modified: info.ModTime().Unix(),
}
l.Debugln("dir:", relPath, f)
@@ -457,7 +457,7 @@ func (w *walker) normalizePath(absPath, relPath string) (normPath string, skip b
// We will attempt to normalize it.
normalizedPath := filepath.Join(w.Dir, normPath)
if _, err := osutil.Lstat(normalizedPath); os.IsNotExist(err) {
if _, err := w.Lstater.Lstat(normalizedPath); os.IsNotExist(err) {
// Nothing exists with the normalized filename. Good.
if err = os.Rename(absPath, normalizedPath); err != nil {
l.Infof(`Error normalizing UTF8 encoding of file "%s": %v`, relPath, err)
@@ -475,13 +475,13 @@ func (w *walker) normalizePath(absPath, relPath string) (normPath string, skip b
return normPath, false
}
func checkDir(dir string) error {
if info, err := osutil.Lstat(dir); err != nil {
func (w *walker) checkDir() error {
if info, err := w.Lstater.Lstat(w.Dir); err != nil {
return err
} else if !info.IsDir() {
return errors.New(dir + ": not a directory")
return errors.New(w.Dir + ": not a directory")
} else {
l.Debugln("checkDir", dir, info)
l.Debugln("checkDir", w.Dir, info)
}
return nil
}
@@ -591,10 +591,10 @@ func (noTempNamer) IsTemporary(path string) bool {
return false
}
// A no-op MtimeRepo
// A no-op Lstater
type noMtimeRepo struct{}
type defaultLstater struct{}
func (noMtimeRepo) GetMtime(relPath string, mtime time.Time) time.Time {
return mtime
func (defaultLstater) Lstat(name string) (os.FileInfo, error) {
return osutil.Lstat(name)
}