requests_test.go 39 KB

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