set.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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/internal/lamport"
  14. "github.com/syncthing/syncthing/internal/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. if len(fs) == 0 {
  59. // Reset the local version if all files were removed.
  60. s.localVersion[node] = 0
  61. }
  62. }
  63. func (s *Set) ReplaceWithDelete(node protocol.NodeID, fs []protocol.FileInfo) {
  64. if debug {
  65. l.Debugf("%s ReplaceWithDelete(%v, [%d])", s.repo, node, len(fs))
  66. }
  67. normalizeFilenames(fs)
  68. s.mutex.Lock()
  69. defer s.mutex.Unlock()
  70. if lv := ldbReplaceWithDelete(s.db, []byte(s.repo), node[:], fs); lv > s.localVersion[node] {
  71. s.localVersion[node] = lv
  72. }
  73. }
  74. func (s *Set) Update(node protocol.NodeID, fs []protocol.FileInfo) {
  75. if debug {
  76. l.Debugf("%s Update(%v, [%d])", s.repo, node, len(fs))
  77. }
  78. normalizeFilenames(fs)
  79. s.mutex.Lock()
  80. defer s.mutex.Unlock()
  81. if lv := ldbUpdate(s.db, []byte(s.repo), node[:], fs); lv > s.localVersion[node] {
  82. s.localVersion[node] = lv
  83. }
  84. }
  85. func (s *Set) WithNeed(node protocol.NodeID, fn fileIterator) {
  86. if debug {
  87. l.Debugf("%s WithNeed(%v)", s.repo, node)
  88. }
  89. ldbWithNeed(s.db, []byte(s.repo), node[:], false, nativeFileIterator(fn))
  90. }
  91. func (s *Set) WithNeedTruncated(node protocol.NodeID, fn fileIterator) {
  92. if debug {
  93. l.Debugf("%s WithNeedTruncated(%v)", s.repo, node)
  94. }
  95. ldbWithNeed(s.db, []byte(s.repo), node[:], true, nativeFileIterator(fn))
  96. }
  97. func (s *Set) WithHave(node protocol.NodeID, fn fileIterator) {
  98. if debug {
  99. l.Debugf("%s WithHave(%v)", s.repo, node)
  100. }
  101. ldbWithHave(s.db, []byte(s.repo), node[:], false, nativeFileIterator(fn))
  102. }
  103. func (s *Set) WithHaveTruncated(node protocol.NodeID, fn fileIterator) {
  104. if debug {
  105. l.Debugf("%s WithHaveTruncated(%v)", s.repo, node)
  106. }
  107. ldbWithHave(s.db, []byte(s.repo), node[:], true, nativeFileIterator(fn))
  108. }
  109. func (s *Set) WithGlobal(fn fileIterator) {
  110. if debug {
  111. l.Debugf("%s WithGlobal()", s.repo)
  112. }
  113. ldbWithGlobal(s.db, []byte(s.repo), false, nativeFileIterator(fn))
  114. }
  115. func (s *Set) WithGlobalTruncated(fn fileIterator) {
  116. if debug {
  117. l.Debugf("%s WithGlobalTruncated()", s.repo)
  118. }
  119. ldbWithGlobal(s.db, []byte(s.repo), true, nativeFileIterator(fn))
  120. }
  121. func (s *Set) Get(node protocol.NodeID, file string) protocol.FileInfo {
  122. f := ldbGet(s.db, []byte(s.repo), node[:], []byte(normalizedFilename(file)))
  123. f.Name = nativeFilename(f.Name)
  124. return f
  125. }
  126. func (s *Set) GetGlobal(file string) protocol.FileInfo {
  127. f := ldbGetGlobal(s.db, []byte(s.repo), []byte(normalizedFilename(file)))
  128. f.Name = nativeFilename(f.Name)
  129. return f
  130. }
  131. func (s *Set) Availability(file string) []protocol.NodeID {
  132. return ldbAvailability(s.db, []byte(s.repo), []byte(normalizedFilename(file)))
  133. }
  134. func (s *Set) LocalVersion(node protocol.NodeID) uint64 {
  135. s.mutex.Lock()
  136. defer s.mutex.Unlock()
  137. return s.localVersion[node]
  138. }
  139. // ListRepos returns the repository IDs seen in the database.
  140. func ListRepos(db *leveldb.DB) []string {
  141. return ldbListRepos(db)
  142. }
  143. // DropRepo clears out all information related to the given repo from the
  144. // database.
  145. func DropRepo(db *leveldb.DB, repo string) {
  146. ldbDropRepo(db, []byte(repo))
  147. }
  148. func normalizeFilenames(fs []protocol.FileInfo) {
  149. for i := range fs {
  150. fs[i].Name = normalizedFilename(fs[i].Name)
  151. }
  152. }
  153. func nativeFileIterator(fn fileIterator) fileIterator {
  154. return func(fi protocol.FileIntf) bool {
  155. switch f := fi.(type) {
  156. case protocol.FileInfo:
  157. f.Name = nativeFilename(f.Name)
  158. return fn(f)
  159. case protocol.FileInfoTruncated:
  160. f.Name = nativeFilename(f.Name)
  161. return fn(f)
  162. default:
  163. panic("unknown interface type")
  164. }
  165. }
  166. }