requests_test.go 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464
  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(nil)
  31. defer cleanupModelAndRemoveDir(m, tfs.URI())
  32. // We listen for incoming index updates and trigger when we see one for
  33. // the expected test file.
  34. done := make(chan struct{})
  35. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  36. select {
  37. case <-done:
  38. t.Error("More than one index update sent")
  39. default:
  40. }
  41. for _, f := range fs {
  42. if f.Name == "testfile" {
  43. close(done)
  44. return nil
  45. }
  46. }
  47. return nil
  48. })
  49. // Send an update for the test file, wait for it to sync and be reported back.
  50. contents := []byte("test file contents\n")
  51. fc.addFile("testfile", 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(nil).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(nil).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(nil).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(nil)
  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(nil).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(nil)
  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. if f.Blocks[0].WeakHash != 103547413 {
  386. t.Fatalf("unexpected weak hash: %d != 103547413", f.Blocks[0].WeakHash)
  387. }
  388. res, err := m.Request(device1Conn, &protocol.Request{Folder: "default", Name: "foo", Size: len(payload), Hash: f.Blocks[0].Hash, WeakHash: f.Blocks[0].WeakHash})
  389. if err != nil {
  390. t.Fatal(err)
  391. }
  392. buf := res.Data()
  393. if !bytes.Equal(buf, payload) {
  394. t.Errorf("%s != %s", buf, payload)
  395. }
  396. payload = []byte("bye")
  397. buf = make([]byte, len(payload))
  398. writeFile(t, tfs, "foo", payload)
  399. _, err = m.Request(device1Conn, &protocol.Request{Folder: "default", Name: "foo", Size: len(payload), Hash: f.Blocks[0].Hash, WeakHash: f.Blocks[0].WeakHash})
  400. if err == nil {
  401. t.Fatalf("expected failure")
  402. }
  403. select {
  404. case fs := <-received:
  405. f := checkReceived(fs)
  406. if f.Blocks[0].WeakHash != 41943361 {
  407. t.Fatalf("unexpected weak hash: %d != 41943361", f.Blocks[0].WeakHash)
  408. }
  409. case <-time.After(time.Second):
  410. t.Fatalf("timed out")
  411. }
  412. }
  413. func TestParentDeletion(t *testing.T) {
  414. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  415. defer wcfgCancel()
  416. testFs := fcfg.Filesystem(nil)
  417. defer cleanupModelAndRemoveDir(m, testFs.URI())
  418. parent := "foo"
  419. child := filepath.Join(parent, "bar")
  420. received := make(chan []protocol.FileInfo)
  421. fc.addFile(parent, 0o777, protocol.FileInfoTypeDirectory, nil)
  422. fc.addFile(child, 0o777, protocol.FileInfoTypeDirectory, nil)
  423. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  424. received <- fs
  425. return nil
  426. })
  427. fc.sendIndexUpdate()
  428. // Get back index from initial setup
  429. select {
  430. case fs := <-received:
  431. if len(fs) != 2 {
  432. t.Fatalf("Sent index with %d files, should be 2", len(fs))
  433. }
  434. case <-time.After(time.Second):
  435. t.Fatalf("timed out")
  436. }
  437. // Delete parent dir
  438. must(t, testFs.RemoveAll(parent))
  439. // Scan only the child dir (not the parent)
  440. if err := m.ScanFolderSubdirs("default", []string{child}); err != nil {
  441. t.Fatal("Failed scanning:", err)
  442. }
  443. select {
  444. case fs := <-received:
  445. if len(fs) != 1 {
  446. t.Fatalf("Sent index with %d files, should be 1", len(fs))
  447. }
  448. if fs[0].Name != child {
  449. t.Fatalf(`Sent index with file "%v", should be "%v"`, fs[0].Name, child)
  450. }
  451. case <-time.After(time.Second):
  452. t.Fatalf("timed out")
  453. }
  454. // Recreate the child dir on the remote
  455. fc.updateFile(child, 0o777, protocol.FileInfoTypeDirectory, nil)
  456. fc.sendIndexUpdate()
  457. // Wait for the child dir to be recreated and sent to the remote
  458. select {
  459. case fs := <-received:
  460. l.Debugln("sent:", fs)
  461. found := false
  462. for _, f := range fs {
  463. if f.Name == child {
  464. if f.Deleted {
  465. t.Fatalf(`File "%v" is still deleted`, child)
  466. }
  467. found = true
  468. }
  469. }
  470. if !found {
  471. t.Fatalf(`File "%v" is missing in index`, child)
  472. }
  473. case <-time.After(5 * time.Second):
  474. t.Fatalf("timed out")
  475. }
  476. }
  477. // TestRequestSymlinkWindows checks that symlinks aren't marked as deleted on windows
  478. // Issue: https://github.com/syncthing/syncthing/issues/5125
  479. func TestRequestSymlinkWindows(t *testing.T) {
  480. if !build.IsWindows {
  481. t.Skip("windows specific test")
  482. }
  483. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  484. defer wcfgCancel()
  485. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  486. received := make(chan []protocol.FileInfo)
  487. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  488. select {
  489. case <-received:
  490. t.Error("More than one index update sent")
  491. default:
  492. }
  493. received <- fs
  494. return nil
  495. })
  496. fc.addFile("link", 0o644, protocol.FileInfoTypeSymlink, nil)
  497. fc.sendIndexUpdate()
  498. select {
  499. case fs := <-received:
  500. close(received)
  501. // expected first index
  502. if len(fs) != 1 {
  503. t.Fatalf("Expected just one file in index, got %v", fs)
  504. }
  505. f := fs[0]
  506. if f.Name != "link" {
  507. t.Fatalf(`Got file info with path "%v", expected "link"`, f.Name)
  508. }
  509. if !f.IsInvalid() {
  510. t.Errorf(`File info was not marked as invalid`)
  511. }
  512. case <-time.After(time.Second):
  513. t.Fatalf("timed out before pull was finished")
  514. }
  515. sub := m.evLogger.Subscribe(events.StateChanged | events.LocalIndexUpdated)
  516. defer sub.Unsubscribe()
  517. m.ScanFolder("default")
  518. for {
  519. select {
  520. case ev := <-sub.C():
  521. switch data := ev.Data.(map[string]interface{}); {
  522. case ev.Type == events.LocalIndexUpdated:
  523. t.Fatalf("Local index was updated unexpectedly: %v", data)
  524. case ev.Type == events.StateChanged:
  525. if data["from"] == "scanning" {
  526. return
  527. }
  528. }
  529. case <-time.After(5 * time.Second):
  530. t.Fatalf("Timed out before scan finished")
  531. }
  532. }
  533. }
  534. func equalContents(fs fs.Filesystem, path string, contents []byte) error {
  535. fd, err := fs.Open(path)
  536. if err != nil {
  537. return err
  538. }
  539. defer fd.Close()
  540. bs, err := io.ReadAll(fd)
  541. if err != nil {
  542. return err
  543. }
  544. if !bytes.Equal(bs, contents) {
  545. return errors.New("incorrect data")
  546. }
  547. return nil
  548. }
  549. func TestRequestRemoteRenameChanged(t *testing.T) {
  550. t.Skip("flaky")
  551. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  552. defer wcfgCancel()
  553. tfs := fcfg.Filesystem(nil)
  554. defer cleanupModel(m)
  555. received := make(chan []protocol.FileInfo)
  556. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  557. select {
  558. case <-received:
  559. t.Error("More than one index update sent")
  560. default:
  561. }
  562. received <- fs
  563. return nil
  564. })
  565. // setup
  566. a := "a"
  567. b := "b"
  568. data := map[string][]byte{
  569. a: []byte("aData"),
  570. b: []byte("bData"),
  571. }
  572. for _, n := range [2]string{a, b} {
  573. fc.addFile(n, 0o644, protocol.FileInfoTypeFile, data[n])
  574. }
  575. fc.sendIndexUpdate()
  576. select {
  577. case fs := <-received:
  578. close(received)
  579. if len(fs) != 2 {
  580. t.Fatalf("Received index with %v indexes instead of 2", len(fs))
  581. }
  582. case <-time.After(10 * time.Second):
  583. t.Fatal("timed out")
  584. }
  585. for _, n := range [2]string{a, b} {
  586. must(t, equalContents(tfs, n, data[n]))
  587. }
  588. var gotA, gotB, gotConfl bool
  589. done := make(chan struct{})
  590. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  591. select {
  592. case <-done:
  593. t.Error("Received more index updates than expected")
  594. return nil
  595. default:
  596. }
  597. for _, f := range fs {
  598. switch {
  599. case f.Name == a:
  600. if gotA {
  601. t.Error("Got more than one index update for", f.Name)
  602. }
  603. gotA = true
  604. case f.Name == b:
  605. if gotB {
  606. t.Error("Got more than one index update for", f.Name)
  607. }
  608. if f.Version.Counter(fc.id.Short()) == 0 {
  609. // This index entry might be superseded
  610. // by the final one or sent before it separately.
  611. break
  612. }
  613. gotB = true
  614. case strings.HasPrefix(f.Name, "b.sync-conflict-"):
  615. if gotConfl {
  616. t.Error("Got more than one index update for conflicts of", f.Name)
  617. }
  618. gotConfl = true
  619. default:
  620. t.Error("Got unexpected file in index update", f.Name)
  621. }
  622. }
  623. if gotA && gotB && gotConfl {
  624. close(done)
  625. }
  626. return nil
  627. })
  628. fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0o644)
  629. if err != nil {
  630. t.Fatal(err)
  631. }
  632. otherData := []byte("otherData")
  633. if _, err = fd.Write(otherData); err != nil {
  634. t.Fatal(err)
  635. }
  636. fd.Close()
  637. // rename
  638. fc.deleteFile(a)
  639. fc.updateFile(b, 0o644, protocol.FileInfoTypeFile, data[a])
  640. // Make sure the remote file for b is newer and thus stays global -> local conflict
  641. fc.mut.Lock()
  642. for i := range fc.files {
  643. if fc.files[i].Name == b {
  644. fc.files[i].ModifiedS += 100
  645. break
  646. }
  647. }
  648. fc.mut.Unlock()
  649. fc.sendIndexUpdate()
  650. select {
  651. case <-done:
  652. case <-time.After(10 * time.Second):
  653. t.Errorf("timed out without receiving all expected index updates")
  654. }
  655. // Check outcome
  656. tfs.Walk(".", func(path string, info fs.FileInfo, err error) error {
  657. switch {
  658. case path == a:
  659. t.Errorf(`File "a" was not removed`)
  660. case path == b:
  661. if err := equalContents(tfs, b, data[a]); err != nil {
  662. t.Error(`File "b" has unexpected content (renamed from a on remote)`)
  663. }
  664. case strings.HasPrefix(path, b+".sync-conflict-"):
  665. if err := equalContents(tfs, path, otherData); err != nil {
  666. t.Error(`Sync conflict of "b" has unexptected content`)
  667. }
  668. case path == "." || strings.HasPrefix(path, ".stfolder"):
  669. default:
  670. t.Error("Found unexpected file", path)
  671. }
  672. return nil
  673. })
  674. }
  675. func TestRequestRemoteRenameConflict(t *testing.T) {
  676. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  677. defer wcfgCancel()
  678. tfs := fcfg.Filesystem(nil)
  679. defer cleanupModel(m)
  680. recv := make(chan int)
  681. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  682. recv <- len(fs)
  683. return nil
  684. })
  685. // setup
  686. a := "a"
  687. b := "b"
  688. data := map[string][]byte{
  689. a: []byte("aData"),
  690. b: []byte("bData"),
  691. }
  692. for _, n := range [2]string{a, b} {
  693. fc.addFile(n, 0o644, protocol.FileInfoTypeFile, data[n])
  694. }
  695. fc.sendIndexUpdate()
  696. select {
  697. case i := <-recv:
  698. if i != 2 {
  699. t.Fatalf("received %v items in index, expected 1", i)
  700. }
  701. case <-time.After(10 * time.Second):
  702. t.Fatal("timed out")
  703. }
  704. for _, n := range [2]string{a, b} {
  705. must(t, equalContents(tfs, n, data[n]))
  706. }
  707. fd, err := tfs.OpenFile(b, fs.OptReadWrite, 0o644)
  708. if err != nil {
  709. t.Fatal(err)
  710. }
  711. otherData := []byte("otherData")
  712. if _, err = fd.Write(otherData); err != nil {
  713. t.Fatal(err)
  714. }
  715. fd.Close()
  716. m.ScanFolders()
  717. select {
  718. case i := <-recv:
  719. if i != 1 {
  720. t.Fatalf("received %v items in index, expected 1", i)
  721. }
  722. case <-time.After(10 * time.Second):
  723. t.Fatal("timed out")
  724. }
  725. // make sure the following rename is more recent (not concurrent)
  726. time.Sleep(2 * time.Second)
  727. // rename
  728. fc.deleteFile(a)
  729. fc.updateFile(b, 0o644, protocol.FileInfoTypeFile, data[a])
  730. fc.sendIndexUpdate()
  731. select {
  732. case <-recv:
  733. case <-time.After(10 * time.Second):
  734. t.Fatal("timed out")
  735. }
  736. // Check outcome
  737. foundB := false
  738. foundBConfl := false
  739. tfs.Walk(".", func(path string, info fs.FileInfo, err error) error {
  740. switch {
  741. case path == a:
  742. t.Errorf(`File "a" was not removed`)
  743. case path == b:
  744. foundB = true
  745. case strings.HasPrefix(path, b) && strings.Contains(path, ".sync-conflict-"):
  746. foundBConfl = true
  747. }
  748. return nil
  749. })
  750. if !foundB {
  751. t.Errorf(`File "b" was removed`)
  752. }
  753. if !foundBConfl {
  754. t.Errorf(`No conflict file for "b" was created`)
  755. }
  756. }
  757. func TestRequestDeleteChanged(t *testing.T) {
  758. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  759. defer wcfgCancel()
  760. tfs := fcfg.Filesystem(nil)
  761. defer cleanupModelAndRemoveDir(m, tfs.URI())
  762. done := make(chan struct{})
  763. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  764. select {
  765. case <-done:
  766. t.Error("More than one index update sent")
  767. default:
  768. }
  769. close(done)
  770. return nil
  771. })
  772. // setup
  773. a := "a"
  774. data := []byte("aData")
  775. fc.addFile(a, 0o644, protocol.FileInfoTypeFile, data)
  776. fc.sendIndexUpdate()
  777. select {
  778. case <-done:
  779. done = make(chan struct{})
  780. case <-time.After(10 * time.Second):
  781. t.Fatal("timed out")
  782. }
  783. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  784. select {
  785. case <-done:
  786. t.Error("More than one index update sent")
  787. default:
  788. }
  789. close(done)
  790. return nil
  791. })
  792. fd, err := tfs.OpenFile(a, fs.OptReadWrite, 0o644)
  793. if err != nil {
  794. t.Fatal(err)
  795. }
  796. otherData := []byte("otherData")
  797. if _, err = fd.Write(otherData); err != nil {
  798. t.Fatal(err)
  799. }
  800. fd.Close()
  801. // rename
  802. fc.deleteFile(a)
  803. fc.sendIndexUpdate()
  804. select {
  805. case <-done:
  806. case <-time.After(10 * time.Second):
  807. t.Fatal("timed out")
  808. }
  809. // Check outcome
  810. if _, err := tfs.Lstat(a); err != nil {
  811. if fs.IsNotExist(err) {
  812. t.Error(`Modified file "a" was removed`)
  813. } else {
  814. t.Error(`Error stating file "a":`, err)
  815. }
  816. }
  817. }
  818. func TestNeedFolderFiles(t *testing.T) {
  819. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  820. defer wcfgCancel()
  821. defer cleanupModel(m)
  822. sub := m.evLogger.Subscribe(events.RemoteIndexUpdated)
  823. defer sub.Unsubscribe()
  824. errPreventSync := errors.New("you aren't getting any of this")
  825. fc.RequestCalls(func(_ context.Context, _ *protocol.Request) ([]byte, error) {
  826. return nil, errPreventSync
  827. })
  828. data := []byte("foo")
  829. num := 20
  830. for i := 0; i < num; i++ {
  831. fc.addFile(strconv.Itoa(i), 0o644, protocol.FileInfoTypeFile, data)
  832. }
  833. fc.sendIndexUpdate()
  834. select {
  835. case <-sub.C():
  836. case <-time.After(5 * time.Second):
  837. t.Fatal("Timed out before receiving index")
  838. }
  839. progress, queued, rest, err := m.NeedFolderFiles(fcfg.ID, 1, 100)
  840. must(t, err)
  841. if got := len(progress) + len(queued) + len(rest); got != num {
  842. t.Errorf("Got %v needed items, expected %v", got, num)
  843. }
  844. exp := 10
  845. for page := 1; page < 3; page++ {
  846. progress, queued, rest, err := m.NeedFolderFiles(fcfg.ID, page, exp)
  847. must(t, err)
  848. if got := len(progress) + len(queued) + len(rest); got != exp {
  849. t.Errorf("Got %v needed items on page %v, expected %v", got, page, exp)
  850. }
  851. }
  852. }
  853. // TestIgnoreDeleteUnignore checks that the deletion of an ignored file is not
  854. // propagated upon un-ignoring.
  855. // https://github.com/syncthing/syncthing/issues/6038
  856. func TestIgnoreDeleteUnignore(t *testing.T) {
  857. w, fcfg, wCancel := newDefaultCfgWrapper()
  858. defer wCancel()
  859. m := setupModel(t, w)
  860. fss := fcfg.Filesystem(nil)
  861. defer cleanupModel(m)
  862. folderIgnoresAlwaysReload(t, m, fcfg)
  863. m.ScanFolders()
  864. fc := addFakeConn(m, device1, fcfg.ID)
  865. fc.folder = "default"
  866. fc.mut.Lock()
  867. fc.mut.Unlock()
  868. file := "foobar"
  869. contents := []byte("test file contents\n")
  870. basicCheck := func(fs []protocol.FileInfo) {
  871. t.Helper()
  872. if len(fs) != 1 {
  873. t.Fatal("expected a single index entry, got", len(fs))
  874. } else if fs[0].Name != file {
  875. t.Fatalf("expected a index entry for %v, got one for %v", file, fs[0].Name)
  876. }
  877. }
  878. done := make(chan struct{})
  879. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  880. basicCheck(fs)
  881. close(done)
  882. return nil
  883. })
  884. writeFile(t, fss, file, contents)
  885. m.ScanFolders()
  886. select {
  887. case <-time.After(5 * time.Second):
  888. t.Fatalf("timed out before index was received")
  889. case <-done:
  890. }
  891. done = make(chan struct{})
  892. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  893. basicCheck(fs)
  894. f := fs[0]
  895. if !f.IsInvalid() {
  896. t.Errorf("Received non-invalid index update")
  897. }
  898. close(done)
  899. return nil
  900. })
  901. if err := m.SetIgnores("default", []string{"foobar"}); err != nil {
  902. panic(err)
  903. }
  904. select {
  905. case <-time.After(5 * time.Second):
  906. t.Fatal("timed out before receiving index update")
  907. case <-done:
  908. }
  909. done = make(chan struct{})
  910. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  911. basicCheck(fs)
  912. f := fs[0]
  913. if f.IsInvalid() {
  914. t.Errorf("Received invalid index update")
  915. }
  916. if !f.Version.Equal(protocol.Vector{}) && f.Deleted {
  917. t.Error("Received deleted index entry with non-empty version")
  918. }
  919. l.Infoln(f)
  920. close(done)
  921. return nil
  922. })
  923. if err := fss.Remove(file); err != nil {
  924. t.Fatal(err)
  925. }
  926. if err := m.SetIgnores("default", []string{}); err != nil {
  927. panic(err)
  928. }
  929. select {
  930. case <-time.After(5 * time.Second):
  931. t.Fatalf("timed out before index was received")
  932. case <-done:
  933. }
  934. }
  935. // TestRequestLastFileProgress checks that the last pulled file (here only) is registered
  936. // as in progress.
  937. func TestRequestLastFileProgress(t *testing.T) {
  938. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  939. defer wcfgCancel()
  940. tfs := fcfg.Filesystem(nil)
  941. defer cleanupModelAndRemoveDir(m, tfs.URI())
  942. done := make(chan struct{})
  943. fc.RequestCalls(func(_ context.Context, req *protocol.Request) ([]byte, error) {
  944. defer close(done)
  945. progress, queued, rest, err := m.NeedFolderFiles(req.Folder, 1, 10)
  946. must(t, err)
  947. if len(queued)+len(rest) != 0 {
  948. t.Error(`There should not be any queued or "rest" items`)
  949. }
  950. if len(progress) != 1 {
  951. t.Error("Expected exactly one item in progress.")
  952. }
  953. return fc.fileData[req.Name], nil
  954. })
  955. contents := []byte("test file contents\n")
  956. fc.addFile("testfile", 0o644, protocol.FileInfoTypeFile, contents)
  957. fc.sendIndexUpdate()
  958. select {
  959. case <-done:
  960. case <-time.After(5 * time.Second):
  961. t.Fatal("Timed out before file was requested")
  962. }
  963. }
  964. func TestRequestIndexSenderPause(t *testing.T) {
  965. done := make(chan struct{})
  966. defer close(done)
  967. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  968. defer wcfgCancel()
  969. tfs := fcfg.Filesystem(nil)
  970. defer cleanupModelAndRemoveDir(m, tfs.URI())
  971. indexChan := make(chan []protocol.FileInfo)
  972. fc.setIndexFn(func(ctx context.Context, folder string, fs []protocol.FileInfo) error {
  973. select {
  974. case indexChan <- fs:
  975. case <-done:
  976. case <-ctx.Done():
  977. }
  978. return nil
  979. })
  980. var seq int64 = 1
  981. files := []protocol.FileInfo{{Name: "foo", Size: 10, Version: protocol.Vector{}.Update(myID.Short()), Sequence: seq}}
  982. // Both devices connected, none paused
  983. localIndexUpdate(m, fcfg.ID, files)
  984. select {
  985. case <-time.After(5 * time.Second):
  986. t.Fatal("timed out before receiving index")
  987. case <-indexChan:
  988. }
  989. // Remote paused
  990. cc := basicClusterConfig(device1, myID, fcfg.ID)
  991. cc.Folders[0].Paused = true
  992. m.ClusterConfig(fc, cc)
  993. seq++
  994. files[0].Sequence = seq
  995. files[0].Version = files[0].Version.Update(myID.Short())
  996. localIndexUpdate(m, fcfg.ID, files)
  997. // I don't see what to hook into to ensure an index update is not sent.
  998. dur := 50 * time.Millisecond
  999. if !testing.Short() {
  1000. dur = 2 * time.Second
  1001. }
  1002. select {
  1003. case <-time.After(dur):
  1004. case <-indexChan:
  1005. t.Error("Received index despite remote being paused")
  1006. }
  1007. // Remote unpaused
  1008. cc.Folders[0].Paused = false
  1009. m.ClusterConfig(fc, cc)
  1010. select {
  1011. case <-time.After(5 * time.Second):
  1012. t.Fatal("timed out before receiving index")
  1013. case <-indexChan:
  1014. }
  1015. // Local paused and resume
  1016. pauseFolder(t, m.cfg, fcfg.ID, true)
  1017. pauseFolder(t, m.cfg, fcfg.ID, false)
  1018. seq++
  1019. files[0].Sequence = seq
  1020. files[0].Version = files[0].Version.Update(myID.Short())
  1021. localIndexUpdate(m, fcfg.ID, files)
  1022. select {
  1023. case <-time.After(5 * time.Second):
  1024. t.Fatal("timed out before receiving index")
  1025. case <-indexChan:
  1026. }
  1027. // Local and remote paused, then first resume remote, then local
  1028. cc.Folders[0].Paused = true
  1029. m.ClusterConfig(fc, cc)
  1030. pauseFolder(t, m.cfg, fcfg.ID, true)
  1031. cc.Folders[0].Paused = false
  1032. m.ClusterConfig(fc, cc)
  1033. pauseFolder(t, m.cfg, fcfg.ID, false)
  1034. seq++
  1035. files[0].Sequence = seq
  1036. files[0].Version = files[0].Version.Update(myID.Short())
  1037. localIndexUpdate(m, fcfg.ID, files)
  1038. select {
  1039. case <-time.After(5 * time.Second):
  1040. t.Fatal("timed out before receiving index")
  1041. case <-indexChan:
  1042. }
  1043. // Folder removed on remote
  1044. cc = &protocol.ClusterConfig{}
  1045. m.ClusterConfig(fc, cc)
  1046. seq++
  1047. files[0].Sequence = seq
  1048. files[0].Version = files[0].Version.Update(myID.Short())
  1049. localIndexUpdate(m, fcfg.ID, files)
  1050. select {
  1051. case <-time.After(dur):
  1052. case <-indexChan:
  1053. t.Error("Received index despite remote not having the folder")
  1054. }
  1055. }
  1056. func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
  1057. w, fcfg, wCancel := newDefaultCfgWrapper()
  1058. defer wCancel()
  1059. tfs := fcfg.Filesystem(nil)
  1060. dir1 := "foo"
  1061. dir2 := "bar"
  1062. // Initialise db with an entry and then stop everything again
  1063. must(t, tfs.Mkdir(dir1, 0o777))
  1064. m := newModel(t, w, myID, nil)
  1065. defer cleanupModelAndRemoveDir(m, tfs.URI())
  1066. m.ServeBackground()
  1067. m.ScanFolders()
  1068. m.cancel()
  1069. <-m.stopped
  1070. // Add connection (sends incoming cluster config) before starting the new model
  1071. m = &testModel{
  1072. model: NewModel(m.cfg, m.id, m.db, m.protectedFiles, m.evLogger, protocol.NewKeyGenerator()).(*model),
  1073. evCancel: m.evCancel,
  1074. stopped: make(chan struct{}),
  1075. }
  1076. defer cleanupModel(m)
  1077. fc := addFakeConn(m, device1, fcfg.ID)
  1078. done := make(chan struct{})
  1079. defer close(done) // Must be the last thing to be deferred, thus first to run.
  1080. indexChan := make(chan []protocol.FileInfo, 1)
  1081. ccChan := make(chan *protocol.ClusterConfig, 1)
  1082. fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error {
  1083. select {
  1084. case indexChan <- fs:
  1085. case <-done:
  1086. }
  1087. return nil
  1088. })
  1089. fc.ClusterConfigCalls(func(cc *protocol.ClusterConfig) {
  1090. select {
  1091. case ccChan <- cc:
  1092. case <-done:
  1093. }
  1094. })
  1095. m.ServeBackground()
  1096. timeout := time.After(5 * time.Second)
  1097. // Check that cluster-config is resent after adding folders when starting model
  1098. select {
  1099. case <-timeout:
  1100. t.Fatal("timed out before receiving cluster-config")
  1101. case <-ccChan:
  1102. }
  1103. // Check that an index is sent for the newly added item
  1104. must(t, tfs.Mkdir(dir2, 0o777))
  1105. m.ScanFolders()
  1106. select {
  1107. case <-timeout:
  1108. t.Fatal("timed out before receiving index")
  1109. case <-indexChan:
  1110. }
  1111. }
  1112. func TestRequestReceiveEncrypted(t *testing.T) {
  1113. if testing.Short() {
  1114. t.Skip("skipping on short testing - scrypt is too slow")
  1115. }
  1116. w, fcfg, wCancel := newDefaultCfgWrapper()
  1117. defer wCancel()
  1118. tfs := fcfg.Filesystem(nil)
  1119. fcfg.Type = config.FolderTypeReceiveEncrypted
  1120. setFolder(t, w, fcfg)
  1121. encToken := protocol.PasswordToken(protocol.NewKeyGenerator(), fcfg.ID, "pw")
  1122. must(t, writeEncryptionToken(encToken, fcfg))
  1123. m := setupModel(t, w)
  1124. defer cleanupModelAndRemoveDir(m, tfs.URI())
  1125. files := genFiles(2)
  1126. files[1].LocalFlags = protocol.FlagLocalReceiveOnly
  1127. m.mut.RLock()
  1128. fset := m.folderFiles[fcfg.ID]
  1129. m.mut.RUnlock()
  1130. fset.Update(protocol.LocalDeviceID, files)
  1131. indexChan := make(chan []protocol.FileInfo, 10)
  1132. done := make(chan struct{})
  1133. defer close(done)
  1134. fc := newFakeConnection(device1, m)
  1135. fc.folder = fcfg.ID
  1136. fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error {
  1137. select {
  1138. case indexChan <- fs:
  1139. case <-done:
  1140. }
  1141. return nil
  1142. })
  1143. m.AddConnection(fc, protocol.Hello{})
  1144. m.ClusterConfig(fc, &protocol.ClusterConfig{
  1145. Folders: []protocol.Folder{
  1146. {
  1147. ID: "default",
  1148. Devices: []protocol.Device{
  1149. {
  1150. ID: myID,
  1151. EncryptionPasswordToken: encToken,
  1152. },
  1153. {ID: device1},
  1154. },
  1155. },
  1156. },
  1157. })
  1158. select {
  1159. case fs := <-indexChan:
  1160. if len(fs) != 1 {
  1161. t.Error("Expected index with one file, got", fs)
  1162. }
  1163. if got := fs[0].Name; got != files[0].Name {
  1164. t.Errorf("Expected file %v, got %v", got, files[0].Name)
  1165. }
  1166. case <-time.After(5 * time.Second):
  1167. t.Fatal("timed out before receiving index")
  1168. }
  1169. // Detects deletion, as we never really created the file on disk
  1170. // Shouldn't send anything because receive-encrypted
  1171. must(t, m.ScanFolder(fcfg.ID))
  1172. // One real file to be sent
  1173. name := "foo"
  1174. data := make([]byte, 2000)
  1175. rand.Read(data)
  1176. fc.addFile(name, 0o664, protocol.FileInfoTypeFile, data)
  1177. fc.sendIndexUpdate()
  1178. select {
  1179. case fs := <-indexChan:
  1180. if len(fs) != 1 {
  1181. t.Error("Expected index with one file, got", fs)
  1182. }
  1183. if got := fs[0].Name; got != name {
  1184. t.Errorf("Expected file %v, got %v", got, files[0].Name)
  1185. }
  1186. case <-time.After(5 * time.Second):
  1187. t.Fatal("timed out before receiving index")
  1188. }
  1189. // Simulate request from device that is untrusted too, i.e. with non-empty, but garbage hash
  1190. _, err := m.Request(fc, &protocol.Request{Folder: fcfg.ID, Name: name, Size: 1064, Hash: []byte("garbage")})
  1191. must(t, err)
  1192. changed, err := m.LocalChangedFolderFiles(fcfg.ID, 1, 10)
  1193. must(t, err)
  1194. if l := len(changed); l != 1 {
  1195. t.Errorf("Expected one locally changed file, got %v", l)
  1196. } else if changed[0].Name != files[0].Name {
  1197. t.Errorf("Expected %v, got %v", files[0].Name, changed[0].Name)
  1198. }
  1199. }
  1200. func TestRequestGlobalInvalidToValid(t *testing.T) {
  1201. done := make(chan struct{})
  1202. defer close(done)
  1203. m, fc, fcfg, wcfgCancel := setupModelWithConnection(t)
  1204. defer wcfgCancel()
  1205. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
  1206. waiter, err := m.cfg.Modify(func(cfg *config.Configuration) {
  1207. cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device2, "device2"))
  1208. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
  1209. cfg.SetFolder(fcfg)
  1210. })
  1211. must(t, err)
  1212. waiter.Wait()
  1213. conn := addFakeConn(m, device2, fcfg.ID)
  1214. tfs := fcfg.Filesystem(nil)
  1215. defer cleanupModelAndRemoveDir(m, tfs.URI())
  1216. indexChan := make(chan []protocol.FileInfo, 1)
  1217. fc.setIndexFn(func(ctx context.Context, folder string, fs []protocol.FileInfo) error {
  1218. select {
  1219. case indexChan <- fs:
  1220. case <-done:
  1221. case <-ctx.Done():
  1222. }
  1223. return nil
  1224. })
  1225. name := "foo"
  1226. // Setup device with valid file, do not send index yet
  1227. contents := []byte("test file contents\n")
  1228. fc.addFile(name, 0o644, protocol.FileInfoTypeFile, contents)
  1229. // Third device ignoring the same file
  1230. fc.mut.Lock()
  1231. file := fc.files[0]
  1232. fc.mut.Unlock()
  1233. file.SetIgnored()
  1234. m.IndexUpdate(conn, &protocol.IndexUpdate{Folder: fcfg.ID, Files: []protocol.FileInfo{prepareFileInfoForIndex(file)}})
  1235. // Wait for the ignored file to be received and possible pulled
  1236. timeout := time.After(10 * time.Second)
  1237. globalUpdated := false
  1238. for {
  1239. select {
  1240. case <-timeout:
  1241. t.Fatalf("timed out (globalUpdated == %v)", globalUpdated)
  1242. default:
  1243. time.Sleep(10 * time.Millisecond)
  1244. }
  1245. if !globalUpdated {
  1246. _, ok, err := m.CurrentGlobalFile(fcfg.ID, name)
  1247. if err != nil {
  1248. t.Fatal(err)
  1249. }
  1250. if !ok {
  1251. continue
  1252. }
  1253. globalUpdated = true
  1254. }
  1255. snap, err := m.DBSnapshot(fcfg.ID)
  1256. if err != nil {
  1257. t.Fatal(err)
  1258. }
  1259. need := snap.NeedSize(protocol.LocalDeviceID)
  1260. snap.Release()
  1261. if need.Files == 0 {
  1262. break
  1263. }
  1264. }
  1265. // Send the valid file
  1266. fc.sendIndexUpdate()
  1267. gotInvalid := false
  1268. for {
  1269. select {
  1270. case <-timeout:
  1271. t.Fatal("timed out before receiving index")
  1272. case fs := <-indexChan:
  1273. if len(fs) != 1 {
  1274. t.Fatalf("Expected one file in index, got %v", len(fs))
  1275. }
  1276. if !fs[0].IsInvalid() {
  1277. return
  1278. }
  1279. if gotInvalid {
  1280. t.Fatal("Received two invalid index updates")
  1281. }
  1282. t.Log("got index with invalid file")
  1283. gotInvalid = true
  1284. }
  1285. }
  1286. }