db_folderdb.go 11 KB

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