folderconfiguration.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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 config
  7. import (
  8. "errors"
  9. "fmt"
  10. "runtime"
  11. "github.com/syncthing/syncthing/lib/fs"
  12. "github.com/syncthing/syncthing/lib/protocol"
  13. )
  14. var (
  15. errPathMissing = errors.New("folder path missing")
  16. errMarkerMissing = errors.New("folder marker missing")
  17. )
  18. const DefaultMarkerName = ".stfolder"
  19. type FolderConfiguration struct {
  20. ID string `xml:"id,attr" json:"id"`
  21. Label string `xml:"label,attr" json:"label"`
  22. FilesystemType fs.FilesystemType `xml:"filesystemType" json:"filesystemType"`
  23. Path string `xml:"path,attr" json:"path"`
  24. Type FolderType `xml:"type,attr" json:"type"`
  25. Devices []FolderDeviceConfiguration `xml:"device" json:"devices"`
  26. RescanIntervalS int `xml:"rescanIntervalS,attr" json:"rescanIntervalS"`
  27. FSWatcherEnabled bool `xml:"fsWatcherEnabled,attr" json:"fsWatcherEnabled"`
  28. FSWatcherDelayS int `xml:"fsWatcherDelayS,attr" json:"fsWatcherDelayS"`
  29. IgnorePerms bool `xml:"ignorePerms,attr" json:"ignorePerms"`
  30. AutoNormalize bool `xml:"autoNormalize,attr" json:"autoNormalize"`
  31. MinDiskFree Size `xml:"minDiskFree" json:"minDiskFree"`
  32. Versioning VersioningConfiguration `xml:"versioning" json:"versioning"`
  33. Copiers int `xml:"copiers" json:"copiers"` // This defines how many files are handled concurrently.
  34. Pullers int `xml:"pullers" json:"pullers"` // Defines how many blocks are fetched at the same time, possibly between separate copier routines.
  35. Hashers int `xml:"hashers" json:"hashers"` // Less than one sets the value to the number of cores. These are CPU bound due to hashing.
  36. Order PullOrder `xml:"order" json:"order"`
  37. IgnoreDelete bool `xml:"ignoreDelete" json:"ignoreDelete"`
  38. ScanProgressIntervalS int `xml:"scanProgressIntervalS" json:"scanProgressIntervalS"` // Set to a negative value to disable. Value of 0 will get replaced with value of 2 (default value)
  39. PullerPauseS int `xml:"pullerPauseS" json:"pullerPauseS"`
  40. MaxConflicts int `xml:"maxConflicts" json:"maxConflicts"`
  41. DisableSparseFiles bool `xml:"disableSparseFiles" json:"disableSparseFiles"`
  42. DisableTempIndexes bool `xml:"disableTempIndexes" json:"disableTempIndexes"`
  43. Paused bool `xml:"paused" json:"paused"`
  44. WeakHashThresholdPct int `xml:"weakHashThresholdPct" json:"weakHashThresholdPct"` // Use weak hash if more than X percent of the file has changed. Set to -1 to always use weak hash.
  45. MarkerName string `xml:"markerName" json:"markerName"`
  46. cachedFilesystem fs.Filesystem
  47. DeprecatedReadOnly bool `xml:"ro,attr,omitempty" json:"-"`
  48. DeprecatedMinDiskFreePct float64 `xml:"minDiskFreePct,omitempty" json:"-"`
  49. }
  50. type FolderDeviceConfiguration struct {
  51. DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
  52. IntroducedBy protocol.DeviceID `xml:"introducedBy,attr" json:"introducedBy"`
  53. }
  54. func NewFolderConfiguration(id string, fsType fs.FilesystemType, path string) FolderConfiguration {
  55. f := FolderConfiguration{
  56. ID: id,
  57. FilesystemType: fsType,
  58. Path: path,
  59. }
  60. f.prepare()
  61. return f
  62. }
  63. func (f FolderConfiguration) Copy() FolderConfiguration {
  64. c := f
  65. c.Devices = make([]FolderDeviceConfiguration, len(f.Devices))
  66. copy(c.Devices, f.Devices)
  67. c.Versioning = f.Versioning.Copy()
  68. return c
  69. }
  70. func (f FolderConfiguration) Filesystem() fs.Filesystem {
  71. // This is intentionally not a pointer method, because things like
  72. // cfg.Folders["default"].Filesystem() should be valid.
  73. if f.cachedFilesystem == nil && f.Path != "" {
  74. l.Infoln("bug: uncached filesystem call (should only happen in tests)")
  75. return fs.NewFilesystem(f.FilesystemType, f.Path)
  76. }
  77. return f.cachedFilesystem
  78. }
  79. func (f *FolderConfiguration) CreateMarker() error {
  80. if err := f.CheckPath(); err != errMarkerMissing {
  81. return err
  82. }
  83. if f.MarkerName != DefaultMarkerName {
  84. // Folder uses a non-default marker so we shouldn't mess with it.
  85. // Pretend we created it and let the subsequent health checks sort
  86. // out the actual situation.
  87. return nil
  88. }
  89. permBits := fs.FileMode(0777)
  90. if runtime.GOOS == "windows" {
  91. // Windows has no umask so we must chose a safer set of bits to
  92. // begin with.
  93. permBits = 0700
  94. }
  95. fs := f.Filesystem()
  96. err := fs.Mkdir(DefaultMarkerName, permBits)
  97. if err != nil {
  98. return err
  99. }
  100. if dir, err := fs.Open("."); err != nil {
  101. l.Debugln("folder marker: open . failed:", err)
  102. } else if err := dir.Sync(); err != nil {
  103. l.Debugln("folder marker: fsync . failed:", err)
  104. }
  105. fs.Hide(DefaultMarkerName)
  106. return nil
  107. }
  108. // CheckPath returns nil if the folder root exists and contains the marker file
  109. func (f *FolderConfiguration) CheckPath() error {
  110. fi, err := f.Filesystem().Stat(".")
  111. if err != nil || !fi.IsDir() {
  112. return errPathMissing
  113. }
  114. _, err = f.Filesystem().Stat(f.MarkerName)
  115. if err != nil {
  116. return errMarkerMissing
  117. }
  118. return nil
  119. }
  120. func (f *FolderConfiguration) CreateRoot() (err error) {
  121. // Directory permission bits. Will be filtered down to something
  122. // sane by umask on Unixes.
  123. permBits := fs.FileMode(0777)
  124. if runtime.GOOS == "windows" {
  125. // Windows has no umask so we must chose a safer set of bits to
  126. // begin with.
  127. permBits = 0700
  128. }
  129. filesystem := f.Filesystem()
  130. if _, err = filesystem.Stat("."); fs.IsNotExist(err) {
  131. if err = filesystem.MkdirAll(".", permBits); err != nil {
  132. l.Warnf("Creating directory for %v: %v", f.Description(), err)
  133. }
  134. }
  135. return err
  136. }
  137. func (f FolderConfiguration) Description() string {
  138. if f.Label == "" {
  139. return f.ID
  140. }
  141. return fmt.Sprintf("%q (%s)", f.Label, f.ID)
  142. }
  143. func (f *FolderConfiguration) DeviceIDs() []protocol.DeviceID {
  144. deviceIDs := make([]protocol.DeviceID, len(f.Devices))
  145. for i, n := range f.Devices {
  146. deviceIDs[i] = n.DeviceID
  147. }
  148. return deviceIDs
  149. }
  150. func (f *FolderConfiguration) prepare() {
  151. if f.Path != "" {
  152. f.cachedFilesystem = fs.NewFilesystem(f.FilesystemType, f.Path)
  153. }
  154. if f.RescanIntervalS > MaxRescanIntervalS {
  155. f.RescanIntervalS = MaxRescanIntervalS
  156. } else if f.RescanIntervalS < 0 {
  157. f.RescanIntervalS = 0
  158. }
  159. if f.FSWatcherDelayS <= 0 {
  160. f.FSWatcherEnabled = false
  161. f.FSWatcherDelayS = 10
  162. }
  163. if f.Versioning.Params == nil {
  164. f.Versioning.Params = make(map[string]string)
  165. }
  166. if f.WeakHashThresholdPct == 0 {
  167. f.WeakHashThresholdPct = 25
  168. }
  169. if f.MarkerName == "" {
  170. f.MarkerName = DefaultMarkerName
  171. }
  172. }
  173. type FolderDeviceConfigurationList []FolderDeviceConfiguration
  174. func (l FolderDeviceConfigurationList) Less(a, b int) bool {
  175. return l[a].DeviceID.Compare(l[b].DeviceID) == -1
  176. }
  177. func (l FolderDeviceConfigurationList) Swap(a, b int) {
  178. l[a], l[b] = l[b], l[a]
  179. }
  180. func (l FolderDeviceConfigurationList) Len() int {
  181. return len(l)
  182. }
  183. func (f *FolderConfiguration) CheckFreeSpace() (err error) {
  184. return checkFreeSpace(f.MinDiskFree, f.Filesystem())
  185. }