diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 6fb62d34..90f0b2e7 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -1497,13 +1497,16 @@ func (f jsonFileInfo) MarshalJSON() ([]byte, error) { "size": f.Size, "permissions": fmt.Sprintf("%#o", f.Permissions), "deleted": f.Deleted, - "invalid": f.Invalid, + "invalid": protocol.FileInfo(f).IsInvalid(), + "ignored": protocol.FileInfo(f).IsIgnored(), + "mustRescan": protocol.FileInfo(f).MustRescan(), "noPermissions": f.NoPermissions, "modified": protocol.FileInfo(f).ModTime(), "modifiedBy": f.ModifiedBy.String(), "sequence": f.Sequence, "numBlocks": len(f.Blocks), "version": jsonVersionVector(f.Version), + "localFlags": f.LocalFlags, }) } @@ -1516,11 +1519,16 @@ func (f jsonDBFileInfo) MarshalJSON() ([]byte, error) { "size": f.Size, "permissions": fmt.Sprintf("%#o", f.Permissions), "deleted": f.Deleted, - "invalid": f.Invalid, + "invalid": db.FileInfoTruncated(f).IsInvalid(), + "ignored": db.FileInfoTruncated(f).IsIgnored(), + "mustRescan": db.FileInfoTruncated(f).MustRescan(), "noPermissions": f.NoPermissions, "modified": db.FileInfoTruncated(f).ModTime(), "modifiedBy": f.ModifiedBy.String(), "sequence": f.Sequence, + "numBlocks": nil, // explicitly unknown + "version": jsonVersionVector(f.Version), + "localFlags": f.LocalFlags, }) } diff --git a/lib/db/blockmap_test.go b/lib/db/blockmap_test.go index daa668d9..b7446272 100644 --- a/lib/db/blockmap_test.go +++ b/lib/db/blockmap_test.go @@ -96,11 +96,8 @@ func TestBlockMapAddUpdateWipe(t *testing.T) { return true }) - f3.Permissions = f1.Permissions - f3.Deleted = f1.Deleted - f3.Invalid = f1.Invalid f1.Deleted = true - f2.Invalid = true + f2.LocalFlags = protocol.FlagLocalMustRescan // one of the invalid markers // Should remove err = m.Update([]protocol.FileInfo{f1, f2, f3}) @@ -145,14 +142,11 @@ func TestBlockMapAddUpdateWipe(t *testing.T) { } f1.Deleted = false - f1.Invalid = false - f1.Permissions = 0 + f1.LocalFlags = 0 f2.Deleted = false - f2.Invalid = false - f2.Permissions = 0 + f2.LocalFlags = 0 f3.Deleted = false - f3.Invalid = false - f3.Permissions = 0 + f3.LocalFlags = 0 } func TestBlockFinderLookup(t *testing.T) { diff --git a/lib/db/leveldb.go b/lib/db/leveldb.go index 728d5533..358de868 100644 --- a/lib/db/leveldb.go +++ b/lib/db/leveldb.go @@ -61,7 +61,7 @@ func (vl VersionList) update(folder, device []byte, file protocol.FileInfo, db * nv := FileVersion{ Device: device, Version: file.Version, - Invalid: file.Invalid, + Invalid: file.IsInvalid(), } for i, v := range vl.Versions { switch v.Version.Compare(file.Version) { diff --git a/lib/db/leveldb_dbinstance.go b/lib/db/leveldb_dbinstance.go index 0d43439e..18ef4117 100644 --- a/lib/db/leveldb_dbinstance.go +++ b/lib/db/leveldb_dbinstance.go @@ -115,8 +115,9 @@ func (db *Instance) updateFiles(folder, device []byte, fs []protocol.FileInfo, m err = ef.Unmarshal(bs) } - // The Invalid flag might change without the version being bumped. - if err == nil && ef.Version.Equal(f.Version) && ef.Invalid == f.Invalid { + // Local flags or the invalid bit might change without the version + // being bumped. The IsInvalid() method handles both. + if err == nil && ef.Version.Equal(f.Version) && ef.IsInvalid() == f.IsInvalid() { continue } diff --git a/lib/db/leveldb_dbinstance_updateschema.go b/lib/db/leveldb_dbinstance_updateschema.go index 5ef2a799..609331df 100644 --- a/lib/db/leveldb_dbinstance_updateschema.go +++ b/lib/db/leveldb_dbinstance_updateschema.go @@ -13,7 +13,7 @@ import ( "github.com/syndtr/goleveldb/leveldb/util" ) -const dbVersion = 4 +const dbVersion = 5 func (db *Instance) updateSchema() { miscDB := NewNamespacedKV(db, string(KeyTypeMiscData)) @@ -23,8 +23,6 @@ func (db *Instance) updateSchema() { return } - l.Infof("Updating database schema version from %v to %v...", prevVersion, dbVersion) - if prevVersion < 1 { db.updateSchema0to1() } @@ -38,6 +36,9 @@ func (db *Instance) updateSchema() { if prevVersion == 3 { db.updateSchema3to4() } + if prevVersion < 5 { + db.updateSchema4to5() + } miscDB.PutInt64("dbVersion", dbVersion) } @@ -94,7 +95,7 @@ func (db *Instance) updateSchema0to1() { } // Add invalid files to global list - if f.Invalid { + if f.IsInvalid() { gk = db.globalKeyInto(gk, folder, name) if t.updateGlobal(gk, folder, device, f, meta) { if _, ok := changedFolders[string(folder)]; !ok { @@ -172,3 +173,33 @@ func (db *Instance) updateSchema3to4() { db.updateSchema2to3() } + +func (db *Instance) updateSchema4to5() { + // For every local file with the Invalid bit set, clear the Invalid bit and + // set LocalFlags = FlagLocalIgnored. + + t := db.newReadWriteTransaction() + defer t.close() + + var dk []byte + + for _, folderStr := range db.ListFolders() { + folder := []byte(folderStr) + db.withHave(folder, protocol.LocalDeviceID[:], nil, false, func(f FileIntf) bool { + if !f.IsInvalid() { + return true + } + + fi := f.(protocol.FileInfo) + fi.RawInvalid = false + fi.LocalFlags = protocol.FlagLocalIgnored + bs, _ := fi.Marshal() + + dk = db.deviceKeyInto(dk, folder, protocol.LocalDeviceID[:], []byte(fi.Name)) + t.Put(dk, bs) + + t.checkFlush() + return true + }) + } +} diff --git a/lib/db/leveldb_test.go b/lib/db/leveldb_test.go index e724271c..1e28cde8 100644 --- a/lib/db/leveldb_test.go +++ b/lib/db/leveldb_test.go @@ -154,7 +154,7 @@ func TestDropIndexIDs(t *testing.T) { } } -func TestInvalidFiles(t *testing.T) { +func TestIgnoredFiles(t *testing.T) { ldb, err := openJSONS("testdata/v0.14.48-ignoredfiles.db.jsons") if err != nil { t.Fatal(err) @@ -192,27 +192,42 @@ func TestInvalidFiles(t *testing.T) { // }, // }) + // Local files should have the "ignored" bit in addition to just being + // generally invalid if we want to look at the simulation of that bit. + fi, ok := fs.Get(protocol.LocalDeviceID, "foo") if !ok { t.Fatal("foo should exist") } - if !fi.Invalid { + if !fi.IsInvalid() { t.Error("foo should be invalid") } + if !fi.IsIgnored() { + t.Error("foo should be ignored") + } fi, ok = fs.Get(protocol.LocalDeviceID, "bar") if !ok { t.Fatal("bar should exist") } - if fi.Invalid { + if fi.IsInvalid() { t.Error("bar should not be invalid") } + if fi.IsIgnored() { + t.Error("bar should not be ignored") + } + + // Remote files have the invalid bit as usual, and the IsInvalid() method + // should pick this up too. fi, ok = fs.Get(protocol.DeviceID{42}, "baz") if !ok { t.Fatal("baz should exist") } - if !fi.Invalid { + if !fi.IsInvalid() { + t.Error("baz should be invalid") + } + if !fi.IsInvalid() { t.Error("baz should be invalid") } @@ -220,7 +235,10 @@ func TestInvalidFiles(t *testing.T) { if !ok { t.Fatal("quux should exist") } - if fi.Invalid { + if fi.IsInvalid() { + t.Error("quux should not be invalid") + } + if fi.IsInvalid() { t.Error("quux should not be invalid") } } @@ -245,13 +263,13 @@ func init() { }, remoteDevice0: { protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, }, remoteDevice1: { protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true}, - protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true}, + protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, }, } } diff --git a/lib/db/leveldb_transactions.go b/lib/db/leveldb_transactions.go index 2a5b4727..ae07445e 100644 --- a/lib/db/leveldb_transactions.go +++ b/lib/db/leveldb_transactions.go @@ -85,7 +85,7 @@ func (t readWriteTransaction) insertFile(fk, folder, device []byte, file protoco // file. If the device is already present in the list, the version is updated. // If the file does not have an entry in the global list, it is created. func (t readWriteTransaction) updateGlobal(gk, folder, device []byte, file protocol.FileInfo, meta *metadataTracker) bool { - l.Debugf("update global; folder=%q device=%v file=%q version=%v invalid=%v", folder, protocol.DeviceIDFromBytes(device), file.Name, file.Version, file.Invalid) + l.Debugf("update global; folder=%q device=%v file=%q version=%v invalid=%v", folder, protocol.DeviceIDFromBytes(device), file.Name, file.Version, file.IsInvalid()) var fl VersionList if svl, err := t.Get(gk, nil); err == nil { diff --git a/lib/db/set.go b/lib/db/set.go index cdf155ed..314ed453 100644 --- a/lib/db/set.go +++ b/lib/db/set.go @@ -147,7 +147,7 @@ func (s *FileSet) Update(device protocol.DeviceID, fs []protocol.FileInfo) { for _, nf := range oldFs { dk = s.db.deviceKeyInto(dk, folder, device[:], []byte(osutil.NormalizedFilename(nf.Name))) ef, ok := s.db.getFile(dk) - if ok && ef.Version.Equal(nf.Version) && ef.Invalid == nf.Invalid { + if ok && ef.Version.Equal(nf.Version) && ef.IsInvalid() == nf.IsInvalid() { continue } diff --git a/lib/db/set_test.go b/lib/db/set_test.go index a8b61e66..866feb67 100644 --- a/lib/db/set_test.go +++ b/lib/db/set_test.go @@ -117,7 +117,7 @@ func (l fileList) String() string { func TestGlobalSet(t *testing.T) { ldb := db.OpenMemory() - m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) local0 := fileList{ protocol.FileInfo{Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, @@ -332,20 +332,20 @@ func TestGlobalSet(t *testing.T) { func TestNeedWithInvalid(t *testing.T) { ldb := db.OpenMemory() - s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) localHave := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, } remote0Have := fileList{ protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, } remote1Have := fileList{ protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true}, - protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true}, + protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, } expectedNeed := fileList{ @@ -369,16 +369,16 @@ func TestNeedWithInvalid(t *testing.T) { func TestUpdateToInvalid(t *testing.T) { ldb := db.OpenMemory() - folder := "test)" + folder := "test" s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) f := db.NewBlockFinder(ldb) localHave := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), LocalFlags: protocol.FlagLocalIgnored}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Invalid: true}, + protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, LocalFlags: protocol.FlagLocalIgnored}, } replace(s, protocol.LocalDeviceID, localHave) @@ -391,10 +391,13 @@ func TestUpdateToInvalid(t *testing.T) { } oldBlockHash := localHave[1].Blocks[0].Hash - localHave[1].Invalid = true + + localHave[1].LocalFlags = protocol.FlagLocalIgnored localHave[1].Blocks = nil - localHave[4].Invalid = false + + localHave[4].LocalFlags = 0 localHave[4].Blocks = genBlocks(3) + s.Update(protocol.LocalDeviceID, append(fileList{}, localHave[1], localHave[4])) have = fileList(haveList(s, protocol.LocalDeviceID)) @@ -425,19 +428,19 @@ func TestUpdateToInvalid(t *testing.T) { func TestInvalidAvailability(t *testing.T) { ldb := db.OpenMemory() - s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) remote0Have := fileList{ protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, - protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, } remote1Have := fileList{ protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, - protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true}, - protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true}, + protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true}, } replace(s, remoteDevice0, remote0Have) @@ -463,7 +466,7 @@ func TestInvalidAvailability(t *testing.T) { func TestGlobalReset(t *testing.T) { ldb := db.OpenMemory() - m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) local := []protocol.FileInfo{ {Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, @@ -501,7 +504,7 @@ func TestGlobalReset(t *testing.T) { func TestNeed(t *testing.T) { ldb := db.OpenMemory() - m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) local := []protocol.FileInfo{ {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, @@ -539,7 +542,7 @@ func TestNeed(t *testing.T) { func TestSequence(t *testing.T) { ldb := db.OpenMemory() - m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) local1 := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, @@ -623,7 +626,7 @@ func TestGlobalNeedWithInvalid(t *testing.T) { rem0 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true}, + protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: remoteDevice0.Short(), Value: 1002}}}}, } @@ -632,8 +635,8 @@ func TestGlobalNeedWithInvalid(t *testing.T) { rem1 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true}, - protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true, ModifiedS: 10}, + protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true}, + protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, RawInvalid: true, ModifiedS: 10}, } replace(s, remoteDevice1, rem1) @@ -690,7 +693,7 @@ func TestCommitted(t *testing.T) { ldb := db.OpenMemory() - s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) local := []protocol.FileInfo{ {Name: string("file"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, @@ -736,7 +739,7 @@ func BenchmarkUpdateOneFile(b *testing.B) { os.RemoveAll("testdata/benchmarkupdate.db") }() - m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) replace(m, protocol.LocalDeviceID, local0) l := local0[4:5] @@ -751,7 +754,7 @@ func BenchmarkUpdateOneFile(b *testing.B) { func TestIndexID(t *testing.T) { ldb := db.OpenMemory() - s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) // The Index ID for some random device is zero by default. id := s.IndexID(remoteDevice0) @@ -783,7 +786,7 @@ func TestIndexID(t *testing.T) { func TestDropFiles(t *testing.T) { ldb := db.OpenMemory() - m := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + m := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) local0 := fileList{ protocol.FileInfo{Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, @@ -846,11 +849,11 @@ func TestDropFiles(t *testing.T) { func TestIssue4701(t *testing.T) { ldb := db.OpenMemory() - s := db.NewFileSet("test)", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) + s := db.NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) localHave := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, - protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Invalid: true}, + protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, LocalFlags: protocol.FlagLocalIgnored}, } s.Update(protocol.LocalDeviceID, localHave) @@ -862,7 +865,7 @@ func TestIssue4701(t *testing.T) { t.Errorf("Expected 1 global file, got %v", c.Files) } - localHave[1].Invalid = false + localHave[1].LocalFlags = 0 s.Update(protocol.LocalDeviceID, localHave) if c := s.LocalSize(); c.Files != 2 { @@ -872,8 +875,8 @@ func TestIssue4701(t *testing.T) { t.Errorf("Expected 2 global files, got %v", c.Files) } - localHave[0].Invalid = true - localHave[1].Invalid = true + localHave[0].LocalFlags = protocol.FlagLocalIgnored + localHave[1].LocalFlags = protocol.FlagLocalIgnored s.Update(protocol.LocalDeviceID, localHave) if c := s.LocalSize(); c.Files != 0 { @@ -887,16 +890,16 @@ func TestIssue4701(t *testing.T) { func TestWithHaveSequence(t *testing.T) { ldb := db.OpenMemory() - folder := "test)" + folder := "test" s := db.NewFileSet(folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), ldb) // The files must not be in alphabetical order localHave := fileList{ - protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Invalid: true}, + protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, RawInvalid: true}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, - protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, + protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true}, } replace(s, protocol.LocalDeviceID, localHave) @@ -1018,7 +1021,7 @@ func TestIssue5007(t *testing.T) { t.Fatalf("Local need incorrect;\n A: %v !=\n E: %v", need[0], fs[0]) } - fs[0].Invalid = true + fs[0].LocalFlags = protocol.FlagLocalIgnored s.Update(protocol.LocalDeviceID, fs) if need := needList(s, protocol.LocalDeviceID); len(need) != 0 { diff --git a/lib/db/structs.go b/lib/db/structs.go index d0c4061d..91aff840 100644 --- a/lib/db/structs.go +++ b/lib/db/structs.go @@ -17,8 +17,19 @@ import ( ) func (f FileInfoTruncated) String() string { - return fmt.Sprintf("File{Name:%q, Permissions:0%o, Modified:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, NoPermissions:%v, BlockSize:%d}", - f.Name, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.Invalid, f.NoPermissions, f.RawBlockSize) + switch f.Type { + case protocol.FileInfoTypeDirectory: + return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v}", + f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions) + case protocol.FileInfoTypeFile: + return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d}", + f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.RawBlockSize) + case protocol.FileInfoTypeSymlink, protocol.FileInfoTypeDeprecatedSymlinkDirectory, protocol.FileInfoTypeDeprecatedSymlinkFile: + return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q}", + f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.SymlinkTarget) + default: + panic("mystery file type detected") + } } func (f FileInfoTruncated) IsDeleted() bool { @@ -26,7 +37,15 @@ func (f FileInfoTruncated) IsDeleted() bool { } func (f FileInfoTruncated) IsInvalid() bool { - return f.Invalid + return f.RawInvalid || f.LocalFlags&protocol.LocalInvalidFlags != 0 +} + +func (f FileInfoTruncated) IsIgnored() bool { + return f.LocalFlags&protocol.FlagLocalIgnored != 0 +} + +func (f FileInfoTruncated) MustRescan() bool { + return f.LocalFlags&protocol.FlagLocalMustRescan != 0 } func (f FileInfoTruncated) IsDirectory() bool { @@ -79,15 +98,15 @@ func (f FileInfoTruncated) FileVersion() protocol.Vector { return f.Version } -func (f FileInfoTruncated) ConvertToInvalidFileInfo(invalidatedBy protocol.ShortID) protocol.FileInfo { +func (f FileInfoTruncated) ConvertToIgnoredFileInfo(by protocol.ShortID) protocol.FileInfo { return protocol.FileInfo{ Name: f.Name, Type: f.Type, ModifiedS: f.ModifiedS, ModifiedNs: f.ModifiedNs, - ModifiedBy: invalidatedBy, - Invalid: true, + ModifiedBy: by, Version: f.Version, RawBlockSize: f.RawBlockSize, + LocalFlags: protocol.FlagLocalIgnored, } } diff --git a/lib/db/structs.pb.go b/lib/db/structs.pb.go index ae4235a3..e11fc535 100644 --- a/lib/db/structs.pb.go +++ b/lib/db/structs.pb.go @@ -66,13 +66,15 @@ type FileInfoTruncated struct { ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"` ModifiedBy github_com_syncthing_syncthing_lib_protocol.ShortID `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.ShortID" json:"modified_by"` Deleted bool `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted,omitempty"` - Invalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"` + RawInvalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"` NoPermissions bool `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"no_permissions,omitempty"` Version protocol.Vector `protobuf:"bytes,9,opt,name=version" json:"version"` Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"` RawBlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"` // repeated BlockInfo Blocks = 16 SymlinkTarget string `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"` + // see bep.proto + LocalFlags uint32 `protobuf:"varint,1000,opt,name=local_flags,json=localFlags,proto3" json:"local_flags,omitempty"` } func (m *FileInfoTruncated) Reset() { *m = FileInfoTruncated{} } @@ -236,10 +238,10 @@ func (m *FileInfoTruncated) MarshalTo(dAtA []byte) (int, error) { } i++ } - if m.Invalid { + if m.RawInvalid { dAtA[i] = 0x38 i++ - if m.Invalid { + if m.RawInvalid { dAtA[i] = 1 } else { dAtA[i] = 0 @@ -292,6 +294,13 @@ func (m *FileInfoTruncated) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintStructs(dAtA, i, uint64(len(m.SymlinkTarget))) i += copy(dAtA[i:], m.SymlinkTarget) } + if m.LocalFlags != 0 { + dAtA[i] = 0xc0 + i++ + dAtA[i] = 0x3e + i++ + i = encodeVarintStructs(dAtA, i, uint64(m.LocalFlags)) + } return i, nil } @@ -462,7 +471,7 @@ func (m *FileInfoTruncated) ProtoSize() (n int) { if m.Deleted { n += 2 } - if m.Invalid { + if m.RawInvalid { n += 2 } if m.NoPermissions { @@ -486,6 +495,9 @@ func (m *FileInfoTruncated) ProtoSize() (n int) { if l > 0 { n += 2 + l + sovStructs(uint64(l)) } + if m.LocalFlags != 0 { + n += 2 + sovStructs(uint64(m.LocalFlags)) + } return n } @@ -913,7 +925,7 @@ func (m *FileInfoTruncated) Unmarshal(dAtA []byte) error { m.Deleted = bool(v != 0) case 7: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Invalid", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RawInvalid", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -930,7 +942,7 @@ func (m *FileInfoTruncated) Unmarshal(dAtA []byte) error { break } } - m.Invalid = bool(v != 0) + m.RawInvalid = bool(v != 0) case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NoPermissions", wireType) @@ -1086,6 +1098,25 @@ func (m *FileInfoTruncated) Unmarshal(dAtA []byte) error { } m.SymlinkTarget = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 1000: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalFlags", wireType) + } + m.LocalFlags = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStructs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LocalFlags |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipStructs(dAtA[iNdEx:]) @@ -1510,45 +1541,47 @@ var ( func init() { proto.RegisterFile("structs.proto", fileDescriptorStructs) } var fileDescriptorStructs = []byte{ - // 630 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcf, 0x6a, 0xdb, 0x4c, - 0x10, 0xb7, 0x62, 0xc9, 0xb1, 0x47, 0x76, 0xbe, 0x64, 0x09, 0x41, 0x18, 0x3e, 0x5b, 0x18, 0x0a, - 0xa2, 0x50, 0xb9, 0x4d, 0xe8, 0xa5, 0xbd, 0xb9, 0x21, 0x10, 0x28, 0x6d, 0x59, 0x87, 0x9c, 0x0a, - 0xc6, 0x92, 0xd6, 0xce, 0x12, 0x79, 0xd7, 0xd1, 0xae, 0x52, 0x94, 0x27, 0xe9, 0x31, 0x4f, 0xd0, - 0xa7, 0xe8, 0x21, 0xc7, 0x9e, 0x7b, 0x08, 0xad, 0xfb, 0x22, 0x45, 0xbb, 0x92, 0xa2, 0xf4, 0xd4, - 0xde, 0xe6, 0x37, 0xff, 0x67, 0x7e, 0x33, 0xd0, 0x13, 0x32, 0x49, 0x43, 0x29, 0xfc, 0x75, 0xc2, - 0x25, 0x47, 0x5b, 0x51, 0xd0, 0x7f, 0xb6, 0xa4, 0xf2, 0x22, 0x0d, 0xfc, 0x90, 0xaf, 0xc6, 0x4b, - 0xbe, 0xe4, 0x63, 0x65, 0x0a, 0xd2, 0x85, 0x42, 0x0a, 0x28, 0x49, 0x87, 0xf4, 0x5f, 0xd6, 0xdc, - 0x45, 0xc6, 0x42, 0x79, 0x41, 0xd9, 0xb2, 0x26, 0xc5, 0x34, 0xd0, 0x19, 0x42, 0x1e, 0x8f, 0x03, - 0xb2, 0xd6, 0x61, 0xa3, 0x2b, 0xb0, 0x4f, 0x68, 0x4c, 0xce, 0x49, 0x22, 0x28, 0x67, 0xe8, 0x39, - 0x6c, 0x5f, 0x6b, 0xd1, 0x31, 0x5c, 0xc3, 0xb3, 0x0f, 0x77, 0xfd, 0x32, 0xc8, 0x3f, 0x27, 0xa1, - 0xe4, 0xc9, 0xc4, 0xbc, 0xbb, 0x1f, 0x36, 0x70, 0xe9, 0x86, 0x0e, 0xa0, 0x15, 0x91, 0x6b, 0x1a, - 0x12, 0x67, 0xcb, 0x35, 0xbc, 0x2e, 0x2e, 0x10, 0x72, 0x60, 0x9b, 0xb2, 0xeb, 0x79, 0x4c, 0x23, - 0xa7, 0xe9, 0x1a, 0x5e, 0x1b, 0x97, 0x70, 0x74, 0x02, 0x76, 0x51, 0xee, 0x2d, 0x15, 0x12, 0xbd, - 0x80, 0x76, 0x91, 0x4b, 0x38, 0x86, 0xdb, 0xf4, 0xec, 0xc3, 0xff, 0xfc, 0x28, 0xf0, 0x6b, 0x5d, - 0x15, 0x25, 0x2b, 0xb7, 0x57, 0xe6, 0xe7, 0xdb, 0x61, 0x63, 0xf4, 0xc5, 0x84, 0xbd, 0xdc, 0xeb, - 0x94, 0x2d, 0xf8, 0x59, 0x92, 0xb2, 0x70, 0x2e, 0x49, 0x84, 0x10, 0x98, 0x6c, 0xbe, 0x22, 0xaa, - 0xfd, 0x0e, 0x56, 0x32, 0x7a, 0x0a, 0xa6, 0xcc, 0xd6, 0xba, 0xc3, 0x9d, 0xc3, 0x83, 0x87, 0x91, - 0xaa, 0xf0, 0x6c, 0x4d, 0xb0, 0xf2, 0xc9, 0xe3, 0x05, 0xbd, 0x21, 0xaa, 0xe9, 0x26, 0x56, 0x32, - 0x72, 0xc1, 0x5e, 0x93, 0x64, 0x45, 0x85, 0xee, 0xd2, 0x74, 0x0d, 0xaf, 0x87, 0xeb, 0x2a, 0xf4, - 0x3f, 0xc0, 0x8a, 0x47, 0x74, 0x41, 0x49, 0x34, 0x13, 0x8e, 0xa5, 0x62, 0x3b, 0xa5, 0x66, 0x9a, - 0x2f, 0x23, 0x22, 0x31, 0x91, 0x24, 0x72, 0x5a, 0x7a, 0x19, 0x05, 0xac, 0xaf, 0x69, 0xfb, 0xd1, - 0x9a, 0xd0, 0x13, 0xd8, 0x61, 0x7c, 0x56, 0xaf, 0xdb, 0x56, 0x0e, 0x3d, 0xc6, 0x3f, 0xd4, 0x2a, - 0xd7, 0x18, 0xeb, 0xfc, 0x1d, 0x63, 0x7d, 0x68, 0x0b, 0x72, 0x95, 0x12, 0x16, 0x12, 0x07, 0x54, - 0xa7, 0x15, 0x46, 0x43, 0xb0, 0xab, 0x39, 0x98, 0x70, 0x6c, 0xd7, 0xf0, 0x2c, 0x5c, 0x8d, 0xf6, - 0x4e, 0xa0, 0x8f, 0x35, 0x87, 0x20, 0x73, 0xba, 0xae, 0xe1, 0x99, 0x93, 0xd7, 0x79, 0x81, 0xef, - 0xf7, 0xc3, 0xa3, 0x7f, 0xb8, 0x41, 0x7f, 0x7a, 0xc1, 0x13, 0x79, 0x7a, 0xfc, 0x90, 0x7d, 0x92, - 0xa1, 0x31, 0x40, 0x10, 0xf3, 0xf0, 0x72, 0xa6, 0x28, 0xe8, 0xe5, 0xd5, 0x27, 0xbb, 0x9b, 0xfb, - 0x61, 0x17, 0xcf, 0x3f, 0x4d, 0x72, 0xc3, 0x94, 0xde, 0x10, 0xdc, 0x09, 0x4a, 0x31, 0x5f, 0x92, - 0xc8, 0x56, 0x31, 0x65, 0x97, 0x33, 0x39, 0x4f, 0x96, 0x44, 0x3a, 0x7b, 0x8a, 0xf7, 0x5e, 0xa1, - 0x3d, 0x53, 0xca, 0xe2, 0x60, 0xbe, 0x1a, 0xd0, 0x7a, 0xc3, 0x53, 0x26, 0x05, 0xda, 0x07, 0x6b, - 0x41, 0x63, 0x22, 0xd4, 0x99, 0x58, 0x58, 0x83, 0x9c, 0xe7, 0x88, 0x26, 0x6a, 0x69, 0x94, 0x08, - 0x75, 0x2e, 0x16, 0xae, 0xab, 0xd4, 0xee, 0x74, 0x66, 0xa1, 0x2e, 0xc4, 0xc2, 0x15, 0xae, 0x93, - 0x6c, 0x2a, 0x53, 0x45, 0xf2, 0x3e, 0x58, 0x41, 0x26, 0x49, 0x79, 0x18, 0x1a, 0x3c, 0xe2, 0xa1, - 0xf5, 0x07, 0x0f, 0x7d, 0x68, 0xeb, 0x3f, 0x3a, 0x3d, 0x56, 0x13, 0x75, 0x71, 0x85, 0x47, 0xef, - 0xa1, 0xa3, 0xa7, 0x98, 0x12, 0x89, 0x3c, 0x68, 0x85, 0x0a, 0x14, 0xbf, 0x03, 0xf9, 0xef, 0x68, - 0x73, 0xc1, 0x7b, 0x61, 0xcf, 0xdb, 0x0b, 0x13, 0x92, 0xff, 0x88, 0x1a, 0xac, 0x89, 0x4b, 0x38, - 0xd9, 0xbf, 0xfb, 0x39, 0x68, 0xdc, 0x6d, 0x06, 0xc6, 0xb7, 0xcd, 0xc0, 0xf8, 0xb1, 0x19, 0x34, - 0x6e, 0x7f, 0x0d, 0x8c, 0xa0, 0xa5, 0x98, 0x3a, 0xfa, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x28, 0xfa, - 0x1e, 0x63, 0x9b, 0x04, 0x00, 0x00, + // 663 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcd, 0x6a, 0xdb, 0x40, + 0x10, 0xb6, 0x62, 0xf9, 0x6f, 0x6c, 0xa7, 0xc9, 0x12, 0x82, 0x30, 0xd4, 0x16, 0x86, 0x82, 0x28, + 0xd4, 0x6e, 0x13, 0x7a, 0x69, 0x6f, 0x6a, 0x08, 0x18, 0x4a, 0x5b, 0xd6, 0x21, 0xa7, 0x82, 0xd1, + 0xcf, 0xda, 0x59, 0x22, 0x6b, 0x1d, 0xed, 0x3a, 0x41, 0x79, 0x92, 0x1e, 0xf3, 0x30, 0x3d, 0xe4, + 0xd8, 0x73, 0x0f, 0x26, 0x75, 0x2f, 0x7d, 0x8c, 0xb2, 0xbb, 0x92, 0xa2, 0xf6, 0xd4, 0xde, 0xe6, + 0x9b, 0x9f, 0x9d, 0x6f, 0x66, 0xbe, 0x85, 0x2e, 0x17, 0xc9, 0x3a, 0x10, 0x7c, 0xb4, 0x4a, 0x98, + 0x60, 0x68, 0x27, 0xf4, 0x7b, 0x2f, 0x16, 0x54, 0x5c, 0xac, 0xfd, 0x51, 0xc0, 0x96, 0xe3, 0x05, + 0x5b, 0xb0, 0xb1, 0x0a, 0xf9, 0xeb, 0xb9, 0x42, 0x0a, 0x28, 0x4b, 0x97, 0xf4, 0x5e, 0x97, 0xd2, + 0x79, 0x1a, 0x07, 0xe2, 0x82, 0xc6, 0x8b, 0x92, 0x15, 0x51, 0x5f, 0xbf, 0x10, 0xb0, 0x68, 0xec, + 0x93, 0x95, 0x2e, 0x1b, 0x5e, 0x41, 0xfb, 0x94, 0x46, 0xe4, 0x9c, 0x24, 0x9c, 0xb2, 0x18, 0xbd, + 0x84, 0xc6, 0xb5, 0x36, 0x2d, 0xc3, 0x36, 0x9c, 0xf6, 0xd1, 0xde, 0x28, 0x2f, 0x1a, 0x9d, 0x93, + 0x40, 0xb0, 0xc4, 0x35, 0xef, 0x37, 0x83, 0x0a, 0xce, 0xd3, 0xd0, 0x21, 0xd4, 0x43, 0x72, 0x4d, + 0x03, 0x62, 0xed, 0xd8, 0x86, 0xd3, 0xc1, 0x19, 0x42, 0x16, 0x34, 0x68, 0x7c, 0xed, 0x45, 0x34, + 0xb4, 0xaa, 0xb6, 0xe1, 0x34, 0x71, 0x0e, 0x87, 0xa7, 0xd0, 0xce, 0xda, 0xbd, 0xa7, 0x5c, 0xa0, + 0x57, 0xd0, 0xcc, 0xde, 0xe2, 0x96, 0x61, 0x57, 0x9d, 0xf6, 0xd1, 0x93, 0x51, 0xe8, 0x8f, 0x4a, + 0xac, 0xb2, 0x96, 0x45, 0xda, 0x1b, 0xf3, 0xcb, 0xdd, 0xa0, 0x32, 0x7c, 0x30, 0x61, 0x5f, 0x66, + 0x4d, 0xe2, 0x39, 0x3b, 0x4b, 0xd6, 0x71, 0xe0, 0x09, 0x12, 0x22, 0x04, 0x66, 0xec, 0x2d, 0x89, + 0xa2, 0xdf, 0xc2, 0xca, 0x46, 0xcf, 0xc1, 0x14, 0xe9, 0x4a, 0x33, 0xdc, 0x3d, 0x3a, 0x7c, 0x1c, + 0xa9, 0x28, 0x4f, 0x57, 0x04, 0xab, 0x1c, 0x59, 0xcf, 0xe9, 0x2d, 0x51, 0xa4, 0xab, 0x58, 0xd9, + 0xc8, 0x86, 0xf6, 0x8a, 0x24, 0x4b, 0xca, 0x35, 0x4b, 0xd3, 0x36, 0x9c, 0x2e, 0x2e, 0xbb, 0xd0, + 0x53, 0x80, 0x25, 0x0b, 0xe9, 0x9c, 0x92, 0x70, 0xc6, 0xad, 0x9a, 0xaa, 0x6d, 0xe5, 0x9e, 0xa9, + 0x5c, 0x46, 0x48, 0x22, 0x22, 0x48, 0x68, 0xd5, 0xf5, 0x32, 0x32, 0x88, 0x9c, 0xc7, 0x35, 0x35, + 0x64, 0xc4, 0xdd, 0xdd, 0x6e, 0x06, 0x80, 0xbd, 0x9b, 0x89, 0xf6, 0x16, 0x6b, 0x43, 0xcf, 0x60, + 0x37, 0x66, 0xb3, 0x32, 0x8f, 0xa6, 0x7a, 0xaa, 0x1b, 0xb3, 0x4f, 0x25, 0x26, 0xa5, 0x0b, 0xb6, + 0xfe, 0xed, 0x82, 0x3d, 0x68, 0x72, 0x72, 0xb5, 0x26, 0x71, 0x40, 0x2c, 0x50, 0xcc, 0x0b, 0x8c, + 0x06, 0xd0, 0x2e, 0xe6, 0x8a, 0xb9, 0xd5, 0xb6, 0x0d, 0xa7, 0x86, 0x8b, 0x51, 0x3f, 0x70, 0xf4, + 0xb9, 0x94, 0xe0, 0xa7, 0x56, 0xc7, 0x36, 0x1c, 0xd3, 0x7d, 0x2b, 0x1b, 0x7c, 0xdf, 0x0c, 0x8e, + 0xff, 0x43, 0x93, 0xa3, 0xe9, 0x05, 0x4b, 0xc4, 0xe4, 0xe4, 0xf1, 0x75, 0x37, 0x45, 0x63, 0x00, + 0x3f, 0x62, 0xc1, 0xe5, 0x4c, 0x9d, 0xa4, 0x2b, 0xbb, 0xbb, 0x7b, 0xdb, 0xcd, 0xa0, 0x83, 0xbd, + 0x1b, 0x57, 0x06, 0xa6, 0xf4, 0x96, 0xe0, 0x96, 0x9f, 0x9b, 0x72, 0x49, 0x3c, 0x5d, 0x46, 0x34, + 0xbe, 0x9c, 0x09, 0x2f, 0x59, 0x10, 0x61, 0xed, 0x2b, 0x1d, 0x74, 0x33, 0xef, 0x99, 0x72, 0xca, + 0x83, 0x46, 0x2c, 0xf0, 0xa2, 0xd9, 0x3c, 0xf2, 0x16, 0xdc, 0xfa, 0xd5, 0x50, 0x17, 0x05, 0xe5, + 0x3b, 0x95, 0xae, 0x4c, 0x62, 0x5f, 0x0d, 0xa8, 0xbf, 0x63, 0xeb, 0x58, 0x70, 0x74, 0x00, 0xb5, + 0x39, 0x8d, 0x08, 0x57, 0xc2, 0xaa, 0x61, 0x0d, 0xe4, 0x43, 0x21, 0x4d, 0xd4, 0x5a, 0x29, 0xe1, + 0x4a, 0x60, 0x35, 0x5c, 0x76, 0xa9, 0xed, 0xea, 0xde, 0x5c, 0x69, 0xaa, 0x86, 0x0b, 0x5c, 0x96, + 0x85, 0xa9, 0x42, 0x85, 0x2c, 0x0e, 0xa0, 0xe6, 0xa7, 0x82, 0xe4, 0x52, 0xd2, 0xe0, 0x8f, 0x4b, + 0xd5, 0xff, 0xba, 0x54, 0x0f, 0x9a, 0xfa, 0xe7, 0x4d, 0x4e, 0xd4, 0xcc, 0x1d, 0x5c, 0xe0, 0xe1, + 0x47, 0x68, 0xe9, 0x29, 0xa6, 0x44, 0x20, 0x07, 0xea, 0x81, 0x02, 0xd9, 0x6f, 0x03, 0xf9, 0xdb, + 0x74, 0x38, 0x53, 0x46, 0x16, 0x97, 0xf4, 0x82, 0x84, 0xc8, 0x5f, 0xa5, 0x06, 0xab, 0xe2, 0x1c, + 0xba, 0x07, 0xf7, 0x3f, 0xfa, 0x95, 0xfb, 0x6d, 0xdf, 0xf8, 0xb6, 0xed, 0x1b, 0x0f, 0xdb, 0x7e, + 0xe5, 0xee, 0x67, 0xdf, 0xf0, 0xeb, 0xea, 0x96, 0xc7, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9a, + 0x4b, 0x16, 0x44, 0xcd, 0x04, 0x00, 0x00, } diff --git a/lib/db/structs.proto b/lib/db/structs.proto index 1aaac22d..03309f1c 100644 --- a/lib/db/structs.proto +++ b/lib/db/structs.proto @@ -31,14 +31,16 @@ message FileInfoTruncated { int32 modified_ns = 11; uint64 modified_by = 12 [(gogoproto.customtype) = "github.com/syncthing/syncthing/lib/protocol.ShortID", (gogoproto.nullable) = false]; bool deleted = 6; - bool invalid = 7; + bool invalid = 7 [(gogoproto.customname) = "RawInvalid"]; bool no_permissions = 8; protocol.Vector version = 9 [(gogoproto.nullable) = false]; int64 sequence = 10; int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"]; - // repeated BlockInfo Blocks = 16 string symlink_target = 17; + + // see bep.proto + uint32 local_flags = 1000; } // For each folder and device we keep one of these to track the current diff --git a/lib/model/folder_sendonly.go b/lib/model/folder_sendonly.go index 584b3f9b..d3a7304c 100644 --- a/lib/model/folder_sendonly.go +++ b/lib/model/folder_sendonly.go @@ -60,7 +60,7 @@ func (f *sendOnlyFolder) pull() bool { if ignores.ShouldIgnore(intf.FileName()) { file := intf.(protocol.FileInfo) - file.Invalidate(f.shortID) + file.SetIgnored(f.shortID) batch = append(batch, file) batchSizeBytes += file.ProtoSize() l.Debugln(f, "Handling ignored file", file) diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index 2ee17268..6070cc11 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -269,7 +269,7 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan switch { case ignores.ShouldIgnore(file.Name): - file.Invalidate(f.shortID) + file.SetIgnored(f.shortID) l.Debugln(f, "Handling ignored file", file) dbUpdateChan <- dbUpdateJob{file, dbUpdateInvalidate} changed++ @@ -297,7 +297,7 @@ func (f *sendReceiveFolder) pullerIteration(ignores *ignore.Matcher, ignoresChan l.Debugln(f, "Needed file is unavailable", file) case runtime.GOOS == "windows" && file.IsSymlink(): - file.Invalidate(f.shortID) + file.SetUnsupported(f.shortID) l.Debugln(f, "Invalidating symlink (unsupported)", file.Name) dbUpdateChan <- dbUpdateJob{file, dbUpdateInvalidate} changed++ diff --git a/lib/model/model.go b/lib/model/model.go index 302f06c5..d859b056 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -862,6 +862,11 @@ func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot if !update { files.Drop(deviceID) } + for i := range fs { + // The local flags should never be transmitted over the wire. Make + // sure they look like they weren't. + fs[i].LocalFlags = 0 + } files.Update(deviceID, fs) events.Default.Log(events.RemoteIndexUpdated, map[string]interface{}{ @@ -1389,7 +1394,7 @@ func (m *Model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem, // The hashes provided part of the request match what we expect to find according // to what we have in the database, yet the content we've read off the filesystem doesn't // Something is fishy, invalidate the file and rescan it. - cf.Invalidate(m.shortID) + cf.SetMustRescan(m.shortID) // Update the index and tell others // The file will temporarily become invalid, which is ok as the content is messed up. @@ -1736,6 +1741,10 @@ func sendIndexTo(prevSequence int64, conn protocol.Connection, folder string, fs f = fi.(protocol.FileInfo) + // Mark the file as invalid if any of the local bad stuff flags are set. + f.RawInvalid = f.IsInvalid() + f.LocalFlags = 0 // never sent externally + if dropSymlinks && f.IsSymlink() { // Do not send index entries with symlinks to clients that can't // handle it. Fixes issue #3802. Once both sides are upgraded, a @@ -2064,23 +2073,23 @@ func (m *Model) internalScanFolderSubdirs(ctx context.Context, folder string, su } switch { - case !f.IsInvalid() && ignores.Match(f.Name).IsIgnored(): - // File was valid at last pass but has been ignored. Set invalid bit. - l.Debugln("setting invalid bit on ignored", f) - nf := f.ConvertToInvalidFileInfo(m.id.Short()) + case !f.IsIgnored() && ignores.Match(f.Name).IsIgnored(): + // File was not ignored at last pass but has been ignored. + l.Debugln("marking file as ignored", f) + nf := f.ConvertToIgnoredFileInfo(m.id.Short()) batch = append(batch, nf) batchSizeBytes += nf.ProtoSize() changes++ - case f.IsInvalid() && !ignores.Match(f.Name).IsIgnored(): + case f.IsIgnored() && !ignores.Match(f.Name).IsIgnored(): // Successfully scanned items are already un-ignored during // the scan, so check whether it is deleted. fallthrough - case !f.IsInvalid() && !f.IsDeleted(): - // The file is valid and not deleted. Lets check if it's - // still here. - // Simply stating it wont do as there are tons of corner - // cases (e.g. parent dir->simlink, missing permissions) + case !f.IsIgnored() && !f.IsDeleted(): + // The file is not ignored and not deleted. Lets check if + // it's still here. Simply stat:ing it wont do as there are + // tons of corner cases (e.g. parent dir->symlink, missing + // permissions) if !osutil.IsDeleted(mtimefs, f.Name) { return true } @@ -2099,7 +2108,7 @@ func (m *Model) internalScanFolderSubdirs(ctx context.Context, folder string, su // counter makes sure we are in conflict with any // other existing versions, which will be resolved // by the normal pulling mechanisms. - if f.IsInvalid() { + if f.IsIgnored() { nf.Version = nf.Version.DropOthers(m.shortID) } diff --git a/lib/model/requests_test.go b/lib/model/requests_test.go index 19261dd5..126a9b84 100644 --- a/lib/model/requests_test.go +++ b/lib/model/requests_test.go @@ -179,7 +179,7 @@ func TestRequestCreateTmpSymlink(t *testing.T) { fc.indexFn = func(folder string, fs []protocol.FileInfo) { for _, f := range fs { if f.Name == name { - if f.Invalid { + if f.IsInvalid() { goodIdx <- struct{}{} } else { t.Fatal("Received index with non-invalid temporary file") @@ -348,7 +348,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) { if _, ok := expected[f.Name]; !ok { t.Errorf("Unexpected file %v was added to index", f.Name) } - if !f.Invalid { + if !f.IsInvalid() { t.Errorf("File %v wasn't marked as invalid", f.Name) } delete(expected, f.Name) @@ -382,7 +382,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) { if _, ok := expected[f.Name]; !ok { t.Fatalf("Unexpected file %v was updated in index", f.Name) } - if f.Invalid { + if f.IsInvalid() { t.Errorf("File %v is still marked as invalid", f.Name) } // The unignored files should only have a local version, @@ -446,10 +446,10 @@ func TestIssue4841(t *testing.T) { // Setup file from remote that was ignored locally m.updateLocals(defaultFolderConfig.ID, []protocol.FileInfo{{ - Name: "foo", - Type: protocol.FileInfoTypeFile, - Invalid: true, - Version: protocol.Vector{}.Update(device2.Short()), + Name: "foo", + Type: protocol.FileInfoTypeFile, + LocalFlags: protocol.FlagLocalIgnored, + Version: protocol.Vector{}.Update(device2.Short()), }}) <-received diff --git a/lib/protocol/bep.pb.go b/lib/protocol/bep.pb.go index d6522731..522f0438 100644 --- a/lib/protocol/bep.pb.go +++ b/lib/protocol/bep.pb.go @@ -302,13 +302,18 @@ type FileInfo struct { ModifiedNs int32 `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3" json:"modified_ns,omitempty"` ModifiedBy ShortID `protobuf:"varint,12,opt,name=modified_by,json=modifiedBy,proto3,customtype=ShortID" json:"modified_by"` Deleted bool `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted,omitempty"` - Invalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"` + RawInvalid bool `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"` NoPermissions bool `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"no_permissions,omitempty"` Version Vector `protobuf:"bytes,9,opt,name=version" json:"version"` Sequence int64 `protobuf:"varint,10,opt,name=sequence,proto3" json:"sequence,omitempty"` RawBlockSize int32 `protobuf:"varint,13,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"` Blocks []BlockInfo `protobuf:"bytes,16,rep,name=Blocks" json:"Blocks"` SymlinkTarget string `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlink_target,omitempty"` + // The local_flags fields stores flags that are relevant to the local + // host only. It is not part of the protocol, doesn't get sent or + // received (we make sure to zero it), nonetheless we need it on our + // struct and to be able to serialize it to/from the database. + LocalFlags uint32 `protobuf:"varint,1000,opt,name=local_flags,json=localFlags,proto3" json:"local_flags,omitempty"` } func (m *FileInfo) Reset() { *m = FileInfo{} } @@ -835,10 +840,10 @@ func (m *FileInfo) MarshalTo(dAtA []byte) (int, error) { } i++ } - if m.Invalid { + if m.RawInvalid { dAtA[i] = 0x38 i++ - if m.Invalid { + if m.RawInvalid { dAtA[i] = 1 } else { dAtA[i] = 0 @@ -905,6 +910,13 @@ func (m *FileInfo) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintBep(dAtA, i, uint64(len(m.SymlinkTarget))) i += copy(dAtA[i:], m.SymlinkTarget) } + if m.LocalFlags != 0 { + dAtA[i] = 0xc0 + i++ + dAtA[i] = 0x3e + i++ + i = encodeVarintBep(dAtA, i, uint64(m.LocalFlags)) + } return i, nil } @@ -1422,7 +1434,7 @@ func (m *FileInfo) ProtoSize() (n int) { if m.Deleted { n += 2 } - if m.Invalid { + if m.RawInvalid { n += 2 } if m.NoPermissions { @@ -1452,6 +1464,9 @@ func (m *FileInfo) ProtoSize() (n int) { if l > 0 { n += 2 + l + sovBep(uint64(l)) } + if m.LocalFlags != 0 { + n += 2 + sovBep(uint64(m.LocalFlags)) + } return n } @@ -2797,7 +2812,7 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error { m.Deleted = bool(v != 0) case 7: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Invalid", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RawInvalid", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -2814,7 +2829,7 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error { break } } - m.Invalid = bool(v != 0) + m.RawInvalid = bool(v != 0) case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NoPermissions", wireType) @@ -3001,6 +3016,25 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error { } m.SymlinkTarget = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 1000: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalFlags", wireType) + } + m.LocalFlags = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBep + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LocalFlags |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipBep(dAtA[iNdEx:]) @@ -4220,116 +4254,118 @@ var ( func init() { proto.RegisterFile("bep.proto", fileDescriptorBep) } var fileDescriptorBep = []byte{ - // 1762 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x8f, 0xdb, 0xc6, - 0x15, 0x5f, 0x4a, 0x94, 0x44, 0x3d, 0x69, 0x37, 0xdc, 0xb1, 0xbd, 0x65, 0x99, 0x8d, 0x44, 0x2b, - 0x76, 0xbc, 0x59, 0x24, 0x6b, 0x37, 0x49, 0x5b, 0xb4, 0x68, 0x0b, 0xe8, 0x0f, 0x77, 0x2d, 0x54, - 0xa6, 0xd4, 0x91, 0xd6, 0xa9, 0x73, 0x28, 0x41, 0x89, 0x23, 0x2d, 0x61, 0x8a, 0xa3, 0x92, 0xd4, - 0xda, 0xca, 0x47, 0xd0, 0x27, 0xe8, 0x45, 0x40, 0x80, 0x9e, 0x0a, 0xf4, 0x83, 0xf8, 0x98, 0xf6, - 0xd0, 0x43, 0x0f, 0x46, 0xb3, 0xbd, 0xf4, 0xd8, 0x4f, 0x50, 0x14, 0x9c, 0x21, 0x25, 0x6a, 0xd7, - 0x0e, 0x7c, 0xc8, 0x89, 0x33, 0xef, 0xfd, 0xe6, 0x0d, 0xdf, 0x6f, 0x7e, 0xef, 0xcd, 0x40, 0x71, - 0x48, 0x66, 0x27, 0x33, 0x9f, 0x86, 0x14, 0x49, 0xec, 0x33, 0xa2, 0xae, 0xfa, 0xe9, 0xc4, 0x09, - 0x2f, 0xe6, 0xc3, 0x93, 0x11, 0x9d, 0x3e, 0x9c, 0xd0, 0x09, 0x7d, 0xc8, 0x3c, 0xc3, 0xf9, 0x98, - 0xcd, 0xd8, 0x84, 0x8d, 0xf8, 0xc2, 0xda, 0x0c, 0x72, 0x8f, 0x89, 0xeb, 0x52, 0x54, 0x85, 0x92, - 0x4d, 0x2e, 0x9d, 0x11, 0x31, 0x3d, 0x6b, 0x4a, 0x14, 0x41, 0x13, 0x8e, 0x8a, 0x18, 0xb8, 0xc9, - 0xb0, 0xa6, 0x24, 0x02, 0x8c, 0x5c, 0x87, 0x78, 0x21, 0x07, 0x64, 0x38, 0x80, 0x9b, 0x18, 0xe0, - 0x3e, 0xec, 0xc5, 0x80, 0x4b, 0xe2, 0x07, 0x0e, 0xf5, 0x94, 0x2c, 0xc3, 0xec, 0x72, 0xeb, 0x53, - 0x6e, 0xac, 0x05, 0x90, 0x7f, 0x4c, 0x2c, 0x9b, 0xf8, 0xe8, 0x63, 0x10, 0xc3, 0xc5, 0x8c, 0xef, - 0xb5, 0xf7, 0xd9, 0x9d, 0x93, 0x24, 0x87, 0x93, 0x27, 0x24, 0x08, 0xac, 0x09, 0x19, 0x2c, 0x66, - 0x04, 0x33, 0x08, 0xfa, 0x0d, 0x94, 0x46, 0x74, 0x3a, 0xf3, 0x49, 0xc0, 0x02, 0x67, 0xd8, 0x8a, - 0xc3, 0x1b, 0x2b, 0x9a, 0x1b, 0x0c, 0x4e, 0x2f, 0xa8, 0xd5, 0x61, 0xb7, 0xe9, 0xce, 0x83, 0x90, - 0xf8, 0x4d, 0xea, 0x8d, 0x9d, 0x09, 0x7a, 0x04, 0x85, 0x31, 0x75, 0x6d, 0xe2, 0x07, 0x8a, 0xa0, - 0x65, 0x8f, 0x4a, 0x9f, 0xc9, 0x9b, 0x60, 0xa7, 0xcc, 0xd1, 0x10, 0x5f, 0xbd, 0xae, 0xee, 0xe0, - 0x04, 0x56, 0xfb, 0x73, 0x06, 0xf2, 0xdc, 0x83, 0x0e, 0x20, 0xe3, 0xd8, 0x9c, 0xa2, 0x46, 0xfe, - 0xea, 0x75, 0x35, 0xd3, 0x6e, 0xe1, 0x8c, 0x63, 0xa3, 0xdb, 0x90, 0x73, 0xad, 0x21, 0x71, 0x63, - 0x72, 0xf8, 0x04, 0xbd, 0x0f, 0x45, 0x9f, 0x58, 0xb6, 0x49, 0x3d, 0x77, 0xc1, 0x28, 0x91, 0xb0, - 0x14, 0x19, 0xba, 0x9e, 0xbb, 0x40, 0x9f, 0x02, 0x72, 0x26, 0x1e, 0xf5, 0x89, 0x39, 0x23, 0xfe, - 0xd4, 0x61, 0x7f, 0x1b, 0x28, 0x22, 0x43, 0xed, 0x73, 0x4f, 0x6f, 0xe3, 0x40, 0x1f, 0xc2, 0x6e, - 0x0c, 0xb7, 0x89, 0x4b, 0x42, 0xa2, 0xe4, 0x18, 0xb2, 0xcc, 0x8d, 0x2d, 0x66, 0x43, 0x8f, 0xe0, - 0xb6, 0xed, 0x04, 0xd6, 0xd0, 0x25, 0x66, 0x48, 0xa6, 0x33, 0xd3, 0xf1, 0x6c, 0xf2, 0x92, 0x04, - 0x4a, 0x9e, 0x61, 0x51, 0xec, 0x1b, 0x90, 0xe9, 0xac, 0xcd, 0x3d, 0xe8, 0x00, 0xf2, 0x33, 0x6b, - 0x1e, 0x10, 0x5b, 0x29, 0x30, 0x4c, 0x3c, 0x8b, 0x58, 0xe2, 0x0a, 0x08, 0x14, 0xf9, 0x3a, 0x4b, - 0x2d, 0xe6, 0x48, 0x58, 0x8a, 0x61, 0xb5, 0xff, 0x66, 0x20, 0xcf, 0x3d, 0xe8, 0xa3, 0x35, 0x4b, - 0xe5, 0xc6, 0x41, 0x84, 0xfa, 0xe7, 0xeb, 0xaa, 0xc4, 0x7d, 0xed, 0x56, 0x8a, 0x35, 0x04, 0x62, - 0x4a, 0x51, 0x6c, 0x8c, 0x0e, 0xa1, 0x68, 0xd9, 0x76, 0x74, 0x7a, 0x24, 0x50, 0xb2, 0x5a, 0xf6, - 0xa8, 0x88, 0x37, 0x06, 0xf4, 0xf3, 0x6d, 0x35, 0x88, 0xd7, 0xf5, 0xf3, 0x36, 0x19, 0x44, 0x47, - 0x31, 0x22, 0x7e, 0xac, 0xe0, 0x1c, 0xdb, 0x4f, 0x8a, 0x0c, 0x4c, 0xbf, 0x77, 0xa1, 0x3c, 0xb5, - 0x5e, 0x9a, 0x01, 0xf9, 0xe3, 0x9c, 0x78, 0x23, 0xc2, 0xe8, 0xca, 0xe2, 0xd2, 0xd4, 0x7a, 0xd9, - 0x8f, 0x4d, 0xa8, 0x02, 0xe0, 0x78, 0xa1, 0x4f, 0xed, 0xf9, 0x88, 0xf8, 0x31, 0x57, 0x29, 0x0b, - 0xfa, 0x29, 0x48, 0x8c, 0x6c, 0xd3, 0xb1, 0x15, 0x49, 0x13, 0x8e, 0xc4, 0x86, 0x1a, 0x27, 0x5e, - 0x60, 0x54, 0xb3, 0xbc, 0x93, 0x21, 0x2e, 0x30, 0x6c, 0xdb, 0x46, 0xbf, 0x02, 0x35, 0x78, 0xee, - 0x44, 0x07, 0xc5, 0x23, 0x85, 0x0e, 0xf5, 0x4c, 0x9f, 0x4c, 0xe9, 0xa5, 0xe5, 0x06, 0x4a, 0x91, - 0x6d, 0xa3, 0x44, 0x88, 0x76, 0x0a, 0x80, 0x63, 0x7f, 0xad, 0x0b, 0x39, 0x16, 0x31, 0x3a, 0x45, - 0x2e, 0xd6, 0xb8, 0x7a, 0xe3, 0x19, 0x3a, 0x81, 0xdc, 0xd8, 0x71, 0x49, 0xa0, 0x64, 0xd8, 0x19, - 0xa2, 0x94, 0xd2, 0x1d, 0x97, 0xb4, 0xbd, 0x31, 0x8d, 0x4f, 0x91, 0xc3, 0x6a, 0xe7, 0x50, 0x62, - 0x01, 0xcf, 0x67, 0xb6, 0x15, 0x92, 0x1f, 0x2c, 0xec, 0x5f, 0x45, 0x90, 0x12, 0xcf, 0xfa, 0xd0, - 0x85, 0xd4, 0xa1, 0x1f, 0xc7, 0xfd, 0x80, 0x57, 0xf7, 0xc1, 0xcd, 0x78, 0xa9, 0x86, 0x80, 0x40, - 0x0c, 0x9c, 0xaf, 0x09, 0xab, 0xa7, 0x2c, 0x66, 0x63, 0xa4, 0x41, 0xe9, 0x7a, 0x11, 0xed, 0xe2, - 0xb4, 0x09, 0x7d, 0x00, 0x30, 0xa5, 0xb6, 0x33, 0x76, 0x88, 0x6d, 0x06, 0x4c, 0x00, 0x59, 0x5c, - 0x4c, 0x2c, 0x7d, 0xa4, 0x44, 0x72, 0x8f, 0x4a, 0xc8, 0x8e, 0x6b, 0x25, 0x99, 0x46, 0x1e, 0xc7, - 0xbb, 0xb4, 0x5c, 0x27, 0xa9, 0x90, 0x64, 0x1a, 0x75, 0x3d, 0x8f, 0x6e, 0x15, 0xaf, 0xc4, 0x00, - 0xbb, 0x1e, 0x4d, 0x17, 0xee, 0x23, 0x28, 0x24, 0x5d, 0x31, 0x3a, 0xcf, 0xad, 0x4a, 0x7a, 0x4a, - 0x46, 0x21, 0x5d, 0xf7, 0x9b, 0x18, 0x86, 0x54, 0x90, 0xd6, 0x52, 0x04, 0xf6, 0xa7, 0xeb, 0x79, - 0xd4, 0x8b, 0xd7, 0x79, 0x78, 0x81, 0x52, 0xd2, 0x84, 0xa3, 0x1c, 0x5e, 0xa7, 0x66, 0x44, 0xdb, - 0x6d, 0x00, 0xc3, 0x85, 0x52, 0x66, 0x5a, 0x7c, 0x2f, 0xd1, 0x62, 0xff, 0x82, 0xfa, 0x61, 0xbb, - 0xb5, 0x59, 0xd1, 0x58, 0xa0, 0x87, 0x00, 0x43, 0x97, 0x8e, 0x9e, 0x9b, 0x8c, 0xd6, 0xdd, 0x28, - 0x62, 0x43, 0xbe, 0x7a, 0x5d, 0x2d, 0x63, 0xeb, 0x45, 0x23, 0x72, 0xf4, 0x9d, 0xaf, 0x09, 0x2e, - 0x0e, 0x93, 0x21, 0xfa, 0x09, 0xe4, 0x99, 0x3d, 0x69, 0x0d, 0xb7, 0x36, 0x09, 0x31, 0x7b, 0x4a, - 0x00, 0x31, 0x30, 0xe2, 0x2a, 0x58, 0x4c, 0x5d, 0xc7, 0x7b, 0x6e, 0x86, 0x96, 0x3f, 0x21, 0xa1, - 0xb2, 0xcf, 0x6f, 0x88, 0xd8, 0x3a, 0x60, 0xc6, 0x5f, 0x8a, 0x7f, 0xfa, 0xa6, 0xba, 0x53, 0xf3, - 0xa0, 0xb8, 0x8e, 0x13, 0x69, 0x90, 0x8e, 0xc7, 0x01, 0x09, 0x99, 0x60, 0xb2, 0x38, 0x9e, 0xad, - 0x65, 0x90, 0x61, 0x0c, 0x70, 0x19, 0x20, 0x10, 0x2f, 0xac, 0xe0, 0x82, 0x49, 0xa3, 0x8c, 0xd9, - 0x38, 0x2a, 0xfc, 0x17, 0xc4, 0x7a, 0x6e, 0x32, 0x07, 0x17, 0x86, 0x14, 0x19, 0x1e, 0x5b, 0xc1, - 0x45, 0xbc, 0xdf, 0xaf, 0x21, 0xcf, 0x0f, 0x02, 0x7d, 0x0e, 0xd2, 0x88, 0xce, 0xbd, 0x70, 0x73, - 0x39, 0xec, 0xa7, 0x7b, 0x0b, 0xf3, 0xc4, 0x99, 0xad, 0x81, 0xb5, 0x53, 0x28, 0xc4, 0x2e, 0x74, - 0x7f, 0xdd, 0xf8, 0xc4, 0xc6, 0x9d, 0x6b, 0x9c, 0x6f, 0xdf, 0x16, 0x97, 0x96, 0x3b, 0xe7, 0x3f, - 0x2f, 0x62, 0x3e, 0xa9, 0xfd, 0x4d, 0x80, 0x02, 0x8e, 0xce, 0x39, 0x08, 0x53, 0xf7, 0x4c, 0x6e, - 0xeb, 0x9e, 0xd9, 0x54, 0x64, 0x66, 0xab, 0x22, 0x93, 0xa2, 0xca, 0xa6, 0x8a, 0x6a, 0xc3, 0x9c, - 0xf8, 0x46, 0xe6, 0x72, 0x6f, 0x60, 0x2e, 0x9f, 0x62, 0xee, 0x3e, 0xec, 0x8d, 0x7d, 0x3a, 0x65, - 0x37, 0x09, 0xf5, 0x2d, 0x7f, 0x11, 0x17, 0xc0, 0x6e, 0x64, 0x1d, 0x24, 0xc6, 0x6d, 0x82, 0xa5, - 0x6d, 0x82, 0x6b, 0x26, 0x48, 0x98, 0x04, 0x33, 0xea, 0x05, 0xe4, 0xad, 0x39, 0x21, 0x10, 0x6d, - 0x2b, 0xb4, 0x58, 0x46, 0x65, 0xcc, 0xc6, 0xe8, 0x01, 0x88, 0x23, 0x6a, 0xf3, 0x7c, 0xf6, 0xd2, - 0x02, 0xd3, 0x7d, 0x9f, 0xfa, 0x4d, 0x6a, 0x13, 0xcc, 0x00, 0xb5, 0x19, 0xc8, 0x2d, 0xfa, 0xc2, - 0x73, 0xa9, 0x65, 0xf7, 0x7c, 0x3a, 0x89, 0xda, 0xfd, 0x5b, 0xdb, 0x56, 0x0b, 0x0a, 0x73, 0xd6, - 0xd8, 0x92, 0xc6, 0x75, 0x6f, 0xbb, 0xd1, 0x5c, 0x0f, 0xc4, 0xbb, 0x60, 0x52, 0x9d, 0xf1, 0xd2, - 0xda, 0x3f, 0x04, 0x50, 0xdf, 0x8e, 0x46, 0x6d, 0x28, 0x71, 0xa4, 0x99, 0x7a, 0xe1, 0x1c, 0xbd, - 0xcb, 0x46, 0xac, 0xc7, 0xc1, 0x7c, 0x3d, 0x7e, 0xe3, 0xf5, 0x98, 0xea, 0x26, 0xd9, 0x77, 0xeb, - 0x26, 0x0f, 0x60, 0x97, 0x97, 0x77, 0xf2, 0x18, 0x10, 0xb5, 0xec, 0x51, 0xae, 0x91, 0x91, 0x77, - 0x70, 0x79, 0xc8, 0xcb, 0x8c, 0xd9, 0x6b, 0x79, 0x10, 0x7b, 0x8e, 0x37, 0xa9, 0x55, 0x21, 0xd7, - 0x74, 0x29, 0x3b, 0xb0, 0xbc, 0x4f, 0xac, 0x80, 0x7a, 0x09, 0x8f, 0x7c, 0x76, 0xfc, 0xf7, 0x0c, - 0x94, 0x52, 0x0f, 0x35, 0xf4, 0x08, 0xf6, 0x9a, 0x9d, 0xf3, 0xfe, 0x40, 0xc7, 0x66, 0xb3, 0x6b, - 0x9c, 0xb6, 0xcf, 0xe4, 0x1d, 0xf5, 0x70, 0xb9, 0xd2, 0x94, 0xe9, 0x06, 0xb4, 0xfd, 0x06, 0xab, - 0x42, 0xae, 0x6d, 0xb4, 0xf4, 0xdf, 0xcb, 0x82, 0x7a, 0x7b, 0xb9, 0xd2, 0xe4, 0x14, 0x90, 0x5f, - 0x68, 0x9f, 0x40, 0x99, 0x01, 0xcc, 0xf3, 0x5e, 0xab, 0x3e, 0xd0, 0xe5, 0x8c, 0xaa, 0x2e, 0x57, - 0xda, 0xc1, 0x75, 0x5c, 0xcc, 0xf9, 0x87, 0x50, 0xc0, 0xfa, 0xef, 0xce, 0xf5, 0xfe, 0x40, 0xce, - 0xaa, 0x07, 0xcb, 0x95, 0x86, 0x52, 0xc0, 0xa4, 0xa4, 0xee, 0x83, 0x84, 0xf5, 0x7e, 0xaf, 0x6b, - 0xf4, 0x75, 0x59, 0x54, 0x7f, 0xb4, 0x5c, 0x69, 0xb7, 0xb6, 0x50, 0xb1, 0x4a, 0x7f, 0x06, 0xfb, - 0xad, 0xee, 0x97, 0x46, 0xa7, 0x5b, 0x6f, 0x99, 0x3d, 0xdc, 0x3d, 0xc3, 0x7a, 0xbf, 0x2f, 0xe7, - 0xd4, 0xea, 0x72, 0xa5, 0xbd, 0x9f, 0xc2, 0xdf, 0x10, 0xdd, 0x07, 0x20, 0xf6, 0xda, 0xc6, 0x99, - 0x9c, 0x57, 0x6f, 0x2d, 0x57, 0xda, 0x7b, 0x29, 0x68, 0x44, 0x6a, 0x94, 0x71, 0xb3, 0xd3, 0xed, - 0xeb, 0x72, 0xe1, 0x46, 0xc6, 0x8c, 0xec, 0xe3, 0x3f, 0x00, 0xba, 0xf9, 0x94, 0x45, 0xf7, 0x40, - 0x34, 0xba, 0x86, 0x2e, 0xef, 0xf0, 0xfc, 0x6f, 0x22, 0x0c, 0xea, 0x11, 0x54, 0x83, 0x6c, 0xe7, - 0xab, 0x2f, 0x64, 0x41, 0xfd, 0xf1, 0x72, 0xa5, 0xdd, 0xb9, 0x09, 0xea, 0x7c, 0xf5, 0xc5, 0x31, - 0x85, 0x52, 0x3a, 0x70, 0x0d, 0xa4, 0x27, 0xfa, 0xa0, 0xde, 0xaa, 0x0f, 0xea, 0xf2, 0x0e, 0xff, - 0xa5, 0xc4, 0xfd, 0x84, 0x84, 0x16, 0x2b, 0xc2, 0x43, 0xc8, 0x19, 0xfa, 0x53, 0x1d, 0xcb, 0x82, - 0xba, 0xbf, 0x5c, 0x69, 0xbb, 0x09, 0xc0, 0x20, 0x97, 0xc4, 0x47, 0x15, 0xc8, 0xd7, 0x3b, 0x5f, - 0xd6, 0x9f, 0xf5, 0xe5, 0x8c, 0x8a, 0x96, 0x2b, 0x6d, 0x2f, 0x71, 0xd7, 0xdd, 0x17, 0xd6, 0x22, - 0x38, 0xfe, 0x9f, 0x00, 0xe5, 0xf4, 0xf5, 0x8d, 0x2a, 0x20, 0x9e, 0xb6, 0x3b, 0x7a, 0xb2, 0x5d, - 0xda, 0x17, 0x8d, 0xd1, 0x11, 0x14, 0x5b, 0x6d, 0xac, 0x37, 0x07, 0x5d, 0xfc, 0x2c, 0xc9, 0x25, - 0x0d, 0x6a, 0x39, 0x3e, 0x13, 0xf8, 0x02, 0xfd, 0x02, 0xca, 0xfd, 0x67, 0x4f, 0x3a, 0x6d, 0xe3, - 0xb7, 0x26, 0x8b, 0x98, 0x51, 0x1f, 0x2c, 0x57, 0xda, 0xdd, 0x2d, 0x30, 0x99, 0xf9, 0x64, 0x64, - 0x85, 0xc4, 0xee, 0xf3, 0x1b, 0x26, 0x72, 0x4a, 0x02, 0x6a, 0xc2, 0x7e, 0xb2, 0x74, 0xb3, 0x59, - 0x56, 0xfd, 0x64, 0xb9, 0xd2, 0x3e, 0xfa, 0xde, 0xf5, 0xeb, 0xdd, 0x25, 0x01, 0xdd, 0x83, 0x42, - 0x1c, 0x24, 0x51, 0x52, 0x7a, 0x69, 0xbc, 0xe0, 0xf8, 0x2f, 0x02, 0x14, 0xd7, 0xed, 0x2a, 0x22, - 0xdc, 0xe8, 0x9a, 0x3a, 0xc6, 0x5d, 0x9c, 0x30, 0xb0, 0x76, 0x1a, 0x94, 0x0d, 0xd1, 0x5d, 0x28, - 0x9c, 0xe9, 0x86, 0x8e, 0xdb, 0xcd, 0xa4, 0x30, 0xd6, 0x90, 0x33, 0xe2, 0x11, 0xdf, 0x19, 0xa1, - 0x8f, 0xa1, 0x6c, 0x74, 0xcd, 0xfe, 0x79, 0xf3, 0x71, 0x92, 0x3a, 0xdb, 0x3f, 0x15, 0xaa, 0x3f, - 0x1f, 0x5d, 0x30, 0x3e, 0x8f, 0xa3, 0x1a, 0x7a, 0x5a, 0xef, 0xb4, 0x5b, 0x1c, 0x9a, 0x55, 0x95, - 0xe5, 0x4a, 0xbb, 0xbd, 0x86, 0xb6, 0xf9, 0x3b, 0x26, 0xc2, 0x1e, 0xdb, 0x50, 0xf9, 0xfe, 0xc6, - 0x84, 0x34, 0xc8, 0xd7, 0x7b, 0x3d, 0xdd, 0x68, 0x25, 0x7f, 0xbf, 0xf1, 0xd5, 0x67, 0x33, 0xe2, - 0xd9, 0x11, 0xe2, 0xb4, 0x8b, 0xcf, 0xf4, 0x41, 0xf2, 0xf3, 0x1b, 0xc4, 0x29, 0x8d, 0xae, 0xf7, - 0xc6, 0xe1, 0xab, 0xef, 0x2a, 0x3b, 0xdf, 0x7e, 0x57, 0xd9, 0x79, 0x75, 0x55, 0x11, 0xbe, 0xbd, - 0xaa, 0x08, 0xff, 0xba, 0xaa, 0xec, 0xfc, 0xe7, 0xaa, 0x22, 0x7c, 0xf3, 0xef, 0x8a, 0x30, 0xcc, - 0xb3, 0x46, 0xf6, 0xf9, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x17, 0x2b, 0x62, 0xdd, 0x0e, - 0x00, 0x00, + // 1793 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x73, 0xdb, 0xc6, + 0x15, 0x17, 0x48, 0xf0, 0xdf, 0x23, 0xa5, 0x40, 0x6b, 0x5b, 0x45, 0x11, 0x85, 0x84, 0x19, 0x3b, + 0x56, 0x34, 0x89, 0xec, 0x26, 0x69, 0x3b, 0xed, 0xb4, 0x9d, 0xe1, 0x1f, 0x48, 0xe6, 0x54, 0x26, + 0xd9, 0x25, 0xe5, 0xd4, 0x39, 0x14, 0x03, 0x12, 0x4b, 0x0a, 0x63, 0x10, 0xcb, 0x02, 0xa0, 0x64, + 0xe6, 0x23, 0xf0, 0x13, 0xf4, 0xc2, 0x99, 0xcc, 0xf4, 0xd4, 0x6f, 0xe2, 0x63, 0xda, 0x43, 0x0f, + 0x3d, 0x68, 0x1a, 0xf5, 0x92, 0x63, 0x3f, 0x41, 0xa7, 0xb3, 0xbb, 0x00, 0x09, 0x4a, 0x76, 0xc6, + 0x87, 0x9c, 0xb8, 0xfb, 0xde, 0x6f, 0xdf, 0xf2, 0xfd, 0xde, 0xef, 0xbd, 0x05, 0x14, 0x06, 0x64, + 0x7a, 0x34, 0xf5, 0x69, 0x48, 0x51, 0x9e, 0xff, 0x0c, 0xa9, 0xab, 0x7d, 0x3a, 0x76, 0xc2, 0xf3, + 0xd9, 0xe0, 0x68, 0x48, 0x27, 0x8f, 0xc7, 0x74, 0x4c, 0x1f, 0x73, 0xcf, 0x60, 0x36, 0xe2, 0x3b, + 0xbe, 0xe1, 0x2b, 0x71, 0xb0, 0x3a, 0x85, 0xcc, 0x53, 0xe2, 0xba, 0x14, 0x55, 0xa0, 0x68, 0x93, + 0x0b, 0x67, 0x48, 0x4c, 0xcf, 0x9a, 0x10, 0x55, 0xd2, 0xa5, 0x83, 0x02, 0x06, 0x61, 0x6a, 0x5b, + 0x13, 0xc2, 0x00, 0x43, 0xd7, 0x21, 0x5e, 0x28, 0x00, 0x29, 0x01, 0x10, 0x26, 0x0e, 0x78, 0x08, + 0x3b, 0x11, 0xe0, 0x82, 0xf8, 0x81, 0x43, 0x3d, 0x35, 0xcd, 0x31, 0xdb, 0xc2, 0xfa, 0x5c, 0x18, + 0xab, 0x01, 0x64, 0x9f, 0x12, 0xcb, 0x26, 0x3e, 0xfa, 0x18, 0xe4, 0x70, 0x3e, 0x15, 0x77, 0xed, + 0x7c, 0x76, 0xef, 0x28, 0xce, 0xe1, 0xe8, 0x19, 0x09, 0x02, 0x6b, 0x4c, 0xfa, 0xf3, 0x29, 0xc1, + 0x1c, 0x82, 0x7e, 0x07, 0xc5, 0x21, 0x9d, 0x4c, 0x7d, 0x12, 0xf0, 0xc0, 0x29, 0x7e, 0x62, 0xff, + 0xd6, 0x89, 0xc6, 0x1a, 0x83, 0x93, 0x07, 0xaa, 0x35, 0xd8, 0x6e, 0xb8, 0xb3, 0x20, 0x24, 0x7e, + 0x83, 0x7a, 0x23, 0x67, 0x8c, 0x9e, 0x40, 0x6e, 0x44, 0x5d, 0x9b, 0xf8, 0x81, 0x2a, 0xe9, 0xe9, + 0x83, 0xe2, 0x67, 0xca, 0x3a, 0xd8, 0x31, 0x77, 0xd4, 0xe5, 0xd7, 0x57, 0x95, 0x2d, 0x1c, 0xc3, + 0xaa, 0x7f, 0x4d, 0x41, 0x56, 0x78, 0xd0, 0x1e, 0xa4, 0x1c, 0x5b, 0x50, 0x54, 0xcf, 0x5e, 0x5f, + 0x55, 0x52, 0xad, 0x26, 0x4e, 0x39, 0x36, 0xba, 0x0b, 0x19, 0xd7, 0x1a, 0x10, 0x37, 0x22, 0x47, + 0x6c, 0xd0, 0xfb, 0x50, 0xf0, 0x89, 0x65, 0x9b, 0xd4, 0x73, 0xe7, 0x9c, 0x92, 0x3c, 0xce, 0x33, + 0x43, 0xc7, 0x73, 0xe7, 0xe8, 0x53, 0x40, 0xce, 0xd8, 0xa3, 0x3e, 0x31, 0xa7, 0xc4, 0x9f, 0x38, + 0xfc, 0xdf, 0x06, 0xaa, 0xcc, 0x51, 0xbb, 0xc2, 0xd3, 0x5d, 0x3b, 0xd0, 0x87, 0xb0, 0x1d, 0xc1, + 0x6d, 0xe2, 0x92, 0x90, 0xa8, 0x19, 0x8e, 0x2c, 0x09, 0x63, 0x93, 0xdb, 0xd0, 0x13, 0xb8, 0x6b, + 0x3b, 0x81, 0x35, 0x70, 0x89, 0x19, 0x92, 0xc9, 0xd4, 0x74, 0x3c, 0x9b, 0xbc, 0x22, 0x81, 0x9a, + 0xe5, 0x58, 0x14, 0xf9, 0xfa, 0x64, 0x32, 0x6d, 0x09, 0x0f, 0xda, 0x83, 0xec, 0xd4, 0x9a, 0x05, + 0xc4, 0x56, 0x73, 0x1c, 0x13, 0xed, 0x18, 0x4b, 0x42, 0x01, 0x81, 0xaa, 0xdc, 0x64, 0xa9, 0xc9, + 0x1d, 0x31, 0x4b, 0x11, 0xac, 0xfa, 0xdf, 0x14, 0x64, 0x85, 0x07, 0x7d, 0xb4, 0x62, 0xa9, 0x54, + 0xdf, 0x63, 0xa8, 0x7f, 0x5d, 0x55, 0xf2, 0xc2, 0xd7, 0x6a, 0x26, 0x58, 0x43, 0x20, 0x27, 0x14, + 0xc5, 0xd7, 0x68, 0x1f, 0x0a, 0x96, 0x6d, 0xb3, 0xea, 0x91, 0x40, 0x4d, 0xeb, 0xe9, 0x83, 0x02, + 0x5e, 0x1b, 0xd0, 0x2f, 0x37, 0xd5, 0x20, 0xdf, 0xd4, 0xcf, 0xdb, 0x64, 0xc0, 0x4a, 0x31, 0x24, + 0x7e, 0xa4, 0xe0, 0x0c, 0xbf, 0x2f, 0xcf, 0x0c, 0x5c, 0xbf, 0xf7, 0xa1, 0x34, 0xb1, 0x5e, 0x99, + 0x01, 0xf9, 0xf3, 0x8c, 0x78, 0x43, 0xc2, 0xe9, 0x4a, 0xe3, 0xe2, 0xc4, 0x7a, 0xd5, 0x8b, 0x4c, + 0xa8, 0x0c, 0xe0, 0x78, 0xa1, 0x4f, 0xed, 0xd9, 0x90, 0xf8, 0x11, 0x57, 0x09, 0x0b, 0xfa, 0x39, + 0xe4, 0x39, 0xd9, 0xa6, 0x63, 0xab, 0x79, 0x5d, 0x3a, 0x90, 0xeb, 0x5a, 0x94, 0x78, 0x8e, 0x53, + 0xcd, 0xf3, 0x8e, 0x97, 0x38, 0xc7, 0xb1, 0x2d, 0x1b, 0xfd, 0x06, 0xb4, 0xe0, 0xa5, 0xc3, 0x0a, + 0x25, 0x22, 0x85, 0x0e, 0xf5, 0x4c, 0x9f, 0x4c, 0xe8, 0x85, 0xe5, 0x06, 0x6a, 0x81, 0x5f, 0xa3, + 0x32, 0x44, 0x2b, 0x01, 0xc0, 0x91, 0xbf, 0xda, 0x81, 0x0c, 0x8f, 0xc8, 0xaa, 0x28, 0xc4, 0x1a, + 0x75, 0x6f, 0xb4, 0x43, 0x47, 0x90, 0x19, 0x39, 0x2e, 0x09, 0xd4, 0x14, 0xaf, 0x21, 0x4a, 0x28, + 0xdd, 0x71, 0x49, 0xcb, 0x1b, 0xd1, 0xa8, 0x8a, 0x02, 0x56, 0x3d, 0x83, 0x22, 0x0f, 0x78, 0x36, + 0xb5, 0xad, 0x90, 0xfc, 0x68, 0x61, 0xaf, 0x64, 0xc8, 0xc7, 0x9e, 0x55, 0xd1, 0xa5, 0x44, 0xd1, + 0x0f, 0xa3, 0x79, 0x20, 0xba, 0x7b, 0xef, 0x76, 0xbc, 0xc4, 0x40, 0x40, 0x20, 0x07, 0xce, 0xd7, + 0x84, 0xf7, 0x53, 0x1a, 0xf3, 0x35, 0xd2, 0xa1, 0x78, 0xb3, 0x89, 0xb6, 0x71, 0xd2, 0x84, 0x3e, + 0x00, 0x98, 0x50, 0xdb, 0x19, 0x39, 0xc4, 0x36, 0x03, 0x2e, 0x80, 0x34, 0x2e, 0xc4, 0x96, 0x1e, + 0x52, 0x99, 0xdc, 0x59, 0x0b, 0xd9, 0x51, 0xaf, 0xc4, 0x5b, 0x74, 0x00, 0x39, 0xc7, 0xbb, 0xb0, + 0x5c, 0x27, 0xea, 0x90, 0xfa, 0xce, 0xf5, 0x55, 0x05, 0xb0, 0x75, 0xd9, 0x12, 0x56, 0x1c, 0xbb, + 0xd9, 0x14, 0xf4, 0xe8, 0x46, 0x33, 0xe7, 0x79, 0xa8, 0x6d, 0x8f, 0x26, 0x1b, 0xf9, 0x09, 0xe4, + 0xe2, 0x29, 0xc9, 0xea, 0xbb, 0xd1, 0x59, 0xcf, 0xc9, 0x30, 0xa4, 0xab, 0xf9, 0x13, 0xc1, 0x90, + 0x06, 0xf9, 0x95, 0x34, 0x81, 0xff, 0xf3, 0xd5, 0x9e, 0xcd, 0xe6, 0x55, 0x5e, 0x5e, 0xa0, 0x16, + 0x75, 0xe9, 0x20, 0x83, 0x57, 0xa9, 0xb6, 0xd9, 0x75, 0x6b, 0xc0, 0x60, 0xae, 0x96, 0xb8, 0x36, + 0xdf, 0x8b, 0xb5, 0xd9, 0x3b, 0xa7, 0x7e, 0xd8, 0x6a, 0xae, 0x4f, 0xd4, 0xe7, 0xe8, 0x31, 0xc0, + 0xc0, 0xa5, 0xc3, 0x97, 0x26, 0xa7, 0x79, 0x9b, 0x45, 0xac, 0x2b, 0xd7, 0x57, 0x95, 0x12, 0xb6, + 0x2e, 0xeb, 0xcc, 0xd1, 0x73, 0xbe, 0x26, 0xb8, 0x30, 0x88, 0x97, 0xe8, 0x67, 0x90, 0xe5, 0xf6, + 0x78, 0x54, 0xdc, 0x59, 0x27, 0xc4, 0xed, 0x09, 0x41, 0x44, 0x40, 0xc6, 0x55, 0x30, 0x9f, 0xb8, + 0x8e, 0xf7, 0xd2, 0x0c, 0x2d, 0x7f, 0x4c, 0x42, 0x75, 0x57, 0xbc, 0x18, 0x91, 0xb5, 0xcf, 0x8d, + 0xac, 0xae, 0x2e, 0x1d, 0x5a, 0xae, 0x39, 0x72, 0xad, 0x71, 0xa0, 0x7e, 0x9f, 0xe3, 0x85, 0x05, + 0x6e, 0x3b, 0x66, 0xa6, 0x5f, 0xcb, 0x7f, 0xf9, 0xa6, 0xb2, 0x55, 0xf5, 0xa0, 0xb0, 0xba, 0x89, + 0xa9, 0x96, 0x8e, 0x46, 0x01, 0x09, 0xb9, 0xc4, 0xd2, 0x38, 0xda, 0xad, 0x84, 0x93, 0xe2, 0x1c, + 0x09, 0xe1, 0x20, 0x90, 0xcf, 0xad, 0xe0, 0x9c, 0x8b, 0xa9, 0x84, 0xf9, 0x9a, 0x8d, 0x8a, 0x4b, + 0x62, 0xbd, 0x34, 0xb9, 0x43, 0x48, 0x29, 0xcf, 0x0c, 0x4f, 0xad, 0xe0, 0x3c, 0xba, 0xef, 0xb7, + 0x90, 0x15, 0xa5, 0x42, 0x9f, 0x43, 0x7e, 0x48, 0x67, 0x5e, 0xb8, 0x7e, 0x4e, 0x76, 0x93, 0xd3, + 0x88, 0x7b, 0xa2, 0xdc, 0x57, 0xc0, 0xea, 0x31, 0xe4, 0x22, 0x17, 0x7a, 0xb8, 0x1a, 0x95, 0x72, + 0xfd, 0xde, 0x8d, 0xaa, 0x6c, 0xbe, 0x2f, 0x17, 0x96, 0x3b, 0x13, 0x7f, 0x5e, 0xc6, 0x62, 0x53, + 0xfd, 0xbb, 0x04, 0x39, 0xcc, 0x94, 0x10, 0x84, 0x89, 0x97, 0x29, 0xb3, 0xf1, 0x32, 0xad, 0x7b, + 0x38, 0xb5, 0xd1, 0xc3, 0x71, 0x1b, 0xa6, 0x13, 0x6d, 0xb8, 0x66, 0x4e, 0x7e, 0x23, 0x73, 0x99, + 0x37, 0x30, 0x97, 0x4d, 0x30, 0xf7, 0x10, 0x76, 0x46, 0x3e, 0x9d, 0xf0, 0xb7, 0x87, 0xfa, 0x96, + 0x3f, 0x8f, 0x06, 0xe5, 0x36, 0xb3, 0xf6, 0x63, 0xe3, 0x26, 0xc1, 0xf9, 0x4d, 0x82, 0xab, 0x26, + 0xe4, 0x31, 0x09, 0xa6, 0xd4, 0x0b, 0xc8, 0x5b, 0x73, 0x42, 0x20, 0xdb, 0x56, 0x68, 0xf1, 0x8c, + 0x4a, 0x98, 0xaf, 0xd1, 0x23, 0x90, 0x87, 0xd4, 0x16, 0xf9, 0xec, 0x24, 0x25, 0x68, 0xf8, 0x3e, + 0xf5, 0x1b, 0xd4, 0x26, 0x98, 0x03, 0xaa, 0x53, 0x50, 0x9a, 0xf4, 0xd2, 0x73, 0xa9, 0x65, 0x77, + 0x7d, 0x3a, 0x66, 0x0f, 0xc4, 0x5b, 0x07, 0x5d, 0x13, 0x72, 0x33, 0x3e, 0x0a, 0xe3, 0x51, 0xf7, + 0x60, 0x73, 0x34, 0xdd, 0x0c, 0x24, 0xe6, 0x66, 0xdc, 0xbf, 0xd1, 0xd1, 0xea, 0x3f, 0x25, 0xd0, + 0xde, 0x8e, 0x46, 0x2d, 0x28, 0x0a, 0xa4, 0x99, 0xf8, 0x26, 0x3a, 0x78, 0x97, 0x8b, 0xf8, 0x54, + 0x84, 0xd9, 0x6a, 0xfd, 0xc6, 0x07, 0x35, 0x31, 0x6f, 0xd2, 0xef, 0x36, 0x6f, 0x1e, 0xc1, 0xb6, + 0x18, 0x00, 0xf1, 0xe7, 0x83, 0xac, 0xa7, 0x0f, 0x32, 0xf5, 0x94, 0xb2, 0x85, 0x4b, 0x03, 0xd1, + 0x66, 0xdc, 0x5e, 0xcd, 0x82, 0xdc, 0x75, 0xbc, 0x71, 0xb5, 0x02, 0x99, 0x86, 0x4b, 0x79, 0xc1, + 0xb2, 0x3e, 0xb1, 0x02, 0xea, 0xc5, 0x3c, 0x8a, 0xdd, 0xe1, 0x3f, 0x52, 0x50, 0x4c, 0x7c, 0xda, + 0xa1, 0x27, 0xb0, 0xd3, 0x38, 0x3d, 0xeb, 0xf5, 0x0d, 0x6c, 0x36, 0x3a, 0xed, 0xe3, 0xd6, 0x89, + 0xb2, 0xa5, 0xed, 0x2f, 0x96, 0xba, 0x3a, 0x59, 0x83, 0x36, 0xbf, 0xda, 0x2a, 0x90, 0x69, 0xb5, + 0x9b, 0xc6, 0x1f, 0x15, 0x49, 0xbb, 0xbb, 0x58, 0xea, 0x4a, 0x02, 0x28, 0x9e, 0xc0, 0x4f, 0xa0, + 0xc4, 0x01, 0xe6, 0x59, 0xb7, 0x59, 0xeb, 0x1b, 0x4a, 0x4a, 0xd3, 0x16, 0x4b, 0x7d, 0xef, 0x26, + 0x2e, 0xe2, 0xfc, 0x43, 0xc8, 0x61, 0xe3, 0x0f, 0x67, 0x46, 0xaf, 0xaf, 0xa4, 0xb5, 0xbd, 0xc5, + 0x52, 0x47, 0x09, 0x60, 0xdc, 0x52, 0x0f, 0x21, 0x8f, 0x8d, 0x5e, 0xb7, 0xd3, 0xee, 0x19, 0x8a, + 0xac, 0xfd, 0x64, 0xb1, 0xd4, 0xef, 0x6c, 0xa0, 0x22, 0x95, 0xfe, 0x02, 0x76, 0x9b, 0x9d, 0x2f, + 0xdb, 0xa7, 0x9d, 0x5a, 0xd3, 0xec, 0xe2, 0xce, 0x09, 0x36, 0x7a, 0x3d, 0x25, 0xa3, 0x55, 0x16, + 0x4b, 0xfd, 0xfd, 0x04, 0xfe, 0x96, 0xe8, 0x3e, 0x00, 0xb9, 0xdb, 0x6a, 0x9f, 0x28, 0x59, 0xed, + 0xce, 0x62, 0xa9, 0xbf, 0x97, 0x80, 0x32, 0x52, 0x59, 0xc6, 0x8d, 0xd3, 0x4e, 0xcf, 0x50, 0x72, + 0xb7, 0x32, 0xe6, 0x64, 0x1f, 0xfe, 0x09, 0xd0, 0xed, 0x8f, 0x5f, 0xf4, 0x00, 0xe4, 0x76, 0xa7, + 0x6d, 0x28, 0x5b, 0x22, 0xff, 0xdb, 0x88, 0x36, 0xf5, 0x08, 0xaa, 0x42, 0xfa, 0xf4, 0xab, 0x2f, + 0x14, 0x49, 0xfb, 0xe9, 0x62, 0xa9, 0xdf, 0xbb, 0x0d, 0x3a, 0xfd, 0xea, 0x8b, 0x43, 0x0a, 0xc5, + 0x64, 0xe0, 0x2a, 0xe4, 0x9f, 0x19, 0xfd, 0x5a, 0xb3, 0xd6, 0xaf, 0x29, 0x5b, 0xe2, 0x2f, 0xc5, + 0xee, 0x67, 0x24, 0xb4, 0x78, 0x13, 0xee, 0x43, 0xa6, 0x6d, 0x3c, 0x37, 0xb0, 0x22, 0x69, 0xbb, + 0x8b, 0xa5, 0xbe, 0x1d, 0x03, 0xda, 0xe4, 0x82, 0xf8, 0xa8, 0x0c, 0xd9, 0xda, 0xe9, 0x97, 0xb5, + 0x17, 0x3d, 0x25, 0xa5, 0xa1, 0xc5, 0x52, 0xdf, 0x89, 0xdd, 0x35, 0xf7, 0xd2, 0x9a, 0x07, 0x87, + 0xff, 0x93, 0xa0, 0x94, 0x7c, 0xf0, 0x51, 0x19, 0xe4, 0xe3, 0xd6, 0xa9, 0x11, 0x5f, 0x97, 0xf4, + 0xb1, 0x35, 0x3a, 0x80, 0x42, 0xb3, 0x85, 0x8d, 0x46, 0xbf, 0x83, 0x5f, 0xc4, 0xb9, 0x24, 0x41, + 0x4d, 0xc7, 0xe7, 0x02, 0x9f, 0xa3, 0x5f, 0x41, 0xa9, 0xf7, 0xe2, 0xd9, 0x69, 0xab, 0xfd, 0x7b, + 0x93, 0x47, 0x4c, 0x69, 0x8f, 0x16, 0x4b, 0xfd, 0xfe, 0x06, 0x98, 0x4c, 0x7d, 0x32, 0xb4, 0x42, + 0x62, 0xf7, 0xc4, 0x1b, 0xc4, 0x9c, 0x79, 0x09, 0x35, 0x60, 0x37, 0x3e, 0xba, 0xbe, 0x2c, 0xad, + 0x7d, 0xb2, 0x58, 0xea, 0x1f, 0xfd, 0xe0, 0xf9, 0xd5, 0xed, 0x79, 0x09, 0x3d, 0x80, 0x5c, 0x14, + 0x24, 0x56, 0x52, 0xf2, 0x68, 0x74, 0xe0, 0xf0, 0x6f, 0x12, 0x14, 0x56, 0xe3, 0x8a, 0x11, 0xde, + 0xee, 0x98, 0x06, 0xc6, 0x1d, 0x1c, 0x33, 0xb0, 0x72, 0xb6, 0x29, 0x5f, 0xa2, 0xfb, 0x90, 0x3b, + 0x31, 0xda, 0x06, 0x6e, 0x35, 0xe2, 0xc6, 0x58, 0x41, 0x4e, 0x88, 0x47, 0x7c, 0x67, 0x88, 0x3e, + 0x86, 0x52, 0xbb, 0x63, 0xf6, 0xce, 0x1a, 0x4f, 0xe3, 0xd4, 0xf9, 0xfd, 0x89, 0x50, 0xbd, 0xd9, + 0xf0, 0x9c, 0xf3, 0x79, 0xc8, 0x7a, 0xe8, 0x79, 0xed, 0xb4, 0xd5, 0x14, 0xd0, 0xb4, 0xa6, 0x2e, + 0x96, 0xfa, 0xdd, 0x15, 0x34, 0xfa, 0xe4, 0x61, 0xd8, 0x43, 0x1b, 0xca, 0x3f, 0x3c, 0x98, 0x90, + 0x0e, 0xd9, 0x5a, 0xb7, 0x6b, 0xb4, 0x9b, 0xf1, 0xbf, 0x5f, 0xfb, 0x6a, 0xd3, 0x29, 0xf1, 0x6c, + 0x86, 0x38, 0xee, 0xe0, 0x13, 0xa3, 0x1f, 0xff, 0xf9, 0x35, 0xe2, 0x98, 0xb2, 0x0f, 0x80, 0xfa, + 0xfe, 0xeb, 0xef, 0xca, 0x5b, 0xdf, 0x7e, 0x57, 0xde, 0x7a, 0x7d, 0x5d, 0x96, 0xbe, 0xbd, 0x2e, + 0x4b, 0xff, 0xbe, 0x2e, 0x6f, 0x7d, 0x7f, 0x5d, 0x96, 0xbe, 0xf9, 0x4f, 0x59, 0x1a, 0x64, 0xf9, + 0x20, 0xfb, 0xfc, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x41, 0xd9, 0x3a, 0x0f, 0x0f, 0x00, + 0x00, } diff --git a/lib/protocol/bep.proto b/lib/protocol/bep.proto index 5ded2e1e..812218d2 100644 --- a/lib/protocol/bep.proto +++ b/lib/protocol/bep.proto @@ -93,22 +93,27 @@ message IndexUpdate { message FileInfo { option (gogoproto.goproto_stringer) = false; - string name = 1; - FileInfoType type = 2; - int64 size = 3; - uint32 permissions = 4; - int64 modified_s = 5; - int32 modified_ns = 11; - uint64 modified_by = 12 [(gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false]; - bool deleted = 6; - bool invalid = 7; - bool no_permissions = 8; - Vector version = 9 [(gogoproto.nullable) = false]; - int64 sequence = 10; - int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"]; - + string name = 1; + FileInfoType type = 2; + int64 size = 3; + uint32 permissions = 4; + int64 modified_s = 5; + int32 modified_ns = 11; + uint64 modified_by = 12 [(gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false]; + bool deleted = 6; + bool invalid = 7 [(gogoproto.customname) = "RawInvalid"]; + bool no_permissions = 8; + Vector version = 9 [(gogoproto.nullable) = false]; + int64 sequence = 10; + int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"]; repeated BlockInfo Blocks = 16 [(gogoproto.nullable) = false]; string symlink_target = 17; + + // The local_flags fields stores flags that are relevant to the local + // host only. It is not part of the protocol, doesn't get sent or + // received (we make sure to zero it), nonetheless we need it on our + // struct and to be able to serialize it to/from the database. + uint32 local_flags = 1000; } enum FileInfoType { diff --git a/lib/protocol/bep_extensions.go b/lib/protocol/bep_extensions.go index cfc4b988..16c64a05 100644 --- a/lib/protocol/bep_extensions.go +++ b/lib/protocol/bep_extensions.go @@ -28,14 +28,14 @@ func (m Hello) Magic() uint32 { func (f FileInfo) String() string { switch f.Type { case FileInfoTypeDirectory: - return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, NoPermissions:%v}", - f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.Invalid, f.NoPermissions) + return fmt.Sprintf("Directory{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v}", + f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions) case FileInfoTypeFile: - return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, NoPermissions:%v, BlockSize:%d, Blocks:%v}", - f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.Invalid, f.NoPermissions, f.RawBlockSize, f.Blocks) + return fmt.Sprintf("File{Name:%q, Sequence:%d, Permissions:0%o, ModTime:%v, Version:%v, Length:%d, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, BlockSize:%d, Blocks:%v}", + f.Name, f.Sequence, f.Permissions, f.ModTime(), f.Version, f.Size, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.RawBlockSize, f.Blocks) case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile: - return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, NoPermissions:%v, SymlinkTarget:%q}", - f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.Invalid, f.NoPermissions, f.SymlinkTarget) + return fmt.Sprintf("Symlink{Name:%q, Type:%v, Sequence:%d, Version:%v, Deleted:%v, Invalid:%v, LocalFlags:0x%x, NoPermissions:%v, SymlinkTarget:%q}", + f.Name, f.Type, f.Sequence, f.Version, f.Deleted, f.RawInvalid, f.LocalFlags, f.NoPermissions, f.SymlinkTarget) default: panic("mystery file type detected") } @@ -46,13 +46,25 @@ func (f FileInfo) IsDeleted() bool { } func (f FileInfo) IsInvalid() bool { - return f.Invalid + return f.RawInvalid || f.LocalFlags&LocalInvalidFlags != 0 +} + +func (f FileInfo) IsIgnored() bool { + return f.LocalFlags&FlagLocalIgnored != 0 +} + +func (f FileInfo) MustRescan() bool { + return f.LocalFlags&FlagLocalMustRescan != 0 } func (f FileInfo) IsDirectory() bool { return f.Type == FileInfoTypeDirectory } +func (f FileInfo) ShouldConflict() bool { + return f.LocalFlags&LocalConflictFlags != 0 +} + func (f FileInfo) IsSymlink() bool { switch f.Type { case FileInfoTypeSymlink, FileInfoTypeDeprecatedSymlinkDirectory, FileInfoTypeDeprecatedSymlinkFile: @@ -149,7 +161,14 @@ func (f FileInfo) IsEmpty() bool { // - target // A directory does not have anything specific to check. func (f FileInfo) IsEquivalent(other FileInfo, ignorePerms bool, ignoreBlocks bool) bool { - if f.Name != other.Name || f.Type != other.Type || f.Deleted != other.Deleted || f.Invalid != other.Invalid { + if f.MustRescan() || other.MustRescan() { + // These are per definition not equivalent because they don't + // represent a valid state, even if both happen to have the + // MustRescan bit set. + return false + } + + if f.Name != other.Name || f.Type != other.Type || f.Deleted != other.Deleted || f.IsInvalid() != other.IsInvalid() { return false } @@ -197,9 +216,23 @@ func BlocksEqual(a, b []BlockInfo) bool { return true } -func (f *FileInfo) Invalidate(invalidatedBy ShortID) { - f.Invalid = true - f.ModifiedBy = invalidatedBy +func (f *FileInfo) SetMustRescan(by ShortID) { + f.LocalFlags = FlagLocalMustRescan + f.ModifiedBy = by + f.Blocks = nil + f.Sequence = 0 +} + +func (f *FileInfo) SetIgnored(by ShortID) { + f.LocalFlags = FlagLocalIgnored + f.ModifiedBy = by + f.Blocks = nil + f.Sequence = 0 +} + +func (f *FileInfo) SetUnsupported(by ShortID) { + f.LocalFlags = FlagLocalUnsupported + f.ModifiedBy = by f.Blocks = nil f.Sequence = 0 } diff --git a/lib/protocol/conflict_test.go b/lib/protocol/conflict_test.go index e3afb23d..9f1b0f49 100644 --- a/lib/protocol/conflict_test.go +++ b/lib/protocol/conflict_test.go @@ -9,7 +9,7 @@ func TestWinsConflict(t *testing.T) { // The first should always win over the second {{ModifiedS: 42}, {ModifiedS: 41}}, {{ModifiedS: 41}, {ModifiedS: 42, Deleted: true}}, - {{Deleted: true}, {ModifiedS: 10, Invalid: true}}, + {{Deleted: true}, {ModifiedS: 10, RawInvalid: true}}, {{ModifiedS: 41, Version: Vector{[]Counter{{42, 2}, {43, 1}}}}, {ModifiedS: 41, Version: Vector{[]Counter{{42, 1}, {43, 2}}}}}, } diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go index 585fb7a0..cdd20dd5 100644 --- a/lib/protocol/protocol.go +++ b/lib/protocol/protocol.go @@ -89,6 +89,21 @@ const ( FlagShareBits = 0x000000ff ) +// FileInfo.LocalFlags flags +const ( + FlagLocalUnsupported = 1 << 0 // The kind is unsupported, e.g. symlinks on Windows + FlagLocalIgnored = 1 << 1 // Matches local ignore patterns + FlagLocalMustRescan = 1 << 2 // Doesn't match content on disk, must be rechecked fully + + // Flags that should result in the Invalid bit on outgoing updates + LocalInvalidFlags = FlagLocalUnsupported | FlagLocalIgnored | FlagLocalMustRescan + + // Flags that should result in a file being in conflict with its + // successor, due to us not having an up to date picture of its state on + // disk. + LocalConflictFlags = FlagLocalUnsupported | FlagLocalIgnored +) + var ( ErrClosed = errors.New("connection closed") ErrTimeout = errors.New("read timeout") @@ -535,7 +550,7 @@ func checkFileInfoConsistency(f FileInfo) error { // Directories should have no blocks return errDirectoryHasBlocks - case !f.Deleted && !f.Invalid && f.Type == FileInfoTypeFile && len(f.Blocks) == 0: + case !f.Deleted && !f.IsInvalid() && f.Type == FileInfoTypeFile && len(f.Blocks) == 0: // Non-deleted, non-invalid files should have at least one block return errFileHasNoBlocks } diff --git a/lib/protocol/protocol_test.go b/lib/protocol/protocol_test.go index 3bc9af21..1ed48ab3 100644 --- a/lib/protocol/protocol_test.go +++ b/lib/protocol/protocol_test.go @@ -391,6 +391,28 @@ func BenchmarkBlockSize(b *testing.B) { } } +func TestLocalFlagBits(t *testing.T) { + var f FileInfo + if f.IsIgnored() || f.MustRescan() || f.IsInvalid() { + t.Error("file should have no weird bits set by default") + } + + f.SetIgnored(42) + if !f.IsIgnored() || f.MustRescan() || !f.IsInvalid() { + t.Error("file should be ignored and invalid") + } + + f.SetMustRescan(42) + if f.IsIgnored() || !f.MustRescan() || !f.IsInvalid() { + t.Error("file should be must-rescan and invalid") + } + + f.SetUnsupported(42) + if f.IsIgnored() || f.MustRescan() || !f.IsInvalid() { + t.Error("file should be invalid") + } +} + func TestIsEquivalent(t *testing.T) { b := func(v bool) *bool { return &v @@ -430,8 +452,8 @@ func TestIsEquivalent(t *testing.T) { eq: false, }, { - a: FileInfo{Invalid: false}, - b: FileInfo{Invalid: true}, + a: FileInfo{RawInvalid: false}, + b: FileInfo{RawInvalid: true}, eq: false, }, { @@ -445,6 +467,31 @@ func TestIsEquivalent(t *testing.T) { eq: false, }, + // Special handling of local flags and invalidity. "MustRescan" + // files are never equivalent to each other. Otherwise, equivalence + // is based just on whether the file becomes IsInvalid() or not, not + // the specific reason or flag bits. + { + a: FileInfo{LocalFlags: FlagLocalMustRescan}, + b: FileInfo{LocalFlags: FlagLocalMustRescan}, + eq: false, + }, + { + a: FileInfo{RawInvalid: true}, + b: FileInfo{RawInvalid: true}, + eq: true, + }, + { + a: FileInfo{LocalFlags: FlagLocalUnsupported}, + b: FileInfo{LocalFlags: FlagLocalUnsupported}, + eq: true, + }, + { + a: FileInfo{RawInvalid: true}, + b: FileInfo{LocalFlags: FlagLocalUnsupported}, + eq: true, + }, + // Difference in blocks is not OK { a: FileInfo{Blocks: []BlockInfo{{Hash: []byte{1, 2, 3, 4}}}}, diff --git a/lib/scanner/walk.go b/lib/scanner/walk.go index 0104cd52..37e2af4f 100644 --- a/lib/scanner/walk.go +++ b/lib/scanner/walk.go @@ -373,11 +373,12 @@ func (w *walker) walkRegular(ctx context.Context, relPath string, info fs.FileIn if curFile.IsEquivalent(f, w.IgnorePerms, true) { return nil } - if curFile.Invalid { - // We do not want to override the global version with the file we - // currently have. Keeping only our local counter makes sure we are in - // conflict with any other existing versions, which will be resolved by - // the normal pulling mechanisms. + if curFile.ShouldConflict() { + // The old file was invalid for whatever reason and probably not + // up to date with what was out there in the cluster. Drop all + // others from the version vector to indicate that we haven't + // taken their version into account, and possibly cause a + // conflict. f.Version = f.Version.DropOthers(w.ShortID) } l.Debugln("rescan:", curFile, info.ModTime().Unix(), info.Mode()&fs.ModePerm) @@ -412,11 +413,12 @@ func (w *walker) walkDir(ctx context.Context, relPath string, info fs.FileInfo, if cf.IsEquivalent(f, w.IgnorePerms, true) { return nil } - if cf.Invalid { - // We do not want to override the global version with the file we - // currently have. Keeping only our local counter makes sure we are in - // conflict with any other existing versions, which will be resolved by - // the normal pulling mechanisms. + if cf.ShouldConflict() { + // The old file was invalid for whatever reason and probably not + // up to date with what was out there in the cluster. Drop all + // others from the version vector to indicate that we haven't + // taken their version into account, and possibly cause a + // conflict. f.Version = f.Version.DropOthers(w.ShortID) } } @@ -467,11 +469,12 @@ func (w *walker) walkSymlink(ctx context.Context, relPath string, dchan chan pro if cf.IsEquivalent(f, w.IgnorePerms, true) { return nil } - if cf.Invalid { - // We do not want to override the global version with the file we - // currently have. Keeping only our local counter makes sure we are in - // conflict with any other existing versions, which will be resolved by - // the normal pulling mechanisms. + if cf.ShouldConflict() { + // The old file was invalid for whatever reason and probably not + // up to date with what was out there in the cluster. Drop all + // others from the version vector to indicate that we haven't + // taken their version into account, and possibly cause a + // conflict. f.Version = f.Version.DropOthers(w.ShortID) } } diff --git a/lib/scanner/walk_test.go b/lib/scanner/walk_test.go index 21f08a8c..c4d70e79 100644 --- a/lib/scanner/walk_test.go +++ b/lib/scanner/walk_test.go @@ -648,10 +648,10 @@ func TestIssue4841(t *testing.T) { Hashers: 2, CurrentFiler: fakeCurrentFiler{ "foo": { - Name: "foo", - Type: protocol.FileInfoTypeFile, - Invalid: true, - Version: protocol.Vector{}.Update(1), + Name: "foo", + Type: protocol.FileInfoTypeFile, + LocalFlags: protocol.FlagLocalIgnored, + Version: protocol.Vector{}.Update(1), }, }, ShortID: protocol.LocalDeviceID.Short(),