requests_test.go 37 KB

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