folder_recvonly_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. // Copyright (C) 2018 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. "bytes"
  9. "context"
  10. "testing"
  11. "time"
  12. "github.com/syncthing/syncthing/lib/config"
  13. "github.com/syncthing/syncthing/lib/db"
  14. "github.com/syncthing/syncthing/lib/db/backend"
  15. "github.com/syncthing/syncthing/lib/fs"
  16. "github.com/syncthing/syncthing/lib/protocol"
  17. "github.com/syncthing/syncthing/lib/scanner"
  18. )
  19. func TestRecvOnlyRevertDeletes(t *testing.T) {
  20. // Make sure that we delete extraneous files and directories when we hit
  21. // Revert.
  22. // Get us a model up and running
  23. m, f := setupROFolder(t)
  24. ffs := f.Filesystem()
  25. defer cleanupModel(m)
  26. // Create some test data
  27. for _, dir := range []string{".stfolder", "ignDir", "unknownDir"} {
  28. must(t, ffs.MkdirAll(dir, 0755))
  29. }
  30. must(t, writeFile(ffs, "ignDir/ignFile", []byte("hello\n"), 0644))
  31. must(t, writeFile(ffs, "unknownDir/unknownFile", []byte("hello\n"), 0644))
  32. must(t, writeFile(ffs, ".stignore", []byte("ignDir\n"), 0644))
  33. knownFiles := setupKnownFiles(t, ffs, []byte("hello\n"))
  34. // Send and index update for the known stuff
  35. m.Index(device1, "ro", knownFiles)
  36. f.updateLocalsFromScanning(knownFiles)
  37. m.fmut.RLock()
  38. snap := m.folderFiles["ro"].Snapshot()
  39. m.fmut.RUnlock()
  40. size := snap.GlobalSize()
  41. snap.Release()
  42. if size.Files != 1 || size.Directories != 1 {
  43. t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
  44. }
  45. // Scan, should discover the other stuff in the folder
  46. must(t, m.ScanFolder("ro"))
  47. // We should now have two files and two directories.
  48. size = globalSize(t, m, "ro")
  49. if size.Files != 2 || size.Directories != 2 {
  50. t.Fatalf("Global: expected 2 files and 2 directories: %+v", size)
  51. }
  52. size = localSize(t, m, "ro")
  53. if size.Files != 2 || size.Directories != 2 {
  54. t.Fatalf("Local: expected 2 files and 2 directories: %+v", size)
  55. }
  56. size = receiveOnlyChangedSize(t, m, "ro")
  57. if size.Files+size.Directories == 0 {
  58. t.Fatalf("ROChanged: expected something: %+v", size)
  59. }
  60. // Revert should delete the unknown stuff
  61. m.Revert("ro")
  62. // These should still exist
  63. for _, p := range []string{"knownDir/knownFile", "ignDir/ignFile"} {
  64. if _, err := ffs.Stat(p); err != nil {
  65. t.Error("Unexpected error:", err)
  66. }
  67. }
  68. // These should have been removed
  69. for _, p := range []string{"unknownDir", "unknownDir/unknownFile"} {
  70. if _, err := ffs.Stat(p); !fs.IsNotExist(err) {
  71. t.Error("Unexpected existing thing:", p)
  72. }
  73. }
  74. // We should now have one file and directory again.
  75. size = globalSize(t, m, "ro")
  76. if size.Files != 1 || size.Directories != 1 {
  77. t.Fatalf("Global: expected 1 files and 1 directories: %+v", size)
  78. }
  79. size = localSize(t, m, "ro")
  80. if size.Files != 1 || size.Directories != 1 {
  81. t.Fatalf("Local: expected 1 files and 1 directories: %+v", size)
  82. }
  83. }
  84. func TestRecvOnlyRevertNeeds(t *testing.T) {
  85. // Make sure that a new file gets picked up and considered latest, then
  86. // gets considered old when we hit Revert.
  87. // Get us a model up and running
  88. m, f := setupROFolder(t)
  89. ffs := f.Filesystem()
  90. defer cleanupModel(m)
  91. // Create some test data
  92. must(t, ffs.MkdirAll(".stfolder", 0755))
  93. oldData := []byte("hello\n")
  94. knownFiles := setupKnownFiles(t, ffs, oldData)
  95. // Send and index update for the known stuff
  96. m.Index(device1, "ro", knownFiles)
  97. f.updateLocalsFromScanning(knownFiles)
  98. // Scan the folder.
  99. must(t, m.ScanFolder("ro"))
  100. // Everything should be in sync.
  101. size := globalSize(t, m, "ro")
  102. if size.Files != 1 || size.Directories != 1 {
  103. t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
  104. }
  105. size = localSize(t, m, "ro")
  106. if size.Files != 1 || size.Directories != 1 {
  107. t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
  108. }
  109. size = needSize(t, m, "ro")
  110. if size.Files+size.Directories > 0 {
  111. t.Fatalf("Need: expected nothing: %+v", size)
  112. }
  113. size = receiveOnlyChangedSize(t, m, "ro")
  114. if size.Files+size.Directories > 0 {
  115. t.Fatalf("ROChanged: expected nothing: %+v", size)
  116. }
  117. // Update the file.
  118. newData := []byte("totally different data\n")
  119. must(t, writeFile(ffs, "knownDir/knownFile", newData, 0644))
  120. // Rescan.
  121. must(t, m.ScanFolder("ro"))
  122. // We now have a newer file than the rest of the cluster. Global state should reflect this.
  123. size = globalSize(t, m, "ro")
  124. const sizeOfDir = 128
  125. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) {
  126. t.Fatalf("Global: expected no change due to the new file: %+v", size)
  127. }
  128. size = localSize(t, m, "ro")
  129. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
  130. t.Fatalf("Local: expected the new file to be reflected: %+v", size)
  131. }
  132. size = needSize(t, m, "ro")
  133. if size.Files+size.Directories > 0 {
  134. t.Fatalf("Need: expected nothing: %+v", size)
  135. }
  136. size = receiveOnlyChangedSize(t, m, "ro")
  137. if size.Files+size.Directories == 0 {
  138. t.Fatalf("ROChanged: expected something: %+v", size)
  139. }
  140. // We hit the Revert button. The file that was new should become old.
  141. m.Revert("ro")
  142. size = globalSize(t, m, "ro")
  143. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) {
  144. t.Fatalf("Global: expected the global size to revert: %+v", size)
  145. }
  146. size = localSize(t, m, "ro")
  147. if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
  148. t.Fatalf("Local: expected the local size to remain: %+v", size)
  149. }
  150. size = needSize(t, m, "ro")
  151. if size.Files != 1 || size.Bytes != int64(len(oldData)) {
  152. t.Fatalf("Local: expected to need the old file data: %+v", size)
  153. }
  154. }
  155. func TestRecvOnlyUndoChanges(t *testing.T) {
  156. // Get us a model up and running
  157. m, f := setupROFolder(t)
  158. ffs := f.Filesystem()
  159. defer cleanupModel(m)
  160. // Create some test data
  161. must(t, ffs.MkdirAll(".stfolder", 0755))
  162. oldData := []byte("hello\n")
  163. knownFiles := setupKnownFiles(t, ffs, oldData)
  164. // Send an index update for the known stuff
  165. m.Index(device1, "ro", knownFiles)
  166. f.updateLocalsFromScanning(knownFiles)
  167. // Scan the folder.
  168. must(t, m.ScanFolder("ro"))
  169. // Everything should be in sync.
  170. size := globalSize(t, m, "ro")
  171. if size.Files != 1 || size.Directories != 1 {
  172. t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
  173. }
  174. size = localSize(t, m, "ro")
  175. if size.Files != 1 || size.Directories != 1 {
  176. t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
  177. }
  178. size = needSize(t, m, "ro")
  179. if size.Files+size.Directories > 0 {
  180. t.Fatalf("Need: expected nothing: %+v", size)
  181. }
  182. size = receiveOnlyChangedSize(t, m, "ro")
  183. if size.Files+size.Directories > 0 {
  184. t.Fatalf("ROChanged: expected nothing: %+v", size)
  185. }
  186. // Create a file and modify another
  187. const file = "foo"
  188. must(t, writeFile(ffs, file, []byte("hello\n"), 0644))
  189. must(t, writeFile(ffs, "knownDir/knownFile", []byte("bye\n"), 0644))
  190. must(t, m.ScanFolder("ro"))
  191. size = receiveOnlyChangedSize(t, m, "ro")
  192. if size.Files != 2 {
  193. t.Fatalf("Receive only: expected 2 files: %+v", size)
  194. }
  195. // Remove the file again and undo the modification
  196. must(t, ffs.Remove(file))
  197. must(t, writeFile(ffs, "knownDir/knownFile", oldData, 0644))
  198. must(t, ffs.Chtimes("knownDir/knownFile", knownFiles[1].ModTime(), knownFiles[1].ModTime()))
  199. must(t, m.ScanFolder("ro"))
  200. size = receiveOnlyChangedSize(t, m, "ro")
  201. if size.Files+size.Directories+size.Deleted != 0 {
  202. t.Fatalf("Receive only: expected all zero: %+v", size)
  203. }
  204. }
  205. func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
  206. // Get us a model up and running
  207. m, f := setupROFolder(t)
  208. ffs := f.Filesystem()
  209. defer cleanupModel(m)
  210. // Create some test data
  211. must(t, ffs.MkdirAll(".stfolder", 0755))
  212. oldData := []byte("hello\n")
  213. knownFiles := setupKnownFiles(t, ffs, oldData)
  214. // Send an index update for the known stuff
  215. m.Index(device1, "ro", knownFiles)
  216. f.updateLocalsFromScanning(knownFiles)
  217. // Scan the folder.
  218. must(t, m.ScanFolder("ro"))
  219. // Everything should be in sync.
  220. size := globalSize(t, m, "ro")
  221. if size.Files != 1 || size.Directories != 1 {
  222. t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
  223. }
  224. size = localSize(t, m, "ro")
  225. if size.Files != 1 || size.Directories != 1 {
  226. t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
  227. }
  228. size = needSize(t, m, "ro")
  229. if size.Files+size.Directories > 0 {
  230. t.Fatalf("Need: expected nothing: %+v", size)
  231. }
  232. size = receiveOnlyChangedSize(t, m, "ro")
  233. if size.Files+size.Directories > 0 {
  234. t.Fatalf("ROChanged: expected nothing: %+v", size)
  235. }
  236. // Delete our file
  237. must(t, ffs.Remove(knownFiles[1].Name))
  238. must(t, m.ScanFolder("ro"))
  239. size = receiveOnlyChangedSize(t, m, "ro")
  240. if size.Deleted != 1 {
  241. t.Fatalf("Receive only: expected 1 deleted: %+v", size)
  242. }
  243. // Drop the remote
  244. f.fset.Drop(device1)
  245. must(t, m.ScanFolder("ro"))
  246. size = receiveOnlyChangedSize(t, m, "ro")
  247. if size.Deleted != 0 {
  248. t.Fatalf("Receive only: expected no deleted: %+v", size)
  249. }
  250. }
  251. func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.FileInfo {
  252. t.Helper()
  253. must(t, ffs.MkdirAll("knownDir", 0755))
  254. must(t, writeFile(ffs, "knownDir/knownFile", data, 0644))
  255. t0 := time.Now().Add(-1 * time.Minute)
  256. must(t, ffs.Chtimes("knownDir/knownFile", t0, t0))
  257. fi, err := ffs.Stat("knownDir/knownFile")
  258. if err != nil {
  259. t.Fatal(err)
  260. }
  261. blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), protocol.BlockSize(int64(len(data))), int64(len(data)), nil, true)
  262. knownFiles := []protocol.FileInfo{
  263. {
  264. Name: "knownDir",
  265. Type: protocol.FileInfoTypeDirectory,
  266. Permissions: 0755,
  267. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
  268. Sequence: 42,
  269. },
  270. {
  271. Name: "knownDir/knownFile",
  272. Type: protocol.FileInfoTypeFile,
  273. Permissions: 0644,
  274. Size: fi.Size(),
  275. ModifiedS: fi.ModTime().Unix(),
  276. ModifiedNs: int32(fi.ModTime().UnixNano() % 1e9),
  277. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
  278. Sequence: 42,
  279. Blocks: blocks,
  280. },
  281. }
  282. return knownFiles
  283. }
  284. func setupROFolder(t *testing.T) (*model, *receiveOnlyFolder) {
  285. t.Helper()
  286. w := createTmpWrapper(defaultCfg)
  287. cfg := w.RawCopy()
  288. fcfg := testFolderConfigFake()
  289. fcfg.ID = "ro"
  290. fcfg.Label = "ro"
  291. fcfg.Type = config.FolderTypeReceiveOnly
  292. cfg.Folders = []config.FolderConfiguration{fcfg}
  293. w.Replace(cfg)
  294. m := newModel(w, myID, "syncthing", "dev", db.NewLowlevel(backend.OpenMemory()), nil)
  295. m.ServeBackground()
  296. must(t, m.ScanFolder("ro"))
  297. m.fmut.RLock()
  298. defer m.fmut.RUnlock()
  299. f := m.folderRunners["ro"].(*receiveOnlyFolder)
  300. return m, f
  301. }
  302. func writeFile(fs fs.Filesystem, filename string, data []byte, perm fs.FileMode) error {
  303. fd, err := fs.Create(filename)
  304. if err != nil {
  305. return err
  306. }
  307. _, err = fd.Write(data)
  308. if err != nil {
  309. return err
  310. }
  311. if err := fd.Close(); err != nil {
  312. return err
  313. }
  314. return fs.Chmod(filename, perm)
  315. }