idxck.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. "log"
  12. "sort"
  13. "github.com/syncthing/syncthing/lib/db"
  14. "github.com/syncthing/syncthing/lib/db/backend"
  15. "github.com/syncthing/syncthing/lib/protocol"
  16. )
  17. type fileInfoKey struct {
  18. folder uint32
  19. device uint32
  20. name string
  21. }
  22. type globalKey struct {
  23. folder uint32
  24. name string
  25. }
  26. type sequenceKey struct {
  27. folder uint32
  28. sequence uint64
  29. }
  30. func idxck(ldb backend.Backend) (success bool) {
  31. folders := make(map[uint32]string)
  32. devices := make(map[uint32]string)
  33. deviceToIDs := make(map[string]uint32)
  34. fileInfos := make(map[fileInfoKey]protocol.FileInfo)
  35. globals := make(map[globalKey]db.VersionList)
  36. sequences := make(map[sequenceKey]string)
  37. needs := make(map[globalKey]struct{})
  38. blocklists := make(map[string]struct{})
  39. versions := make(map[string]protocol.Vector)
  40. usedBlocklists := make(map[string]struct{})
  41. usedVersions := make(map[string]struct{})
  42. var localDeviceKey uint32
  43. success = true
  44. it, err := ldb.NewPrefixIterator(nil)
  45. if err != nil {
  46. log.Fatal(err)
  47. }
  48. for it.Next() {
  49. key := it.Key()
  50. switch key[0] {
  51. case db.KeyTypeDevice:
  52. folder := binary.BigEndian.Uint32(key[1:])
  53. device := binary.BigEndian.Uint32(key[1+4:])
  54. name := nulString(key[1+4+4:])
  55. var f protocol.FileInfo
  56. err := f.Unmarshal(it.Value())
  57. if err != nil {
  58. fmt.Println("Unable to unmarshal FileInfo:", err)
  59. success = false
  60. continue
  61. }
  62. fileInfos[fileInfoKey{folder, device, name}] = f
  63. case db.KeyTypeGlobal:
  64. folder := binary.BigEndian.Uint32(key[1:])
  65. name := nulString(key[1+4:])
  66. var flv db.VersionList
  67. if err := flv.Unmarshal(it.Value()); err != nil {
  68. fmt.Println("Unable to unmarshal VersionList:", err)
  69. success = false
  70. continue
  71. }
  72. globals[globalKey{folder, name}] = flv
  73. case db.KeyTypeFolderIdx:
  74. key := binary.BigEndian.Uint32(it.Key()[1:])
  75. folders[key] = string(it.Value())
  76. case db.KeyTypeDeviceIdx:
  77. key := binary.BigEndian.Uint32(it.Key()[1:])
  78. devices[key] = string(it.Value())
  79. deviceToIDs[string(it.Value())] = key
  80. if bytes.Equal(it.Value(), protocol.LocalDeviceID[:]) {
  81. localDeviceKey = key
  82. }
  83. case db.KeyTypeSequence:
  84. folder := binary.BigEndian.Uint32(key[1:])
  85. seq := binary.BigEndian.Uint64(key[5:])
  86. val := it.Value()
  87. sequences[sequenceKey{folder, seq}] = string(val[9:])
  88. case db.KeyTypeNeed:
  89. folder := binary.BigEndian.Uint32(key[1:])
  90. name := nulString(key[1+4:])
  91. needs[globalKey{folder, name}] = struct{}{}
  92. case db.KeyTypeBlockList:
  93. hash := string(key[1:])
  94. blocklists[hash] = struct{}{}
  95. case db.KeyTypeVersion:
  96. hash := string(key[1:])
  97. var v protocol.Vector
  98. if err := v.Unmarshal(it.Value()); err != nil {
  99. fmt.Println("Unable to unmarshal Vector:", err)
  100. success = false
  101. continue
  102. }
  103. versions[hash] = v
  104. }
  105. }
  106. if localDeviceKey == 0 {
  107. fmt.Println("Missing key for local device in device index (bailing out)")
  108. success = false
  109. return
  110. }
  111. var missingSeq []sequenceKey
  112. for fk, fi := range fileInfos {
  113. if fk.name != fi.Name {
  114. fmt.Printf("Mismatching FileInfo name, %q (key) != %q (actual)\n", fk.name, fi.Name)
  115. success = false
  116. }
  117. folder := folders[fk.folder]
  118. if folder == "" {
  119. fmt.Printf("Unknown folder ID %d for FileInfo %q\n", fk.folder, fk.name)
  120. success = false
  121. continue
  122. }
  123. if devices[fk.device] == "" {
  124. fmt.Printf("Unknown device ID %d for FileInfo %q, folder %q\n", fk.folder, fk.name, folder)
  125. success = false
  126. }
  127. if fk.device == localDeviceKey {
  128. sk := sequenceKey{fk.folder, uint64(fi.Sequence)}
  129. name, ok := sequences[sk]
  130. if !ok {
  131. fmt.Printf("Sequence entry missing for FileInfo %q, folder %q, seq %d\n", fi.Name, folder, fi.Sequence)
  132. missingSeq = append(missingSeq, sk)
  133. success = false
  134. continue
  135. }
  136. if name != fi.Name {
  137. fmt.Printf("Sequence entry refers to wrong name, %q (seq) != %q (FileInfo), folder %q, seq %d\n", name, fi.Name, folder, fi.Sequence)
  138. success = false
  139. }
  140. }
  141. if len(fi.Blocks) == 0 && len(fi.BlocksHash) != 0 {
  142. key := string(fi.BlocksHash)
  143. if _, ok := blocklists[key]; !ok {
  144. fmt.Printf("Missing block list for file %q, block list hash %x\n", fi.Name, fi.BlocksHash)
  145. success = false
  146. } else {
  147. usedBlocklists[key] = struct{}{}
  148. }
  149. }
  150. if fi.VersionHash != nil {
  151. key := string(fi.VersionHash)
  152. if _, ok := versions[key]; !ok {
  153. fmt.Printf("Missing version vector for file %q, version hash %x\n", fi.Name, fi.VersionHash)
  154. success = false
  155. } else {
  156. usedVersions[key] = struct{}{}
  157. }
  158. }
  159. _, ok := globals[globalKey{fk.folder, fk.name}]
  160. if !ok {
  161. fmt.Printf("Missing global for file %q\n", fi.Name)
  162. success = false
  163. continue
  164. }
  165. }
  166. // Aggregate the ranges of missing sequence entries, print them
  167. sort.Slice(missingSeq, func(a, b int) bool {
  168. if missingSeq[a].folder != missingSeq[b].folder {
  169. return missingSeq[a].folder < missingSeq[b].folder
  170. }
  171. return missingSeq[a].sequence < missingSeq[b].sequence
  172. })
  173. var folder uint32
  174. var startSeq, prevSeq uint64
  175. for _, sk := range missingSeq {
  176. if folder != sk.folder || sk.sequence != prevSeq+1 {
  177. if folder != 0 {
  178. fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq)
  179. }
  180. startSeq = sk.sequence
  181. folder = sk.folder
  182. }
  183. prevSeq = sk.sequence
  184. }
  185. if folder != 0 {
  186. fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq)
  187. }
  188. for gk, vl := range globals {
  189. folder := folders[gk.folder]
  190. if folder == "" {
  191. fmt.Printf("Unknown folder ID %d for VersionList %q\n", gk.folder, gk.name)
  192. success = false
  193. }
  194. checkGlobal := func(i int, device []byte, version protocol.Vector, invalid, deleted bool) {
  195. dev, ok := deviceToIDs[string(device)]
  196. if !ok {
  197. fmt.Printf("VersionList %q, folder %q refers to unknown device %q\n", gk.name, folder, device)
  198. success = false
  199. }
  200. fi, ok := fileInfos[fileInfoKey{gk.folder, dev, gk.name}]
  201. if !ok {
  202. fmt.Printf("VersionList %q, folder %q, entry %d refers to unknown FileInfo\n", gk.name, folder, i)
  203. success = false
  204. }
  205. fiv := fi.Version
  206. if fi.VersionHash != nil {
  207. fiv = versions[string(fi.VersionHash)]
  208. }
  209. if !fiv.Equal(version) {
  210. fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo version mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, version, fi.Version)
  211. success = false
  212. }
  213. if fi.IsInvalid() != invalid {
  214. fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo invalid mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, invalid, fi.IsInvalid())
  215. success = false
  216. }
  217. if fi.IsDeleted() != deleted {
  218. fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo deleted mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, deleted, fi.IsDeleted())
  219. success = false
  220. }
  221. }
  222. for i, fv := range vl.RawVersions {
  223. for _, device := range fv.Devices {
  224. checkGlobal(i, device, fv.Version, false, fv.Deleted)
  225. }
  226. for _, device := range fv.InvalidDevices {
  227. checkGlobal(i, device, fv.Version, true, fv.Deleted)
  228. }
  229. }
  230. // If we need this file we should have a need entry for it. False
  231. // positives from needsLocally for deleted files, where we might
  232. // legitimately lack an entry if we never had it, and ignored files.
  233. if needsLocally(vl) {
  234. _, ok := needs[gk]
  235. if !ok {
  236. fv, _ := vl.GetGlobal()
  237. devB, _ := fv.FirstDevice()
  238. dev := deviceToIDs[string(devB)]
  239. fi := fileInfos[fileInfoKey{gk.folder, dev, gk.name}]
  240. if !fi.IsDeleted() && !fi.IsIgnored() {
  241. fmt.Printf("Missing need entry for needed file %q, folder %q\n", gk.name, folder)
  242. }
  243. }
  244. }
  245. }
  246. seenSeq := make(map[fileInfoKey]uint64)
  247. for sk, name := range sequences {
  248. folder := folders[sk.folder]
  249. if folder == "" {
  250. fmt.Printf("Unknown folder ID %d for sequence entry %d, %q\n", sk.folder, sk.sequence, name)
  251. success = false
  252. continue
  253. }
  254. if prev, ok := seenSeq[fileInfoKey{folder: sk.folder, name: name}]; ok {
  255. fmt.Printf("Duplicate sequence entry for %q, folder %q, seq %d (prev %d)\n", name, folder, sk.sequence, prev)
  256. success = false
  257. }
  258. seenSeq[fileInfoKey{folder: sk.folder, name: name}] = sk.sequence
  259. fi, ok := fileInfos[fileInfoKey{sk.folder, localDeviceKey, name}]
  260. if !ok {
  261. fmt.Printf("Missing FileInfo for sequence entry %d, folder %q, %q\n", sk.sequence, folder, name)
  262. success = false
  263. continue
  264. }
  265. if fi.Sequence != int64(sk.sequence) {
  266. fmt.Printf("Sequence mismatch for %q, folder %q, %d (key) != %d (FileInfo)\n", name, folder, sk.sequence, fi.Sequence)
  267. success = false
  268. }
  269. }
  270. for nk := range needs {
  271. folder := folders[nk.folder]
  272. if folder == "" {
  273. fmt.Printf("Unknown folder ID %d for need entry %q\n", nk.folder, nk.name)
  274. success = false
  275. continue
  276. }
  277. vl, ok := globals[nk]
  278. if !ok {
  279. fmt.Printf("Missing global for need entry %q, folder %q\n", nk.name, folder)
  280. success = false
  281. continue
  282. }
  283. if !needsLocally(vl) {
  284. fmt.Printf("Need entry for file we don't need, %q, folder %q\n", nk.name, folder)
  285. success = false
  286. }
  287. }
  288. if d := len(blocklists) - len(usedBlocklists); d > 0 {
  289. fmt.Printf("%d block list entries out of %d needs GC\n", d, len(blocklists))
  290. }
  291. if d := len(versions) - len(usedVersions); d > 0 {
  292. fmt.Printf("%d version entries out of %d needs GC\n", d, len(versions))
  293. }
  294. return
  295. }
  296. func needsLocally(vl db.VersionList) bool {
  297. fv, ok := vl.Get(protocol.LocalDeviceID[:])
  298. if !ok {
  299. return true // proviosinally, it looks like we need the file
  300. }
  301. gfv, _ := vl.GetGlobal() // Can't not have a global if we got something above
  302. return !fv.Version.GreaterEqual(gfv.Version)
  303. }