requests_test.go 37 KB

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