mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-23 03:18:59 +00:00
Merge pull request #1804 from cdhowie/virtual-mtimes
Implement virtual mtime support for Android
This commit is contained in:
commit
65be18cc93
@ -78,7 +78,6 @@ type FolderConfiguration struct {
|
|||||||
IgnorePerms bool `xml:"ignorePerms,attr" json:"ignorePerms"`
|
IgnorePerms bool `xml:"ignorePerms,attr" json:"ignorePerms"`
|
||||||
AutoNormalize bool `xml:"autoNormalize,attr" json:"autoNormalize"`
|
AutoNormalize bool `xml:"autoNormalize,attr" json:"autoNormalize"`
|
||||||
Versioning VersioningConfiguration `xml:"versioning" json:"versioning"`
|
Versioning VersioningConfiguration `xml:"versioning" json:"versioning"`
|
||||||
LenientMtimes bool `xml:"lenientMtimes" json:"lenientMTimes"`
|
|
||||||
Copiers int `xml:"copiers" json:"copiers"` // This defines how many files are handled concurrently.
|
Copiers int `xml:"copiers" json:"copiers"` // This defines how many files are handled concurrently.
|
||||||
Pullers int `xml:"pullers" json:"pullers"` // Defines how many blocks are fetched at the same time, possibly between separate copier routines.
|
Pullers int `xml:"pullers" json:"pullers"` // Defines how many blocks are fetched at the same time, possibly between separate copier routines.
|
||||||
Hashers int `xml:"hashers" json:"hashers"` // Less than one sets the value to the number of cores. These are CPU bound due to hashing.
|
Hashers int `xml:"hashers" json:"hashers"` // Less than one sets the value to the number of cores. These are CPU bound due to hashing.
|
||||||
|
@ -45,6 +45,7 @@ const (
|
|||||||
KeyTypeBlock
|
KeyTypeBlock
|
||||||
KeyTypeDeviceStatistic
|
KeyTypeDeviceStatistic
|
||||||
KeyTypeFolderStatistic
|
KeyTypeFolderStatistic
|
||||||
|
KeyTypeVirtualMtime
|
||||||
)
|
)
|
||||||
|
|
||||||
type fileVersion struct {
|
type fileVersion struct {
|
||||||
@ -314,6 +315,8 @@ func ldbReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) i
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ldbReplaceWithDelete(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo, myID uint64) int64 {
|
func ldbReplaceWithDelete(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo, myID uint64) int64 {
|
||||||
|
mtimeRepo := NewVirtualMtimeRepo(db, string(folder))
|
||||||
|
|
||||||
return ldbGenericReplace(db, folder, device, fs, func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) int64 {
|
return ldbGenericReplace(db, folder, device, fs, func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) int64 {
|
||||||
var tf FileInfoTruncated
|
var tf FileInfoTruncated
|
||||||
err := tf.UnmarshalXDR(dbi.Value())
|
err := tf.UnmarshalXDR(dbi.Value())
|
||||||
@ -337,6 +340,7 @@ func ldbReplaceWithDelete(db *leveldb.DB, folder, device []byte, fs []protocol.F
|
|||||||
l.Debugf("batch.Put %p %x", batch, dbi.Key())
|
l.Debugf("batch.Put %p %x", batch, dbi.Key())
|
||||||
}
|
}
|
||||||
batch.Put(dbi.Key(), bs)
|
batch.Put(dbi.Key(), bs)
|
||||||
|
mtimeRepo.DeleteMtime(tf.Name)
|
||||||
ldbUpdateGlobal(db, batch, folder, device, deviceKeyName(dbi.Key()), f.Version)
|
ldbUpdateGlobal(db, batch, folder, device, deviceKeyName(dbi.Key()), f.Version)
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,24 @@ func (n NamespacedKV) String(key string) (string, bool) {
|
|||||||
return string(valBs), true
|
return string(valBs), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PutBytes stores a new byte slice. Any existing value (even if of another type)
|
||||||
|
// is overwritten.
|
||||||
|
func (n *NamespacedKV) PutBytes(key string, val []byte) {
|
||||||
|
keyBs := append(n.prefix, []byte(key)...)
|
||||||
|
n.db.Put(keyBs, val, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the stored value as a raw byte slice and a boolean that
|
||||||
|
// is false if no value was stored at the key.
|
||||||
|
func (n NamespacedKV) Bytes(key string) ([]byte, bool) {
|
||||||
|
keyBs := append(n.prefix, []byte(key)...)
|
||||||
|
valBs, err := n.db.Get(keyBs, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return valBs, true
|
||||||
|
}
|
||||||
|
|
||||||
// Delete deletes the specified key. It is allowed to delete a nonexistent
|
// Delete deletes the specified key. It is allowed to delete a nonexistent
|
||||||
// key.
|
// key.
|
||||||
func (n NamespacedKV) Delete(key string) {
|
func (n NamespacedKV) Delete(key string) {
|
||||||
|
@ -228,6 +228,7 @@ func DropFolder(db *leveldb.DB, folder string) {
|
|||||||
folder: folder,
|
folder: folder,
|
||||||
}
|
}
|
||||||
bm.Drop()
|
bm.Drop()
|
||||||
|
NewVirtualMtimeRepo(db, folder).Drop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeFilenames(fs []protocol.FileInfo) {
|
func normalizeFilenames(fs []protocol.FileInfo) {
|
||||||
|
86
internal/db/virtualmtime.go
Normal file
86
internal/db/virtualmtime.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (C) 2015 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/.
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This type encapsulates a repository of mtimes for platforms where file mtimes
|
||||||
|
// can't be set to arbitrary values. For this to work, we need to store both
|
||||||
|
// the mtime we tried to set (the "actual" mtime) as well as the mtime the file
|
||||||
|
// has when we're done touching it (the "disk" mtime) so that we can tell if it
|
||||||
|
// was changed. So in GetMtime(), it's not sufficient that the record exists --
|
||||||
|
// the argument must also equal the "disk" mtime in the record, otherwise it's
|
||||||
|
// been touched locally and the "disk" mtime is actually correct.
|
||||||
|
|
||||||
|
type VirtualMtimeRepo struct {
|
||||||
|
ns *NamespacedKV
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVirtualMtimeRepo(ldb *leveldb.DB, folder string) *VirtualMtimeRepo {
|
||||||
|
prefix := string(KeyTypeVirtualMtime) + folder
|
||||||
|
|
||||||
|
return &VirtualMtimeRepo{
|
||||||
|
ns: NewNamespacedKV(ldb, prefix),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VirtualMtimeRepo) UpdateMtime(path string, diskMtime, actualMtime time.Time) {
|
||||||
|
if debug {
|
||||||
|
l.Debugf("virtual mtime: storing values for path:%s disk:%v actual:%v", path, diskMtime, actualMtime)
|
||||||
|
}
|
||||||
|
|
||||||
|
diskBytes, _ := diskMtime.MarshalBinary()
|
||||||
|
actualBytes, _ := actualMtime.MarshalBinary()
|
||||||
|
|
||||||
|
data := append(diskBytes, actualBytes...)
|
||||||
|
|
||||||
|
r.ns.PutBytes(path, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VirtualMtimeRepo) GetMtime(path string, diskMtime time.Time) time.Time {
|
||||||
|
var debugResult string
|
||||||
|
|
||||||
|
if data, exists := r.ns.Bytes(path); exists {
|
||||||
|
var mtime time.Time
|
||||||
|
|
||||||
|
if err := mtime.UnmarshalBinary(data[:len(data)/2]); err != nil {
|
||||||
|
panic(fmt.Sprintf("Can't unmarshal stored mtime at path %v: %v", path, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if mtime.Equal(diskMtime) {
|
||||||
|
if err := mtime.UnmarshalBinary(data[len(data)/2:]); err != nil {
|
||||||
|
panic(fmt.Sprintf("Can't unmarshal stored mtime at path %v: %v", path, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
debugResult = "got it"
|
||||||
|
diskMtime = mtime
|
||||||
|
} else if debug {
|
||||||
|
debugResult = fmt.Sprintf("record exists, but mismatch inDisk:%v dbDisk:%v", diskMtime, mtime)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugResult = "record does not exist"
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
l.Debugf("virtual mtime: value get result:%v path:%s", debugResult, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return diskMtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VirtualMtimeRepo) DeleteMtime(path string) {
|
||||||
|
r.ns.Delete(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VirtualMtimeRepo) Drop() {
|
||||||
|
r.ns.Reset()
|
||||||
|
}
|
80
internal/db/virtualmtime_test.go
Normal file
80
internal/db/virtualmtime_test.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (C) 2015 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/.
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVirtualMtimeRepo(t *testing.T) {
|
||||||
|
ldb, err := leveldb.Open(storage.NewMemStorage(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A few repos so we can ensure they don't pollute each other
|
||||||
|
repo1 := NewVirtualMtimeRepo(ldb, "folder1")
|
||||||
|
repo2 := NewVirtualMtimeRepo(ldb, "folder2")
|
||||||
|
|
||||||
|
// Since GetMtime() returns its argument if the key isn't found or is outdated, we need a dummy to test with.
|
||||||
|
dummyTime := time.Date(2001, time.February, 3, 4, 5, 6, 0, time.UTC)
|
||||||
|
|
||||||
|
// Some times to test with
|
||||||
|
time1 := time.Date(2001, time.February, 3, 4, 5, 7, 0, time.UTC)
|
||||||
|
time2 := time.Date(2010, time.February, 3, 4, 5, 6, 0, time.UTC)
|
||||||
|
|
||||||
|
file1 := "file1.txt"
|
||||||
|
|
||||||
|
// Files are not present at the start
|
||||||
|
|
||||||
|
if v := repo1.GetMtime(file1, dummyTime); !v.Equal(dummyTime) {
|
||||||
|
t.Errorf("Mtime should be missing (%v) from repo 1 but it's %v", dummyTime, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := repo2.GetMtime(file1, dummyTime); !v.Equal(dummyTime) {
|
||||||
|
t.Errorf("Mtime should be missing (%v) from repo 2 but it's %v", dummyTime, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo1.UpdateMtime(file1, time1, time2)
|
||||||
|
|
||||||
|
// Now it should return time2 only when time1 is passed as the argument
|
||||||
|
|
||||||
|
if v := repo1.GetMtime(file1, time1); !v.Equal(time2) {
|
||||||
|
t.Errorf("Mtime should be %v for disk time %v but we got %v", time2, time1, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := repo1.GetMtime(file1, dummyTime); !v.Equal(dummyTime) {
|
||||||
|
t.Errorf("Mtime should be %v for disk time %v but we got %v", dummyTime, dummyTime, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// repo2 shouldn't know about this file
|
||||||
|
|
||||||
|
if v := repo2.GetMtime(file1, time1); !v.Equal(time1) {
|
||||||
|
t.Errorf("Mtime should be %v for disk time %v in repo 2 but we got %v", time1, time1, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo1.DeleteMtime(file1)
|
||||||
|
|
||||||
|
// Now it should be gone
|
||||||
|
|
||||||
|
if v := repo1.GetMtime(file1, time1); !v.Equal(time1) {
|
||||||
|
t.Errorf("Mtime should be %v for disk time %v but we got %v", time1, time1, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try again but with Drop()
|
||||||
|
|
||||||
|
repo1.UpdateMtime(file1, time1, time2)
|
||||||
|
repo1.Drop()
|
||||||
|
|
||||||
|
if v := repo1.GetMtime(file1, time1); !v.Equal(time1) {
|
||||||
|
t.Errorf("Mtime should be %v for disk time %v but we got %v", time1, time1, v)
|
||||||
|
}
|
||||||
|
}
|
@ -163,10 +163,6 @@ func (m *Model) StartFolderRW(folder string) {
|
|||||||
p.versioner = factory(folder, cfg.Path(), cfg.Versioning.Params)
|
p.versioner = factory(folder, cfg.Path(), cfg.Versioning.Params)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.LenientMtimes {
|
|
||||||
l.Infof("Folder %q is running with LenientMtimes workaround. Syncing may not work properly.", folder)
|
|
||||||
}
|
|
||||||
|
|
||||||
go p.Serve()
|
go p.Serve()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1222,6 +1218,7 @@ nextSub:
|
|||||||
TempNamer: defTempNamer,
|
TempNamer: defTempNamer,
|
||||||
TempLifetime: time.Duration(m.cfg.Options().KeepTemporariesH) * time.Hour,
|
TempLifetime: time.Duration(m.cfg.Options().KeepTemporariesH) * time.Hour,
|
||||||
CurrentFiler: cFiler{m, folder},
|
CurrentFiler: cFiler{m, folder},
|
||||||
|
MtimeRepo: db.NewVirtualMtimeRepo(m.db, folderCfg.ID),
|
||||||
IgnorePerms: folderCfg.IgnorePerms,
|
IgnorePerms: folderCfg.IgnorePerms,
|
||||||
AutoNormalize: folderCfg.AutoNormalize,
|
AutoNormalize: folderCfg.AutoNormalize,
|
||||||
Hashers: m.numHashers(folder),
|
Hashers: m.numHashers(folder),
|
||||||
|
@ -59,13 +59,13 @@ type rwFolder struct {
|
|||||||
|
|
||||||
model *Model
|
model *Model
|
||||||
progressEmitter *ProgressEmitter
|
progressEmitter *ProgressEmitter
|
||||||
|
virtualMtimeRepo *db.VirtualMtimeRepo
|
||||||
|
|
||||||
folder string
|
folder string
|
||||||
dir string
|
dir string
|
||||||
scanIntv time.Duration
|
scanIntv time.Duration
|
||||||
versioner versioner.Versioner
|
versioner versioner.Versioner
|
||||||
ignorePerms bool
|
ignorePerms bool
|
||||||
lenientMtimes bool
|
|
||||||
copiers int
|
copiers int
|
||||||
pullers int
|
pullers int
|
||||||
shortID uint64
|
shortID uint64
|
||||||
@ -89,12 +89,12 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo
|
|||||||
|
|
||||||
model: m,
|
model: m,
|
||||||
progressEmitter: m.progressEmitter,
|
progressEmitter: m.progressEmitter,
|
||||||
|
virtualMtimeRepo: db.NewVirtualMtimeRepo(m.db, cfg.ID),
|
||||||
|
|
||||||
folder: cfg.ID,
|
folder: cfg.ID,
|
||||||
dir: cfg.Path(),
|
dir: cfg.Path(),
|
||||||
scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second,
|
scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second,
|
||||||
ignorePerms: cfg.IgnorePerms,
|
ignorePerms: cfg.IgnorePerms,
|
||||||
lenientMtimes: cfg.LenientMtimes,
|
|
||||||
copiers: cfg.Copiers,
|
copiers: cfg.Copiers,
|
||||||
pullers: cfg.Pullers,
|
pullers: cfg.Pullers,
|
||||||
shortID: shortID,
|
shortID: shortID,
|
||||||
@ -861,30 +861,25 @@ func (p *rwFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocks
|
|||||||
|
|
||||||
// shortcutFile sets file mode and modification time, when that's the only
|
// shortcutFile sets file mode and modification time, when that's the only
|
||||||
// thing that has changed.
|
// thing that has changed.
|
||||||
func (p *rwFolder) shortcutFile(file protocol.FileInfo) (err error) {
|
func (p *rwFolder) shortcutFile(file protocol.FileInfo) error {
|
||||||
realName := filepath.Join(p.dir, file.Name)
|
realName := filepath.Join(p.dir, file.Name)
|
||||||
if !p.ignorePerms {
|
if !p.ignorePerms {
|
||||||
err = os.Chmod(realName, os.FileMode(file.Flags&0777))
|
if err := os.Chmod(realName, os.FileMode(file.Flags&0777)); err != nil {
|
||||||
if err != nil {
|
l.Infof("Puller (folder %q, file %q): shortcut: chmod: %v", p.folder, file.Name, err)
|
||||||
l.Infof("Puller (folder %q, file %q): shortcut: %v", p.folder, file.Name, err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t := time.Unix(file.Modified, 0)
|
t := time.Unix(file.Modified, 0)
|
||||||
err = os.Chtimes(realName, t, t)
|
if err := os.Chtimes(realName, t, t); err != nil {
|
||||||
|
// Try using virtual mtimes
|
||||||
|
info, err := os.Stat(realName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if p.lenientMtimes {
|
l.Infof("Puller (folder %q, file %q): shortcut: unable to stat file: %v", p.folder, file.Name, err)
|
||||||
err = nil
|
return err
|
||||||
// We accept the failure with a warning here and allow the sync to
|
|
||||||
// continue. We'll sync the new mtime back to the other devices later.
|
|
||||||
// If they have the same problem & setting, we might never get in
|
|
||||||
// sync.
|
|
||||||
l.Infof("Puller (folder %q, file %q): shortcut: %v (continuing anyway as requested)", p.folder, file.Name, err)
|
|
||||||
} else {
|
|
||||||
l.Infof("Puller (folder %q, file %q): shortcut: %v", p.folder, file.Name, err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.virtualMtimeRepo.UpdateMtime(file.Name, info.ModTime(), t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This may have been a conflict. We should merge the version vectors so
|
// This may have been a conflict. We should merge the version vectors so
|
||||||
@ -894,7 +889,7 @@ func (p *rwFolder) shortcutFile(file protocol.FileInfo) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.dbUpdates <- file
|
p.dbUpdates <- file
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcutSymlink changes the symlinks type if necessary.
|
// shortcutSymlink changes the symlinks type if necessary.
|
||||||
@ -1078,15 +1073,11 @@ func (p *rwFolder) performFinish(state *sharedPullerState) {
|
|||||||
t := time.Unix(state.file.Modified, 0)
|
t := time.Unix(state.file.Modified, 0)
|
||||||
err = os.Chtimes(state.tempName, t, t)
|
err = os.Chtimes(state.tempName, t, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if p.lenientMtimes {
|
// First try using virtual mtimes
|
||||||
// We accept the failure with a warning here and allow the sync to
|
if info, err := os.Stat(state.tempName); err != nil {
|
||||||
// continue. We'll sync the new mtime back to the other devices later.
|
l.Infof("Puller (folder %q, file %q): final: unable to stat file: %v", p.folder, state.file.Name, err)
|
||||||
// If they have the same problem & setting, we might never get in
|
|
||||||
// sync.
|
|
||||||
l.Infof("Puller (folder %q, file %q): final: %v (continuing anyway as requested)", p.folder, state.file.Name, err)
|
|
||||||
} else {
|
} else {
|
||||||
l.Warnln("Puller: final:", err)
|
p.virtualMtimeRepo.UpdateMtime(state.file.Name, info.ModTime(), t)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/syncthing/protocol"
|
"github.com/syncthing/protocol"
|
||||||
|
"github.com/syncthing/syncthing/internal/db"
|
||||||
"github.com/syncthing/syncthing/internal/ignore"
|
"github.com/syncthing/syncthing/internal/ignore"
|
||||||
"github.com/syncthing/syncthing/internal/osutil"
|
"github.com/syncthing/syncthing/internal/osutil"
|
||||||
"github.com/syncthing/syncthing/internal/symlinks"
|
"github.com/syncthing/syncthing/internal/symlinks"
|
||||||
@ -52,6 +53,8 @@ type Walker struct {
|
|||||||
TempLifetime time.Duration
|
TempLifetime time.Duration
|
||||||
// If CurrentFiler is not nil, it is queried for the current file before rescanning.
|
// If CurrentFiler is not nil, it is queried for the current file before rescanning.
|
||||||
CurrentFiler CurrentFiler
|
CurrentFiler CurrentFiler
|
||||||
|
// If MtimeRepo is not nil, it is used to provide mtimes on systems that don't support setting arbirtary mtimes.
|
||||||
|
MtimeRepo *db.VirtualMtimeRepo
|
||||||
// If IgnorePerms is true, changes to permission bits will not be
|
// If IgnorePerms is true, changes to permission bits will not be
|
||||||
// detected. Scanned files will get zero permission bits and the
|
// detected. Scanned files will get zero permission bits and the
|
||||||
// NoPermissionBits flag set.
|
// NoPermissionBits flag set.
|
||||||
@ -138,15 +141,20 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mtime := info.ModTime()
|
||||||
|
if w.MtimeRepo != nil {
|
||||||
|
mtime = w.MtimeRepo.GetMtime(rn, mtime)
|
||||||
|
}
|
||||||
|
|
||||||
if w.TempNamer != nil && w.TempNamer.IsTemporary(rn) {
|
if w.TempNamer != nil && w.TempNamer.IsTemporary(rn) {
|
||||||
// A temporary file
|
// A temporary file
|
||||||
if debug {
|
if debug {
|
||||||
l.Debugln("temporary:", rn)
|
l.Debugln("temporary:", rn)
|
||||||
}
|
}
|
||||||
if info.Mode().IsRegular() && info.ModTime().Add(w.TempLifetime).Before(now) {
|
if info.Mode().IsRegular() && mtime.Add(w.TempLifetime).Before(now) {
|
||||||
os.Remove(p)
|
os.Remove(p)
|
||||||
if debug {
|
if debug {
|
||||||
l.Debugln("removing temporary:", rn, info.ModTime())
|
l.Debugln("removing temporary:", rn, mtime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -298,7 +306,7 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
Name: rn,
|
Name: rn,
|
||||||
Version: cf.Version.Update(w.ShortID),
|
Version: cf.Version.Update(w.ShortID),
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
Modified: info.ModTime().Unix(),
|
Modified: mtime.Unix(),
|
||||||
}
|
}
|
||||||
if debug {
|
if debug {
|
||||||
l.Debugln("dir:", p, f)
|
l.Debugln("dir:", p, f)
|
||||||
@ -325,13 +333,13 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
// - has the same size as previously
|
// - has the same size as previously
|
||||||
cf, ok = w.CurrentFiler.CurrentFile(rn)
|
cf, ok = w.CurrentFiler.CurrentFile(rn)
|
||||||
permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, curMode)
|
permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, curMode)
|
||||||
if ok && permUnchanged && !cf.IsDeleted() && cf.Modified == info.ModTime().Unix() && !cf.IsDirectory() &&
|
if ok && permUnchanged && !cf.IsDeleted() && cf.Modified == mtime.Unix() && !cf.IsDirectory() &&
|
||||||
!cf.IsSymlink() && !cf.IsInvalid() && cf.Size() == info.Size() {
|
!cf.IsSymlink() && !cf.IsInvalid() && cf.Size() == info.Size() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
l.Debugln("rescan:", cf, info.ModTime().Unix(), info.Mode()&os.ModePerm)
|
l.Debugln("rescan:", cf, mtime.Unix(), info.Mode()&os.ModePerm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +352,7 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
Name: rn,
|
Name: rn,
|
||||||
Version: cf.Version.Update(w.ShortID),
|
Version: cf.Version.Update(w.ShortID),
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
Modified: info.ModTime().Unix(),
|
Modified: mtime.Unix(),
|
||||||
}
|
}
|
||||||
if debug {
|
if debug {
|
||||||
l.Debugln("to hash:", p, f)
|
l.Debugln("to hash:", p, f)
|
||||||
|
Loading…
Reference in New Issue
Block a user