requests_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. "io/ioutil"
  10. "os"
  11. "runtime"
  12. "strings"
  13. "testing"
  14. "time"
  15. "github.com/syncthing/syncthing/lib/config"
  16. "github.com/syncthing/syncthing/lib/db"
  17. "github.com/syncthing/syncthing/lib/protocol"
  18. )
  19. func TestRequestSimple(t *testing.T) {
  20. // Verify that the model performs a request and creates a file based on
  21. // an incoming index update.
  22. defer os.RemoveAll("_tmpfolder")
  23. m, fc := setupModelWithConnection()
  24. defer m.Stop()
  25. // We listen for incoming index updates and trigger when we see one for
  26. // the expected test file.
  27. done := make(chan struct{})
  28. fc.mut.Lock()
  29. fc.indexFn = func(folder string, fs []protocol.FileInfo) {
  30. for _, f := range fs {
  31. if f.Name == "testfile" {
  32. close(done)
  33. return
  34. }
  35. }
  36. }
  37. fc.mut.Unlock()
  38. // Send an update for the test file, wait for it to sync and be reported back.
  39. contents := []byte("test file contents\n")
  40. fc.addFile("testfile", 0644, protocol.FileInfoTypeFile, contents)
  41. fc.sendIndexUpdate()
  42. <-done
  43. // Verify the contents
  44. bs, err := ioutil.ReadFile("_tmpfolder/testfile")
  45. if err != nil {
  46. t.Error("File did not sync correctly:", err)
  47. return
  48. }
  49. if !bytes.Equal(bs, contents) {
  50. t.Error("File did not sync correctly: incorrect data")
  51. }
  52. }
  53. func TestSymlinkTraversalRead(t *testing.T) {
  54. // Verify that a symlink can not be traversed for reading.
  55. if runtime.GOOS == "windows" {
  56. t.Skip("no symlink support on CI")
  57. return
  58. }
  59. defer os.RemoveAll("_tmpfolder")
  60. m, fc := setupModelWithConnection()
  61. defer m.Stop()
  62. // We listen for incoming index updates and trigger when we see one for
  63. // the expected test file.
  64. done := make(chan struct{})
  65. fc.mut.Lock()
  66. fc.indexFn = func(folder string, fs []protocol.FileInfo) {
  67. for _, f := range fs {
  68. if f.Name == "symlink" {
  69. close(done)
  70. return
  71. }
  72. }
  73. }
  74. fc.mut.Unlock()
  75. // Send an update for the symlink, wait for it to sync and be reported back.
  76. contents := []byte("..")
  77. fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
  78. fc.sendIndexUpdate()
  79. <-done
  80. // Request a file by traversing the symlink
  81. buf := make([]byte, 10)
  82. err := m.Request(device1, "default", "symlink/requests_test.go", 0, nil, false, buf)
  83. if err == nil || !bytes.Equal(buf, make([]byte, 10)) {
  84. t.Error("Managed to traverse symlink")
  85. }
  86. }
  87. func TestSymlinkTraversalWrite(t *testing.T) {
  88. // Verify that a symlink can not be traversed for writing.
  89. if runtime.GOOS == "windows" {
  90. t.Skip("no symlink support on CI")
  91. return
  92. }
  93. defer os.RemoveAll("_tmpfolder")
  94. m, fc := setupModelWithConnection()
  95. defer m.Stop()
  96. // We listen for incoming index updates and trigger when we see one for
  97. // the expected names.
  98. done := make(chan struct{}, 1)
  99. badReq := make(chan string, 1)
  100. badIdx := make(chan string, 1)
  101. fc.mut.Lock()
  102. fc.indexFn = func(folder string, fs []protocol.FileInfo) {
  103. for _, f := range fs {
  104. if f.Name == "symlink" {
  105. done <- struct{}{}
  106. return
  107. }
  108. if strings.HasPrefix(f.Name, "symlink") {
  109. badIdx <- f.Name
  110. return
  111. }
  112. }
  113. }
  114. fc.requestFn = func(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) {
  115. if name != "symlink" && strings.HasPrefix(name, "symlink") {
  116. badReq <- name
  117. }
  118. return fc.fileData[name], nil
  119. }
  120. fc.mut.Unlock()
  121. // Send an update for the symlink, wait for it to sync and be reported back.
  122. contents := []byte("..")
  123. fc.addFile("symlink", 0644, protocol.FileInfoTypeSymlink, contents)
  124. fc.sendIndexUpdate()
  125. <-done
  126. // Send an update for things behind the symlink, wait for requests for
  127. // blocks for any of them to come back, or index entries. Hopefully none
  128. // of that should happen.
  129. contents = []byte("testdata testdata\n")
  130. fc.addFile("symlink/testfile", 0644, protocol.FileInfoTypeFile, contents)
  131. fc.addFile("symlink/testdir", 0644, protocol.FileInfoTypeDirectory, contents)
  132. fc.addFile("symlink/testsyml", 0644, protocol.FileInfoTypeSymlink, contents)
  133. fc.sendIndexUpdate()
  134. select {
  135. case name := <-badReq:
  136. t.Fatal("Should not have requested the data for", name)
  137. case name := <-badIdx:
  138. t.Fatal("Should not have sent the index entry for", name)
  139. case <-time.After(3 * time.Second):
  140. // Unfortunately not much else to trigger on here. The puller sleep
  141. // interval is 1s so if we didn't get any requests within two
  142. // iterations we should be fine.
  143. }
  144. }
  145. func TestRequestCreateTmpSymlink(t *testing.T) {
  146. // Verify that the model performs a request and creates a file based on
  147. // an incoming index update.
  148. defer os.RemoveAll("_tmpfolder")
  149. m, fc := setupModelWithConnection()
  150. defer m.Stop()
  151. // We listen for incoming index updates and trigger when we see one for
  152. // the expected test file.
  153. badIdx := make(chan string)
  154. fc.mut.Lock()
  155. fc.indexFn = func(folder string, fs []protocol.FileInfo) {
  156. for _, f := range fs {
  157. if f.Name == ".syncthing.testlink.tmp" {
  158. badIdx <- f.Name
  159. return
  160. }
  161. }
  162. }
  163. fc.mut.Unlock()
  164. // Send an update for the test file, wait for it to sync and be reported back.
  165. fc.addFile(".syncthing.testlink.tmp", 0644, protocol.FileInfoTypeSymlink, []byte(".."))
  166. fc.sendIndexUpdate()
  167. select {
  168. case name := <-badIdx:
  169. t.Fatal("Should not have sent the index entry for", name)
  170. case <-time.After(3 * time.Second):
  171. // Unfortunately not much else to trigger on here. The puller sleep
  172. // interval is 1s so if we didn't get any requests within two
  173. // iterations we should be fine.
  174. }
  175. }
  176. func setupModelWithConnection() (*Model, *fakeConnection) {
  177. cfg := defaultConfig.RawCopy()
  178. cfg.Folders[0] = config.NewFolderConfiguration("default", "_tmpfolder")
  179. cfg.Folders[0].PullerSleepS = 1
  180. cfg.Folders[0].Devices = []config.FolderDeviceConfiguration{
  181. {DeviceID: device1},
  182. {DeviceID: device2},
  183. }
  184. w := config.Wrap("/tmp/cfg", cfg)
  185. db := db.OpenMemory()
  186. m := NewModel(w, device1, "device", "syncthing", "dev", db, nil)
  187. m.AddFolder(cfg.Folders[0])
  188. m.ServeBackground()
  189. m.StartFolder("default")
  190. fc := addFakeConn(m, device2)
  191. fc.folder = "default"
  192. return m, fc
  193. }