From b1b68ceedba618e773a7f120693656420649ae5d Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Sun, 24 Jun 2018 09:50:18 +0200 Subject: [PATCH] Add LocalFlags to FileInfo (#4952) We have the invalid bit to indicate that a file isn't good. That's enough for remote devices. For ourselves, it would be good to know sometimes why the file isn't good - because it's an unsupported type, because it matches an ignore pattern, or because we detected the data is bad and we need to rescan it. Or, and this is the main future reason for the PR, because it's a change detected on a receive only device. We will want something like the invalid flag for those changes, but marking them as invalid today means the scanner will rehash them. Hence something more fine grained is required. This introduces a LocalFlags fields to the FileInfo where we can stash things that we care about locally. For example, 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 The LocalFlags fields isn't sent over the wire; instead the Invalid attribute is calculated based on the flags at index sending time. It's on the FileInfo anyway because that's what we serialize to database etc. The actual Invalid flag should after this just be considered when building the global state and figuring out availability for remote devices. It is not used for local file index entries. --- cmd/syncthing/gui.go | 12 +- lib/db/blockmap_test.go | 14 +- lib/db/leveldb.go | 2 +- lib/db/leveldb_dbinstance.go | 5 +- lib/db/leveldb_dbinstance_updateschema.go | 39 +++- lib/db/leveldb_test.go | 34 ++- lib/db/leveldb_transactions.go | 2 +- lib/db/set.go | 2 +- lib/db/set_test.go | 71 +++--- lib/db/structs.go | 31 ++- lib/db/structs.pb.go | 127 ++++++---- lib/db/structs.proto | 6 +- lib/model/folder_sendonly.go | 2 +- lib/model/folder_sendrecv.go | 4 +- lib/model/model.go | 33 ++- lib/model/requests_test.go | 14 +- lib/protocol/bep.pb.go | 272 ++++++++++++---------- lib/protocol/bep.proto | 33 +-- lib/protocol/bep_extensions.go | 55 ++++- lib/protocol/conflict_test.go | 2 +- lib/protocol/protocol.go | 17 +- lib/protocol/protocol_test.go | 51 +++- lib/scanner/walk.go | 33 +-- lib/scanner/walk_test.go | 8 +- 24 files changed, 563 insertions(+), 306 deletions(-) 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(),