folder.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright (C) 2014 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 model
  7. import (
  8. "context"
  9. "time"
  10. "github.com/syncthing/syncthing/lib/config"
  11. "github.com/syncthing/syncthing/lib/watchaggregator"
  12. )
  13. type folder struct {
  14. stateTracker
  15. config.FolderConfiguration
  16. scan folderScanner
  17. model *Model
  18. ctx context.Context
  19. cancel context.CancelFunc
  20. initialScanFinished chan struct{}
  21. watchCancel context.CancelFunc
  22. watchChan chan []string
  23. restartWatchChan chan struct{}
  24. }
  25. func newFolder(model *Model, cfg config.FolderConfiguration) folder {
  26. ctx, cancel := context.WithCancel(context.Background())
  27. return folder{
  28. stateTracker: newStateTracker(cfg.ID),
  29. FolderConfiguration: cfg,
  30. scan: newFolderScanner(cfg),
  31. ctx: ctx,
  32. cancel: cancel,
  33. model: model,
  34. initialScanFinished: make(chan struct{}),
  35. }
  36. }
  37. func (f *folder) BringToFront(string) {}
  38. func (f *folder) DelayScan(next time.Duration) {
  39. f.scan.Delay(next)
  40. }
  41. func (f *folder) IndexUpdated() {
  42. }
  43. func (f *folder) IgnoresUpdated() {
  44. if f.FSWatcherEnabled {
  45. f.scheduleWatchRestart()
  46. }
  47. }
  48. func (f *folder) Jobs() ([]string, []string) {
  49. return nil, nil
  50. }
  51. func (f *folder) Scan(subdirs []string) error {
  52. <-f.initialScanFinished
  53. return f.scan.Scan(subdirs)
  54. }
  55. func (f *folder) Stop() {
  56. f.cancel()
  57. }
  58. func (f *folder) BlockStats() map[string]int {
  59. return nil
  60. }
  61. // CheckHealth checks the folder for common errors, updates the folder state
  62. // and returns the current folder error, or nil if the folder is healthy.
  63. func (f *folder) CheckHealth() error {
  64. err := f.getHealthError()
  65. f.setError(err)
  66. return err
  67. }
  68. func (f *folder) getHealthError() error {
  69. // Check for folder errors, with the most serious and specific first and
  70. // generic ones like out of space on the home disk later.
  71. if err := f.CheckPath(); err != nil {
  72. return err
  73. }
  74. if err := f.CheckFreeSpace(); err != nil {
  75. return err
  76. }
  77. if err := f.model.cfg.CheckHomeFreeSpace(); err != nil {
  78. return err
  79. }
  80. return nil
  81. }
  82. func (f *folder) scanSubdirs(subDirs []string) error {
  83. if err := f.model.internalScanFolderSubdirs(f.ctx, f.folderID, subDirs); err != nil {
  84. // Potentially sets the error twice, once in the scanner just
  85. // by doing a check, and once here, if the error returned is
  86. // the same one as returned by CheckHealth, though
  87. // duplicate set is handled by setError.
  88. f.setError(err)
  89. return err
  90. }
  91. return nil
  92. }
  93. func (f *folder) scanTimerFired() {
  94. err := f.scanSubdirs(nil)
  95. select {
  96. case <-f.initialScanFinished:
  97. default:
  98. status := "Completed"
  99. if err != nil {
  100. status = "Failed"
  101. }
  102. l.Infoln(status, "initial scan of", f.Type.String(), "folder", f.Description())
  103. close(f.initialScanFinished)
  104. }
  105. f.scan.Reschedule()
  106. }
  107. func (f *folder) startWatch() {
  108. ctx, cancel := context.WithCancel(f.ctx)
  109. f.model.fmut.RLock()
  110. ignores := f.model.folderIgnores[f.folderID]
  111. f.model.fmut.RUnlock()
  112. eventChan, err := f.Filesystem().Watch(".", ignores, ctx, f.IgnorePerms)
  113. if err != nil {
  114. l.Warnf("Failed to start filesystem watcher for folder %s: %v", f.Description(), err)
  115. } else {
  116. f.watchChan = make(chan []string)
  117. f.watchCancel = cancel
  118. watchaggregator.Aggregate(eventChan, f.watchChan, f.FolderConfiguration, f.model.cfg, ctx)
  119. l.Infoln("Started filesystem watcher for folder", f.Description())
  120. }
  121. }
  122. func (f *folder) restartWatch() {
  123. f.watchCancel()
  124. f.startWatch()
  125. f.Scan(nil)
  126. }
  127. func (f *folder) scheduleWatchRestart() {
  128. select {
  129. case f.restartWatchChan <- struct{}{}:
  130. default:
  131. // We might be busy doing a pull and thus not reading from this
  132. // channel. The channel is 1-buffered, so one notification will be
  133. // queued to ensure we recheck after the pull.
  134. }
  135. }
  136. func (f *folder) setError(err error) {
  137. _, _, oldErr := f.getState()
  138. if (err != nil && oldErr != nil && oldErr.Error() == err.Error()) || (err == nil && oldErr == nil) {
  139. return
  140. }
  141. if err != nil {
  142. if oldErr == nil {
  143. l.Warnf("Error on folder %s: %v", f.Description(), err)
  144. } else {
  145. l.Infof("Error on folder %s changed: %q -> %q", f.Description(), oldErr, err)
  146. }
  147. } else {
  148. l.Infoln("Cleared error on folder", f.Description())
  149. }
  150. if f.FSWatcherEnabled {
  151. if err != nil {
  152. f.watchCancel()
  153. } else {
  154. f.scheduleWatchRestart()
  155. }
  156. }
  157. f.stateTracker.setError(err)
  158. }