folder.go 4.5 KB

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