db_folderdb.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. // Copyright (C) 2025 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 sqlite
  7. import (
  8. "database/sql"
  9. "errors"
  10. "fmt"
  11. "iter"
  12. "path/filepath"
  13. "strings"
  14. "time"
  15. "github.com/syncthing/syncthing/internal/db"
  16. "github.com/syncthing/syncthing/internal/itererr"
  17. "github.com/syncthing/syncthing/lib/config"
  18. "github.com/syncthing/syncthing/lib/protocol"
  19. "github.com/syncthing/syncthing/lib/rand"
  20. )
  21. var errNoSuchFolder = errors.New("no such folder")
  22. func (s *DB) getFolderDB(folder string, create bool) (*folderDB, error) {
  23. // Check for an already open database
  24. s.folderDBsMut.RLock()
  25. fdb, ok := s.folderDBs[folder]
  26. s.folderDBsMut.RUnlock()
  27. if ok {
  28. return fdb, nil
  29. }
  30. // Check for an existing database. If we're not supposed to create the
  31. // folder, we don't move on if it doesn't already have a database name.
  32. var dbName string
  33. if err := s.stmt(`
  34. SELECT database_name FROM folders
  35. WHERE folder_id = ?
  36. `).Get(&dbName, folder); err != nil && !errors.Is(err, sql.ErrNoRows) {
  37. return nil, wrap(err)
  38. }
  39. if dbName == "" && !create {
  40. return nil, errNoSuchFolder
  41. }
  42. // Create a folder ID and database if it does not already exist
  43. s.folderDBsMut.Lock()
  44. defer s.folderDBsMut.Unlock()
  45. if fdb, ok := s.folderDBs[folder]; ok {
  46. return fdb, nil
  47. }
  48. if dbName == "" {
  49. // First time we want to access this folder, need to create a new
  50. // folder ID
  51. idx, err := s.folderIdxLocked(folder)
  52. if err != nil {
  53. return nil, wrap(err)
  54. }
  55. // The database name is the folder index ID and a random slug.
  56. slug := strings.ToLower(rand.String(8))
  57. dbName = fmt.Sprintf("folder.%04x-%s.db", idx, slug)
  58. if _, err := s.stmt(`UPDATE folders SET database_name = ? WHERE idx = ?`).Exec(dbName, idx); err != nil {
  59. return nil, wrap(err, "set name")
  60. }
  61. }
  62. l.Debugf("Folder %s in database %s", folder, dbName)
  63. path := dbName
  64. if !filepath.IsAbs(path) {
  65. path = filepath.Join(s.pathBase, dbName)
  66. }
  67. fdb, err := s.folderDBOpener(folder, path, s.deleteRetention)
  68. if err != nil {
  69. return nil, wrap(err)
  70. }
  71. s.folderDBs[folder] = fdb
  72. return fdb, nil
  73. }
  74. func (s *DB) Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error {
  75. fdb, err := s.getFolderDB(folder, true)
  76. if err != nil {
  77. return err
  78. }
  79. return fdb.Update(device, fs)
  80. }
  81. func (s *DB) GetDeviceFile(folder string, device protocol.DeviceID, file string) (protocol.FileInfo, bool, error) {
  82. fdb, err := s.getFolderDB(folder, false)
  83. if errors.Is(err, errNoSuchFolder) {
  84. return protocol.FileInfo{}, false, nil
  85. }
  86. if err != nil {
  87. return protocol.FileInfo{}, false, err
  88. }
  89. return fdb.GetDeviceFile(device, file)
  90. }
  91. func (s *DB) GetGlobalAvailability(folder, file string) ([]protocol.DeviceID, error) {
  92. fdb, err := s.getFolderDB(folder, false)
  93. if errors.Is(err, errNoSuchFolder) {
  94. return nil, nil
  95. }
  96. if err != nil {
  97. return nil, err
  98. }
  99. return fdb.GetGlobalAvailability(file)
  100. }
  101. func (s *DB) GetGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) {
  102. fdb, err := s.getFolderDB(folder, false)
  103. if errors.Is(err, errNoSuchFolder) {
  104. return protocol.FileInfo{}, false, nil
  105. }
  106. if err != nil {
  107. return protocol.FileInfo{}, false, err
  108. }
  109. return fdb.GetGlobalFile(file)
  110. }
  111. func (s *DB) AllGlobalFiles(folder string) (iter.Seq[db.FileMetadata], func() error) {
  112. fdb, err := s.getFolderDB(folder, false)
  113. if errors.Is(err, errNoSuchFolder) {
  114. return func(yield func(db.FileMetadata) bool) {}, func() error { return nil }
  115. }
  116. if err != nil {
  117. return func(yield func(db.FileMetadata) bool) {}, func() error { return err }
  118. }
  119. return fdb.AllGlobalFiles()
  120. }
  121. func (s *DB) AllGlobalFilesPrefix(folder string, prefix string) (iter.Seq[db.FileMetadata], func() error) {
  122. fdb, err := s.getFolderDB(folder, false)
  123. if errors.Is(err, errNoSuchFolder) {
  124. return func(yield func(db.FileMetadata) bool) {}, func() error { return nil }
  125. }
  126. if err != nil {
  127. return func(yield func(db.FileMetadata) bool) {}, func() error { return err }
  128. }
  129. return fdb.AllGlobalFilesPrefix(prefix)
  130. }
  131. func (s *DB) AllLocalBlocksWithHash(hash []byte) ([]db.BlockMapEntry, error) {
  132. var entries []db.BlockMapEntry
  133. err := s.forEachFolder(func(fdb *folderDB) error {
  134. es, err := itererr.Collect(fdb.AllLocalBlocksWithHash(hash))
  135. entries = append(entries, es...)
  136. return err
  137. })
  138. return entries, err
  139. }
  140. func (s *DB) AllLocalFilesWithBlocksHashAnyFolder(hash []byte) (map[string][]db.FileMetadata, error) {
  141. res := make(map[string][]db.FileMetadata)
  142. err := s.forEachFolder(func(fdb *folderDB) error {
  143. files, err := itererr.Collect(fdb.AllLocalFilesWithBlocksHash(hash))
  144. res[fdb.folderID] = files
  145. return err
  146. })
  147. return res, err
  148. }
  149. func (s *DB) AllLocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) {
  150. fdb, err := s.getFolderDB(folder, false)
  151. if errors.Is(err, errNoSuchFolder) {
  152. return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil }
  153. }
  154. if err != nil {
  155. return func(yield func(protocol.FileInfo) bool) {}, func() error { return err }
  156. }
  157. return fdb.AllLocalFiles(device)
  158. }
  159. func (s *DB) AllLocalFilesBySequence(folder string, device protocol.DeviceID, startSeq int64, limit int) (iter.Seq[protocol.FileInfo], func() error) {
  160. fdb, err := s.getFolderDB(folder, false)
  161. if errors.Is(err, errNoSuchFolder) {
  162. return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil }
  163. }
  164. if err != nil {
  165. return func(yield func(protocol.FileInfo) bool) {}, func() error { return err }
  166. }
  167. return fdb.AllLocalFilesBySequence(device, startSeq, limit)
  168. }
  169. func (s *DB) AllLocalFilesWithPrefix(folder string, device protocol.DeviceID, prefix string) (iter.Seq[protocol.FileInfo], func() error) {
  170. fdb, err := s.getFolderDB(folder, false)
  171. if errors.Is(err, errNoSuchFolder) {
  172. return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil }
  173. }
  174. if err != nil {
  175. return func(yield func(protocol.FileInfo) bool) {}, func() error { return err }
  176. }
  177. return fdb.AllLocalFilesWithPrefix(device, prefix)
  178. }
  179. func (s *DB) AllLocalFilesWithBlocksHash(folder string, h []byte) (iter.Seq[db.FileMetadata], func() error) {
  180. fdb, err := s.getFolderDB(folder, false)
  181. if errors.Is(err, errNoSuchFolder) {
  182. return func(yield func(db.FileMetadata) bool) {}, func() error { return nil }
  183. }
  184. if err != nil {
  185. return func(yield func(db.FileMetadata) bool) {}, func() error { return err }
  186. }
  187. return fdb.AllLocalFilesWithBlocksHash(h)
  188. }
  189. func (s *DB) AllNeededGlobalFiles(folder string, device protocol.DeviceID, order config.PullOrder, limit, offset int) (iter.Seq[protocol.FileInfo], func() error) {
  190. fdb, err := s.getFolderDB(folder, false)
  191. if errors.Is(err, errNoSuchFolder) {
  192. return func(yield func(protocol.FileInfo) bool) {}, func() error { return nil }
  193. }
  194. if err != nil {
  195. return func(yield func(protocol.FileInfo) bool) {}, func() error { return err }
  196. }
  197. return fdb.AllNeededGlobalFiles(device, order, limit, offset)
  198. }
  199. func (s *DB) DropAllFiles(folder string, device protocol.DeviceID) error {
  200. fdb, err := s.getFolderDB(folder, false)
  201. if errors.Is(err, errNoSuchFolder) {
  202. return nil
  203. }
  204. if err != nil {
  205. return err
  206. }
  207. return fdb.DropAllFiles(device)
  208. }
  209. func (s *DB) DropFilesNamed(folder string, device protocol.DeviceID, names []string) error {
  210. fdb, err := s.getFolderDB(folder, false)
  211. if errors.Is(err, errNoSuchFolder) {
  212. return nil
  213. }
  214. if err != nil {
  215. return err
  216. }
  217. return fdb.DropFilesNamed(device, names)
  218. }
  219. func (s *DB) ListDevicesForFolder(folder string) ([]protocol.DeviceID, error) {
  220. fdb, err := s.getFolderDB(folder, false)
  221. if errors.Is(err, errNoSuchFolder) {
  222. return nil, nil
  223. }
  224. if err != nil {
  225. return nil, err
  226. }
  227. return fdb.ListDevicesForFolder()
  228. }
  229. func (s *DB) RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) {
  230. fdb, err := s.getFolderDB(folder, false)
  231. if errors.Is(err, errNoSuchFolder) {
  232. return nil, nil
  233. }
  234. if err != nil {
  235. return nil, err
  236. }
  237. return fdb.RemoteSequences()
  238. }
  239. func (s *DB) CountGlobal(folder string) (db.Counts, error) {
  240. fdb, err := s.getFolderDB(folder, false)
  241. if errors.Is(err, errNoSuchFolder) {
  242. return db.Counts{}, nil
  243. }
  244. if err != nil {
  245. return db.Counts{}, err
  246. }
  247. return fdb.CountGlobal()
  248. }
  249. func (s *DB) CountLocal(folder string, device protocol.DeviceID) (db.Counts, error) {
  250. fdb, err := s.getFolderDB(folder, false)
  251. if errors.Is(err, errNoSuchFolder) {
  252. return db.Counts{}, nil
  253. }
  254. if err != nil {
  255. return db.Counts{}, err
  256. }
  257. return fdb.CountLocal(device)
  258. }
  259. func (s *DB) CountNeed(folder string, device protocol.DeviceID) (db.Counts, error) {
  260. fdb, err := s.getFolderDB(folder, false)
  261. if errors.Is(err, errNoSuchFolder) {
  262. return db.Counts{}, nil
  263. }
  264. if err != nil {
  265. return db.Counts{}, err
  266. }
  267. return fdb.CountNeed(device)
  268. }
  269. func (s *DB) CountReceiveOnlyChanged(folder string) (db.Counts, error) {
  270. fdb, err := s.getFolderDB(folder, false)
  271. if errors.Is(err, errNoSuchFolder) {
  272. return db.Counts{}, nil
  273. }
  274. if err != nil {
  275. return db.Counts{}, err
  276. }
  277. return fdb.CountReceiveOnlyChanged()
  278. }
  279. func (s *DB) DropAllIndexIDs() error {
  280. return s.forEachFolder(func(fdb *folderDB) error {
  281. return fdb.DropAllIndexIDs()
  282. })
  283. }
  284. func (s *DB) GetIndexID(folder string, device protocol.DeviceID) (protocol.IndexID, error) {
  285. fdb, err := s.getFolderDB(folder, true)
  286. if err != nil {
  287. return 0, err
  288. }
  289. return fdb.GetIndexID(device)
  290. }
  291. func (s *DB) SetIndexID(folder string, device protocol.DeviceID, id protocol.IndexID) error {
  292. fdb, err := s.getFolderDB(folder, true)
  293. if err != nil {
  294. return err
  295. }
  296. return fdb.SetIndexID(device, id)
  297. }
  298. func (s *DB) GetDeviceSequence(folder string, device protocol.DeviceID) (int64, error) {
  299. fdb, err := s.getFolderDB(folder, false)
  300. if errors.Is(err, errNoSuchFolder) {
  301. return 0, nil
  302. }
  303. if err != nil {
  304. return 0, err
  305. }
  306. return fdb.GetDeviceSequence(device)
  307. }
  308. func (s *DB) DeleteMtime(folder, name string) error {
  309. fdb, err := s.getFolderDB(folder, false)
  310. if errors.Is(err, errNoSuchFolder) {
  311. return nil
  312. }
  313. if err != nil {
  314. return err
  315. }
  316. return fdb.DeleteMtime(name)
  317. }
  318. func (s *DB) GetMtime(folder, name string) (ondisk, virtual time.Time) {
  319. fdb, err := s.getFolderDB(folder, false)
  320. if errors.Is(err, errNoSuchFolder) {
  321. return time.Time{}, time.Time{}
  322. }
  323. if err != nil {
  324. return time.Time{}, time.Time{}
  325. }
  326. return fdb.GetMtime(name)
  327. }
  328. func (s *DB) PutMtime(folder, name string, ondisk, virtual time.Time) error {
  329. fdb, err := s.getFolderDB(folder, true)
  330. if err != nil {
  331. return err
  332. }
  333. return fdb.PutMtime(name, ondisk, virtual)
  334. }
  335. func (s *DB) DropDevice(device protocol.DeviceID) error {
  336. return s.forEachFolder(func(fdb *folderDB) error {
  337. return fdb.DropDevice(device)
  338. })
  339. }
  340. // forEachFolder runs the function for each currently open folderDB,
  341. // returning the first error that was encountered.
  342. func (s *DB) forEachFolder(fn func(fdb *folderDB) error) error {
  343. folders, err := s.ListFolders()
  344. if err != nil {
  345. return err
  346. }
  347. var firstError error
  348. for _, folder := range folders {
  349. fdb, err := s.getFolderDB(folder, false)
  350. if err != nil {
  351. if firstError == nil {
  352. firstError = err
  353. }
  354. continue
  355. }
  356. if err := fn(fdb); err != nil && firstError == nil {
  357. firstError = err
  358. }
  359. }
  360. return firstError
  361. }