folder.go 4.5 KB

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