idxck.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright (C) 2018 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package main
  7. import (
  8. "bytes"
  9. "encoding/binary"
  10. "fmt"
  11. "github.com/syncthing/syncthing/lib/db"
  12. "github.com/syncthing/syncthing/lib/protocol"
  13. )
  14. type fileInfoKey struct {
  15. folder uint32
  16. device uint32
  17. name string
  18. }
  19. type globalKey struct {
  20. folder uint32
  21. name string
  22. }
  23. type sequenceKey struct {
  24. folder uint32
  25. sequence uint64
  26. }
  27. func idxck(ldb *db.Lowlevel) (success bool) {
  28. folders := make(map[uint32]string)
  29. devices := make(map[uint32]string)
  30. deviceToIDs := make(map[string]uint32)
  31. fileInfos := make(map[fileInfoKey]protocol.FileInfo)
  32. globals := make(map[globalKey]db.VersionList)
  33. sequences := make(map[sequenceKey]string)
  34. needs := make(map[globalKey]struct{})
  35. var localDeviceKey uint32
  36. success = true
  37. it := ldb.NewIterator(nil, nil)
  38. for it.Next() {
  39. key := it.Key()
  40. switch key[0] {
  41. case db.KeyTypeDevice:
  42. folder := binary.BigEndian.Uint32(key[1:])
  43. device := binary.BigEndian.Uint32(key[1+4:])
  44. name := nulString(key[1+4+4:])
  45. var f protocol.FileInfo
  46. err := f.Unmarshal(it.Value())
  47. if err != nil {
  48. fmt.Println("Unable to unmarshal FileInfo:", err)
  49. success = false
  50. continue
  51. }
  52. fileInfos[fileInfoKey{folder, device, name}] = f
  53. case db.KeyTypeGlobal:
  54. folder := binary.BigEndian.Uint32(key[1:])
  55. name := nulString(key[1+4:])
  56. var flv db.VersionList
  57. if err := flv.Unmarshal(it.Value()); err != nil {
  58. fmt.Println("Unable to unmarshal VersionList:", err)
  59. success = false
  60. continue
  61. }
  62. globals[globalKey{folder, name}] = flv
  63. case db.KeyTypeFolderIdx:
  64. key := binary.BigEndian.Uint32(it.Key()[1:])
  65. folders[key] = string(it.Value())
  66. case db.KeyTypeDeviceIdx:
  67. key := binary.BigEndian.Uint32(it.Key()[1:])
  68. devices[key] = string(it.Value())
  69. deviceToIDs[string(it.Value())] = key
  70. if bytes.Equal(it.Value(), protocol.LocalDeviceID[:]) {
  71. localDeviceKey = key
  72. }
  73. case db.KeyTypeSequence:
  74. folder := binary.BigEndian.Uint32(key[1:])
  75. seq := binary.BigEndian.Uint64(key[5:])
  76. val := it.Value()
  77. sequences[sequenceKey{folder, seq}] = string(val[9:])
  78. case db.KeyTypeNeed:
  79. folder := binary.BigEndian.Uint32(key[1:])
  80. name := nulString(key[1+4:])
  81. needs[globalKey{folder, name}] = struct{}{}
  82. }
  83. }
  84. if localDeviceKey == 0 {
  85. fmt.Println("Missing key for local device in device index (bailing out)")
  86. success = false
  87. return
  88. }
  89. for fk, fi := range fileInfos {
  90. if fk.name != fi.Name {
  91. fmt.Printf("Mismatching FileInfo name, %q (key) != %q (actual)\n", fk.name, fi.Name)
  92. success = false
  93. }
  94. folder := folders[fk.folder]
  95. if folder == "" {
  96. fmt.Printf("Unknown folder ID %d for FileInfo %q\n", fk.folder, fk.name)
  97. success = false
  98. continue
  99. }
  100. if devices[fk.device] == "" {
  101. fmt.Printf("Unknown device ID %d for FileInfo %q, folder %q\n", fk.folder, fk.name, folder)
  102. success = false
  103. }
  104. if fk.device == localDeviceKey {
  105. name, ok := sequences[sequenceKey{fk.folder, uint64(fi.Sequence)}]
  106. if !ok {
  107. fmt.Printf("Sequence entry missing for FileInfo %q, folder %q, seq %d\n", fi.Name, folder, fi.Sequence)
  108. success = false
  109. continue
  110. }
  111. if name != fi.Name {
  112. fmt.Printf("Sequence entry refers to wrong name, %q (seq) != %q (FileInfo), folder %q, seq %d\n", name, fi.Name, folder, fi.Sequence)
  113. success = false
  114. }
  115. }
  116. }
  117. for gk, vl := range globals {
  118. folder := folders[gk.folder]
  119. if folder == "" {
  120. fmt.Printf("Unknown folder ID %d for VersionList %q\n", gk.folder, gk.name)
  121. success = false
  122. }
  123. for i, fv := range vl.Versions {
  124. dev, ok := deviceToIDs[string(fv.Device)]
  125. if !ok {
  126. fmt.Printf("VersionList %q, folder %q refers to unknown device %q\n", gk.name, folder, fv.Device)
  127. success = false
  128. }
  129. fi, ok := fileInfos[fileInfoKey{gk.folder, dev, gk.name}]
  130. if !ok {
  131. fmt.Printf("VersionList %q, folder %q, entry %d refers to unknown FileInfo\n", gk.name, folder, i)
  132. success = false
  133. }
  134. if !fi.Version.Equal(fv.Version) {
  135. fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo version mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, fv.Version, fi.Version)
  136. success = false
  137. }
  138. if fi.IsInvalid() != fv.Invalid {
  139. fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo invalid mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, fv.Invalid, fi.IsInvalid())
  140. success = false
  141. }
  142. }
  143. // If we need this file we should have a need entry for it. False
  144. // positives from needsLocally for deleted files, where we might
  145. // legitimately lack an entry if we never had it, and ignored files.
  146. if needsLocally(vl) {
  147. _, ok := needs[gk]
  148. if !ok {
  149. dev := deviceToIDs[string(vl.Versions[0].Device)]
  150. fi := fileInfos[fileInfoKey{gk.folder, dev, gk.name}]
  151. if !fi.IsDeleted() && !fi.IsIgnored() {
  152. fmt.Printf("Missing need entry for needed file %q, folder %q\n", gk.name, folder)
  153. }
  154. }
  155. }
  156. }
  157. seenSeq := make(map[fileInfoKey]uint64)
  158. for sk, name := range sequences {
  159. folder := folders[sk.folder]
  160. if folder == "" {
  161. fmt.Printf("Unknown folder ID %d for sequence entry %d, %q\n", sk.folder, sk.sequence, name)
  162. success = false
  163. continue
  164. }
  165. if prev, ok := seenSeq[fileInfoKey{folder: sk.folder, name: name}]; ok {
  166. fmt.Printf("Duplicate sequence entry for %q, folder %q, seq %d (prev %d)\n", name, folder, sk.sequence, prev)
  167. success = false
  168. }
  169. seenSeq[fileInfoKey{folder: sk.folder, name: name}] = sk.sequence
  170. fi, ok := fileInfos[fileInfoKey{sk.folder, localDeviceKey, name}]
  171. if !ok {
  172. fmt.Printf("Missing FileInfo for sequence entry %d, folder %q, %q\n", sk.sequence, folder, name)
  173. success = false
  174. continue
  175. }
  176. if fi.Sequence != int64(sk.sequence) {
  177. fmt.Printf("Sequence mismatch for %q, folder %q, %d (key) != %d (FileInfo)\n", name, folder, sk.sequence, fi.Sequence)
  178. success = false
  179. }
  180. }
  181. for nk := range needs {
  182. folder := folders[nk.folder]
  183. if folder == "" {
  184. fmt.Printf("Unknown folder ID %d for need entry %q\n", nk.folder, nk.name)
  185. success = false
  186. continue
  187. }
  188. vl, ok := globals[nk]
  189. if !ok {
  190. fmt.Printf("Missing global for need entry %q, folder %q\n", nk.name, folder)
  191. success = false
  192. continue
  193. }
  194. if !needsLocally(vl) {
  195. fmt.Printf("Need entry for file we don't need, %q, folder %q\n", nk.name, folder)
  196. success = false
  197. }
  198. }
  199. return
  200. }
  201. func needsLocally(vl db.VersionList) bool {
  202. var lv *protocol.Vector
  203. for _, fv := range vl.Versions {
  204. if bytes.Equal(fv.Device, protocol.LocalDeviceID[:]) {
  205. lv = &fv.Version
  206. break
  207. }
  208. }
  209. if lv == nil {
  210. return true // proviosinally, it looks like we need the file
  211. }
  212. return !lv.GreaterEqual(vl.Versions[0].Version)
  213. }