Fix handling of changed/deleted directories (fixes #231)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"sync"
|
||||
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
@@ -24,7 +24,8 @@ type blockQueue struct {
|
||||
outbox chan bqBlock
|
||||
|
||||
queued []bqBlock
|
||||
qlen uint32
|
||||
|
||||
mut sync.Mutex
|
||||
}
|
||||
|
||||
func newBlockQueue() *blockQueue {
|
||||
@@ -37,6 +38,9 @@ func newBlockQueue() *blockQueue {
|
||||
}
|
||||
|
||||
func (q *blockQueue) addBlock(a bqAdd) {
|
||||
q.mut.Lock()
|
||||
defer q.mut.Unlock()
|
||||
|
||||
// If we already have it queued, return
|
||||
for _, b := range q.queued {
|
||||
if b.file.Name == a.file.Name {
|
||||
@@ -74,15 +78,18 @@ func (q *blockQueue) run() {
|
||||
if len(q.queued) == 0 {
|
||||
q.addBlock(<-q.inbox)
|
||||
} else {
|
||||
q.mut.Lock()
|
||||
next := q.queued[0]
|
||||
q.mut.Unlock()
|
||||
select {
|
||||
case a := <-q.inbox:
|
||||
q.addBlock(a)
|
||||
case q.outbox <- next:
|
||||
q.mut.Lock()
|
||||
q.queued = q.queued[1:]
|
||||
q.mut.Unlock()
|
||||
}
|
||||
}
|
||||
atomic.StoreUint32(&q.qlen, uint32(len(q.queued)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +102,7 @@ func (q *blockQueue) get() bqBlock {
|
||||
}
|
||||
|
||||
func (q *blockQueue) empty() bool {
|
||||
var l uint32
|
||||
atomic.LoadUint32(&l)
|
||||
return l == 0
|
||||
q.mut.Lock()
|
||||
defer q.mut.Unlock()
|
||||
return len(q.queued) == 0
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ func (m *Model) LocalSize(repo string) (files, deleted int, bytes int64) {
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
// NeedFiles returns the list of currently needed files and the total size.
|
||||
// NeedSize returns the number and total size of currently needed files.
|
||||
func (m *Model) NeedSize(repo string) (files int, bytes int64) {
|
||||
f, d, b := sizeOf(m.NeedFilesRepo(repo))
|
||||
return f + d, b
|
||||
@@ -241,13 +241,21 @@ func (m *Model) NeedFilesRepo(repo string) []scanner.File {
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Index(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
if debug {
|
||||
l.Debugf("IDX(in): %s / %q: %d files", nodeID, repo, len(fs))
|
||||
l.Debugf("IDX(in): %s %q: %d files", nodeID, repo, len(fs))
|
||||
}
|
||||
|
||||
var files = make([]scanner.File, len(fs))
|
||||
for i := range fs {
|
||||
lamport.Default.Tick(fs[i].Version)
|
||||
files[i] = fileFromFileInfo(fs[i])
|
||||
f := fs[i]
|
||||
lamport.Default.Tick(f.Version)
|
||||
if debug {
|
||||
var flagComment string
|
||||
if f.Flags&protocol.FlagDeleted != 0 {
|
||||
flagComment = " (deleted)"
|
||||
}
|
||||
l.Debugf("IDX(in): %s %q/%q m=%d f=%o%s v=%d (%d blocks)", nodeID, repo, f.Name, f.Modified, f.Flags, flagComment, f.Version, len(f.Blocks))
|
||||
}
|
||||
files[i] = fileFromFileInfo(f)
|
||||
}
|
||||
|
||||
id := m.cm.Get(nodeID)
|
||||
@@ -269,8 +277,16 @@ func (m *Model) IndexUpdate(nodeID string, repo string, fs []protocol.FileInfo)
|
||||
|
||||
var files = make([]scanner.File, len(fs))
|
||||
for i := range fs {
|
||||
lamport.Default.Tick(fs[i].Version)
|
||||
files[i] = fileFromFileInfo(fs[i])
|
||||
f := fs[i]
|
||||
lamport.Default.Tick(f.Version)
|
||||
if debug {
|
||||
var flagComment string
|
||||
if f.Flags&protocol.FlagDeleted != 0 {
|
||||
flagComment = " (deleted)"
|
||||
}
|
||||
l.Debugf("IDXUP(in): %s %q/%q m=%d f=%o%s v=%d (%d blocks)", nodeID, repo, f.Name, f.Modified, f.Flags, flagComment, f.Version, len(f.Blocks))
|
||||
}
|
||||
files[i] = fileFromFileInfo(f)
|
||||
}
|
||||
|
||||
id := m.cm.Get(nodeID)
|
||||
|
||||
@@ -207,7 +207,9 @@ func (p *puller) runRO() {
|
||||
|
||||
func (p *puller) fixupDirectories() {
|
||||
var deleteDirs []string
|
||||
filepath.Walk(p.dir, func(path string, info os.FileInfo, err error) error {
|
||||
var changed = 0
|
||||
|
||||
var walkFn = func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
@@ -221,9 +223,12 @@ func (p *puller) fixupDirectories() {
|
||||
return nil
|
||||
}
|
||||
|
||||
cur := p.model.CurrentGlobalFile(p.repo, rn)
|
||||
cur := p.model.CurrentRepoFile(p.repo, rn)
|
||||
if cur.Name != rn {
|
||||
// No matching dir in current list; weird
|
||||
if debug {
|
||||
l.Debugf("missing dir: %s; %v", rn, cur)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -241,31 +246,59 @@ func (p *puller) fixupDirectories() {
|
||||
}
|
||||
|
||||
if cur.Flags&uint32(os.ModePerm) != uint32(info.Mode()&os.ModePerm) {
|
||||
os.Chmod(path, os.FileMode(cur.Flags)&os.ModePerm)
|
||||
if debug {
|
||||
l.Debugf("restored dir flags: %o -> %v", info.Mode()&os.ModePerm, cur)
|
||||
err := os.Chmod(path, os.FileMode(cur.Flags)&os.ModePerm)
|
||||
if err != nil {
|
||||
l.Warnln("Restoring folder flags: %q: %v", path, err)
|
||||
} else {
|
||||
changed++
|
||||
if debug {
|
||||
l.Debugf("restored dir flags: %o -> %v", info.Mode()&os.ModePerm, cur)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cur.Modified != info.ModTime().Unix() {
|
||||
t := time.Unix(cur.Modified, 0)
|
||||
os.Chtimes(path, t, t)
|
||||
if debug {
|
||||
l.Debugf("restored dir modtime: %d -> %v", info.ModTime().Unix(), cur)
|
||||
err := os.Chtimes(path, t, t)
|
||||
if err != nil {
|
||||
l.Warnln("Restoring folder modtime: %q: %v", path, err)
|
||||
} else {
|
||||
changed++
|
||||
if debug {
|
||||
l.Debugf("restored dir modtime: %d -> %v", info.ModTime().Unix(), cur)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Delete any queued directories
|
||||
for i := len(deleteDirs) - 1; i >= 0; i-- {
|
||||
if debug {
|
||||
l.Debugln("delete dir:", deleteDirs[i])
|
||||
for {
|
||||
deleteDirs = nil
|
||||
changed = 0
|
||||
filepath.Walk(p.dir, walkFn)
|
||||
|
||||
var deleted = 0
|
||||
// Delete any queued directories
|
||||
for i := len(deleteDirs) - 1; i >= 0; i-- {
|
||||
dir := deleteDirs[i]
|
||||
if debug {
|
||||
l.Debugln("delete dir:", dir)
|
||||
}
|
||||
err := os.Remove(dir)
|
||||
if err != nil {
|
||||
l.Warnln(err)
|
||||
} else {
|
||||
deleted++
|
||||
}
|
||||
}
|
||||
err := os.Remove(deleteDirs[i])
|
||||
if err != nil {
|
||||
l.Warnln(err)
|
||||
|
||||
if debug {
|
||||
l.Debugf("changed %d, deleted %d dirs", changed, deleted)
|
||||
}
|
||||
|
||||
if changed+deleted == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,12 +334,23 @@ func (p *puller) handleRequestResult(res requestResult) {
|
||||
func (p *puller) handleBlock(b bqBlock) bool {
|
||||
f := b.file
|
||||
|
||||
// For directories, simply making sure they exist is enough
|
||||
// For directories, making sure they exist is enough.
|
||||
// Deleted directories we mark as handled and delete later.
|
||||
if f.Flags&protocol.FlagDirectory != 0 {
|
||||
path := filepath.Join(p.dir, f.Name)
|
||||
_, err := os.Stat(path)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
os.MkdirAll(path, 0777)
|
||||
if f.Flags&protocol.FlagDeleted == 0 {
|
||||
path := filepath.Join(p.dir, f.Name)
|
||||
_, err := os.Stat(path)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
if debug {
|
||||
l.Debugf("create dir: %v", f)
|
||||
}
|
||||
err = os.MkdirAll(path, 0777)
|
||||
if err != nil {
|
||||
l.Warnf("Create folder: %q: %v", path, err)
|
||||
}
|
||||
}
|
||||
} else if debug {
|
||||
l.Debugf("ignore delete dir: %v", f)
|
||||
}
|
||||
p.model.updateLocal(p.repo, f)
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user