requests_test.go 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528
  1. // Copyright (C) 2016 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. "errors"
  11. "os"
  12. "path/filepath"
  13. "runtime"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "testing"
  18. "time"
  19. "github.com/syncthing/syncthing/lib/config"
  20. "github.com/syncthing/syncthing/lib/events"
  21. "github.com/syncthing/syncthing/lib/fs"
  22. "github.com/syncthing/syncthing/lib/protocol"
  23. "github.com/syncthing/syncthing/lib/rand"
  24. )
  25. func TestRequestSimple(t *testing.T) {
  26. // Verify that the model performs a request and creates a file based on
  27. // an incoming index update.
  28. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  29. defer wcfgCancel()
  30. tfs := fcfg.Filesystem(nil)
  31. defer cleanupModelAndRemoveDir(m, tfs.URI())
  32. // We listen for incoming index updates and trigger when we see one for
  33. // the expected test file.
  34. done := make(chan struct{})
  35. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  36. select {
  37. case <-done:
  38. t.Error("More than one index update sent")
  39. default:
  40. }
  41. for _, f := range fs {
  42. if f.Name == "testfile" {
  43. close(done)
  44. return nil
  45. }
  46. }
  47. return nil
  48. })
  49. // Send an update for the test file, wait for it to sync and be reported back.
  50. contents := []byte("test file contents\n")
  51. fc.addFile("testfile", 0644, protocol.FileInfoTypeFile, contents)
  52. fc.sendIndexUpdate()
  53. select {
  54. case <-done:
  55. case <-time.After(10 * time.Second):
  56. t.Fatal("timed out")
  57. }
  58. // Verify the contents
  59. if err := equalContents(filepath.Join(tfs.URI(), "testfile"), contents); err != nil {
  60. t.Error("File did not sync correctly:", err)
  61. }
  62. }
  63. func TestSymlinkTraversalRead(t *testing.T) {
  64. // Verify that a symlink can not be traversed for reading.
  65. if runtime.GOOS == "windows" {
  66. t.Skip("no symlink support on CI")
  67. return
  68. }
  69. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  70. defer wcfgCancel()
  71. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  72. // We listen for incoming index updates and trigger when we see one for
  73. // the expected test file.
  74. done := make(chan struct{})
  75. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  76. select {
  77. case <-done:
  78. t.Error("More than one index update sent")
  79. default:
  80. }
  81. for _, f := range fs {
  82. if f.Name == "symlink" {
  83. close(done)
  84. return nil
  85. }
  86. }
  87. return nil
  88. })
  89. // Send an update for the symlink, wait for it to sync and be reported back.
  90. contents := []byte("..")
  91. fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
  92. fc.sendIndexUpdate()
  93. <-done
  94. // Request a file by traversing the symlink
  95. res, err := m.Request(device1, "default", "symlink/requests_test.go", 0, 10, 0, nil, 0, false)
  96. if err == nil || res != nil {
  97. t.Error("Managed to traverse symlink")
  98. }
  99. }
  100. func TestSymlinkTraversalWrite(t *testing.T) {
  101. // Verify that a symlink can not be traversed for writing.
  102. if runtime.GOOS == "windows" {
  103. t.Skip("no symlink support on CI")
  104. return
  105. }
  106. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  107. defer wcfgCancel()
  108. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  109. // We listen for incoming index updates and trigger when we see one for
  110. // the expected names.
  111. done := make(chan struct{}, 1)
  112. badReq := make(chan string, 1)
  113. badIdx := make(chan string, 1)
  114. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  115. for _, f := range fs {
  116. if f.Name == "symlink" {
  117. done <- struct{}{}
  118. return nil
  119. }
  120. if strings.HasPrefix(f.Name, "symlink") {
  121. badIdx <- f.Name
  122. return nil
  123. }
  124. }
  125. return nil
  126. })
  127. fc.RequestCalls(func(ctx context.Context, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
  128. if name != "symlink" && strings.HasPrefix(name, "symlink") {
  129. badReq <- name
  130. }
  131. return fc.fileData[name], nil
  132. })
  133. // Send an update for the symlink, wait for it to sync and be reported back.
  134. contents := []byte("..")
  135. fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
  136. fc.sendIndexUpdate()
  137. <-done
  138. // Send an update for things behind the symlink, wait for requests for
  139. // blocks for any of them to come back, or index entries. Hopefully none
  140. // of that should happen.
  141. contents = []byte("testdata testdata\n")
  142. fc.addFile("symlink/testfile", 0644, protocol.FileInfoTypeFile, contents)
  143. fc.addFile("symlink/testdir", 0644, protocol.FileInfoTypeDirectory, contents)
  144. fc.addFile("symlink/testsyml", 0644, protocol.FileInfoTypeSymlink, contents)
  145. fc.sendIndexUpdate()
  146. select {
  147. case name := <-badReq:
  148. t.Fatal("Should not have requested the data for", name)
  149. case name := <-badIdx:
  150. t.Fatal("Should not have sent the index entry for", name)
  151. case <-time.After(3 * time.Second):
  152. // Unfortunately not much else to trigger on here. The puller sleep
  153. // interval is 1s so if we didn't get any requests within two
  154. // iterations we should be fine.
  155. }
  156. }
  157. func TestRequestCreateTmpSymlink(t *testing.T) {
  158. // Test that an update for a temporary file is invalidated
  159. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  160. defer wcfgCancel()
  161. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  162. // We listen for incoming index updates and trigger when we see one for
  163. // the expected test file.
  164. goodIdx := make(chan struct{})
  165. name := fs.TempName("testlink")
  166. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  167. for _, f := range fs {
  168. if f.Name == name {
  169. if f.IsInvalid() {
  170. goodIdx <- struct{}{}
  171. } else {
  172. t.Error("Received index with non-invalid temporary file")
  173. close(goodIdx)
  174. }
  175. return nil
  176. }
  177. }
  178. return nil
  179. })
  180. // Send an update for the test file, wait for it to sync and be reported back.
  181. fc.addFile(name, 0644, protocol.FileInfoTypeSymlink, []byte(".."))
  182. fc.sendIndexUpdate()
  183. select {
  184. case <-goodIdx:
  185. case <-time.After(3 * time.Second):
  186. t.Fatal("Timed out without index entry being sent")
  187. }
  188. }
  189. func TestRequestVersioningSymlinkAttack(t *testing.T) {
  190. if runtime.GOOS == "windows" {
  191. t.Skip("no symlink support on Windows")
  192. }
  193. // Sets up a folder with trashcan versioning and tries to use a
  194. // deleted symlink to escape
  195. w, fcfg, wCancel := tmpDefaultWrapper(t)
  196. defer wCancel()
  197. defer func() {
  198. os.RemoveAll(fcfg.Filesystem(nil).URI())
  199. os.Remove(w.ConfigPath())
  200. }()
  201. fcfg.Versioning = config.VersioningConfiguration{Type: "trashcan"}
  202. setFolder(t, w, fcfg)
  203. m, fc := setupModelWithConnectionFromWrapper(t, w)
  204. defer cleanupModel(m)
  205. // Create a temporary directory that we will use as target to see if
  206. // we can escape to it
  207. tmpdir := t.TempDir()
  208. // We listen for incoming index updates and trigger when we see one for
  209. // the expected test file.
  210. idx := make(chan int)
  211. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  212. idx <- len(fs)
  213. return nil
  214. })
  215. waitForIdx := func() {
  216. select {
  217. case c := <-idx:
  218. if c == 0 {
  219. t.Fatal("Got empty index update")
  220. }
  221. case <-time.After(5 * time.Second):
  222. t.Fatal("timed out before receiving index update")
  223. }
  224. }
  225. // Send an update for the test file, wait for it to sync and be reported back.
  226. fc.addFile("foo", 0644, protocol.FileInfoTypeSymlink, []byte(tmpdir))
  227. fc.sendIndexUpdate()
  228. waitForIdx()
  229. // Delete the symlink, hoping for it to get versioned
  230. fc.deleteFile("foo")
  231. fc.sendIndexUpdate()
  232. waitForIdx()
  233. // Recreate foo and a file in it with some data
  234. fc.updateFile("foo", 0755, protocol.FileInfoTypeDirectory, nil)
  235. fc.addFile("foo/test", 0644, protocol.FileInfoTypeFile, []byte("testtesttest"))
  236. fc.sendIndexUpdate()
  237. waitForIdx()
  238. // Remove the test file and see if it escaped
  239. fc.deleteFile("foo/test")
  240. fc.sendIndexUpdate()
  241. waitForIdx()
  242. path := filepath.Join(tmpdir, "test")
  243. if _, err := os.Lstat(path); !os.IsNotExist(err) {
  244. t.Fatal("File escaped to", path)
  245. }
  246. }
  247. func TestPullInvalidIgnoredSO(t *testing.T) {
  248. pullInvalidIgnored(t, config.FolderTypeSendOnly)
  249. }
  250. func TestPullInvalidIgnoredSR(t *testing.T) {
  251. pullInvalidIgnored(t, config.FolderTypeSendReceive)
  252. }
  253. // This test checks that (un-)ignored/invalid/deleted files are treated as expected.
  254. func pullInvalidIgnored(t *testing.T, ft config.FolderType) {
  255. w, wCancel := createTmpWrapper(defaultCfgWrapper.RawCopy())
  256. defer wCancel()
  257. fcfg := testFolderConfig(t.TempDir())
  258. fss := fcfg.Filesystem(nil)
  259. fcfg.Type = ft
  260. setFolder(t, w, fcfg)
  261. m := setupModel(t, w)
  262. defer cleanupModelAndRemoveDir(m, fss.URI())
  263. folderIgnoresAlwaysReload(t, m, fcfg)
  264. fc := addFakeConn(m, device1, fcfg.ID)
  265. fc.folder = "default"
  266. if err := m.SetIgnores("default", []string{"*ignored*"}); err != nil {
  267. panic(err)
  268. }
  269. contents := []byte("test file contents\n")
  270. otherContents := []byte("other test file contents\n")
  271. invIgn := "invalid:ignored"
  272. invDel := "invalid:deleted"
  273. ign := "ignoredNonExisting"
  274. ignExisting := "ignoredExisting"
  275. fc.addFile(invIgn, 0644, protocol.FileInfoTypeFile, contents)
  276. fc.addFile(invDel, 0644, protocol.FileInfoTypeFile, contents)
  277. fc.deleteFile(invDel)
  278. fc.addFile(ign, 0644, protocol.FileInfoTypeFile, contents)
  279. fc.addFile(ignExisting, 0644, protocol.FileInfoTypeFile, contents)
  280. writeFile(t, fss, ignExisting, otherContents)
  281. done := make(chan struct{})
  282. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  283. expected := map[string]struct{}{invIgn: {}, ign: {}, ignExisting: {}}
  284. for _, f := range fs {
  285. if _, ok := expected[f.Name]; !ok {
  286. t.Errorf("Unexpected file %v was added to index", f.Name)
  287. }
  288. if !f.IsInvalid() {
  289. t.Errorf("File %v wasn't marked as invalid", f.Name)
  290. }
  291. delete(expected, f.Name)
  292. }
  293. for name := range expected {
  294. t.Errorf("File %v wasn't added to index", name)
  295. }
  296. close(done)
  297. return nil
  298. })
  299. sub := m.evLogger.Subscribe(events.FolderErrors)
  300. defer sub.Unsubscribe()
  301. fc.sendIndexUpdate()
  302. select {
  303. case ev := <-sub.C():
  304. t.Fatalf("Errors while scanning/pulling: %v", ev)
  305. case <-time.After(5 * time.Second):
  306. t.Fatalf("timed out before index was received")
  307. case <-done:
  308. }
  309. done = make(chan struct{})
  310. expected := map[string]struct{}{ign: {}, ignExisting: {}}
  311. var expectedMut sync.Mutex
  312. // The indexes will normally arrive in one update, but it is possible
  313. // that they arrive in separate ones.
  314. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  315. expectedMut.Lock()
  316. for _, f := range fs {
  317. _, ok := expected[f.Name]
  318. if !ok {
  319. t.Errorf("Unexpected file %v was updated in index", f.Name)
  320. continue
  321. }
  322. if f.IsInvalid() {
  323. t.Errorf("File %v is still marked as invalid", f.Name)
  324. }
  325. if f.Name == ign {
  326. // The unignored deleted file should have an
  327. // empty version, to make it not override
  328. // existing global files.
  329. if !f.Deleted {
  330. t.Errorf("File %v was not marked as deleted", f.Name)
  331. }
  332. if len(f.Version.Counters) != 0 {
  333. t.Errorf("File %v has version %v, expected empty", f.Name, f.Version)
  334. }
  335. } else {
  336. // The unignored existing file should have a
  337. // version with only a local counter, to make
  338. // it conflict changed global files.
  339. if f.Deleted {
  340. t.Errorf("File %v is marked as deleted", f.Name)
  341. }
  342. if len(f.Version.Counters) != 1 || f.Version.Counter(myID.Short()) == 0 {
  343. t.Errorf("File %v has version %v, expected one entry for ourselves", f.Name, f.Version)
  344. }
  345. }
  346. delete(expected, f.Name)
  347. }
  348. if len(expected) == 0 {
  349. close(done)
  350. }
  351. expectedMut.Unlock()
  352. return nil
  353. })
  354. // Make sure pulling doesn't interfere, as index updates are racy and
  355. // thus we cannot distinguish between scan and pull results.
  356. fc.RequestCalls(func(ctx context.Context, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
  357. return nil, nil
  358. })
  359. if err := m.SetIgnores("default", []string{"*:ignored*"}); err != nil {
  360. panic(err)
  361. }
  362. select {
  363. case <-time.After(5 * time.Second):
  364. expectedMut.Lock()
  365. t.Fatal("timed out before receiving index updates for all existing files, missing", expected)
  366. expectedMut.Unlock()
  367. case <-done:
  368. }
  369. }
  370. func TestIssue4841(t *testing.T) {
  371. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  372. defer wcfgCancel()
  373. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  374. received := make(chan []protocol.FileInfo)
  375. fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error {
  376. received <- fs
  377. return nil
  378. })
  379. checkReceived := func(fs []protocol.FileInfo) protocol.FileInfo {
  380. t.Helper()
  381. if len(fs) != 1 {
  382. t.Fatalf("Sent index with %d files, should be 1", len(fs))
  383. }
  384. if fs[0].Name != "foo" {
  385. t.Fatalf(`Sent index with file %v, should be "foo"`, fs[0].Name)
  386. }
  387. return fs[0]
  388. }
  389. // Setup file from remote that was ignored locally
  390. folder := m.folderRunners[defaultFolderConfig.ID].(*sendReceiveFolder)
  391. folder.updateLocals([]protocol.FileInfo{{
  392. Name: "foo",
  393. Type: protocol.FileInfoTypeFile,
  394. LocalFlags: protocol.FlagLocalIgnored,
  395. Version: protocol.Vector{}.Update(device1.Short()),
  396. }})
  397. checkReceived(<-received)
  398. // Scan without ignore patterns with "foo" not existing locally
  399. if err := m.ScanFolder("default"); err != nil {
  400. t.Fatal("Failed scanning:", err)
  401. }
  402. select {
  403. case <-time.After(10 * time.Second):
  404. t.Fatal("timed out")
  405. case r := <-received:
  406. f := checkReceived(r)
  407. if !f.Version.Equal(protocol.Vector{}) {
  408. t.Errorf("Got Version == %v, expected empty version", f.Version)
  409. }
  410. }
  411. }
  412. func TestRescanIfHaveInvalidContent(t *testing.T) {
  413. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  414. defer wcfgCancel()
  415. tfs := fcfg.Filesystem(nil)
  416. defer cleanupModelAndRemoveDir(m, tfs.URI())
  417. payload := []byte("hello")
  418. writeFile(t, tfs, "foo", payload)
  419. received := make(chan []protocol.FileInfo)
  420. fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error {
  421. received <- fs
  422. return nil
  423. })
  424. checkReceived := func(fs []protocol.FileInfo) protocol.FileInfo {
  425. t.Helper()
  426. if len(fs) != 1 {
  427. t.Fatalf("Sent index with %d files, should be 1", len(fs))
  428. }
  429. if fs[0].Name != "foo" {
  430. t.Fatalf(`Sent index with file %v, should be "foo"`, fs[0].Name)
  431. }
  432. return fs[0]
  433. }
  434. // Scan without ignore patterns with "foo" not existing locally
  435. if err := m.ScanFolder("default"); err != nil {
  436. t.Fatal("Failed scanning:", err)
  437. }
  438. f := checkReceived(<-received)
  439. if f.Blocks[0].WeakHash != 103547413 {
  440. t.Fatalf("unexpected weak hash: %d != 103547413", f.Blocks[0].WeakHash)
  441. }
  442. res, err := m.Request(device1, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false)
  443. if err != nil {
  444. t.Fatal(err)
  445. }
  446. buf := res.Data()
  447. if !bytes.Equal(buf, payload) {
  448. t.Errorf("%s != %s", buf, payload)
  449. }
  450. payload = []byte("bye")
  451. buf = make([]byte, len(payload))
  452. writeFile(t, tfs, "foo", payload)
  453. _, err = m.Request(device1, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false)
  454. if err == nil {
  455. t.Fatalf("expected failure")
  456. }
  457. select {
  458. case fs := <-received:
  459. f := checkReceived(fs)
  460. if f.Blocks[0].WeakHash != 41943361 {
  461. t.Fatalf("unexpected weak hash: %d != 41943361", f.Blocks[0].WeakHash)
  462. }
  463. case <-time.After(time.Second):
  464. t.Fatalf("timed out")
  465. }
  466. }
  467. func TestParentDeletion(t *testing.T) {
  468. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  469. defer wcfgCancel()
  470. testFs := fcfg.Filesystem(nil)
  471. defer cleanupModelAndRemoveDir(m, testFs.URI())
  472. parent := "foo"
  473. child := filepath.Join(parent, "bar")
  474. received := make(chan []protocol.FileInfo)
  475. fc.addFile(parent, 0777, protocol.FileInfoTypeDirectory, nil)
  476. fc.addFile(child, 0777, protocol.FileInfoTypeDirectory, nil)
  477. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  478. received <- fs
  479. return nil
  480. })
  481. fc.sendIndexUpdate()
  482. // Get back index from initial setup
  483. select {
  484. case fs := <-received:
  485. if len(fs) != 2 {
  486. t.Fatalf("Sent index with %d files, should be 2", len(fs))
  487. }
  488. case <-time.After(time.Second):
  489. t.Fatalf("timed out")
  490. }
  491. // Delete parent dir
  492. must(t, testFs.RemoveAll(parent))
  493. // Scan only the child dir (not the parent)
  494. if err := m.ScanFolderSubdirs("default", []string{child}); err != nil {
  495. t.Fatal("Failed scanning:", err)
  496. }
  497. select {
  498. case fs := <-received:
  499. if len(fs) != 1 {
  500. t.Fatalf("Sent index with %d files, should be 1", len(fs))
  501. }
  502. if fs[0].Name != child {
  503. t.Fatalf(`Sent index with file "%v", should be "%v"`, fs[0].Name, child)
  504. }
  505. case <-time.After(time.Second):
  506. t.Fatalf("timed out")
  507. }
  508. // Recreate the child dir on the remote
  509. fc.updateFile(child, 0777, protocol.FileInfoTypeDirectory, nil)
  510. fc.sendIndexUpdate()
  511. // Wait for the child dir to be recreated and sent to the remote
  512. select {
  513. case fs := <-received:
  514. l.Debugln("sent:", fs)
  515. found := false
  516. for _, f := range fs {
  517. if f.Name == child {
  518. if f.Deleted {
  519. t.Fatalf(`File "%v" is still deleted`, child)
  520. }
  521. found = true
  522. }
  523. }
  524. if !found {
  525. t.Fatalf(`File "%v" is missing in index`, child)
  526. }
  527. case <-time.After(5 * time.Second):
  528. t.Fatalf("timed out")
  529. }
  530. }
  531. // TestRequestSymlinkWindows checks that symlinks aren't marked as deleted on windows
  532. // Issue: https://github.com/syncthing/syncthing/issues/5125
  533. func TestRequestSymlinkWindows(t *testing.T) {
  534. if runtime.GOOS != "windows" {
  535. t.Skip("windows specific test")
  536. }
  537. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  538. defer wcfgCancel()
  539. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  540. received := make(chan []protocol.FileInfo)
  541. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  542. select {
  543. case <-received:
  544. t.Error("More than one index update sent")
  545. default:
  546. }
  547. received <- fs
  548. return nil
  549. })
  550. fc.addFile("link", 0644, protocol.FileInfoTypeSymlink, nil)
  551. fc.sendIndexUpdate()
  552. select {
  553. case fs := <-received:
  554. close(received)
  555. // expected first index
  556. if len(fs) != 1 {
  557. t.Fatalf("Expected just one file in index, got %v", fs)
  558. }
  559. f := fs[0]
  560. if f.Name != "link" {
  561. t.Fatalf(`Got file info with path "%v", expected "link"`, f.Name)
  562. }
  563. if !f.IsInvalid() {
  564. t.Errorf(`File info was not marked as invalid`)
  565. }
  566. case <-time.After(time.Second):
  567. t.Fatalf("timed out before pull was finished")
  568. }
  569. sub := m.evLogger.Subscribe(events.StateChanged | events.LocalIndexUpdated)
  570. defer sub.Unsubscribe()
  571. m.ScanFolder("default")
  572. for {
  573. select {
  574. case ev := <-sub.C():
  575. switch data := ev.Data.(map[string]interface{}); {
  576. case ev.Type == events.LocalIndexUpdated:
  577. t.Fatalf("Local index was updated unexpectedly: %v", data)
  578. case ev.Type == events.StateChanged:
  579. if data["from"] == "scanning" {
  580. return
  581. }
  582. }
  583. case <-time.After(5 * time.Second):
  584. t.Fatalf("Timed out before scan finished")
  585. }
  586. }
  587. }
  588. func equalContents(path string, contents []byte) error {
  589. if bs, err := os.ReadFile(path); err != nil {
  590. return err
  591. } else if !bytes.Equal(bs, contents) {
  592. return errors.New("incorrect data")
  593. }
  594. return nil
  595. }
  596. func TestRequestRemoteRenameChanged(t *testing.T) {
  597. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  598. defer wcfgCancel()
  599. tfs := fcfg.Filesystem(nil)
  600. tmpDir := tfs.URI()
  601. defer cleanupModelAndRemoveDir(m, tfs.URI())
  602. received := make(chan []protocol.FileInfo)
  603. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  604. select {
  605. case <-received:
  606. t.Error("More than one index update sent")
  607. default:
  608. }
  609. received <- fs
  610. return nil
  611. })
  612. // setup
  613. a := "a"
  614. b := "b"
  615. data := map[string][]byte{
  616. a: []byte("aData"),
  617. b: []byte("bData"),
  618. }
  619. for _, n := range [2]string{a, b} {
  620. fc.addFile(n, 0644, protocol.FileInfoTypeFile, data[n])
  621. }
  622. fc.sendIndexUpdate()
  623. select {
  624. case fs := <-received:
  625. close(received)
  626. if len(fs) != 2 {
  627. t.Fatalf("Received index with %v indexes instead of 2", len(fs))
  628. }
  629. case <-time.After(10 * time.Second):
  630. t.Fatal("timed out")
  631. }
  632. for _, n := range [2]string{a, b} {
  633. must(t, equalContents(filepath.Join(tmpDir, n), data[n]))
  634. }
  635. var gotA, gotB, gotConfl bool
  636. done := make(chan struct{})
  637. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  638. select {
  639. case <-done:
  640. t.Error("Received more index updates than expected")
  641. return nil
  642. default:
  643. }
  644. for _, f := range fs {
  645. switch {
  646. case f.Name == a:
  647. if gotA {
  648. t.Error("Got more than one index update for", f.Name)
  649. }
  650. gotA = true
  651. case f.Name == b:
  652. if gotB {
  653. t.Error("Got more than one index update for", f.Name)
  654. }
  655. if f.Version.Counter(fc.id.Short()) == 0 {
  656. // This index entry might be superseeded
  657. // by the final one or sent before it separately.
  658. break
  659. }
  660. gotB = true
  661. case strings.HasPrefix(f.Name, "b.sync-conflict-"):
  662. if gotConfl {
  663. t.Error("Got more than one index update for conflicts of", f.Name)
  664. }
  665. gotConfl = true
  666. default:
  667. t.Error("Got unexpected file in index update", f.Name)
  668. }
  669. }
  670. if gotA && gotB && gotConfl {
  671. close(done)
  672. }
  673. return nil
  674. })
  675. fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0644)
  676. if err != nil {
  677. t.Fatal(err)
  678. }
  679. otherData := []byte("otherData")
  680. if _, err = fd.Write(otherData); err != nil {
  681. t.Fatal(err)
  682. }
  683. fd.Close()
  684. // rename
  685. fc.deleteFile(a)
  686. fc.updateFile(b, 0644, protocol.FileInfoTypeFile, data[a])
  687. // Make sure the remote file for b is newer and thus stays global -> local conflict
  688. fc.mut.Lock()
  689. for i := range fc.files {
  690. if fc.files[i].Name == b {
  691. fc.files[i].ModifiedS += 100
  692. break
  693. }
  694. }
  695. fc.mut.Unlock()
  696. fc.sendIndexUpdate()
  697. select {
  698. case <-done:
  699. case <-time.After(10 * time.Second):
  700. t.Errorf("timed out without receiving all expected index updates")
  701. }
  702. // Check outcome
  703. tfs.Walk(".", func(path string, info fs.FileInfo, err error) error {
  704. switch {
  705. case path == a:
  706. t.Errorf(`File "a" was not removed`)
  707. case path == b:
  708. if err := equalContents(filepath.Join(tmpDir, b), data[a]); err != nil {
  709. t.Error(`File "b" has unexpected content (renamed from a on remote)`)
  710. }
  711. case strings.HasPrefix(path, b+".sync-conflict-"):
  712. if err := equalContents(filepath.Join(tmpDir, path), otherData); err != nil {
  713. t.Error(`Sync conflict of "b" has unexptected content`)
  714. }
  715. case path == "." || strings.HasPrefix(path, ".stfolder"):
  716. default:
  717. t.Error("Found unexpected file", path)
  718. }
  719. return nil
  720. })
  721. }
  722. func TestRequestRemoteRenameConflict(t *testing.T) {
  723. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  724. defer wcfgCancel()
  725. tfs := fcfg.Filesystem(nil)
  726. tmpDir := tfs.URI()
  727. defer cleanupModelAndRemoveDir(m, tmpDir)
  728. recv := make(chan int)
  729. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  730. recv <- len(fs)
  731. return nil
  732. })
  733. // setup
  734. a := "a"
  735. b := "b"
  736. data := map[string][]byte{
  737. a: []byte("aData"),
  738. b: []byte("bData"),
  739. }
  740. for _, n := range [2]string{a, b} {
  741. fc.addFile(n, 0644, protocol.FileInfoTypeFile, data[n])
  742. }
  743. fc.sendIndexUpdate()
  744. select {
  745. case i := <-recv:
  746. if i != 2 {
  747. t.Fatalf("received %v items in index, expected 1", i)
  748. }
  749. case <-time.After(10 * time.Second):
  750. t.Fatal("timed out")
  751. }
  752. for _, n := range [2]string{a, b} {
  753. must(t, equalContents(filepath.Join(tmpDir, n), data[n]))
  754. }
  755. fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0644)
  756. if err != nil {
  757. t.Fatal(err)
  758. }
  759. otherData := []byte("otherData")
  760. if _, err = fd.Write(otherData); err != nil {
  761. t.Fatal(err)
  762. }
  763. fd.Close()
  764. m.ScanFolders()
  765. select {
  766. case i := <-recv:
  767. if i != 1 {
  768. t.Fatalf("received %v items in index, expected 1", i)
  769. }
  770. case <-time.After(10 * time.Second):
  771. t.Fatal("timed out")
  772. }
  773. // make sure the following rename is more recent (not concurrent)
  774. time.Sleep(2 * time.Second)
  775. // rename
  776. fc.deleteFile(a)
  777. fc.updateFile(b, 0644, protocol.FileInfoTypeFile, data[a])
  778. fc.sendIndexUpdate()
  779. select {
  780. case <-recv:
  781. case <-time.After(10 * time.Second):
  782. t.Fatal("timed out")
  783. }
  784. // Check outcome
  785. foundB := false
  786. foundBConfl := false
  787. tfs.Walk(".", func(path string, info fs.FileInfo, err error) error {
  788. switch {
  789. case path == a:
  790. t.Errorf(`File "a" was not removed`)
  791. case path == b:
  792. foundB = true
  793. case strings.HasPrefix(path, b) && strings.Contains(path, ".sync-conflict-"):
  794. foundBConfl = true
  795. }
  796. return nil
  797. })
  798. if !foundB {
  799. t.Errorf(`File "b" was removed`)
  800. }
  801. if !foundBConfl {
  802. t.Errorf(`No conflict file for "b" was created`)
  803. }
  804. }
  805. func TestRequestDeleteChanged(t *testing.T) {
  806. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  807. defer wcfgCancel()
  808. tfs := fcfg.Filesystem(nil)
  809. defer cleanupModelAndRemoveDir(m, tfs.URI())
  810. done := make(chan struct{})
  811. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  812. select {
  813. case <-done:
  814. t.Error("More than one index update sent")
  815. default:
  816. }
  817. close(done)
  818. return nil
  819. })
  820. // setup
  821. a := "a"
  822. data := []byte("aData")
  823. fc.addFile(a, 0644, protocol.FileInfoTypeFile, data)
  824. fc.sendIndexUpdate()
  825. select {
  826. case <-done:
  827. done = make(chan struct{})
  828. case <-time.After(10 * time.Second):
  829. t.Fatal("timed out")
  830. }
  831. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  832. select {
  833. case <-done:
  834. t.Error("More than one index update sent")
  835. default:
  836. }
  837. close(done)
  838. return nil
  839. })
  840. fd, err := tfs.OpenFile(a, fs.OptReadWrite, 0644)
  841. if err != nil {
  842. t.Fatal(err)
  843. }
  844. otherData := []byte("otherData")
  845. if _, err = fd.Write(otherData); err != nil {
  846. t.Fatal(err)
  847. }
  848. fd.Close()
  849. // rename
  850. fc.deleteFile(a)
  851. fc.sendIndexUpdate()
  852. select {
  853. case <-done:
  854. case <-time.After(10 * time.Second):
  855. t.Fatal("timed out")
  856. }
  857. // Check outcome
  858. if _, err := tfs.Lstat(a); err != nil {
  859. if fs.IsNotExist(err) {
  860. t.Error(`Modified file "a" was removed`)
  861. } else {
  862. t.Error(`Error stating file "a":`, err)
  863. }
  864. }
  865. }
  866. func TestNeedFolderFiles(t *testing.T) {
  867. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  868. defer wcfgCancel()
  869. tfs := fcfg.Filesystem(nil)
  870. tmpDir := tfs.URI()
  871. defer cleanupModelAndRemoveDir(m, tmpDir)
  872. sub := m.evLogger.Subscribe(events.RemoteIndexUpdated)
  873. defer sub.Unsubscribe()
  874. errPreventSync := errors.New("you aren't getting any of this")
  875. fc.RequestCalls(func(ctx context.Context, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
  876. return nil, errPreventSync
  877. })
  878. data := []byte("foo")
  879. num := 20
  880. for i := 0; i < num; i++ {
  881. fc.addFile(strconv.Itoa(i), 0644, protocol.FileInfoTypeFile, data)
  882. }
  883. fc.sendIndexUpdate()
  884. select {
  885. case <-sub.C():
  886. case <-time.After(5 * time.Second):
  887. t.Fatal("Timed out before receiving index")
  888. }
  889. progress, queued, rest, err := m.NeedFolderFiles(fcfg.ID, 1, 100)
  890. must(t, err)
  891. if got := len(progress) + len(queued) + len(rest); got != num {
  892. t.Errorf("Got %v needed items, expected %v", got, num)
  893. }
  894. exp := 10
  895. for page := 1; page < 3; page++ {
  896. progress, queued, rest, err := m.NeedFolderFiles(fcfg.ID, page, exp)
  897. must(t, err)
  898. if got := len(progress) + len(queued) + len(rest); got != exp {
  899. t.Errorf("Got %v needed items on page %v, expected %v", got, page, exp)
  900. }
  901. }
  902. }
  903. // TestIgnoreDeleteUnignore checks that the deletion of an ignored file is not
  904. // propagated upon un-ignoring.
  905. // https://github.com/syncthing/syncthing/issues/6038
  906. func TestIgnoreDeleteUnignore(t *testing.T) {
  907. w, fcfg, wCancel := tmpDefaultWrapper(t)
  908. defer wCancel()
  909. m := setupModel(t, w)
  910. fss := fcfg.Filesystem(nil)
  911. tmpDir := fss.URI()
  912. defer cleanupModelAndRemoveDir(m, tmpDir)
  913. folderIgnoresAlwaysReload(t, m, fcfg)
  914. m.ScanFolders()
  915. fc := addFakeConn(m, device1, fcfg.ID)
  916. fc.folder = "default"
  917. fc.mut.Lock()
  918. fc.mut.Unlock()
  919. file := "foobar"
  920. contents := []byte("test file contents\n")
  921. basicCheck := func(fs []protocol.FileInfo) {
  922. t.Helper()
  923. if len(fs) != 1 {
  924. t.Fatal("expected a single index entry, got", len(fs))
  925. } else if fs[0].Name != file {
  926. t.Fatalf("expected a index entry for %v, got one for %v", file, fs[0].Name)
  927. }
  928. }
  929. done := make(chan struct{})
  930. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  931. basicCheck(fs)
  932. close(done)
  933. return nil
  934. })
  935. writeFile(t, fss, file, contents)
  936. m.ScanFolders()
  937. select {
  938. case <-time.After(5 * time.Second):
  939. t.Fatalf("timed out before index was received")
  940. case <-done:
  941. }
  942. done = make(chan struct{})
  943. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  944. basicCheck(fs)
  945. f := fs[0]
  946. if !f.IsInvalid() {
  947. t.Errorf("Received non-invalid index update")
  948. }
  949. close(done)
  950. return nil
  951. })
  952. if err := m.SetIgnores("default", []string{"foobar"}); err != nil {
  953. panic(err)
  954. }
  955. select {
  956. case <-time.After(5 * time.Second):
  957. t.Fatal("timed out before receiving index update")
  958. case <-done:
  959. }
  960. done = make(chan struct{})
  961. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  962. basicCheck(fs)
  963. f := fs[0]
  964. if f.IsInvalid() {
  965. t.Errorf("Received invalid index update")
  966. }
  967. if !f.Version.Equal(protocol.Vector{}) && f.Deleted {
  968. t.Error("Received deleted index entry with non-empty version")
  969. }
  970. l.Infoln(f)
  971. close(done)
  972. return nil
  973. })
  974. if err := fss.Remove(file); err != nil {
  975. t.Fatal(err)
  976. }
  977. if err := m.SetIgnores("default", []string{}); err != nil {
  978. panic(err)
  979. }
  980. select {
  981. case <-time.After(5 * time.Second):
  982. t.Fatalf("timed out before index was received")
  983. case <-done:
  984. }
  985. }
  986. // TestRequestLastFileProgress checks that the last pulled file (here only) is registered
  987. // as in progress.
  988. func TestRequestLastFileProgress(t *testing.T) {
  989. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  990. defer wcfgCancel()
  991. tfs := fcfg.Filesystem(nil)
  992. defer cleanupModelAndRemoveDir(m, tfs.URI())
  993. done := make(chan struct{})
  994. fc.RequestCalls(func(ctx context.Context, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
  995. defer close(done)
  996. progress, queued, rest, err := m.NeedFolderFiles(folder, 1, 10)
  997. must(t, err)
  998. if len(queued)+len(rest) != 0 {
  999. t.Error(`There should not be any queued or "rest" items`)
  1000. }
  1001. if len(progress) != 1 {
  1002. t.Error("Expected exactly one item in progress.")
  1003. }
  1004. return fc.fileData[name], nil
  1005. })
  1006. contents := []byte("test file contents\n")
  1007. fc.addFile("testfile", 0644, protocol.FileInfoTypeFile, contents)
  1008. fc.sendIndexUpdate()
  1009. select {
  1010. case <-done:
  1011. case <-time.After(5 * time.Second):
  1012. t.Fatal("Timed out before file was requested")
  1013. }
  1014. }
  1015. func TestRequestIndexSenderPause(t *testing.T) {
  1016. done := make(chan struct{})
  1017. defer close(done)
  1018. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  1019. defer wcfgCancel()
  1020. tfs := fcfg.Filesystem(nil)
  1021. defer cleanupModelAndRemoveDir(m, tfs.URI())
  1022. indexChan := make(chan []protocol.FileInfo)
  1023. fc.setIndexFn(func(ctx context.Context, folder string, fs []protocol.FileInfo) error {
  1024. select {
  1025. case indexChan <- fs:
  1026. case <-done:
  1027. case <-ctx.Done():
  1028. }
  1029. return nil
  1030. })
  1031. var seq int64 = 1
  1032. files := []protocol.FileInfo{{Name: "foo", Size: 10, Version: protocol.Vector{}.Update(myID.Short()), Sequence: seq}}
  1033. // Both devices connected, noone paused
  1034. localIndexUpdate(m, fcfg.ID, files)
  1035. select {
  1036. case <-time.After(5 * time.Second):
  1037. t.Fatal("timed out before receiving index")
  1038. case <-indexChan:
  1039. }
  1040. // Remote paused
  1041. cc := basicClusterConfig(device1, myID, fcfg.ID)
  1042. cc.Folders[0].Paused = true
  1043. m.ClusterConfig(device1, cc)
  1044. seq++
  1045. files[0].Sequence = seq
  1046. files[0].Version = files[0].Version.Update(myID.Short())
  1047. localIndexUpdate(m, fcfg.ID, files)
  1048. // I don't see what to hook into to ensure an index update is not sent.
  1049. dur := 50 * time.Millisecond
  1050. if !testing.Short() {
  1051. dur = 2 * time.Second
  1052. }
  1053. select {
  1054. case <-time.After(dur):
  1055. case <-indexChan:
  1056. t.Error("Received index despite remote being paused")
  1057. }
  1058. // Remote unpaused
  1059. cc.Folders[0].Paused = false
  1060. m.ClusterConfig(device1, cc)
  1061. select {
  1062. case <-time.After(5 * time.Second):
  1063. t.Fatal("timed out before receiving index")
  1064. case <-indexChan:
  1065. }
  1066. // Local paused and resume
  1067. pauseFolder(t, m.cfg, fcfg.ID, true)
  1068. pauseFolder(t, m.cfg, fcfg.ID, false)
  1069. seq++
  1070. files[0].Sequence = seq
  1071. files[0].Version = files[0].Version.Update(myID.Short())
  1072. localIndexUpdate(m, fcfg.ID, files)
  1073. select {
  1074. case <-time.After(5 * time.Second):
  1075. t.Fatal("timed out before receiving index")
  1076. case <-indexChan:
  1077. }
  1078. // Local and remote paused, then first resume remote, then local
  1079. cc.Folders[0].Paused = true
  1080. m.ClusterConfig(device1, cc)
  1081. pauseFolder(t, m.cfg, fcfg.ID, true)
  1082. cc.Folders[0].Paused = false
  1083. m.ClusterConfig(device1, cc)
  1084. pauseFolder(t, m.cfg, fcfg.ID, false)
  1085. seq++
  1086. files[0].Sequence = seq
  1087. files[0].Version = files[0].Version.Update(myID.Short())
  1088. localIndexUpdate(m, fcfg.ID, files)
  1089. select {
  1090. case <-time.After(5 * time.Second):
  1091. t.Fatal("timed out before receiving index")
  1092. case <-indexChan:
  1093. }
  1094. // Folder removed on remote
  1095. cc = protocol.ClusterConfig{}
  1096. m.ClusterConfig(device1, cc)
  1097. seq++
  1098. files[0].Sequence = seq
  1099. files[0].Version = files[0].Version.Update(myID.Short())
  1100. localIndexUpdate(m, fcfg.ID, files)
  1101. select {
  1102. case <-time.After(dur):
  1103. case <-indexChan:
  1104. t.Error("Received index despite remote not having the folder")
  1105. }
  1106. }
  1107. func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
  1108. w, fcfg, wCancel := tmpDefaultWrapper(t)
  1109. defer wCancel()
  1110. tfs := fcfg.Filesystem(nil)
  1111. dir1 := "foo"
  1112. dir2 := "bar"
  1113. // Initialise db with an entry and then stop everything again
  1114. must(t, tfs.Mkdir(dir1, 0777))
  1115. m := newModel(t, w, myID, "syncthing", "dev", nil)
  1116. defer cleanupModelAndRemoveDir(m, tfs.URI())
  1117. m.ServeBackground()
  1118. m.ScanFolders()
  1119. m.cancel()
  1120. <-m.stopped
  1121. // Add connection (sends incoming cluster config) before starting the new model
  1122. m = &testModel{
  1123. model: NewModel(m.cfg, m.id, m.clientName, m.clientVersion, m.db, m.protectedFiles, m.evLogger).(*model),
  1124. evCancel: m.evCancel,
  1125. stopped: make(chan struct{}),
  1126. }
  1127. defer cleanupModel(m)
  1128. fc := addFakeConn(m, device1, fcfg.ID)
  1129. done := make(chan struct{})
  1130. defer close(done) // Must be the last thing to be deferred, thus first to run.
  1131. indexChan := make(chan []protocol.FileInfo, 1)
  1132. ccChan := make(chan protocol.ClusterConfig, 1)
  1133. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  1134. select {
  1135. case indexChan <- fs:
  1136. case <-done:
  1137. }
  1138. return nil
  1139. })
  1140. fc.ClusterConfigCalls(func(cc protocol.ClusterConfig) {
  1141. select {
  1142. case ccChan <- cc:
  1143. case <-done:
  1144. }
  1145. })
  1146. m.ServeBackground()
  1147. timeout := time.After(5 * time.Second)
  1148. // Check that cluster-config is resent after adding folders when starting model
  1149. select {
  1150. case <-timeout:
  1151. t.Fatal("timed out before receiving cluster-config")
  1152. case <-ccChan:
  1153. }
  1154. // Check that an index is sent for the newly added item
  1155. must(t, tfs.Mkdir(dir2, 0777))
  1156. m.ScanFolders()
  1157. select {
  1158. case <-timeout:
  1159. t.Fatal("timed out before receiving index")
  1160. case <-indexChan:
  1161. }
  1162. }
  1163. func TestRequestReceiveEncrypted(t *testing.T) {
  1164. if testing.Short() {
  1165. t.Skip("skipping on short testing - scrypt is too slow")
  1166. }
  1167. w, fcfg, wCancel := tmpDefaultWrapper(t)
  1168. defer wCancel()
  1169. tfs := fcfg.Filesystem(nil)
  1170. fcfg.Type = config.FolderTypeReceiveEncrypted
  1171. setFolder(t, w, fcfg)
  1172. encToken := protocol.PasswordToken(fcfg.ID, "pw")
  1173. must(t, tfs.Mkdir(config.DefaultMarkerName, 0777))
  1174. must(t, writeEncryptionToken(encToken, fcfg))
  1175. m := setupModel(t, w)
  1176. defer cleanupModelAndRemoveDir(m, tfs.URI())
  1177. files := genFiles(2)
  1178. files[1].LocalFlags = protocol.FlagLocalReceiveOnly
  1179. m.fmut.RLock()
  1180. fset := m.folderFiles[fcfg.ID]
  1181. m.fmut.RUnlock()
  1182. fset.Update(protocol.LocalDeviceID, files)
  1183. indexChan := make(chan []protocol.FileInfo, 10)
  1184. done := make(chan struct{})
  1185. defer close(done)
  1186. fc := newFakeConnection(device1, m)
  1187. fc.folder = fcfg.ID
  1188. fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error {
  1189. select {
  1190. case indexChan <- fs:
  1191. case <-done:
  1192. }
  1193. return nil
  1194. })
  1195. m.AddConnection(fc, protocol.Hello{})
  1196. m.ClusterConfig(device1, protocol.ClusterConfig{
  1197. Folders: []protocol.Folder{
  1198. {
  1199. ID: "default",
  1200. Devices: []protocol.Device{
  1201. {
  1202. ID: myID,
  1203. EncryptionPasswordToken: encToken,
  1204. },
  1205. {ID: device1},
  1206. },
  1207. },
  1208. },
  1209. })
  1210. select {
  1211. case fs := <-indexChan:
  1212. if len(fs) != 1 {
  1213. t.Error("Expected index with one file, got", fs)
  1214. }
  1215. if got := fs[0].Name; got != files[0].Name {
  1216. t.Errorf("Expected file %v, got %v", got, files[0].Name)
  1217. }
  1218. case <-time.After(5 * time.Second):
  1219. t.Fatal("timed out before receiving index")
  1220. }
  1221. // Detects deletion, as we never really created the file on disk
  1222. // Shouldn't send anything because receive-encrypted
  1223. must(t, m.ScanFolder(fcfg.ID))
  1224. // One real file to be sent
  1225. name := "foo"
  1226. data := make([]byte, 2000)
  1227. rand.Read(data)
  1228. fc.addFile(name, 0664, protocol.FileInfoTypeFile, data)
  1229. fc.sendIndexUpdate()
  1230. select {
  1231. case fs := <-indexChan:
  1232. if len(fs) != 1 {
  1233. t.Error("Expected index with one file, got", fs)
  1234. }
  1235. if got := fs[0].Name; got != name {
  1236. t.Errorf("Expected file %v, got %v", got, files[0].Name)
  1237. }
  1238. case <-time.After(5 * time.Second):
  1239. t.Fatal("timed out before receiving index")
  1240. }
  1241. // Simulate request from device that is untrusted too, i.e. with non-empty, but garbage hash
  1242. _, err := m.Request(device1, fcfg.ID, name, 0, 1064, 0, []byte("garbage"), 0, false)
  1243. must(t, err)
  1244. changed, err := m.LocalChangedFolderFiles(fcfg.ID, 1, 10)
  1245. must(t, err)
  1246. if l := len(changed); l != 1 {
  1247. t.Errorf("Expected one locally changed file, got %v", l)
  1248. } else if changed[0].Name != files[0].Name {
  1249. t.Errorf("Expected %v, got %v", files[0].Name, changed[0].Name)
  1250. }
  1251. }
  1252. func TestRequestGlobalInvalidToValid(t *testing.T) {
  1253. done := make(chan struct{})
  1254. defer close(done)
  1255. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  1256. defer wcfgCancel()
  1257. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
  1258. waiter, err := m.cfg.Modify(func(cfg *config.Configuration) {
  1259. cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device2, "device2"))
  1260. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
  1261. cfg.SetFolder(fcfg)
  1262. })
  1263. must(t, err)
  1264. waiter.Wait()
  1265. addFakeConn(m, device2, fcfg.ID)
  1266. tfs := fcfg.Filesystem(nil)
  1267. defer cleanupModelAndRemoveDir(m, tfs.URI())
  1268. indexChan := make(chan []protocol.FileInfo, 1)
  1269. fc.setIndexFn(func(ctx context.Context, folder string, fs []protocol.FileInfo) error {
  1270. select {
  1271. case indexChan <- fs:
  1272. case <-done:
  1273. case <-ctx.Done():
  1274. }
  1275. return nil
  1276. })
  1277. name := "foo"
  1278. // Setup device with valid file, do not send index yet
  1279. contents := []byte("test file contents\n")
  1280. fc.addFile(name, 0644, protocol.FileInfoTypeFile, contents)
  1281. // Third device ignoring the same file
  1282. fc.mut.Lock()
  1283. file := fc.files[0]
  1284. fc.mut.Unlock()
  1285. file.SetIgnored()
  1286. m.IndexUpdate(device2, fcfg.ID, []protocol.FileInfo{prepareFileInfoForIndex(file)})
  1287. // Wait for the ignored file to be received and possible pulled
  1288. timeout := time.After(10 * time.Second)
  1289. globalUpdated := false
  1290. for {
  1291. select {
  1292. case <-timeout:
  1293. t.Fatalf("timed out (globalUpdated == %v)", globalUpdated)
  1294. default:
  1295. time.Sleep(10 * time.Millisecond)
  1296. }
  1297. if !globalUpdated {
  1298. _, ok, err := m.CurrentGlobalFile(fcfg.ID, name)
  1299. if err != nil {
  1300. t.Fatal(err)
  1301. }
  1302. if !ok {
  1303. continue
  1304. }
  1305. globalUpdated = true
  1306. }
  1307. snap, err := m.DBSnapshot(fcfg.ID)
  1308. if err != nil {
  1309. t.Fatal(err)
  1310. }
  1311. need := snap.NeedSize(protocol.LocalDeviceID)
  1312. snap.Release()
  1313. if need.Files == 0 {
  1314. break
  1315. }
  1316. }
  1317. // Send the valid file
  1318. fc.sendIndexUpdate()
  1319. gotInvalid := false
  1320. for {
  1321. select {
  1322. case <-timeout:
  1323. t.Fatal("timed out before receiving index")
  1324. case fs := <-indexChan:
  1325. if len(fs) != 1 {
  1326. t.Fatalf("Expected one file in index, got %v", len(fs))
  1327. }
  1328. if !fs[0].IsInvalid() {
  1329. return
  1330. }
  1331. if gotInvalid {
  1332. t.Fatal("Received two invalid index updates")
  1333. }
  1334. t.Log("got index with invalid file")
  1335. gotInvalid = true
  1336. }
  1337. }
  1338. }