set.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. // All rights reserved. Use of this source code is governed by an MIT-style
  3. // license that can be found in the LICENSE file.
  4. // Package files provides a set type to track local/remote files with newness
  5. // checks. We must do a certain amount of normalization in here. We will get
  6. // fed paths with either native or wire-format separators and encodings
  7. // depending on who calls us. We transform paths to wire-format (NFC and
  8. // slashes) on the way to the database, and transform to native format
  9. // (varying separator and encoding) on the way back out.
  10. package files
  11. import (
  12. "sync"
  13. "github.com/syncthing/syncthing/lamport"
  14. "github.com/syncthing/syncthing/protocol"
  15. "github.com/syndtr/goleveldb/leveldb"
  16. )
  17. type fileRecord struct {
  18. File protocol.FileInfo
  19. Usage int
  20. Global bool
  21. }
  22. type bitset uint64
  23. type Set struct {
  24. localVersion map[protocol.NodeID]uint64
  25. mutex sync.Mutex
  26. repo string
  27. db *leveldb.DB
  28. }
  29. func NewSet(repo string, db *leveldb.DB) *Set {
  30. var s = Set{
  31. localVersion: make(map[protocol.NodeID]uint64),
  32. repo: repo,
  33. db: db,
  34. }
  35. var nodeID protocol.NodeID
  36. ldbWithAllRepoTruncated(db, []byte(repo), func(node []byte, f protocol.FileInfoTruncated) bool {
  37. copy(nodeID[:], node)
  38. if f.LocalVersion > s.localVersion[nodeID] {
  39. s.localVersion[nodeID] = f.LocalVersion
  40. }
  41. lamport.Default.Tick(f.Version)
  42. return true
  43. })
  44. if debug {
  45. l.Debugf("loaded localVersion for %q: %#v", repo, s.localVersion)
  46. }
  47. clock(s.localVersion[protocol.LocalNodeID])
  48. return &s
  49. }
  50. func (s *Set) Replace(node protocol.NodeID, fs []protocol.FileInfo) {
  51. if debug {
  52. l.Debugf("%s Replace(%v, [%d])", s.repo, node, len(fs))
  53. }
  54. normalizeFilenames(fs)
  55. s.mutex.Lock()
  56. defer s.mutex.Unlock()
  57. s.localVersion[node] = ldbReplace(s.db, []byte(s.repo), node[:], fs)
  58. }
  59. func (s *Set) ReplaceWithDelete(node protocol.NodeID, fs []protocol.FileInfo) {
  60. if debug {
  61. l.Debugf("%s ReplaceWithDelete(%v, [%d])", s.repo, node, len(fs))
  62. }
  63. normalizeFilenames(fs)
  64. s.mutex.Lock()
  65. defer s.mutex.Unlock()
  66. if lv := ldbReplaceWithDelete(s.db, []byte(s.repo), node[:], fs); lv > s.localVersion[node] {
  67. s.localVersion[node] = lv
  68. }
  69. }
  70. func (s *Set) Update(node protocol.NodeID, fs []protocol.FileInfo) {
  71. if debug {
  72. l.Debugf("%s Update(%v, [%d])", s.repo, node, len(fs))
  73. }
  74. normalizeFilenames(fs)
  75. s.mutex.Lock()
  76. defer s.mutex.Unlock()
  77. if lv := ldbUpdate(s.db, []byte(s.repo), node[:], fs); lv > s.localVersion[node] {
  78. s.localVersion[node] = lv
  79. }
  80. }
  81. func (s *Set) WithNeed(node protocol.NodeID, fn fileIterator) {
  82. if debug {
  83. l.Debugf("%s WithNeed(%v)", s.repo, node)
  84. }
  85. ldbWithNeed(s.db, []byte(s.repo), node[:], false, nativeFileIterator(fn))
  86. }
  87. func (s *Set) WithNeedTruncated(node protocol.NodeID, fn fileIterator) {
  88. if debug {
  89. l.Debugf("%s WithNeedTruncated(%v)", s.repo, node)
  90. }
  91. ldbWithNeed(s.db, []byte(s.repo), node[:], true, nativeFileIterator(fn))
  92. }
  93. func (s *Set) WithHave(node protocol.NodeID, fn fileIterator) {
  94. if debug {
  95. l.Debugf("%s WithHave(%v)", s.repo, node)
  96. }
  97. ldbWithHave(s.db, []byte(s.repo), node[:], false, nativeFileIterator(fn))
  98. }
  99. func (s *Set) WithHaveTruncated(node protocol.NodeID, fn fileIterator) {
  100. if debug {
  101. l.Debugf("%s WithHaveTruncated(%v)", s.repo, node)
  102. }
  103. ldbWithHave(s.db, []byte(s.repo), node[:], true, nativeFileIterator(fn))
  104. }
  105. func (s *Set) WithGlobal(fn fileIterator) {
  106. if debug {
  107. l.Debugf("%s WithGlobal()", s.repo)
  108. }
  109. ldbWithGlobal(s.db, []byte(s.repo), false, nativeFileIterator(fn))
  110. }
  111. func (s *Set) WithGlobalTruncated(fn fileIterator) {
  112. if debug {
  113. l.Debugf("%s WithGlobalTruncated()", s.repo)
  114. }
  115. ldbWithGlobal(s.db, []byte(s.repo), true, nativeFileIterator(fn))
  116. }
  117. func (s *Set) Get(node protocol.NodeID, file string) protocol.FileInfo {
  118. f := ldbGet(s.db, []byte(s.repo), node[:], []byte(normalizedFilename(file)))
  119. f.Name = nativeFilename(f.Name)
  120. return f
  121. }
  122. func (s *Set) GetGlobal(file string) protocol.FileInfo {
  123. f := ldbGetGlobal(s.db, []byte(s.repo), []byte(normalizedFilename(file)))
  124. f.Name = nativeFilename(f.Name)
  125. return f
  126. }
  127. func (s *Set) Availability(file string) []protocol.NodeID {
  128. return ldbAvailability(s.db, []byte(s.repo), []byte(normalizedFilename(file)))
  129. }
  130. func (s *Set) LocalVersion(node protocol.NodeID) uint64 {
  131. s.mutex.Lock()
  132. defer s.mutex.Unlock()
  133. return s.localVersion[node]
  134. }
  135. func normalizeFilenames(fs []protocol.FileInfo) {
  136. for i := range fs {
  137. fs[i].Name = normalizedFilename(fs[i].Name)
  138. }
  139. }
  140. func nativeFileIterator(fn fileIterator) fileIterator {
  141. return func(fi protocol.FileIntf) bool {
  142. switch f := fi.(type) {
  143. case protocol.FileInfo:
  144. f.Name = nativeFilename(f.Name)
  145. return fn(f)
  146. case protocol.FileInfoTruncated:
  147. f.Name = nativeFilename(f.Name)
  148. return fn(f)
  149. default:
  150. panic("unknown interface type")
  151. }
  152. }
  153. }