model_test.go 93 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756
  1. // Copyright (C) 2014 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. "encoding/json"
  11. "fmt"
  12. "io"
  13. "io/ioutil"
  14. "math/rand"
  15. "net"
  16. "os"
  17. "path/filepath"
  18. "runtime"
  19. "runtime/pprof"
  20. "strconv"
  21. "strings"
  22. "sync"
  23. "sync/atomic"
  24. "testing"
  25. "time"
  26. "github.com/syncthing/syncthing/lib/config"
  27. "github.com/syncthing/syncthing/lib/db"
  28. "github.com/syncthing/syncthing/lib/fs"
  29. "github.com/syncthing/syncthing/lib/ignore"
  30. "github.com/syncthing/syncthing/lib/osutil"
  31. "github.com/syncthing/syncthing/lib/protocol"
  32. srand "github.com/syncthing/syncthing/lib/rand"
  33. "github.com/syncthing/syncthing/lib/scanner"
  34. "github.com/syncthing/syncthing/lib/versioner"
  35. )
  36. var myID, device1, device2 protocol.DeviceID
  37. var defaultCfgWrapper config.Wrapper
  38. var defaultFolderConfig config.FolderConfiguration
  39. var defaultFs fs.Filesystem
  40. var defaultCfg config.Configuration
  41. var defaultAutoAcceptCfg config.Configuration
  42. var tmpLocation string
  43. func init() {
  44. myID, _ = protocol.DeviceIDFromString("ZNWFSWE-RWRV2BD-45BLMCV-LTDE2UR-4LJDW6J-R5BPWEB-TXD27XJ-IZF5RA4")
  45. device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
  46. device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
  47. defaultFs = fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata")
  48. defaultFolderConfig = testFolderConfig("testdata")
  49. defaultCfgWrapper = createTmpWrapper(config.New(myID))
  50. defaultCfgWrapper.SetDevice(config.NewDeviceConfiguration(device1, "device1"))
  51. defaultCfgWrapper.SetFolder(defaultFolderConfig)
  52. opts := defaultCfgWrapper.Options()
  53. opts.KeepTemporariesH = 1
  54. defaultCfgWrapper.SetOptions(opts)
  55. defaultCfg = defaultCfgWrapper.RawCopy()
  56. defaultAutoAcceptCfg = config.Configuration{
  57. Devices: []config.DeviceConfiguration{
  58. {
  59. DeviceID: myID, // self
  60. },
  61. {
  62. DeviceID: device1,
  63. AutoAcceptFolders: true,
  64. },
  65. {
  66. DeviceID: device2,
  67. AutoAcceptFolders: true,
  68. },
  69. },
  70. Options: config.OptionsConfiguration{
  71. DefaultFolderPath: ".",
  72. },
  73. }
  74. }
  75. var testDataExpected = map[string]protocol.FileInfo{
  76. "foo": {
  77. Name: "foo",
  78. Type: protocol.FileInfoTypeFile,
  79. ModifiedS: 0,
  80. Blocks: []protocol.BlockInfo{{Offset: 0x0, Size: 0x7, Hash: []uint8{0xae, 0xc0, 0x70, 0x64, 0x5f, 0xe5, 0x3e, 0xe3, 0xb3, 0x76, 0x30, 0x59, 0x37, 0x61, 0x34, 0xf0, 0x58, 0xcc, 0x33, 0x72, 0x47, 0xc9, 0x78, 0xad, 0xd1, 0x78, 0xb6, 0xcc, 0xdf, 0xb0, 0x1, 0x9f}}},
  81. },
  82. "empty": {
  83. Name: "empty",
  84. Type: protocol.FileInfoTypeFile,
  85. ModifiedS: 0,
  86. Blocks: []protocol.BlockInfo{{Offset: 0x0, Size: 0x0, Hash: []uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}},
  87. },
  88. "bar": {
  89. Name: "bar",
  90. Type: protocol.FileInfoTypeFile,
  91. ModifiedS: 0,
  92. Blocks: []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}},
  93. },
  94. }
  95. func init() {
  96. // Fix expected test data to match reality
  97. for n, f := range testDataExpected {
  98. fi, _ := os.Stat("testdata/" + n)
  99. f.Permissions = uint32(fi.Mode())
  100. f.ModifiedS = fi.ModTime().Unix()
  101. f.Size = fi.Size()
  102. testDataExpected[n] = f
  103. }
  104. }
  105. func TestMain(m *testing.M) {
  106. tmpLocation = "/tmp"
  107. if runtime.GOOS == "windows" {
  108. tmpLocation = "test-tmp"
  109. if err := os.MkdirAll(tmpLocation, 0777); err != nil {
  110. panic(err)
  111. }
  112. }
  113. tmpName, err := prepareTmpFile(defaultFs)
  114. if err != nil {
  115. panic(err)
  116. }
  117. exitCode := m.Run()
  118. os.Remove(defaultCfgWrapper.ConfigPath())
  119. defaultFs.Remove(tmpName)
  120. defaultFs.RemoveAll(config.DefaultMarkerName)
  121. defaultFs.RemoveAll(tmpLocation)
  122. os.Exit(exitCode)
  123. }
  124. func prepareTmpFile(to fs.Filesystem) (string, error) {
  125. tmpName := fs.TempName("file")
  126. in, err := defaultFs.Open("tmpfile")
  127. if err != nil {
  128. return "", err
  129. }
  130. defer in.Close()
  131. out, err := to.Create(tmpName)
  132. if err != nil {
  133. return "", err
  134. }
  135. defer out.Close()
  136. if _, err = io.Copy(out, in); err != nil {
  137. return "", err
  138. }
  139. future := time.Now().Add(time.Hour)
  140. if err := os.Chtimes(filepath.Join("testdata", tmpName), future, future); err != nil {
  141. return "", err
  142. }
  143. return tmpName, nil
  144. }
  145. func createTmpWrapper(cfg config.Configuration) config.Wrapper {
  146. tmpFile, err := ioutil.TempFile(tmpLocation, "syncthing-testConfig-")
  147. if err != nil {
  148. panic(err)
  149. }
  150. wrapper := config.Wrap(tmpFile.Name(), cfg)
  151. tmpFile.Close()
  152. return wrapper
  153. }
  154. func newModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *model {
  155. return NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles).(*model)
  156. }
  157. func newState(cfg config.Configuration) (config.Wrapper, *model) {
  158. wcfg := createTmpWrapper(cfg)
  159. m := setupModel(wcfg)
  160. for _, dev := range cfg.Devices {
  161. m.AddConnection(&fakeConnection{id: dev.DeviceID}, protocol.HelloResult{})
  162. }
  163. return wcfg, m
  164. }
  165. func setupModel(w config.Wrapper) *model {
  166. db := db.OpenMemory()
  167. m := newModel(w, myID, "syncthing", "dev", db, nil)
  168. m.ServeBackground()
  169. for id, cfg := range w.Folders() {
  170. if !cfg.Paused {
  171. m.AddFolder(cfg)
  172. m.StartFolder(id)
  173. }
  174. }
  175. m.ScanFolders()
  176. return m
  177. }
  178. func TestRequest(t *testing.T) {
  179. m := setupModel(defaultCfgWrapper)
  180. defer m.Stop()
  181. // Existing, shared file
  182. res, err := m.Request(device1, "default", "foo", 6, 0, nil, 0, false)
  183. if err != nil {
  184. t.Error(err)
  185. }
  186. bs := res.Data()
  187. if !bytes.Equal(bs, []byte("foobar")) {
  188. t.Errorf("Incorrect data from request: %q", string(bs))
  189. }
  190. // Existing, nonshared file
  191. _, err = m.Request(device2, "default", "foo", 6, 0, nil, 0, false)
  192. if err == nil {
  193. t.Error("Unexpected nil error on insecure file read")
  194. }
  195. // Nonexistent file
  196. _, err = m.Request(device1, "default", "nonexistent", 6, 0, nil, 0, false)
  197. if err == nil {
  198. t.Error("Unexpected nil error on insecure file read")
  199. }
  200. // Shared folder, but disallowed file name
  201. _, err = m.Request(device1, "default", "../walk.go", 6, 0, nil, 0, false)
  202. if err == nil {
  203. t.Error("Unexpected nil error on insecure file read")
  204. }
  205. // Negative offset
  206. _, err = m.Request(device1, "default", "foo", -4, 0, nil, 0, false)
  207. if err == nil {
  208. t.Error("Unexpected nil error on insecure file read")
  209. }
  210. // Larger block than available
  211. _, err = m.Request(device1, "default", "foo", 42, 0, nil, 0, false)
  212. if err == nil {
  213. t.Error("Unexpected nil error on insecure file read")
  214. }
  215. }
  216. func genFiles(n int) []protocol.FileInfo {
  217. files := make([]protocol.FileInfo, n)
  218. t := time.Now().Unix()
  219. for i := 0; i < n; i++ {
  220. files[i] = protocol.FileInfo{
  221. Name: fmt.Sprintf("file%d", i),
  222. ModifiedS: t,
  223. Sequence: int64(i + 1),
  224. Blocks: []protocol.BlockInfo{{Offset: 0, Size: 100, Hash: []byte("some hash bytes")}},
  225. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}},
  226. }
  227. }
  228. return files
  229. }
  230. func BenchmarkIndex_10000(b *testing.B) {
  231. benchmarkIndex(b, 10000)
  232. }
  233. func BenchmarkIndex_100(b *testing.B) {
  234. benchmarkIndex(b, 100)
  235. }
  236. func benchmarkIndex(b *testing.B, nfiles int) {
  237. m := setupModel(defaultCfgWrapper)
  238. defer m.Stop()
  239. files := genFiles(nfiles)
  240. m.Index(device1, "default", files)
  241. b.ResetTimer()
  242. for i := 0; i < b.N; i++ {
  243. m.Index(device1, "default", files)
  244. }
  245. b.ReportAllocs()
  246. }
  247. func BenchmarkIndexUpdate_10000_10000(b *testing.B) {
  248. benchmarkIndexUpdate(b, 10000, 10000)
  249. }
  250. func BenchmarkIndexUpdate_10000_100(b *testing.B) {
  251. benchmarkIndexUpdate(b, 10000, 100)
  252. }
  253. func BenchmarkIndexUpdate_10000_1(b *testing.B) {
  254. benchmarkIndexUpdate(b, 10000, 1)
  255. }
  256. func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
  257. m := setupModel(defaultCfgWrapper)
  258. defer m.Stop()
  259. files := genFiles(nfiles)
  260. ufiles := genFiles(nufiles)
  261. m.Index(device1, "default", files)
  262. b.ResetTimer()
  263. for i := 0; i < b.N; i++ {
  264. m.IndexUpdate(device1, "default", ufiles)
  265. }
  266. b.ReportAllocs()
  267. }
  268. type downloadProgressMessage struct {
  269. folder string
  270. updates []protocol.FileDownloadProgressUpdate
  271. }
  272. type fakeConnection struct {
  273. id protocol.DeviceID
  274. downloadProgressMessages []downloadProgressMessage
  275. closed bool
  276. files []protocol.FileInfo
  277. fileData map[string][]byte
  278. folder string
  279. model *model
  280. indexFn func(string, []protocol.FileInfo)
  281. requestFn func(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error)
  282. mut sync.Mutex
  283. }
  284. func (f *fakeConnection) Close(_ error) {
  285. f.mut.Lock()
  286. defer f.mut.Unlock()
  287. f.closed = true
  288. }
  289. func (f *fakeConnection) Start() {
  290. }
  291. func (f *fakeConnection) ID() protocol.DeviceID {
  292. return f.id
  293. }
  294. func (f *fakeConnection) Name() string {
  295. return ""
  296. }
  297. func (f *fakeConnection) String() string {
  298. return ""
  299. }
  300. func (f *fakeConnection) Option(string) string {
  301. return ""
  302. }
  303. func (f *fakeConnection) Index(folder string, fs []protocol.FileInfo) error {
  304. f.mut.Lock()
  305. defer f.mut.Unlock()
  306. if f.indexFn != nil {
  307. f.indexFn(folder, fs)
  308. }
  309. return nil
  310. }
  311. func (f *fakeConnection) IndexUpdate(folder string, fs []protocol.FileInfo) error {
  312. f.mut.Lock()
  313. defer f.mut.Unlock()
  314. if f.indexFn != nil {
  315. f.indexFn(folder, fs)
  316. }
  317. return nil
  318. }
  319. func (f *fakeConnection) Request(folder, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
  320. f.mut.Lock()
  321. defer f.mut.Unlock()
  322. if f.requestFn != nil {
  323. return f.requestFn(folder, name, offset, size, hash, fromTemporary)
  324. }
  325. return f.fileData[name], nil
  326. }
  327. func (f *fakeConnection) ClusterConfig(protocol.ClusterConfig) {}
  328. func (f *fakeConnection) Ping() bool {
  329. f.mut.Lock()
  330. defer f.mut.Unlock()
  331. return f.closed
  332. }
  333. func (f *fakeConnection) Closed() bool {
  334. f.mut.Lock()
  335. defer f.mut.Unlock()
  336. return f.closed
  337. }
  338. func (f *fakeConnection) Statistics() protocol.Statistics {
  339. return protocol.Statistics{}
  340. }
  341. func (f *fakeConnection) RemoteAddr() net.Addr {
  342. return &fakeAddr{}
  343. }
  344. func (f *fakeConnection) Type() string {
  345. return "fake"
  346. }
  347. func (f *fakeConnection) Crypto() string {
  348. return "fake"
  349. }
  350. func (f *fakeConnection) Transport() string {
  351. return "fake"
  352. }
  353. func (f *fakeConnection) Priority() int {
  354. return 9000
  355. }
  356. func (f *fakeConnection) DownloadProgress(folder string, updates []protocol.FileDownloadProgressUpdate) {
  357. f.downloadProgressMessages = append(f.downloadProgressMessages, downloadProgressMessage{
  358. folder: folder,
  359. updates: updates,
  360. })
  361. }
  362. func (f *fakeConnection) addFileLocked(name string, flags uint32, ftype protocol.FileInfoType, data []byte, version protocol.Vector) {
  363. blockSize := protocol.BlockSize(int64(len(data)))
  364. blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), blockSize, int64(len(data)), nil, true)
  365. if ftype == protocol.FileInfoTypeFile || ftype == protocol.FileInfoTypeDirectory {
  366. f.files = append(f.files, protocol.FileInfo{
  367. Name: name,
  368. Type: ftype,
  369. Size: int64(len(data)),
  370. ModifiedS: time.Now().Unix(),
  371. Permissions: flags,
  372. Version: version,
  373. Sequence: time.Now().UnixNano(),
  374. RawBlockSize: int32(blockSize),
  375. Blocks: blocks,
  376. })
  377. } else {
  378. // Symlink
  379. f.files = append(f.files, protocol.FileInfo{
  380. Name: name,
  381. Type: ftype,
  382. Version: version,
  383. Sequence: time.Now().UnixNano(),
  384. SymlinkTarget: string(data),
  385. NoPermissions: true,
  386. })
  387. }
  388. if f.fileData == nil {
  389. f.fileData = make(map[string][]byte)
  390. }
  391. f.fileData[name] = data
  392. }
  393. func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) {
  394. f.mut.Lock()
  395. defer f.mut.Unlock()
  396. var version protocol.Vector
  397. version = version.Update(f.id.Short())
  398. f.addFileLocked(name, flags, ftype, data, version)
  399. }
  400. func (f *fakeConnection) updateFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) {
  401. f.mut.Lock()
  402. defer f.mut.Unlock()
  403. for i, fi := range f.files {
  404. if fi.Name == name {
  405. f.files = append(f.files[:i], f.files[i+1:]...)
  406. f.addFileLocked(name, flags, ftype, data, fi.Version.Update(f.id.Short()))
  407. return
  408. }
  409. }
  410. }
  411. func (f *fakeConnection) deleteFile(name string) {
  412. f.mut.Lock()
  413. defer f.mut.Unlock()
  414. for i, fi := range f.files {
  415. if fi.Name == name {
  416. fi.Deleted = true
  417. fi.ModifiedS = time.Now().Unix()
  418. fi.Version = fi.Version.Update(f.id.Short())
  419. fi.Sequence = time.Now().UnixNano()
  420. fi.Blocks = nil
  421. f.files = append(append(f.files[:i], f.files[i+1:]...), fi)
  422. return
  423. }
  424. }
  425. }
  426. func (f *fakeConnection) sendIndexUpdate() {
  427. f.model.IndexUpdate(f.id, f.folder, f.files)
  428. }
  429. func BenchmarkRequestOut(b *testing.B) {
  430. m := setupModel(defaultCfgWrapper)
  431. defer m.Stop()
  432. const n = 1000
  433. files := genFiles(n)
  434. fc := &fakeConnection{id: device1}
  435. for _, f := range files {
  436. fc.addFile(f.Name, 0644, protocol.FileInfoTypeFile, []byte("some data to return"))
  437. }
  438. m.AddConnection(fc, protocol.HelloResult{})
  439. m.Index(device1, "default", files)
  440. b.ResetTimer()
  441. for i := 0; i < b.N; i++ {
  442. data, err := m.requestGlobal(device1, "default", files[i%n].Name, 0, 32, nil, 0, false)
  443. if err != nil {
  444. b.Error(err)
  445. }
  446. if data == nil {
  447. b.Error("nil data")
  448. }
  449. }
  450. }
  451. func BenchmarkRequestInSingleFile(b *testing.B) {
  452. testOs := &fatalOs{b}
  453. m := setupModel(defaultCfgWrapper)
  454. buf := make([]byte, 128<<10)
  455. rand.Read(buf)
  456. testOs.RemoveAll("testdata/request")
  457. defer testOs.RemoveAll("testdata/request")
  458. testOs.MkdirAll("testdata/request/for/a/file/in/a/couple/of/dirs", 0755)
  459. ioutil.WriteFile("testdata/request/for/a/file/in/a/couple/of/dirs/128k", buf, 0644)
  460. b.ResetTimer()
  461. for i := 0; i < b.N; i++ {
  462. if _, err := m.Request(device1, "default", "request/for/a/file/in/a/couple/of/dirs/128k", 128<<10, 0, nil, 0, false); err != nil {
  463. b.Error(err)
  464. }
  465. }
  466. b.SetBytes(128 << 10)
  467. }
  468. func TestDeviceRename(t *testing.T) {
  469. testOs := &fatalOs{t}
  470. hello := protocol.HelloResult{
  471. ClientName: "syncthing",
  472. ClientVersion: "v0.9.4",
  473. }
  474. defer testOs.Remove("testdata/tmpconfig.xml")
  475. rawCfg := config.New(device1)
  476. rawCfg.Devices = []config.DeviceConfiguration{
  477. {
  478. DeviceID: device1,
  479. },
  480. }
  481. cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg)
  482. db := db.OpenMemory()
  483. m := newModel(cfg, myID, "syncthing", "dev", db, nil)
  484. if cfg.Devices()[device1].Name != "" {
  485. t.Errorf("Device already has a name")
  486. }
  487. conn := &fakeConnection{id: device1}
  488. m.AddConnection(conn, hello)
  489. m.ServeBackground()
  490. defer m.Stop()
  491. if cfg.Devices()[device1].Name != "" {
  492. t.Errorf("Device already has a name")
  493. }
  494. m.Closed(conn, protocol.ErrTimeout)
  495. hello.DeviceName = "tester"
  496. m.AddConnection(conn, hello)
  497. if cfg.Devices()[device1].Name != "tester" {
  498. t.Errorf("Device did not get a name")
  499. }
  500. m.Closed(conn, protocol.ErrTimeout)
  501. hello.DeviceName = "tester2"
  502. m.AddConnection(conn, hello)
  503. if cfg.Devices()[device1].Name != "tester" {
  504. t.Errorf("Device name got overwritten")
  505. }
  506. cfgw, err := config.Load("testdata/tmpconfig.xml", myID)
  507. if err != nil {
  508. t.Error(err)
  509. return
  510. }
  511. if cfgw.Devices()[device1].Name != "tester" {
  512. t.Errorf("Device name not saved in config")
  513. }
  514. m.Closed(conn, protocol.ErrTimeout)
  515. opts := cfg.Options()
  516. opts.OverwriteRemoteDevNames = true
  517. cfg.SetOptions(opts)
  518. hello.DeviceName = "tester2"
  519. m.AddConnection(conn, hello)
  520. if cfg.Devices()[device1].Name != "tester2" {
  521. t.Errorf("Device name not overwritten")
  522. }
  523. }
  524. func TestClusterConfig(t *testing.T) {
  525. cfg := config.New(device1)
  526. cfg.Devices = []config.DeviceConfiguration{
  527. {
  528. DeviceID: device1,
  529. Introducer: true,
  530. },
  531. {
  532. DeviceID: device2,
  533. },
  534. }
  535. cfg.Folders = []config.FolderConfiguration{
  536. {
  537. ID: "folder1",
  538. Path: "testdata1",
  539. Devices: []config.FolderDeviceConfiguration{
  540. {DeviceID: device1},
  541. {DeviceID: device2},
  542. },
  543. },
  544. {
  545. ID: "folder2",
  546. Path: "testdata2",
  547. Paused: true, // should still be included
  548. Devices: []config.FolderDeviceConfiguration{
  549. {DeviceID: device1},
  550. {DeviceID: device2},
  551. },
  552. },
  553. {
  554. ID: "folder3",
  555. Path: "testdata3",
  556. Devices: []config.FolderDeviceConfiguration{
  557. {DeviceID: device1},
  558. // should not be included, does not include device2
  559. },
  560. },
  561. }
  562. db := db.OpenMemory()
  563. wrapper := createTmpWrapper(cfg)
  564. defer os.Remove(wrapper.ConfigPath())
  565. m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
  566. m.AddFolder(cfg.Folders[0])
  567. m.AddFolder(cfg.Folders[1])
  568. m.ServeBackground()
  569. defer m.Stop()
  570. cm := m.generateClusterConfig(device2)
  571. if l := len(cm.Folders); l != 2 {
  572. t.Fatalf("Incorrect number of folders %d != 2", l)
  573. }
  574. r := cm.Folders[0]
  575. if r.ID != "folder1" {
  576. t.Errorf("Incorrect folder %q != folder1", r.ID)
  577. }
  578. if l := len(r.Devices); l != 2 {
  579. t.Errorf("Incorrect number of devices %d != 2", l)
  580. }
  581. if id := r.Devices[0].ID; id != device1 {
  582. t.Errorf("Incorrect device ID %s != %s", id, device1)
  583. }
  584. if !r.Devices[0].Introducer {
  585. t.Error("Device1 should be flagged as Introducer")
  586. }
  587. if id := r.Devices[1].ID; id != device2 {
  588. t.Errorf("Incorrect device ID %s != %s", id, device2)
  589. }
  590. if r.Devices[1].Introducer {
  591. t.Error("Device2 should not be flagged as Introducer")
  592. }
  593. r = cm.Folders[1]
  594. if r.ID != "folder2" {
  595. t.Errorf("Incorrect folder %q != folder2", r.ID)
  596. }
  597. if l := len(r.Devices); l != 2 {
  598. t.Errorf("Incorrect number of devices %d != 2", l)
  599. }
  600. if id := r.Devices[0].ID; id != device1 {
  601. t.Errorf("Incorrect device ID %s != %s", id, device1)
  602. }
  603. if !r.Devices[0].Introducer {
  604. t.Error("Device1 should be flagged as Introducer")
  605. }
  606. if id := r.Devices[1].ID; id != device2 {
  607. t.Errorf("Incorrect device ID %s != %s", id, device2)
  608. }
  609. if r.Devices[1].Introducer {
  610. t.Error("Device2 should not be flagged as Introducer")
  611. }
  612. }
  613. func TestIntroducer(t *testing.T) {
  614. var introducedByAnyone protocol.DeviceID
  615. // LocalDeviceID is a magic value meaning don't check introducer
  616. contains := func(cfg config.FolderConfiguration, id, introducedBy protocol.DeviceID) bool {
  617. for _, dev := range cfg.Devices {
  618. if dev.DeviceID.Equals(id) {
  619. if introducedBy.Equals(introducedByAnyone) {
  620. return true
  621. }
  622. return dev.IntroducedBy.Equals(introducedBy)
  623. }
  624. }
  625. return false
  626. }
  627. wcfg, m := newState(config.Configuration{
  628. Devices: []config.DeviceConfiguration{
  629. {
  630. DeviceID: device1,
  631. Introducer: true,
  632. },
  633. },
  634. Folders: []config.FolderConfiguration{
  635. {
  636. ID: "folder1",
  637. Path: "testdata",
  638. Devices: []config.FolderDeviceConfiguration{
  639. {DeviceID: device1},
  640. },
  641. },
  642. {
  643. ID: "folder2",
  644. Path: "testdata",
  645. Devices: []config.FolderDeviceConfiguration{
  646. {DeviceID: device1},
  647. },
  648. },
  649. },
  650. })
  651. defer os.Remove(wcfg.ConfigPath())
  652. m.ClusterConfig(device1, protocol.ClusterConfig{
  653. Folders: []protocol.Folder{
  654. {
  655. ID: "folder1",
  656. Devices: []protocol.Device{
  657. {
  658. ID: device2,
  659. Introducer: true,
  660. SkipIntroductionRemovals: true,
  661. },
  662. },
  663. },
  664. },
  665. })
  666. if newDev, ok := wcfg.Device(device2); !ok || !newDev.Introducer || !newDev.SkipIntroductionRemovals {
  667. t.Error("devie 2 missing or wrong flags")
  668. }
  669. if !contains(wcfg.Folders()["folder1"], device2, device1) {
  670. t.Error("expected folder 1 to have device2 introduced by device 1")
  671. }
  672. wcfg, m = newState(config.Configuration{
  673. Devices: []config.DeviceConfiguration{
  674. {
  675. DeviceID: device1,
  676. Introducer: true,
  677. },
  678. {
  679. DeviceID: device2,
  680. IntroducedBy: device1,
  681. },
  682. },
  683. Folders: []config.FolderConfiguration{
  684. {
  685. ID: "folder1",
  686. Path: "testdata",
  687. Devices: []config.FolderDeviceConfiguration{
  688. {DeviceID: device1},
  689. {DeviceID: device2, IntroducedBy: device1},
  690. },
  691. },
  692. {
  693. ID: "folder2",
  694. Path: "testdata",
  695. Devices: []config.FolderDeviceConfiguration{
  696. {DeviceID: device1},
  697. },
  698. },
  699. },
  700. })
  701. defer os.Remove(wcfg.ConfigPath())
  702. m.ClusterConfig(device1, protocol.ClusterConfig{
  703. Folders: []protocol.Folder{
  704. {
  705. ID: "folder2",
  706. Devices: []protocol.Device{
  707. {
  708. ID: device2,
  709. Introducer: true,
  710. SkipIntroductionRemovals: true,
  711. },
  712. },
  713. },
  714. },
  715. })
  716. // Should not get introducer, as it's already unset, and it's an existing device.
  717. if newDev, ok := wcfg.Device(device2); !ok || newDev.Introducer || newDev.SkipIntroductionRemovals {
  718. t.Error("device 2 missing or changed flags")
  719. }
  720. if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
  721. t.Error("expected device 2 to be removed from folder 1")
  722. }
  723. if !contains(wcfg.Folders()["folder2"], device2, device1) {
  724. t.Error("expected device 2 to be added to folder 2")
  725. }
  726. wcfg, m = newState(config.Configuration{
  727. Devices: []config.DeviceConfiguration{
  728. {
  729. DeviceID: device1,
  730. Introducer: true,
  731. },
  732. {
  733. DeviceID: device2,
  734. IntroducedBy: device1,
  735. },
  736. },
  737. Folders: []config.FolderConfiguration{
  738. {
  739. ID: "folder1",
  740. Path: "testdata",
  741. Devices: []config.FolderDeviceConfiguration{
  742. {DeviceID: device1},
  743. {DeviceID: device2, IntroducedBy: device1},
  744. },
  745. },
  746. {
  747. ID: "folder2",
  748. Path: "testdata",
  749. Devices: []config.FolderDeviceConfiguration{
  750. {DeviceID: device1},
  751. {DeviceID: device2, IntroducedBy: device1},
  752. },
  753. },
  754. },
  755. })
  756. defer os.Remove(wcfg.ConfigPath())
  757. m.ClusterConfig(device1, protocol.ClusterConfig{})
  758. if _, ok := wcfg.Device(device2); ok {
  759. t.Error("device 2 should have been removed")
  760. }
  761. if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
  762. t.Error("expected device 2 to be removed from folder 1")
  763. }
  764. if contains(wcfg.Folders()["folder2"], device2, introducedByAnyone) {
  765. t.Error("expected device 2 to be removed from folder 2")
  766. }
  767. // Two cases when removals should not happen
  768. // 1. Introducer flag no longer set on device
  769. wcfg, m = newState(config.Configuration{
  770. Devices: []config.DeviceConfiguration{
  771. {
  772. DeviceID: device1,
  773. Introducer: false,
  774. },
  775. {
  776. DeviceID: device2,
  777. IntroducedBy: device1,
  778. },
  779. },
  780. Folders: []config.FolderConfiguration{
  781. {
  782. ID: "folder1",
  783. Path: "testdata",
  784. Devices: []config.FolderDeviceConfiguration{
  785. {DeviceID: device1},
  786. {DeviceID: device2, IntroducedBy: device1},
  787. },
  788. },
  789. {
  790. ID: "folder2",
  791. Path: "testdata",
  792. Devices: []config.FolderDeviceConfiguration{
  793. {DeviceID: device1},
  794. {DeviceID: device2, IntroducedBy: device1},
  795. },
  796. },
  797. },
  798. })
  799. defer os.Remove(wcfg.ConfigPath())
  800. m.ClusterConfig(device1, protocol.ClusterConfig{})
  801. if _, ok := wcfg.Device(device2); !ok {
  802. t.Error("device 2 should not have been removed")
  803. }
  804. if !contains(wcfg.Folders()["folder1"], device2, device1) {
  805. t.Error("expected device 2 not to be removed from folder 1")
  806. }
  807. if !contains(wcfg.Folders()["folder2"], device2, device1) {
  808. t.Error("expected device 2 not to be removed from folder 2")
  809. }
  810. // 2. SkipIntroductionRemovals is set
  811. wcfg, m = newState(config.Configuration{
  812. Devices: []config.DeviceConfiguration{
  813. {
  814. DeviceID: device1,
  815. Introducer: true,
  816. SkipIntroductionRemovals: true,
  817. },
  818. {
  819. DeviceID: device2,
  820. IntroducedBy: device1,
  821. },
  822. },
  823. Folders: []config.FolderConfiguration{
  824. {
  825. ID: "folder1",
  826. Path: "testdata",
  827. Devices: []config.FolderDeviceConfiguration{
  828. {DeviceID: device1},
  829. {DeviceID: device2, IntroducedBy: device1},
  830. },
  831. },
  832. {
  833. ID: "folder2",
  834. Path: "testdata",
  835. Devices: []config.FolderDeviceConfiguration{
  836. {DeviceID: device1},
  837. },
  838. },
  839. },
  840. })
  841. defer os.Remove(wcfg.ConfigPath())
  842. m.ClusterConfig(device1, protocol.ClusterConfig{
  843. Folders: []protocol.Folder{
  844. {
  845. ID: "folder2",
  846. Devices: []protocol.Device{
  847. {
  848. ID: device2,
  849. Introducer: true,
  850. SkipIntroductionRemovals: true,
  851. },
  852. },
  853. },
  854. },
  855. })
  856. if _, ok := wcfg.Device(device2); !ok {
  857. t.Error("device 2 should not have been removed")
  858. }
  859. if !contains(wcfg.Folders()["folder1"], device2, device1) {
  860. t.Error("expected device 2 not to be removed from folder 1")
  861. }
  862. if !contains(wcfg.Folders()["folder2"], device2, device1) {
  863. t.Error("expected device 2 not to be added to folder 2")
  864. }
  865. // Test device not being removed as it's shared without an introducer.
  866. wcfg, m = newState(config.Configuration{
  867. Devices: []config.DeviceConfiguration{
  868. {
  869. DeviceID: device1,
  870. Introducer: true,
  871. },
  872. {
  873. DeviceID: device2,
  874. IntroducedBy: device1,
  875. },
  876. },
  877. Folders: []config.FolderConfiguration{
  878. {
  879. ID: "folder1",
  880. Path: "testdata",
  881. Devices: []config.FolderDeviceConfiguration{
  882. {DeviceID: device1},
  883. {DeviceID: device2, IntroducedBy: device1},
  884. },
  885. },
  886. {
  887. ID: "folder2",
  888. Path: "testdata",
  889. Devices: []config.FolderDeviceConfiguration{
  890. {DeviceID: device1},
  891. {DeviceID: device2},
  892. },
  893. },
  894. },
  895. })
  896. defer os.Remove(wcfg.ConfigPath())
  897. m.ClusterConfig(device1, protocol.ClusterConfig{})
  898. if _, ok := wcfg.Device(device2); !ok {
  899. t.Error("device 2 should not have been removed")
  900. }
  901. if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
  902. t.Error("expected device 2 to be removed from folder 1")
  903. }
  904. if !contains(wcfg.Folders()["folder2"], device2, introducedByAnyone) {
  905. t.Error("expected device 2 not to be removed from folder 2")
  906. }
  907. // Test device not being removed as it's shared by a different introducer.
  908. wcfg, m = newState(config.Configuration{
  909. Devices: []config.DeviceConfiguration{
  910. {
  911. DeviceID: device1,
  912. Introducer: true,
  913. },
  914. {
  915. DeviceID: device2,
  916. IntroducedBy: device1,
  917. },
  918. },
  919. Folders: []config.FolderConfiguration{
  920. {
  921. ID: "folder1",
  922. Path: "testdata",
  923. Devices: []config.FolderDeviceConfiguration{
  924. {DeviceID: device1},
  925. {DeviceID: device2, IntroducedBy: device1},
  926. },
  927. },
  928. {
  929. ID: "folder2",
  930. Path: "testdata",
  931. Devices: []config.FolderDeviceConfiguration{
  932. {DeviceID: device1},
  933. {DeviceID: device2, IntroducedBy: myID},
  934. },
  935. },
  936. },
  937. })
  938. defer os.Remove(wcfg.ConfigPath())
  939. m.ClusterConfig(device1, protocol.ClusterConfig{})
  940. if _, ok := wcfg.Device(device2); !ok {
  941. t.Error("device 2 should not have been removed")
  942. }
  943. if contains(wcfg.Folders()["folder1"], device2, introducedByAnyone) {
  944. t.Error("expected device 2 to be removed from folder 1")
  945. }
  946. if !contains(wcfg.Folders()["folder2"], device2, introducedByAnyone) {
  947. t.Error("expected device 2 not to be removed from folder 2")
  948. }
  949. }
  950. func TestIssue4897(t *testing.T) {
  951. wcfg, m := newState(config.Configuration{
  952. Devices: []config.DeviceConfiguration{
  953. {
  954. DeviceID: device1,
  955. Introducer: true,
  956. },
  957. },
  958. Folders: []config.FolderConfiguration{
  959. {
  960. ID: "folder1",
  961. Path: "testdata",
  962. Devices: []config.FolderDeviceConfiguration{
  963. {DeviceID: device1},
  964. },
  965. Paused: true,
  966. },
  967. },
  968. })
  969. defer os.Remove(wcfg.ConfigPath())
  970. cm := m.generateClusterConfig(device1)
  971. if l := len(cm.Folders); l != 1 {
  972. t.Errorf("Cluster config contains %v folders, expected 1", l)
  973. }
  974. }
  975. // TestIssue5063 is about a panic in connection with modifying config in quick
  976. // succession, related with auto accepted folders. It's unclear what exactly, a
  977. // relevant bit seems to be here:
  978. // PR-comments: https://github.com/syncthing/syncthing/pull/5069/files#r203146546
  979. // Issue: https://github.com/syncthing/syncthing/pull/5509
  980. func TestIssue5063(t *testing.T) {
  981. wcfg, m := newState(defaultAutoAcceptCfg)
  982. defer os.Remove(wcfg.ConfigPath())
  983. wg := sync.WaitGroup{}
  984. addAndVerify := func(id string) {
  985. m.ClusterConfig(device1, protocol.ClusterConfig{
  986. Folders: []protocol.Folder{
  987. {
  988. ID: id,
  989. Label: id,
  990. },
  991. },
  992. })
  993. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  994. t.Error("expected shared", id)
  995. }
  996. wg.Done()
  997. }
  998. reps := 10
  999. ids := make([]string, reps)
  1000. for i := 0; i < reps; i++ {
  1001. wg.Add(1)
  1002. ids[i] = srand.String(8)
  1003. go addAndVerify(ids[i])
  1004. }
  1005. defer func() {
  1006. for _, id := range ids {
  1007. os.RemoveAll(id)
  1008. }
  1009. }()
  1010. defer m.Stop()
  1011. finished := make(chan struct{})
  1012. go func() {
  1013. wg.Wait()
  1014. close(finished)
  1015. }()
  1016. select {
  1017. case <-finished:
  1018. case <-time.After(10 * time.Second):
  1019. pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
  1020. t.Fatal("Timed out before all devices were added")
  1021. }
  1022. }
  1023. func TestAutoAcceptRejected(t *testing.T) {
  1024. // Nothing happens if AutoAcceptFolders not set
  1025. tcfg := defaultAutoAcceptCfg.Copy()
  1026. for i := range tcfg.Devices {
  1027. tcfg.Devices[i].AutoAcceptFolders = false
  1028. }
  1029. wcfg, m := newState(tcfg)
  1030. defer os.Remove(wcfg.ConfigPath())
  1031. id := srand.String(8)
  1032. defer os.RemoveAll(id)
  1033. m.ClusterConfig(device1, protocol.ClusterConfig{
  1034. Folders: []protocol.Folder{
  1035. {
  1036. ID: id,
  1037. Label: id,
  1038. },
  1039. },
  1040. })
  1041. if cfg, ok := m.cfg.Folder(id); ok && cfg.SharedWith(device1) {
  1042. t.Error("unexpected shared", id)
  1043. }
  1044. }
  1045. func TestAutoAcceptNewFolder(t *testing.T) {
  1046. // New folder
  1047. wcfg, m := newState(defaultAutoAcceptCfg)
  1048. defer os.Remove(wcfg.ConfigPath())
  1049. id := srand.String(8)
  1050. defer os.RemoveAll(id)
  1051. m.ClusterConfig(device1, protocol.ClusterConfig{
  1052. Folders: []protocol.Folder{
  1053. {
  1054. ID: id,
  1055. Label: id,
  1056. },
  1057. },
  1058. })
  1059. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1060. t.Error("expected shared", id)
  1061. }
  1062. }
  1063. func TestAutoAcceptNewFolderFromTwoDevices(t *testing.T) {
  1064. wcfg, m := newState(defaultAutoAcceptCfg)
  1065. defer os.Remove(wcfg.ConfigPath())
  1066. id := srand.String(8)
  1067. defer os.RemoveAll(id)
  1068. m.ClusterConfig(device1, protocol.ClusterConfig{
  1069. Folders: []protocol.Folder{
  1070. {
  1071. ID: id,
  1072. Label: id,
  1073. },
  1074. },
  1075. })
  1076. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1077. t.Error("expected shared", id)
  1078. }
  1079. if fcfg, ok := wcfg.Folder(id); !ok || fcfg.SharedWith(device2) {
  1080. t.Error("unexpected expected shared", id)
  1081. }
  1082. m.ClusterConfig(device2, protocol.ClusterConfig{
  1083. Folders: []protocol.Folder{
  1084. {
  1085. ID: id,
  1086. Label: id,
  1087. },
  1088. },
  1089. })
  1090. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device2) {
  1091. t.Error("expected shared", id)
  1092. }
  1093. m.Stop()
  1094. }
  1095. func TestAutoAcceptNewFolderFromOnlyOneDevice(t *testing.T) {
  1096. modifiedCfg := defaultAutoAcceptCfg.Copy()
  1097. modifiedCfg.Devices[2].AutoAcceptFolders = false
  1098. wcfg, m := newState(modifiedCfg)
  1099. defer os.Remove(wcfg.ConfigPath())
  1100. id := srand.String(8)
  1101. defer os.RemoveAll(id)
  1102. m.ClusterConfig(device1, protocol.ClusterConfig{
  1103. Folders: []protocol.Folder{
  1104. {
  1105. ID: id,
  1106. Label: id,
  1107. },
  1108. },
  1109. })
  1110. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1111. t.Error("expected shared", id)
  1112. }
  1113. if fcfg, ok := wcfg.Folder(id); !ok || fcfg.SharedWith(device2) {
  1114. t.Error("unexpected expected shared", id)
  1115. }
  1116. m.ClusterConfig(device2, protocol.ClusterConfig{
  1117. Folders: []protocol.Folder{
  1118. {
  1119. ID: id,
  1120. Label: id,
  1121. },
  1122. },
  1123. })
  1124. if fcfg, ok := wcfg.Folder(id); !ok || fcfg.SharedWith(device2) {
  1125. t.Error("unexpected shared", id)
  1126. }
  1127. m.Stop()
  1128. }
  1129. func TestAutoAcceptNewFolderPremutationsNoPanic(t *testing.T) {
  1130. if testing.Short() {
  1131. t.Skip("short tests only")
  1132. }
  1133. testOs := &fatalOs{t}
  1134. id := srand.String(8)
  1135. label := srand.String(8)
  1136. premutations := []protocol.Folder{
  1137. {ID: id, Label: id},
  1138. {ID: id, Label: label},
  1139. {ID: label, Label: id},
  1140. {ID: label, Label: label},
  1141. }
  1142. localFolders := append(premutations, protocol.Folder{})
  1143. for _, localFolder := range localFolders {
  1144. for _, localFolderPaused := range []bool{false, true} {
  1145. for _, dev1folder := range premutations {
  1146. for _, dev2folder := range premutations {
  1147. cfg := defaultAutoAcceptCfg.Copy()
  1148. if localFolder.Label != "" {
  1149. fcfg := config.NewFolderConfiguration(myID, localFolder.ID, localFolder.Label, fs.FilesystemTypeBasic, localFolder.ID)
  1150. fcfg.Paused = localFolderPaused
  1151. cfg.Folders = append(cfg.Folders, fcfg)
  1152. }
  1153. wcfg, m := newState(cfg)
  1154. m.ClusterConfig(device1, protocol.ClusterConfig{
  1155. Folders: []protocol.Folder{dev1folder},
  1156. })
  1157. m.ClusterConfig(device2, protocol.ClusterConfig{
  1158. Folders: []protocol.Folder{dev2folder},
  1159. })
  1160. m.Stop()
  1161. testOs.RemoveAll(id)
  1162. testOs.RemoveAll(label)
  1163. os.Remove(wcfg.ConfigPath())
  1164. }
  1165. }
  1166. }
  1167. }
  1168. }
  1169. func TestAutoAcceptMultipleFolders(t *testing.T) {
  1170. // Multiple new folders
  1171. wcfg, m := newState(defaultAutoAcceptCfg)
  1172. defer os.Remove(wcfg.ConfigPath())
  1173. id1 := srand.String(8)
  1174. defer os.RemoveAll(id1)
  1175. id2 := srand.String(8)
  1176. defer os.RemoveAll(id2)
  1177. defer m.Stop()
  1178. m.ClusterConfig(device1, protocol.ClusterConfig{
  1179. Folders: []protocol.Folder{
  1180. {
  1181. ID: id1,
  1182. Label: id1,
  1183. },
  1184. {
  1185. ID: id2,
  1186. Label: id2,
  1187. },
  1188. },
  1189. })
  1190. if fcfg, ok := wcfg.Folder(id1); !ok || !fcfg.SharedWith(device1) {
  1191. t.Error("expected shared", id1)
  1192. }
  1193. if fcfg, ok := wcfg.Folder(id2); !ok || !fcfg.SharedWith(device1) {
  1194. t.Error("expected shared", id2)
  1195. }
  1196. }
  1197. func TestAutoAcceptExistingFolder(t *testing.T) {
  1198. // Existing folder
  1199. id := srand.String(8)
  1200. idOther := srand.String(8) // To check that path does not get changed.
  1201. defer os.RemoveAll(id)
  1202. defer os.RemoveAll(idOther)
  1203. tcfg := defaultAutoAcceptCfg.Copy()
  1204. tcfg.Folders = []config.FolderConfiguration{
  1205. {
  1206. ID: id,
  1207. Path: idOther, // To check that path does not get changed.
  1208. },
  1209. }
  1210. wcfg, m := newState(tcfg)
  1211. defer os.Remove(wcfg.ConfigPath())
  1212. defer m.Stop()
  1213. if fcfg, ok := wcfg.Folder(id); !ok || fcfg.SharedWith(device1) {
  1214. t.Error("missing folder, or shared", id)
  1215. }
  1216. m.ClusterConfig(device1, protocol.ClusterConfig{
  1217. Folders: []protocol.Folder{
  1218. {
  1219. ID: id,
  1220. Label: id,
  1221. },
  1222. },
  1223. })
  1224. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) || fcfg.Path != idOther {
  1225. t.Error("missing folder, or unshared, or path changed", id)
  1226. }
  1227. }
  1228. func TestAutoAcceptNewAndExistingFolder(t *testing.T) {
  1229. // New and existing folder
  1230. id1 := srand.String(8)
  1231. defer os.RemoveAll(id1)
  1232. id2 := srand.String(8)
  1233. defer os.RemoveAll(id2)
  1234. tcfg := defaultAutoAcceptCfg.Copy()
  1235. tcfg.Folders = []config.FolderConfiguration{
  1236. {
  1237. ID: id1,
  1238. Path: id1, // from previous test case, to verify that path doesn't get changed.
  1239. },
  1240. }
  1241. wcfg, m := newState(tcfg)
  1242. defer os.Remove(wcfg.ConfigPath())
  1243. defer m.Stop()
  1244. if fcfg, ok := wcfg.Folder(id1); !ok || fcfg.SharedWith(device1) {
  1245. t.Error("missing folder, or shared", id1)
  1246. }
  1247. m.ClusterConfig(device1, protocol.ClusterConfig{
  1248. Folders: []protocol.Folder{
  1249. {
  1250. ID: id1,
  1251. Label: id1,
  1252. },
  1253. {
  1254. ID: id2,
  1255. Label: id2,
  1256. },
  1257. },
  1258. })
  1259. for i, id := range []string{id1, id2} {
  1260. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1261. t.Error("missing folder, or unshared", i, id)
  1262. }
  1263. }
  1264. }
  1265. func TestAutoAcceptAlreadyShared(t *testing.T) {
  1266. // Already shared
  1267. id := srand.String(8)
  1268. defer os.RemoveAll(id)
  1269. tcfg := defaultAutoAcceptCfg.Copy()
  1270. tcfg.Folders = []config.FolderConfiguration{
  1271. {
  1272. ID: id,
  1273. Path: id,
  1274. Devices: []config.FolderDeviceConfiguration{
  1275. {
  1276. DeviceID: device1,
  1277. },
  1278. },
  1279. },
  1280. }
  1281. wcfg, m := newState(tcfg)
  1282. defer os.Remove(wcfg.ConfigPath())
  1283. defer m.Stop()
  1284. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1285. t.Error("missing folder, or not shared", id)
  1286. }
  1287. m.ClusterConfig(device1, protocol.ClusterConfig{
  1288. Folders: []protocol.Folder{
  1289. {
  1290. ID: id,
  1291. Label: id,
  1292. },
  1293. },
  1294. })
  1295. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1296. t.Error("missing folder, or not shared", id)
  1297. }
  1298. }
  1299. func TestAutoAcceptNameConflict(t *testing.T) {
  1300. testOs := &fatalOs{t}
  1301. id := srand.String(8)
  1302. label := srand.String(8)
  1303. testOs.MkdirAll(id, 0777)
  1304. testOs.MkdirAll(label, 0777)
  1305. defer os.RemoveAll(id)
  1306. defer os.RemoveAll(label)
  1307. wcfg, m := newState(defaultAutoAcceptCfg)
  1308. defer os.Remove(wcfg.ConfigPath())
  1309. defer m.Stop()
  1310. m.ClusterConfig(device1, protocol.ClusterConfig{
  1311. Folders: []protocol.Folder{
  1312. {
  1313. ID: id,
  1314. Label: label,
  1315. },
  1316. },
  1317. })
  1318. if fcfg, ok := wcfg.Folder(id); ok && fcfg.SharedWith(device1) {
  1319. t.Error("unexpected folder", id)
  1320. }
  1321. }
  1322. func TestAutoAcceptPrefersLabel(t *testing.T) {
  1323. // Prefers label, falls back to ID.
  1324. wcfg, m := newState(defaultAutoAcceptCfg)
  1325. defer os.Remove(wcfg.ConfigPath())
  1326. id := srand.String(8)
  1327. label := srand.String(8)
  1328. defer os.RemoveAll(id)
  1329. defer os.RemoveAll(label)
  1330. defer m.Stop()
  1331. m.ClusterConfig(device1, protocol.ClusterConfig{
  1332. Folders: []protocol.Folder{
  1333. {
  1334. ID: id,
  1335. Label: label,
  1336. },
  1337. },
  1338. })
  1339. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) || !strings.HasSuffix(fcfg.Path, label) {
  1340. t.Error("expected shared, or wrong path", id, label, fcfg.Path)
  1341. }
  1342. }
  1343. func TestAutoAcceptFallsBackToID(t *testing.T) {
  1344. testOs := &fatalOs{t}
  1345. // Prefers label, falls back to ID.
  1346. wcfg, m := newState(defaultAutoAcceptCfg)
  1347. defer os.Remove(wcfg.ConfigPath())
  1348. id := srand.String(8)
  1349. label := srand.String(8)
  1350. t.Log(id, label)
  1351. testOs.MkdirAll(label, 0777)
  1352. defer os.RemoveAll(label)
  1353. defer os.RemoveAll(id)
  1354. defer m.Stop()
  1355. m.ClusterConfig(device1, protocol.ClusterConfig{
  1356. Folders: []protocol.Folder{
  1357. {
  1358. ID: id,
  1359. Label: label,
  1360. },
  1361. },
  1362. })
  1363. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) || !strings.HasSuffix(fcfg.Path, id) {
  1364. t.Error("expected shared, or wrong path", id, label, fcfg.Path)
  1365. }
  1366. }
  1367. func TestAutoAcceptPausedWhenFolderConfigChanged(t *testing.T) {
  1368. // Existing folder
  1369. id := srand.String(8)
  1370. idOther := srand.String(8) // To check that path does not get changed.
  1371. defer os.RemoveAll(id)
  1372. defer os.RemoveAll(idOther)
  1373. tcfg := defaultAutoAcceptCfg.Copy()
  1374. fcfg := config.NewFolderConfiguration(myID, id, "", fs.FilesystemTypeBasic, idOther)
  1375. fcfg.Paused = true
  1376. // The order of devices here is wrong (cfg.clean() sorts them), which will cause the folder to restart.
  1377. // Because of the restart, folder gets removed from m.deviceFolder, which means that generateClusterConfig will not panic.
  1378. // This wasn't an issue before, yet keeping the test case to prove that it still isn't.
  1379. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{
  1380. DeviceID: device1,
  1381. })
  1382. tcfg.Folders = []config.FolderConfiguration{fcfg}
  1383. wcfg, m := newState(tcfg)
  1384. defer os.Remove(wcfg.ConfigPath())
  1385. defer m.Stop()
  1386. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1387. t.Error("missing folder, or not shared", id)
  1388. }
  1389. if _, ok := m.folderRunners[id]; ok {
  1390. t.Fatal("folder running?")
  1391. }
  1392. m.ClusterConfig(device1, protocol.ClusterConfig{
  1393. Folders: []protocol.Folder{
  1394. {
  1395. ID: id,
  1396. Label: id,
  1397. },
  1398. },
  1399. })
  1400. m.generateClusterConfig(device1)
  1401. if fcfg, ok := wcfg.Folder(id); !ok {
  1402. t.Error("missing folder")
  1403. } else if fcfg.Path != idOther {
  1404. t.Error("folder path changed")
  1405. } else {
  1406. for _, dev := range fcfg.DeviceIDs() {
  1407. if dev == device1 {
  1408. return
  1409. }
  1410. }
  1411. t.Error("device missing")
  1412. }
  1413. if _, ok := m.folderRunners[id]; ok {
  1414. t.Error("folder started")
  1415. }
  1416. }
  1417. func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) {
  1418. // Existing folder
  1419. id := srand.String(8)
  1420. idOther := srand.String(8) // To check that path does not get changed.
  1421. defer os.RemoveAll(id)
  1422. defer os.RemoveAll(idOther)
  1423. tcfg := defaultAutoAcceptCfg.Copy()
  1424. fcfg := config.NewFolderConfiguration(myID, id, "", fs.FilesystemTypeBasic, idOther)
  1425. fcfg.Paused = true
  1426. // The new folder is exactly the same as the one constructed by handleAutoAccept, which means
  1427. // the folder will not be restarted (even if it's paused), yet handleAutoAccept used to add the folder
  1428. // to m.deviceFolders which had caused panics when calling generateClusterConfig, as the folder
  1429. // did not have a file set.
  1430. fcfg.Devices = append([]config.FolderDeviceConfiguration{
  1431. {
  1432. DeviceID: device1,
  1433. },
  1434. }, fcfg.Devices...) // Need to ensure this device order to avoid folder restart.
  1435. tcfg.Folders = []config.FolderConfiguration{fcfg}
  1436. wcfg, m := newState(tcfg)
  1437. defer os.Remove(wcfg.ConfigPath())
  1438. defer m.Stop()
  1439. if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1440. t.Error("missing folder, or not shared", id)
  1441. }
  1442. if _, ok := m.folderRunners[id]; ok {
  1443. t.Fatal("folder running?")
  1444. }
  1445. m.ClusterConfig(device1, protocol.ClusterConfig{
  1446. Folders: []protocol.Folder{
  1447. {
  1448. ID: id,
  1449. Label: id,
  1450. },
  1451. },
  1452. })
  1453. m.generateClusterConfig(device1)
  1454. if fcfg, ok := wcfg.Folder(id); !ok {
  1455. t.Error("missing folder")
  1456. } else if fcfg.Path != idOther {
  1457. t.Error("folder path changed")
  1458. } else {
  1459. for _, dev := range fcfg.DeviceIDs() {
  1460. if dev == device1 {
  1461. return
  1462. }
  1463. }
  1464. t.Error("device missing")
  1465. }
  1466. if _, ok := m.folderRunners[id]; ok {
  1467. t.Error("folder started")
  1468. }
  1469. }
  1470. func changeIgnores(t *testing.T, m *model, expected []string) {
  1471. arrEqual := func(a, b []string) bool {
  1472. if len(a) != len(b) {
  1473. return false
  1474. }
  1475. for i := range a {
  1476. if a[i] != b[i] {
  1477. return false
  1478. }
  1479. }
  1480. return true
  1481. }
  1482. ignores, _, err := m.GetIgnores("default")
  1483. if err != nil {
  1484. t.Error(err)
  1485. }
  1486. if !arrEqual(ignores, expected) {
  1487. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  1488. }
  1489. ignores = append(ignores, "pox")
  1490. err = m.SetIgnores("default", ignores)
  1491. if err != nil {
  1492. t.Error(err)
  1493. }
  1494. ignores2, _, err := m.GetIgnores("default")
  1495. if err != nil {
  1496. t.Error(err)
  1497. }
  1498. if !arrEqual(ignores, ignores2) {
  1499. t.Errorf("Incorrect ignores: %v != %v", ignores2, ignores)
  1500. }
  1501. if runtime.GOOS == "darwin" {
  1502. // see above
  1503. time.Sleep(time.Second)
  1504. } else {
  1505. time.Sleep(time.Millisecond)
  1506. }
  1507. err = m.SetIgnores("default", expected)
  1508. if err != nil {
  1509. t.Error(err)
  1510. }
  1511. ignores, _, err = m.GetIgnores("default")
  1512. if err != nil {
  1513. t.Error(err)
  1514. }
  1515. if !arrEqual(ignores, expected) {
  1516. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  1517. }
  1518. }
  1519. func TestIgnores(t *testing.T) {
  1520. testOs := &fatalOs{t}
  1521. // Assure a clean start state
  1522. testOs.RemoveAll(filepath.Join("testdata", config.DefaultMarkerName))
  1523. testOs.MkdirAll(filepath.Join("testdata", config.DefaultMarkerName), 0644)
  1524. ioutil.WriteFile("testdata/.stignore", []byte(".*\nquux\n"), 0644)
  1525. m := setupModel(defaultCfgWrapper)
  1526. defer m.Stop()
  1527. // Reach in and update the ignore matcher to one that always does
  1528. // reloads when asked to, instead of checking file mtimes. This is
  1529. // because we will be changing the files on disk often enough that the
  1530. // mtimes will be unreliable to determine change status.
  1531. m.fmut.Lock()
  1532. m.folderIgnores["default"] = ignore.New(defaultFs, ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged()))
  1533. m.fmut.Unlock()
  1534. // Make sure the initial scan has finished (ScanFolders is blocking)
  1535. m.ScanFolders()
  1536. expected := []string{
  1537. ".*",
  1538. "quux",
  1539. }
  1540. changeIgnores(t, m, expected)
  1541. _, _, err := m.GetIgnores("doesnotexist")
  1542. if err == nil {
  1543. t.Error("No error")
  1544. }
  1545. err = m.SetIgnores("doesnotexist", expected)
  1546. if err == nil {
  1547. t.Error("No error")
  1548. }
  1549. // Invalid path, marker should be missing, hence returns an error.
  1550. m.AddFolder(config.FolderConfiguration{ID: "fresh", Path: "XXX"})
  1551. _, _, err = m.GetIgnores("fresh")
  1552. if err == nil {
  1553. t.Error("No error")
  1554. }
  1555. // Repeat tests with paused folder
  1556. pausedDefaultFolderConfig := defaultFolderConfig
  1557. pausedDefaultFolderConfig.Paused = true
  1558. m.RestartFolder(defaultFolderConfig, pausedDefaultFolderConfig)
  1559. // Here folder initialization is not an issue as a paused folder isn't
  1560. // added to the model and thus there is no initial scan happening.
  1561. changeIgnores(t, m, expected)
  1562. // Make sure no .stignore file is considered valid
  1563. defer func() {
  1564. testOs.Rename("testdata/.stignore.bak", "testdata/.stignore")
  1565. }()
  1566. testOs.Rename("testdata/.stignore", "testdata/.stignore.bak")
  1567. changeIgnores(t, m, []string{})
  1568. }
  1569. func TestROScanRecovery(t *testing.T) {
  1570. testOs := &fatalOs{t}
  1571. ldb := db.OpenMemory()
  1572. set := db.NewFileSet("default", defaultFs, ldb)
  1573. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  1574. {Name: "dummyfile", Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}}},
  1575. })
  1576. fcfg := config.FolderConfiguration{
  1577. ID: "default",
  1578. Path: "rotestfolder",
  1579. Type: config.FolderTypeSendOnly,
  1580. RescanIntervalS: 1,
  1581. MarkerName: config.DefaultMarkerName,
  1582. }
  1583. cfg := createTmpWrapper(config.Configuration{
  1584. Folders: []config.FolderConfiguration{fcfg},
  1585. Devices: []config.DeviceConfiguration{
  1586. {
  1587. DeviceID: device1,
  1588. },
  1589. },
  1590. })
  1591. defer os.Remove(cfg.ConfigPath())
  1592. testOs.RemoveAll(fcfg.Path)
  1593. m := newModel(cfg, myID, "syncthing", "dev", ldb, nil)
  1594. m.AddFolder(fcfg)
  1595. m.StartFolder("default")
  1596. m.ServeBackground()
  1597. defer m.Stop()
  1598. waitFor := func(status string) error {
  1599. timeout := time.Now().Add(2 * time.Second)
  1600. for {
  1601. _, _, err := m.State("default")
  1602. if err == nil && status == "" {
  1603. return nil
  1604. }
  1605. if err != nil && err.Error() == status {
  1606. return nil
  1607. }
  1608. if time.Now().After(timeout) {
  1609. return fmt.Errorf("Timed out waiting for status: %s, current status: %v", status, err)
  1610. }
  1611. time.Sleep(10 * time.Millisecond)
  1612. }
  1613. }
  1614. if err := waitFor("folder path missing"); err != nil {
  1615. t.Error(err)
  1616. return
  1617. }
  1618. testOs.Mkdir(fcfg.Path, 0700)
  1619. if err := waitFor("folder marker missing"); err != nil {
  1620. t.Error(err)
  1621. return
  1622. }
  1623. fd := testOs.Create(filepath.Join(fcfg.Path, config.DefaultMarkerName))
  1624. fd.Close()
  1625. if err := waitFor(""); err != nil {
  1626. t.Error(err)
  1627. return
  1628. }
  1629. testOs.Remove(filepath.Join(fcfg.Path, config.DefaultMarkerName))
  1630. if err := waitFor("folder marker missing"); err != nil {
  1631. t.Error(err)
  1632. return
  1633. }
  1634. testOs.Remove(fcfg.Path)
  1635. if err := waitFor("folder path missing"); err != nil {
  1636. t.Error(err)
  1637. return
  1638. }
  1639. }
  1640. func TestRWScanRecovery(t *testing.T) {
  1641. testOs := &fatalOs{t}
  1642. ldb := db.OpenMemory()
  1643. set := db.NewFileSet("default", defaultFs, ldb)
  1644. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  1645. {Name: "dummyfile", Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}}},
  1646. })
  1647. fcfg := config.FolderConfiguration{
  1648. ID: "default",
  1649. Path: "rwtestfolder",
  1650. Type: config.FolderTypeSendReceive,
  1651. RescanIntervalS: 1,
  1652. MarkerName: config.DefaultMarkerName,
  1653. }
  1654. cfg := createTmpWrapper(config.Configuration{
  1655. Folders: []config.FolderConfiguration{fcfg},
  1656. Devices: []config.DeviceConfiguration{
  1657. {
  1658. DeviceID: device1,
  1659. },
  1660. },
  1661. })
  1662. defer os.Remove(cfg.ConfigPath())
  1663. testOs.RemoveAll(fcfg.Path)
  1664. m := newModel(cfg, myID, "syncthing", "dev", ldb, nil)
  1665. m.AddFolder(fcfg)
  1666. m.StartFolder("default")
  1667. m.ServeBackground()
  1668. defer m.Stop()
  1669. waitFor := func(status string) error {
  1670. timeout := time.Now().Add(2 * time.Second)
  1671. for {
  1672. _, _, err := m.State("default")
  1673. if err == nil && status == "" {
  1674. return nil
  1675. }
  1676. if err != nil && err.Error() == status {
  1677. return nil
  1678. }
  1679. if time.Now().After(timeout) {
  1680. return fmt.Errorf("Timed out waiting for status: %s, current status: %v", status, err)
  1681. }
  1682. time.Sleep(10 * time.Millisecond)
  1683. }
  1684. }
  1685. if err := waitFor("folder path missing"); err != nil {
  1686. t.Error(err)
  1687. return
  1688. }
  1689. testOs.Mkdir(fcfg.Path, 0700)
  1690. if err := waitFor("folder marker missing"); err != nil {
  1691. t.Error(err)
  1692. return
  1693. }
  1694. fd := testOs.Create(filepath.Join(fcfg.Path, config.DefaultMarkerName))
  1695. fd.Close()
  1696. if err := waitFor(""); err != nil {
  1697. t.Fatal(err)
  1698. }
  1699. testOs.Remove(filepath.Join(fcfg.Path, config.DefaultMarkerName))
  1700. if err := waitFor("folder marker missing"); err != nil {
  1701. t.Fatal(err)
  1702. }
  1703. testOs.Remove(fcfg.Path)
  1704. if err := waitFor("folder path missing"); err != nil {
  1705. t.Fatal(err)
  1706. }
  1707. }
  1708. func TestGlobalDirectoryTree(t *testing.T) {
  1709. db := db.OpenMemory()
  1710. m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
  1711. m.AddFolder(defaultFolderConfig)
  1712. m.ServeBackground()
  1713. defer m.Stop()
  1714. b := func(isfile bool, path ...string) protocol.FileInfo {
  1715. typ := protocol.FileInfoTypeDirectory
  1716. blocks := []protocol.BlockInfo{}
  1717. if isfile {
  1718. typ = protocol.FileInfoTypeFile
  1719. blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
  1720. }
  1721. return protocol.FileInfo{
  1722. Name: filepath.Join(path...),
  1723. Type: typ,
  1724. ModifiedS: 0x666,
  1725. Blocks: blocks,
  1726. Size: 0xa,
  1727. }
  1728. }
  1729. filedata := []interface{}{time.Unix(0x666, 0), 0xa}
  1730. testdata := []protocol.FileInfo{
  1731. b(false, "another"),
  1732. b(false, "another", "directory"),
  1733. b(true, "another", "directory", "afile"),
  1734. b(false, "another", "directory", "with"),
  1735. b(false, "another", "directory", "with", "a"),
  1736. b(true, "another", "directory", "with", "a", "file"),
  1737. b(true, "another", "directory", "with", "file"),
  1738. b(true, "another", "file"),
  1739. b(false, "other"),
  1740. b(false, "other", "rand"),
  1741. b(false, "other", "random"),
  1742. b(false, "other", "random", "dir"),
  1743. b(false, "other", "random", "dirx"),
  1744. b(false, "other", "randomx"),
  1745. b(false, "some"),
  1746. b(false, "some", "directory"),
  1747. b(false, "some", "directory", "with"),
  1748. b(false, "some", "directory", "with", "a"),
  1749. b(true, "some", "directory", "with", "a", "file"),
  1750. b(true, "rootfile"),
  1751. }
  1752. expectedResult := map[string]interface{}{
  1753. "another": map[string]interface{}{
  1754. "directory": map[string]interface{}{
  1755. "afile": filedata,
  1756. "with": map[string]interface{}{
  1757. "a": map[string]interface{}{
  1758. "file": filedata,
  1759. },
  1760. "file": filedata,
  1761. },
  1762. },
  1763. "file": filedata,
  1764. },
  1765. "other": map[string]interface{}{
  1766. "rand": map[string]interface{}{},
  1767. "random": map[string]interface{}{
  1768. "dir": map[string]interface{}{},
  1769. "dirx": map[string]interface{}{},
  1770. },
  1771. "randomx": map[string]interface{}{},
  1772. },
  1773. "some": map[string]interface{}{
  1774. "directory": map[string]interface{}{
  1775. "with": map[string]interface{}{
  1776. "a": map[string]interface{}{
  1777. "file": filedata,
  1778. },
  1779. },
  1780. },
  1781. },
  1782. "rootfile": filedata,
  1783. }
  1784. mm := func(data interface{}) string {
  1785. bytes, err := json.Marshal(data)
  1786. if err != nil {
  1787. panic(err)
  1788. }
  1789. return string(bytes)
  1790. }
  1791. m.Index(device1, "default", testdata)
  1792. result := m.GlobalDirectoryTree("default", "", -1, false)
  1793. if mm(result) != mm(expectedResult) {
  1794. t.Errorf("Does not match:\n%#v\n%#v", result, expectedResult)
  1795. }
  1796. result = m.GlobalDirectoryTree("default", "another", -1, false)
  1797. if mm(result) != mm(expectedResult["another"]) {
  1798. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(expectedResult["another"]))
  1799. }
  1800. result = m.GlobalDirectoryTree("default", "", 0, false)
  1801. currentResult := map[string]interface{}{
  1802. "another": map[string]interface{}{},
  1803. "other": map[string]interface{}{},
  1804. "some": map[string]interface{}{},
  1805. "rootfile": filedata,
  1806. }
  1807. if mm(result) != mm(currentResult) {
  1808. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1809. }
  1810. result = m.GlobalDirectoryTree("default", "", 1, false)
  1811. currentResult = map[string]interface{}{
  1812. "another": map[string]interface{}{
  1813. "directory": map[string]interface{}{},
  1814. "file": filedata,
  1815. },
  1816. "other": map[string]interface{}{
  1817. "rand": map[string]interface{}{},
  1818. "random": map[string]interface{}{},
  1819. "randomx": map[string]interface{}{},
  1820. },
  1821. "some": map[string]interface{}{
  1822. "directory": map[string]interface{}{},
  1823. },
  1824. "rootfile": filedata,
  1825. }
  1826. if mm(result) != mm(currentResult) {
  1827. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1828. }
  1829. result = m.GlobalDirectoryTree("default", "", -1, true)
  1830. currentResult = map[string]interface{}{
  1831. "another": map[string]interface{}{
  1832. "directory": map[string]interface{}{
  1833. "with": map[string]interface{}{
  1834. "a": map[string]interface{}{},
  1835. },
  1836. },
  1837. },
  1838. "other": map[string]interface{}{
  1839. "rand": map[string]interface{}{},
  1840. "random": map[string]interface{}{
  1841. "dir": map[string]interface{}{},
  1842. "dirx": map[string]interface{}{},
  1843. },
  1844. "randomx": map[string]interface{}{},
  1845. },
  1846. "some": map[string]interface{}{
  1847. "directory": map[string]interface{}{
  1848. "with": map[string]interface{}{
  1849. "a": map[string]interface{}{},
  1850. },
  1851. },
  1852. },
  1853. }
  1854. if mm(result) != mm(currentResult) {
  1855. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1856. }
  1857. result = m.GlobalDirectoryTree("default", "", 1, true)
  1858. currentResult = map[string]interface{}{
  1859. "another": map[string]interface{}{
  1860. "directory": map[string]interface{}{},
  1861. },
  1862. "other": map[string]interface{}{
  1863. "rand": map[string]interface{}{},
  1864. "random": map[string]interface{}{},
  1865. "randomx": map[string]interface{}{},
  1866. },
  1867. "some": map[string]interface{}{
  1868. "directory": map[string]interface{}{},
  1869. },
  1870. }
  1871. if mm(result) != mm(currentResult) {
  1872. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1873. }
  1874. result = m.GlobalDirectoryTree("default", "another", 0, false)
  1875. currentResult = map[string]interface{}{
  1876. "directory": map[string]interface{}{},
  1877. "file": filedata,
  1878. }
  1879. if mm(result) != mm(currentResult) {
  1880. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1881. }
  1882. result = m.GlobalDirectoryTree("default", "some/directory", 0, false)
  1883. currentResult = map[string]interface{}{
  1884. "with": map[string]interface{}{},
  1885. }
  1886. if mm(result) != mm(currentResult) {
  1887. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1888. }
  1889. result = m.GlobalDirectoryTree("default", "some/directory", 1, false)
  1890. currentResult = map[string]interface{}{
  1891. "with": map[string]interface{}{
  1892. "a": map[string]interface{}{},
  1893. },
  1894. }
  1895. if mm(result) != mm(currentResult) {
  1896. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1897. }
  1898. result = m.GlobalDirectoryTree("default", "some/directory", 2, false)
  1899. currentResult = map[string]interface{}{
  1900. "with": map[string]interface{}{
  1901. "a": map[string]interface{}{
  1902. "file": filedata,
  1903. },
  1904. },
  1905. }
  1906. if mm(result) != mm(currentResult) {
  1907. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1908. }
  1909. result = m.GlobalDirectoryTree("default", "another", -1, true)
  1910. currentResult = map[string]interface{}{
  1911. "directory": map[string]interface{}{
  1912. "with": map[string]interface{}{
  1913. "a": map[string]interface{}{},
  1914. },
  1915. },
  1916. }
  1917. if mm(result) != mm(currentResult) {
  1918. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1919. }
  1920. // No prefix matching!
  1921. result = m.GlobalDirectoryTree("default", "som", -1, false)
  1922. currentResult = map[string]interface{}{}
  1923. if mm(result) != mm(currentResult) {
  1924. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1925. }
  1926. }
  1927. func TestGlobalDirectorySelfFixing(t *testing.T) {
  1928. db := db.OpenMemory()
  1929. m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
  1930. m.AddFolder(defaultFolderConfig)
  1931. m.ServeBackground()
  1932. b := func(isfile bool, path ...string) protocol.FileInfo {
  1933. typ := protocol.FileInfoTypeDirectory
  1934. blocks := []protocol.BlockInfo{}
  1935. if isfile {
  1936. typ = protocol.FileInfoTypeFile
  1937. blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
  1938. }
  1939. return protocol.FileInfo{
  1940. Name: filepath.Join(path...),
  1941. Type: typ,
  1942. ModifiedS: 0x666,
  1943. Blocks: blocks,
  1944. Size: 0xa,
  1945. }
  1946. }
  1947. filedata := []interface{}{time.Unix(0x666, 0).Format(time.RFC3339), 0xa}
  1948. testdata := []protocol.FileInfo{
  1949. b(true, "another", "directory", "afile"),
  1950. b(true, "another", "directory", "with", "a", "file"),
  1951. b(true, "another", "directory", "with", "file"),
  1952. b(false, "other", "random", "dirx"),
  1953. b(false, "other", "randomx"),
  1954. b(false, "some", "directory", "with", "x"),
  1955. b(true, "some", "directory", "with", "a", "file"),
  1956. b(false, "this", "is", "a", "deep", "invalid", "directory"),
  1957. b(true, "xthis", "is", "a", "deep", "invalid", "file"),
  1958. }
  1959. expectedResult := map[string]interface{}{
  1960. "another": map[string]interface{}{
  1961. "directory": map[string]interface{}{
  1962. "afile": filedata,
  1963. "with": map[string]interface{}{
  1964. "a": map[string]interface{}{
  1965. "file": filedata,
  1966. },
  1967. "file": filedata,
  1968. },
  1969. },
  1970. },
  1971. "other": map[string]interface{}{
  1972. "random": map[string]interface{}{
  1973. "dirx": map[string]interface{}{},
  1974. },
  1975. "randomx": map[string]interface{}{},
  1976. },
  1977. "some": map[string]interface{}{
  1978. "directory": map[string]interface{}{
  1979. "with": map[string]interface{}{
  1980. "a": map[string]interface{}{
  1981. "file": filedata,
  1982. },
  1983. "x": map[string]interface{}{},
  1984. },
  1985. },
  1986. },
  1987. "this": map[string]interface{}{
  1988. "is": map[string]interface{}{
  1989. "a": map[string]interface{}{
  1990. "deep": map[string]interface{}{
  1991. "invalid": map[string]interface{}{
  1992. "directory": map[string]interface{}{},
  1993. },
  1994. },
  1995. },
  1996. },
  1997. },
  1998. "xthis": map[string]interface{}{
  1999. "is": map[string]interface{}{
  2000. "a": map[string]interface{}{
  2001. "deep": map[string]interface{}{
  2002. "invalid": map[string]interface{}{
  2003. "file": filedata,
  2004. },
  2005. },
  2006. },
  2007. },
  2008. },
  2009. }
  2010. mm := func(data interface{}) string {
  2011. bytes, err := json.Marshal(data)
  2012. if err != nil {
  2013. panic(err)
  2014. }
  2015. return string(bytes)
  2016. }
  2017. m.Index(device1, "default", testdata)
  2018. result := m.GlobalDirectoryTree("default", "", -1, false)
  2019. if mm(result) != mm(expectedResult) {
  2020. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(expectedResult))
  2021. }
  2022. result = m.GlobalDirectoryTree("default", "xthis/is/a/deep", -1, false)
  2023. currentResult := map[string]interface{}{
  2024. "invalid": map[string]interface{}{
  2025. "file": filedata,
  2026. },
  2027. }
  2028. if mm(result) != mm(currentResult) {
  2029. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  2030. }
  2031. result = m.GlobalDirectoryTree("default", "xthis/is/a/deep", -1, true)
  2032. currentResult = map[string]interface{}{
  2033. "invalid": map[string]interface{}{},
  2034. }
  2035. if mm(result) != mm(currentResult) {
  2036. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  2037. }
  2038. // !!! This is actually BAD, because we don't have enough level allowance
  2039. // to accept this file, hence the tree is left unbuilt !!!
  2040. result = m.GlobalDirectoryTree("default", "xthis", 1, false)
  2041. currentResult = map[string]interface{}{}
  2042. if mm(result) != mm(currentResult) {
  2043. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  2044. }
  2045. }
  2046. func genDeepFiles(n, d int) []protocol.FileInfo {
  2047. rand.Seed(int64(n))
  2048. files := make([]protocol.FileInfo, n)
  2049. t := time.Now().Unix()
  2050. for i := 0; i < n; i++ {
  2051. path := ""
  2052. for i := 0; i <= d; i++ {
  2053. path = filepath.Join(path, strconv.Itoa(rand.Int()))
  2054. }
  2055. sofar := ""
  2056. for _, path := range filepath.SplitList(path) {
  2057. sofar = filepath.Join(sofar, path)
  2058. files[i] = protocol.FileInfo{
  2059. Name: sofar,
  2060. }
  2061. i++
  2062. }
  2063. files[i].ModifiedS = t
  2064. files[i].Blocks = []protocol.BlockInfo{{Offset: 0, Size: 100, Hash: []byte("some hash bytes")}}
  2065. }
  2066. return files
  2067. }
  2068. func BenchmarkTree_10000_50(b *testing.B) {
  2069. benchmarkTree(b, 10000, 50)
  2070. }
  2071. func BenchmarkTree_100_50(b *testing.B) {
  2072. benchmarkTree(b, 100, 50)
  2073. }
  2074. func BenchmarkTree_100_10(b *testing.B) {
  2075. benchmarkTree(b, 100, 10)
  2076. }
  2077. func benchmarkTree(b *testing.B, n1, n2 int) {
  2078. db := db.OpenMemory()
  2079. m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil)
  2080. m.AddFolder(defaultFolderConfig)
  2081. m.ServeBackground()
  2082. m.ScanFolder("default")
  2083. files := genDeepFiles(n1, n2)
  2084. m.Index(device1, "default", files)
  2085. b.ResetTimer()
  2086. for i := 0; i < b.N; i++ {
  2087. m.GlobalDirectoryTree("default", "", -1, false)
  2088. }
  2089. b.ReportAllocs()
  2090. }
  2091. func TestIssue3028(t *testing.T) {
  2092. testOs := &fatalOs{t}
  2093. // Create two files that we'll delete, one with a name that is a prefix of the other.
  2094. if err := ioutil.WriteFile("testdata/testrm", []byte("Hello"), 0644); err != nil {
  2095. t.Fatal(err)
  2096. }
  2097. defer testOs.Remove("testdata/testrm")
  2098. if err := ioutil.WriteFile("testdata/testrm2", []byte("Hello"), 0644); err != nil {
  2099. t.Fatal(err)
  2100. }
  2101. defer testOs.Remove("testdata/testrm2")
  2102. // Create a model and default folder
  2103. m := setupModel(defaultCfgWrapper)
  2104. // Get a count of how many files are there now
  2105. locorigfiles := m.LocalSize("default").Files
  2106. globorigfiles := m.GlobalSize("default").Files
  2107. // Delete and rescan specifically these two
  2108. testOs.Remove("testdata/testrm")
  2109. testOs.Remove("testdata/testrm2")
  2110. m.ScanFolderSubdirs("default", []string{"testrm", "testrm2"})
  2111. // Verify that the number of files decreased by two and the number of
  2112. // deleted files increases by two
  2113. loc := m.LocalSize("default")
  2114. glob := m.GlobalSize("default")
  2115. if loc.Files != locorigfiles-2 {
  2116. t.Errorf("Incorrect local accounting; got %d current files, expected %d", loc.Files, locorigfiles-2)
  2117. }
  2118. if glob.Files != globorigfiles-2 {
  2119. t.Errorf("Incorrect global accounting; got %d current files, expected %d", glob.Files, globorigfiles-2)
  2120. }
  2121. if loc.Deleted != 2 {
  2122. t.Errorf("Incorrect local accounting; got %d deleted files, expected 2", loc.Deleted)
  2123. }
  2124. if glob.Deleted != 2 {
  2125. t.Errorf("Incorrect global accounting; got %d deleted files, expected 2", glob.Deleted)
  2126. }
  2127. }
  2128. func TestIssue4357(t *testing.T) {
  2129. db := db.OpenMemory()
  2130. cfg := defaultCfgWrapper.RawCopy()
  2131. // Create a separate wrapper not to pollute other tests.
  2132. wrapper := createTmpWrapper(config.Configuration{})
  2133. defer os.Remove(wrapper.ConfigPath())
  2134. m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
  2135. m.ServeBackground()
  2136. defer m.Stop()
  2137. // Force the model to wire itself and add the folders
  2138. p, err := wrapper.Replace(cfg)
  2139. p.Wait()
  2140. if err != nil {
  2141. t.Error(err)
  2142. }
  2143. if _, ok := m.folderCfgs["default"]; !ok {
  2144. t.Error("Folder should be running")
  2145. }
  2146. newCfg := wrapper.RawCopy()
  2147. newCfg.Folders[0].Paused = true
  2148. p, err = wrapper.Replace(newCfg)
  2149. p.Wait()
  2150. if err != nil {
  2151. t.Error(err)
  2152. }
  2153. if _, ok := m.folderCfgs["default"]; ok {
  2154. t.Error("Folder should not be running")
  2155. }
  2156. if _, ok := m.cfg.Folder("default"); !ok {
  2157. t.Error("should still have folder in config")
  2158. }
  2159. p, err = wrapper.Replace(config.Configuration{})
  2160. p.Wait()
  2161. if err != nil {
  2162. t.Error(err)
  2163. }
  2164. if _, ok := m.cfg.Folder("default"); ok {
  2165. t.Error("should not have folder in config")
  2166. }
  2167. // Add the folder back, should be running
  2168. p, err = wrapper.Replace(cfg)
  2169. p.Wait()
  2170. if err != nil {
  2171. t.Error(err)
  2172. }
  2173. if _, ok := m.folderCfgs["default"]; !ok {
  2174. t.Error("Folder should be running")
  2175. }
  2176. if _, ok := m.cfg.Folder("default"); !ok {
  2177. t.Error("should still have folder in config")
  2178. }
  2179. // Should not panic when removing a running folder.
  2180. p, err = wrapper.Replace(config.Configuration{})
  2181. p.Wait()
  2182. if err != nil {
  2183. t.Error(err)
  2184. }
  2185. if _, ok := m.folderCfgs["default"]; ok {
  2186. t.Error("Folder should not be running")
  2187. }
  2188. if _, ok := m.cfg.Folder("default"); ok {
  2189. t.Error("should not have folder in config")
  2190. }
  2191. }
  2192. func TestIssue2782(t *testing.T) {
  2193. // CheckHealth should accept a symlinked folder, when using tilde-expanded path.
  2194. if runtime.GOOS == "windows" {
  2195. t.Skip("not reliable on Windows")
  2196. return
  2197. }
  2198. home := os.Getenv("HOME")
  2199. if home == "" {
  2200. t.Skip("no home")
  2201. }
  2202. // Create the test env. Needs to be based on $HOME as tilde expansion is
  2203. // part of the issue. Skip the test if any of this fails, as we are a
  2204. // bit outside of our stated domain here...
  2205. testName := ".syncthing-test." + srand.String(16)
  2206. testDir := filepath.Join(home, testName)
  2207. if err := os.RemoveAll(testDir); err != nil {
  2208. t.Skip(err)
  2209. }
  2210. if err := os.MkdirAll(testDir+"/syncdir", 0755); err != nil {
  2211. t.Skip(err)
  2212. }
  2213. if err := ioutil.WriteFile(testDir+"/syncdir/file", []byte("hello, world\n"), 0644); err != nil {
  2214. t.Skip(err)
  2215. }
  2216. if err := os.Symlink("syncdir", testDir+"/synclink"); err != nil {
  2217. t.Skip(err)
  2218. }
  2219. defer os.RemoveAll(testDir)
  2220. m := setupModel(defaultCfgWrapper)
  2221. if err := m.ScanFolder("default"); err != nil {
  2222. t.Error("scan error:", err)
  2223. }
  2224. m.fmut.Lock()
  2225. runner := m.folderRunners["default"]
  2226. m.fmut.Unlock()
  2227. if err := runner.CheckHealth(); err != nil {
  2228. t.Error("health check error:", err)
  2229. }
  2230. }
  2231. func TestIndexesForUnknownDevicesDropped(t *testing.T) {
  2232. dbi := db.OpenMemory()
  2233. files := db.NewFileSet("default", defaultFs, dbi)
  2234. files.Drop(device1)
  2235. files.Update(device1, genFiles(1))
  2236. files.Drop(device2)
  2237. files.Update(device2, genFiles(1))
  2238. if len(files.ListDevices()) != 2 {
  2239. t.Error("expected two devices")
  2240. }
  2241. m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", dbi, nil)
  2242. m.AddFolder(defaultFolderConfig)
  2243. m.StartFolder("default")
  2244. // Remote sequence is cached, hence need to recreated.
  2245. files = db.NewFileSet("default", defaultFs, dbi)
  2246. if len(files.ListDevices()) != 1 {
  2247. t.Error("Expected one device")
  2248. }
  2249. }
  2250. func TestSharedWithClearedOnDisconnect(t *testing.T) {
  2251. wcfg := createTmpWrapper(defaultCfg)
  2252. wcfg.SetDevice(config.NewDeviceConfiguration(device2, "device2"))
  2253. fcfg := wcfg.FolderList()[0]
  2254. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
  2255. wcfg.SetFolder(fcfg)
  2256. defer os.Remove(wcfg.ConfigPath())
  2257. m := setupModel(wcfg)
  2258. defer m.Stop()
  2259. conn1 := &fakeConnection{id: device1}
  2260. m.AddConnection(conn1, protocol.HelloResult{})
  2261. conn2 := &fakeConnection{id: device2}
  2262. m.AddConnection(conn2, protocol.HelloResult{})
  2263. m.ClusterConfig(device1, protocol.ClusterConfig{
  2264. Folders: []protocol.Folder{
  2265. {
  2266. ID: "default",
  2267. Devices: []protocol.Device{
  2268. {ID: myID},
  2269. {ID: device1},
  2270. {ID: device2},
  2271. },
  2272. },
  2273. },
  2274. })
  2275. m.ClusterConfig(device2, protocol.ClusterConfig{
  2276. Folders: []protocol.Folder{
  2277. {
  2278. ID: "default",
  2279. Devices: []protocol.Device{
  2280. {ID: myID},
  2281. {ID: device1},
  2282. {ID: device2},
  2283. },
  2284. },
  2285. },
  2286. })
  2287. if fcfg, ok := m.cfg.Folder("default"); !ok || !fcfg.SharedWith(device1) {
  2288. t.Error("not shared with device1")
  2289. }
  2290. if fcfg, ok := m.cfg.Folder("default"); !ok || !fcfg.SharedWith(device2) {
  2291. t.Error("not shared with device2")
  2292. }
  2293. if conn2.Closed() {
  2294. t.Error("conn already closed")
  2295. }
  2296. if _, err := wcfg.RemoveDevice(device2); err != nil {
  2297. t.Error(err)
  2298. }
  2299. time.Sleep(100 * time.Millisecond) // Committer notification happens in a separate routine
  2300. fcfg, ok := m.cfg.Folder("default")
  2301. if !ok {
  2302. t.Fatal("default folder missing")
  2303. }
  2304. if !fcfg.SharedWith(device1) {
  2305. t.Error("not shared with device1")
  2306. }
  2307. if fcfg.SharedWith(device2) {
  2308. t.Error("shared with device2")
  2309. }
  2310. for _, dev := range fcfg.Devices {
  2311. if dev.DeviceID == device2 {
  2312. t.Error("still there")
  2313. }
  2314. }
  2315. if !conn2.Closed() {
  2316. t.Error("connection not closed")
  2317. }
  2318. if _, ok := wcfg.Devices()[device2]; ok {
  2319. t.Error("device still in config")
  2320. }
  2321. if _, ok := m.conn[device2]; !ok {
  2322. t.Error("conn missing early")
  2323. }
  2324. if _, ok := m.helloMessages[device2]; !ok {
  2325. t.Error("hello missing early")
  2326. }
  2327. if _, ok := m.deviceDownloads[device2]; !ok {
  2328. t.Error("downloads missing early")
  2329. }
  2330. m.Closed(conn2, fmt.Errorf("foo"))
  2331. if _, ok := m.conn[device2]; ok {
  2332. t.Error("conn not missing")
  2333. }
  2334. if _, ok := m.helloMessages[device2]; ok {
  2335. t.Error("hello not missing")
  2336. }
  2337. if _, ok := m.deviceDownloads[device2]; ok {
  2338. t.Error("downloads not missing")
  2339. }
  2340. }
  2341. func TestIssue3496(t *testing.T) {
  2342. t.Skip("This test deletes files that the other test depend on. Needs fixing.")
  2343. // It seems like lots of deleted files can cause negative completion
  2344. // percentages. Lets make sure that doesn't happen. Also do some general
  2345. // checks on the completion calculation stuff.
  2346. m := setupModel(defaultCfgWrapper)
  2347. defer m.Stop()
  2348. m.ScanFolder("default")
  2349. addFakeConn(m, device1)
  2350. addFakeConn(m, device2)
  2351. // Reach into the model and grab the current file list...
  2352. m.fmut.RLock()
  2353. fs := m.folderFiles["default"]
  2354. m.fmut.RUnlock()
  2355. var localFiles []protocol.FileInfo
  2356. fs.WithHave(protocol.LocalDeviceID, func(i db.FileIntf) bool {
  2357. localFiles = append(localFiles, i.(protocol.FileInfo))
  2358. return true
  2359. })
  2360. // Mark all files as deleted and fake it as update from device1
  2361. for i := range localFiles {
  2362. localFiles[i].Deleted = true
  2363. localFiles[i].Version = localFiles[i].Version.Update(device1.Short())
  2364. localFiles[i].Blocks = nil
  2365. }
  2366. // Also add a small file that we're supposed to need, or the global size
  2367. // stuff will bail out early due to the entire folder being zero size.
  2368. localFiles = append(localFiles, protocol.FileInfo{
  2369. Name: "fake",
  2370. Size: 1234,
  2371. Type: protocol.FileInfoTypeFile,
  2372. Version: protocol.Vector{Counters: []protocol.Counter{{ID: device1.Short(), Value: 42}}},
  2373. })
  2374. m.IndexUpdate(device1, "default", localFiles)
  2375. // Check that the completion percentage for us makes sense
  2376. comp := m.Completion(protocol.LocalDeviceID, "default")
  2377. if comp.NeedBytes > comp.GlobalBytes {
  2378. t.Errorf("Need more bytes than exist, not possible: %d > %d", comp.NeedBytes, comp.GlobalBytes)
  2379. }
  2380. if comp.CompletionPct < 0 {
  2381. t.Errorf("Less than zero percent complete, not possible: %.02f%%", comp.CompletionPct)
  2382. }
  2383. if comp.NeedBytes == 0 {
  2384. t.Error("Need no bytes even though some files are deleted")
  2385. }
  2386. if comp.CompletionPct == 100 {
  2387. t.Errorf("Fully complete, not possible: %.02f%%", comp.CompletionPct)
  2388. }
  2389. t.Log(comp)
  2390. // Check that NeedSize does the correct thing
  2391. need := m.NeedSize("default")
  2392. if need.Files != 1 || need.Bytes != 1234 {
  2393. // The one we added synthetically above
  2394. t.Errorf("Incorrect need size; %d, %d != 1, 1234", need.Files, need.Bytes)
  2395. }
  2396. if int(need.Deleted) != len(localFiles)-1 {
  2397. // The rest
  2398. t.Errorf("Incorrect need deletes; %d != %d", need.Deleted, len(localFiles)-1)
  2399. }
  2400. }
  2401. func TestIssue3804(t *testing.T) {
  2402. m := setupModel(defaultCfgWrapper)
  2403. defer m.Stop()
  2404. // Subdirs ending in slash should be accepted
  2405. if err := m.ScanFolderSubdirs("default", []string{"baz/", "foo"}); err != nil {
  2406. t.Error("Unexpected error:", err)
  2407. }
  2408. }
  2409. func TestIssue3829(t *testing.T) {
  2410. m := setupModel(defaultCfgWrapper)
  2411. defer m.Stop()
  2412. // Empty subdirs should be accepted
  2413. if err := m.ScanFolderSubdirs("default", []string{""}); err != nil {
  2414. t.Error("Unexpected error:", err)
  2415. }
  2416. }
  2417. func TestNoRequestsFromPausedDevices(t *testing.T) {
  2418. t.Skip("broken, fails randomly, #3843")
  2419. wcfg := createTmpWrapper(defaultCfg)
  2420. wcfg.SetDevice(config.NewDeviceConfiguration(device2, "device2"))
  2421. fcfg := wcfg.FolderList()[0]
  2422. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
  2423. wcfg.SetFolder(fcfg)
  2424. defer os.Remove(wcfg.ConfigPath())
  2425. m := setupModel(wcfg)
  2426. defer m.Stop()
  2427. file := testDataExpected["foo"]
  2428. files := m.folderFiles["default"]
  2429. files.Update(device1, []protocol.FileInfo{file})
  2430. files.Update(device2, []protocol.FileInfo{file})
  2431. avail := m.Availability("default", file, file.Blocks[0])
  2432. if len(avail) != 0 {
  2433. t.Errorf("should not be available, no connections")
  2434. }
  2435. addFakeConn(m, device1)
  2436. addFakeConn(m, device2)
  2437. // !!! This is not what I'd expect to happen, as we don't even know if the peer has the original index !!!
  2438. avail = m.Availability("default", file, file.Blocks[0])
  2439. if len(avail) != 2 {
  2440. t.Errorf("should have two available")
  2441. }
  2442. cc := protocol.ClusterConfig{
  2443. Folders: []protocol.Folder{
  2444. {
  2445. ID: "default",
  2446. Devices: []protocol.Device{
  2447. {ID: device1},
  2448. {ID: device2},
  2449. },
  2450. },
  2451. },
  2452. }
  2453. m.ClusterConfig(device1, cc)
  2454. m.ClusterConfig(device2, cc)
  2455. avail = m.Availability("default", file, file.Blocks[0])
  2456. if len(avail) != 2 {
  2457. t.Errorf("should have two available")
  2458. }
  2459. m.Closed(&fakeConnection{id: device1}, errDeviceUnknown)
  2460. m.Closed(&fakeConnection{id: device2}, errDeviceUnknown)
  2461. avail = m.Availability("default", file, file.Blocks[0])
  2462. if len(avail) != 0 {
  2463. t.Errorf("should have no available")
  2464. }
  2465. // Test that remote paused folders are not used.
  2466. addFakeConn(m, device1)
  2467. addFakeConn(m, device2)
  2468. m.ClusterConfig(device1, cc)
  2469. ccp := cc
  2470. ccp.Folders[0].Paused = true
  2471. m.ClusterConfig(device1, ccp)
  2472. avail = m.Availability("default", file, file.Blocks[0])
  2473. if len(avail) != 1 {
  2474. t.Errorf("should have one available")
  2475. }
  2476. }
  2477. // TestIssue2571 tests replacing a directory with content with a symlink
  2478. func TestIssue2571(t *testing.T) {
  2479. if runtime.GOOS == "windows" {
  2480. t.Skip("Scanning symlinks isn't supported on windows")
  2481. }
  2482. w, tmpDir := tmpDefaultWrapper()
  2483. defer func() {
  2484. os.RemoveAll(tmpDir)
  2485. os.Remove(w.ConfigPath())
  2486. }()
  2487. testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
  2488. for _, dir := range []string{"toLink", "linkTarget"} {
  2489. err := testFs.MkdirAll(dir, 0775)
  2490. if err != nil {
  2491. t.Fatal(err)
  2492. }
  2493. fd, err := testFs.Create(filepath.Join(dir, "a"))
  2494. if err != nil {
  2495. t.Fatal(err)
  2496. }
  2497. fd.Close()
  2498. }
  2499. m := setupModel(w)
  2500. if err := testFs.RemoveAll("toLink"); err != nil {
  2501. t.Fatal(err)
  2502. }
  2503. if err := osutil.DebugSymlinkForTestsOnly(filepath.Join(testFs.URI(), "linkTarget"), filepath.Join(testFs.URI(), "toLink")); err != nil {
  2504. t.Fatal(err)
  2505. }
  2506. m.ScanFolder("default")
  2507. if dir, ok := m.CurrentFolderFile("default", "toLink"); !ok {
  2508. t.Fatalf("Dir missing in db")
  2509. } else if !dir.IsSymlink() {
  2510. t.Errorf("Dir wasn't changed to symlink")
  2511. }
  2512. if file, ok := m.CurrentFolderFile("default", filepath.Join("toLink", "a")); !ok {
  2513. t.Fatalf("File missing in db")
  2514. } else if !file.Deleted {
  2515. t.Errorf("File below symlink has not been marked as deleted")
  2516. }
  2517. }
  2518. // TestIssue4573 tests that contents of an unavailable dir aren't marked deleted
  2519. func TestIssue4573(t *testing.T) {
  2520. if runtime.GOOS == "windows" {
  2521. t.Skip("Can't make the dir inaccessible on windows")
  2522. }
  2523. w, tmpDir := tmpDefaultWrapper()
  2524. defer func() {
  2525. os.RemoveAll(tmpDir)
  2526. os.Remove(w.ConfigPath())
  2527. }()
  2528. testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
  2529. err := testFs.MkdirAll("inaccessible", 0755)
  2530. if err != nil {
  2531. t.Fatal(err)
  2532. }
  2533. defer testFs.Chmod("inaccessible", 0777)
  2534. file := filepath.Join("inaccessible", "a")
  2535. fd, err := testFs.Create(file)
  2536. if err != nil {
  2537. t.Fatal(err)
  2538. }
  2539. fd.Close()
  2540. m := setupModel(w)
  2541. err = testFs.Chmod("inaccessible", 0000)
  2542. if err != nil {
  2543. t.Fatal(err)
  2544. }
  2545. m.ScanFolder("default")
  2546. if file, ok := m.CurrentFolderFile("default", file); !ok {
  2547. t.Fatalf("File missing in db")
  2548. } else if file.Deleted {
  2549. t.Errorf("Inaccessible file has been marked as deleted.")
  2550. }
  2551. }
  2552. // TestInternalScan checks whether various fs operations are correctly represented
  2553. // in the db after scanning.
  2554. func TestInternalScan(t *testing.T) {
  2555. w, tmpDir := tmpDefaultWrapper()
  2556. defer func() {
  2557. os.RemoveAll(tmpDir)
  2558. os.Remove(w.ConfigPath())
  2559. }()
  2560. testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
  2561. testCases := map[string]func(protocol.FileInfo) bool{
  2562. "removeDir": func(f protocol.FileInfo) bool {
  2563. return !f.Deleted
  2564. },
  2565. "dirToFile": func(f protocol.FileInfo) bool {
  2566. return f.Deleted || f.IsDirectory()
  2567. },
  2568. }
  2569. baseDirs := []string{"dirToFile", "removeDir"}
  2570. for _, dir := range baseDirs {
  2571. sub := filepath.Join(dir, "subDir")
  2572. for _, dir := range []string{dir, sub} {
  2573. err := testFs.MkdirAll(dir, 0775)
  2574. if err != nil {
  2575. t.Fatalf("%v: %v", dir, err)
  2576. }
  2577. }
  2578. testCases[sub] = func(f protocol.FileInfo) bool {
  2579. return !f.Deleted
  2580. }
  2581. for _, dir := range []string{dir, sub} {
  2582. file := filepath.Join(dir, "a")
  2583. fd, err := testFs.Create(file)
  2584. if err != nil {
  2585. t.Fatal(err)
  2586. }
  2587. fd.Close()
  2588. testCases[file] = func(f protocol.FileInfo) bool {
  2589. return !f.Deleted
  2590. }
  2591. }
  2592. }
  2593. m := setupModel(w)
  2594. for _, dir := range baseDirs {
  2595. if err := testFs.RemoveAll(dir); err != nil {
  2596. t.Fatal(err)
  2597. }
  2598. }
  2599. fd, err := testFs.Create("dirToFile")
  2600. if err != nil {
  2601. t.Fatal(err)
  2602. }
  2603. fd.Close()
  2604. m.ScanFolder("default")
  2605. for path, cond := range testCases {
  2606. if f, ok := m.CurrentFolderFile("default", path); !ok {
  2607. t.Fatalf("%v missing in db", path)
  2608. } else if cond(f) {
  2609. t.Errorf("Incorrect db entry for %v", path)
  2610. }
  2611. }
  2612. }
  2613. func TestCustomMarkerName(t *testing.T) {
  2614. testOs := &fatalOs{t}
  2615. ldb := db.OpenMemory()
  2616. set := db.NewFileSet("default", defaultFs, ldb)
  2617. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  2618. {Name: "dummyfile"},
  2619. })
  2620. fcfg := config.FolderConfiguration{
  2621. ID: "default",
  2622. Path: "rwtestfolder",
  2623. Type: config.FolderTypeSendReceive,
  2624. RescanIntervalS: 1,
  2625. MarkerName: "myfile",
  2626. }
  2627. cfg := createTmpWrapper(config.Configuration{
  2628. Folders: []config.FolderConfiguration{fcfg},
  2629. Devices: []config.DeviceConfiguration{
  2630. {
  2631. DeviceID: device1,
  2632. },
  2633. },
  2634. })
  2635. defer os.Remove(cfg.ConfigPath())
  2636. testOs.RemoveAll(fcfg.Path)
  2637. defer testOs.RemoveAll(fcfg.Path)
  2638. m := newModel(cfg, myID, "syncthing", "dev", ldb, nil)
  2639. m.AddFolder(fcfg)
  2640. m.StartFolder("default")
  2641. m.ServeBackground()
  2642. defer m.Stop()
  2643. waitFor := func(status string) error {
  2644. timeout := time.Now().Add(2 * time.Second)
  2645. for {
  2646. _, _, err := m.State("default")
  2647. if err == nil && status == "" {
  2648. return nil
  2649. }
  2650. if err != nil && err.Error() == status {
  2651. return nil
  2652. }
  2653. if time.Now().After(timeout) {
  2654. return fmt.Errorf("Timed out waiting for status: %s, current status: %v", status, err)
  2655. }
  2656. time.Sleep(10 * time.Millisecond)
  2657. }
  2658. }
  2659. if err := waitFor("folder path missing"); err != nil {
  2660. t.Fatal(err)
  2661. }
  2662. testOs.Mkdir(fcfg.Path, 0700)
  2663. fd := testOs.Create(filepath.Join(fcfg.Path, "myfile"))
  2664. fd.Close()
  2665. if err := waitFor(""); err != nil {
  2666. t.Fatal(err)
  2667. }
  2668. }
  2669. func TestRemoveDirWithContent(t *testing.T) {
  2670. defer func() {
  2671. defaultFs.RemoveAll("dirwith")
  2672. }()
  2673. defaultFs.MkdirAll("dirwith", 0755)
  2674. content := filepath.Join("dirwith", "content")
  2675. fd, err := defaultFs.Create(content)
  2676. if err != nil {
  2677. t.Fatal(err)
  2678. return
  2679. }
  2680. fd.Close()
  2681. m := setupModel(defaultCfgWrapper)
  2682. defer m.Stop()
  2683. dir, ok := m.CurrentFolderFile("default", "dirwith")
  2684. if !ok {
  2685. t.Fatalf("Can't get dir \"dirwith\" after initial scan")
  2686. }
  2687. dir.Deleted = true
  2688. dir.Version = dir.Version.Update(device1.Short()).Update(device1.Short())
  2689. file, ok := m.CurrentFolderFile("default", content)
  2690. if !ok {
  2691. t.Fatalf("Can't get file \"%v\" after initial scan", content)
  2692. }
  2693. file.Deleted = true
  2694. file.Version = file.Version.Update(device1.Short()).Update(device1.Short())
  2695. m.IndexUpdate(device1, "default", []protocol.FileInfo{dir, file})
  2696. // Is there something we could trigger on instead of just waiting?
  2697. timeout := time.NewTimer(5 * time.Second)
  2698. for {
  2699. dir, ok := m.CurrentFolderFile("default", "dirwith")
  2700. if !ok {
  2701. t.Fatalf("Can't get dir \"dirwith\" after index update")
  2702. }
  2703. file, ok := m.CurrentFolderFile("default", content)
  2704. if !ok {
  2705. t.Fatalf("Can't get file \"%v\" after index update", content)
  2706. }
  2707. if dir.Deleted && file.Deleted {
  2708. return
  2709. }
  2710. select {
  2711. case <-timeout.C:
  2712. if !dir.Deleted && !file.Deleted {
  2713. t.Errorf("Neither the dir nor its content was deleted before timing out.")
  2714. } else if !dir.Deleted {
  2715. t.Errorf("The dir was not deleted before timing out.")
  2716. } else {
  2717. t.Errorf("The content of the dir was not deleted before timing out.")
  2718. }
  2719. return
  2720. default:
  2721. time.Sleep(100 * time.Millisecond)
  2722. }
  2723. }
  2724. }
  2725. func TestIssue4475(t *testing.T) {
  2726. m, conn, tmpDir, w := setupModelWithConnection()
  2727. defer func() {
  2728. m.Stop()
  2729. os.RemoveAll(tmpDir)
  2730. os.Remove(w.ConfigPath())
  2731. }()
  2732. testFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir)
  2733. // Scenario: Dir is deleted locally and before syncing/index exchange
  2734. // happens, a file is create in that dir on the remote.
  2735. // This should result in the directory being recreated and added to the
  2736. // db locally.
  2737. err := testFs.MkdirAll("delDir", 0755)
  2738. if err != nil {
  2739. t.Fatal(err)
  2740. }
  2741. m.ScanFolder("default")
  2742. if err = testFs.RemoveAll("delDir"); err != nil {
  2743. t.Fatal(err)
  2744. }
  2745. m.ScanFolder("default")
  2746. if fcfg, ok := m.cfg.Folder("default"); !ok || !fcfg.SharedWith(device1) {
  2747. t.Fatal("not shared with device1")
  2748. }
  2749. fileName := filepath.Join("delDir", "file")
  2750. conn.addFile(fileName, 0644, protocol.FileInfoTypeFile, nil)
  2751. conn.sendIndexUpdate()
  2752. // Is there something we could trigger on instead of just waiting?
  2753. timeout := time.NewTimer(5 * time.Second)
  2754. created := false
  2755. for {
  2756. if !created {
  2757. if _, ok := m.CurrentFolderFile("default", fileName); ok {
  2758. created = true
  2759. }
  2760. } else {
  2761. dir, ok := m.CurrentFolderFile("default", "delDir")
  2762. if !ok {
  2763. t.Fatalf("can't get dir from db")
  2764. }
  2765. if !dir.Deleted {
  2766. return
  2767. }
  2768. }
  2769. select {
  2770. case <-timeout.C:
  2771. if created {
  2772. t.Errorf("Timed out before file from remote was created")
  2773. } else {
  2774. t.Errorf("Timed out before directory was resurrected in db")
  2775. }
  2776. return
  2777. default:
  2778. time.Sleep(100 * time.Millisecond)
  2779. }
  2780. }
  2781. }
  2782. func TestVersionRestore(t *testing.T) {
  2783. // We create a bunch of files which we restore
  2784. // In each file, we write the filename as the content
  2785. // We verify that the content matches at the expected filenames
  2786. // after the restore operation.
  2787. dir, err := ioutil.TempDir("", "")
  2788. if err != nil {
  2789. t.Fatal(err)
  2790. }
  2791. defer os.RemoveAll(dir)
  2792. fcfg := config.NewFolderConfiguration(myID, "default", "default", fs.FilesystemTypeBasic, dir)
  2793. fcfg.Versioning.Type = "simple"
  2794. fcfg.FSWatcherEnabled = false
  2795. filesystem := fcfg.Filesystem()
  2796. rawConfig := config.Configuration{
  2797. Folders: []config.FolderConfiguration{fcfg},
  2798. }
  2799. cfg := createTmpWrapper(rawConfig)
  2800. defer os.Remove(cfg.ConfigPath())
  2801. m := setupModel(cfg)
  2802. m.ScanFolder("default")
  2803. sentinel, err := time.ParseInLocation(versioner.TimeFormat, "20200101-010101", locationLocal)
  2804. if err != nil {
  2805. t.Fatal(err)
  2806. }
  2807. sentinelTag := sentinel.Format(versioner.TimeFormat)
  2808. for _, file := range []string{
  2809. // Versions directory
  2810. ".stversions/file~20171210-040404.txt", // will be restored
  2811. ".stversions/existing~20171210-040404", // exists, should expect to be archived.
  2812. ".stversions/something~20171210-040404", // will become directory, hence error
  2813. ".stversions/dir/file~20171210-040404.txt",
  2814. ".stversions/dir/file~20171210-040405.txt",
  2815. ".stversions/dir/file~20171210-040406.txt",
  2816. ".stversions/very/very/deep/one~20171210-040406.txt", // lives deep down, no directory exists.
  2817. ".stversions/dir/existing~20171210-040406.txt", // exists, should expect to be archived.
  2818. ".stversions/dir/file.txt~20171210-040405", // incorrect tag format, ignored.
  2819. ".stversions/dir/cat", // incorrect tag format, ignored.
  2820. // "file.txt" will be restored
  2821. "existing",
  2822. "something/file", // Becomes directory
  2823. "dir/file.txt",
  2824. "dir/existing.txt",
  2825. } {
  2826. if runtime.GOOS == "windows" {
  2827. file = filepath.FromSlash(file)
  2828. }
  2829. dir := filepath.Dir(file)
  2830. if err := filesystem.MkdirAll(dir, 0755); err != nil {
  2831. t.Fatal(err)
  2832. }
  2833. if fd, err := filesystem.Create(file); err != nil {
  2834. t.Fatal(err)
  2835. } else if _, err := fd.Write([]byte(file)); err != nil {
  2836. t.Fatal(err)
  2837. } else if err := fd.Close(); err != nil {
  2838. t.Fatal(err)
  2839. } else if err := filesystem.Chtimes(file, sentinel, sentinel); err != nil {
  2840. t.Fatal(err)
  2841. }
  2842. }
  2843. versions, err := m.GetFolderVersions("default")
  2844. if err != nil {
  2845. t.Fatal(err)
  2846. }
  2847. expectedVersions := map[string]int{
  2848. "file.txt": 1,
  2849. "existing": 1,
  2850. "something": 1,
  2851. "dir/file.txt": 3,
  2852. "dir/existing.txt": 1,
  2853. "very/very/deep/one.txt": 1,
  2854. }
  2855. for name, vers := range versions {
  2856. cnt, ok := expectedVersions[name]
  2857. if !ok {
  2858. t.Errorf("unexpected %s", name)
  2859. }
  2860. if len(vers) != cnt {
  2861. t.Errorf("%s: %d != %d", name, cnt, len(vers))
  2862. }
  2863. // Delete, so we can check if we didn't hit something we expect afterwards.
  2864. delete(expectedVersions, name)
  2865. }
  2866. for name := range expectedVersions {
  2867. t.Errorf("not found expected %s", name)
  2868. }
  2869. // Restoring non existing folder fails.
  2870. _, err = m.RestoreFolderVersions("does not exist", nil)
  2871. if err == nil {
  2872. t.Errorf("expected an error")
  2873. }
  2874. makeTime := func(s string) time.Time {
  2875. tm, err := time.ParseInLocation(versioner.TimeFormat, s, locationLocal)
  2876. if err != nil {
  2877. t.Error(err)
  2878. }
  2879. return tm.Truncate(time.Second)
  2880. }
  2881. restore := map[string]time.Time{
  2882. "file.txt": makeTime("20171210-040404"),
  2883. "existing": makeTime("20171210-040404"),
  2884. "something": makeTime("20171210-040404"),
  2885. "dir/file.txt": makeTime("20171210-040406"),
  2886. "dir/existing.txt": makeTime("20171210-040406"),
  2887. "very/very/deep/one.txt": makeTime("20171210-040406"),
  2888. }
  2889. ferr, err := m.RestoreFolderVersions("default", restore)
  2890. if err != nil {
  2891. t.Fatal(err)
  2892. }
  2893. if err, ok := ferr["something"]; len(ferr) > 1 || !ok || err != "cannot replace a non-file" {
  2894. t.Fatalf("incorrect error or count: %d %s", len(ferr), ferr)
  2895. }
  2896. // Failed items are not expected to be restored.
  2897. // Remove them from expectations
  2898. for name := range ferr {
  2899. delete(restore, name)
  2900. }
  2901. // Check that content of files matches to the version they've been restored.
  2902. for file, version := range restore {
  2903. if runtime.GOOS == "windows" {
  2904. file = filepath.FromSlash(file)
  2905. }
  2906. tag := version.In(locationLocal).Truncate(time.Second).Format(versioner.TimeFormat)
  2907. taggedName := filepath.Join(".stversions", versioner.TagFilename(file, tag))
  2908. fd, err := filesystem.Open(file)
  2909. if err != nil {
  2910. t.Error(err)
  2911. }
  2912. defer fd.Close()
  2913. content, err := ioutil.ReadAll(fd)
  2914. if err != nil {
  2915. t.Error(err)
  2916. }
  2917. if !bytes.Equal(content, []byte(taggedName)) {
  2918. t.Errorf("%s: %s != %s", file, string(content), taggedName)
  2919. }
  2920. }
  2921. // Simple versioner uses modtime for timestamp generation, so we can check
  2922. // if existing stuff was correctly archived as we restored.
  2923. expectArchived := map[string]struct{}{
  2924. "existing": {},
  2925. "dir/file.txt": {},
  2926. "dir/existing.txt": {},
  2927. }
  2928. // Even if they are at the archived path, content should have the non
  2929. // archived name.
  2930. for file := range expectArchived {
  2931. if runtime.GOOS == "windows" {
  2932. file = filepath.FromSlash(file)
  2933. }
  2934. taggedName := versioner.TagFilename(file, sentinelTag)
  2935. taggedArchivedName := filepath.Join(".stversions", taggedName)
  2936. fd, err := filesystem.Open(taggedArchivedName)
  2937. if err != nil {
  2938. t.Fatal(err)
  2939. }
  2940. defer fd.Close()
  2941. content, err := ioutil.ReadAll(fd)
  2942. if err != nil {
  2943. t.Error(err)
  2944. }
  2945. if !bytes.Equal(content, []byte(file)) {
  2946. t.Errorf("%s: %s != %s", file, string(content), file)
  2947. }
  2948. }
  2949. // Check for other unexpected things that are tagged.
  2950. filesystem.Walk(".", func(path string, f fs.FileInfo, err error) error {
  2951. if !f.IsRegular() {
  2952. return nil
  2953. }
  2954. if strings.Contains(path, sentinelTag) {
  2955. path = osutil.NormalizedFilename(path)
  2956. name, _ := versioner.UntagFilename(path)
  2957. name = strings.TrimPrefix(name, ".stversions/")
  2958. if _, ok := expectArchived[name]; !ok {
  2959. t.Errorf("unexpected file with sentinel tag: %s", name)
  2960. }
  2961. }
  2962. return nil
  2963. })
  2964. }
  2965. func TestPausedFolders(t *testing.T) {
  2966. // Create a separate wrapper not to pollute other tests.
  2967. wrapper := createTmpWrapper(defaultCfgWrapper.RawCopy())
  2968. defer os.Remove(wrapper.ConfigPath())
  2969. m := setupModel(wrapper)
  2970. defer m.Stop()
  2971. if err := m.ScanFolder("default"); err != nil {
  2972. t.Error(err)
  2973. }
  2974. pausedConfig := wrapper.RawCopy()
  2975. pausedConfig.Folders[0].Paused = true
  2976. w, err := m.cfg.Replace(pausedConfig)
  2977. if err != nil {
  2978. t.Fatal(err)
  2979. }
  2980. w.Wait()
  2981. if err := m.ScanFolder("default"); err != ErrFolderPaused {
  2982. t.Errorf("Expected folder paused error, received: %v", err)
  2983. }
  2984. if err := m.ScanFolder("nonexistent"); err != errFolderMissing {
  2985. t.Errorf("Expected missing folder error, received: %v", err)
  2986. }
  2987. }
  2988. func TestIssue4094(t *testing.T) {
  2989. testOs := &fatalOs{t}
  2990. db := db.OpenMemory()
  2991. // Create a separate wrapper not to pollute other tests.
  2992. wrapper := createTmpWrapper(config.Configuration{})
  2993. defer os.Remove(wrapper.ConfigPath())
  2994. m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
  2995. m.ServeBackground()
  2996. defer m.Stop()
  2997. // Force the model to wire itself and add the folders
  2998. folderPath := "nonexistent"
  2999. defer testOs.RemoveAll(folderPath)
  3000. cfg := defaultCfgWrapper.RawCopy()
  3001. fcfg := config.FolderConfiguration{
  3002. ID: "folder1",
  3003. Path: folderPath,
  3004. Paused: true,
  3005. Devices: []config.FolderDeviceConfiguration{
  3006. {DeviceID: device1},
  3007. },
  3008. }
  3009. cfg.Folders = []config.FolderConfiguration{fcfg}
  3010. p, err := wrapper.Replace(cfg)
  3011. if err != nil {
  3012. t.Fatal(err)
  3013. }
  3014. p.Wait()
  3015. if err := m.SetIgnores(fcfg.ID, []string{"foo"}); err != nil {
  3016. t.Fatalf("failed setting ignores: %v", err)
  3017. }
  3018. if _, err := fcfg.Filesystem().Lstat(".stignore"); err != nil {
  3019. t.Fatalf("failed stating .stignore: %v", err)
  3020. }
  3021. }
  3022. func TestIssue4903(t *testing.T) {
  3023. testOs := &fatalOs{t}
  3024. db := db.OpenMemory()
  3025. // Create a separate wrapper not to pollute other tests.
  3026. wrapper := createTmpWrapper(config.Configuration{})
  3027. defer os.Remove(wrapper.ConfigPath())
  3028. m := newModel(wrapper, myID, "syncthing", "dev", db, nil)
  3029. m.ServeBackground()
  3030. defer m.Stop()
  3031. // Force the model to wire itself and add the folders
  3032. folderPath := "nonexistent"
  3033. defer testOs.RemoveAll(folderPath)
  3034. cfg := defaultCfgWrapper.RawCopy()
  3035. fcfg := config.FolderConfiguration{
  3036. ID: "folder1",
  3037. Path: folderPath,
  3038. Paused: true,
  3039. Devices: []config.FolderDeviceConfiguration{
  3040. {DeviceID: device1},
  3041. },
  3042. }
  3043. cfg.Folders = []config.FolderConfiguration{fcfg}
  3044. p, err := wrapper.Replace(cfg)
  3045. if err != nil {
  3046. t.Fatal(err)
  3047. }
  3048. p.Wait()
  3049. if err := fcfg.CheckPath(); err != config.ErrPathMissing {
  3050. t.Fatalf("expected path missing error, got: %v", err)
  3051. }
  3052. if _, err := fcfg.Filesystem().Lstat("."); !fs.IsNotExist(err) {
  3053. t.Fatalf("Expected missing path error, got: %v", err)
  3054. }
  3055. }
  3056. func TestIssue5002(t *testing.T) {
  3057. // recheckFile should not panic when given an index equal to the number of blocks
  3058. m := setupModel(defaultCfgWrapper)
  3059. defer m.Stop()
  3060. if err := m.ScanFolder("default"); err != nil {
  3061. t.Error(err)
  3062. }
  3063. file, ok := m.CurrentFolderFile("default", "foo")
  3064. if !ok {
  3065. t.Fatal("test file should exist")
  3066. }
  3067. nBlocks := len(file.Blocks)
  3068. m.recheckFile(protocol.LocalDeviceID, defaultFolderConfig.Filesystem(), "default", "foo", nBlocks-1, []byte{1, 2, 3, 4})
  3069. m.recheckFile(protocol.LocalDeviceID, defaultFolderConfig.Filesystem(), "default", "foo", nBlocks, []byte{1, 2, 3, 4}) // panic
  3070. m.recheckFile(protocol.LocalDeviceID, defaultFolderConfig.Filesystem(), "default", "foo", nBlocks+1, []byte{1, 2, 3, 4})
  3071. }
  3072. func TestParentOfUnignored(t *testing.T) {
  3073. wcfg, m := newState(defaultCfg)
  3074. defer func() {
  3075. m.Stop()
  3076. defaultFolderConfig.Filesystem().Remove(".stignore")
  3077. os.Remove(wcfg.ConfigPath())
  3078. }()
  3079. m.SetIgnores("default", []string{"!quux", "*"})
  3080. if parent, ok := m.CurrentFolderFile("default", "baz"); !ok {
  3081. t.Errorf(`Directory "baz" missing in db`)
  3082. } else if parent.IsIgnored() {
  3083. t.Errorf(`Directory "baz" is ignored`)
  3084. }
  3085. }
  3086. func addFakeConn(m *model, dev protocol.DeviceID) *fakeConnection {
  3087. fc := &fakeConnection{id: dev, model: m}
  3088. m.AddConnection(fc, protocol.HelloResult{})
  3089. m.ClusterConfig(dev, protocol.ClusterConfig{
  3090. Folders: []protocol.Folder{
  3091. {
  3092. ID: "default",
  3093. Devices: []protocol.Device{
  3094. {ID: myID},
  3095. {ID: device1},
  3096. },
  3097. },
  3098. },
  3099. })
  3100. return fc
  3101. }
  3102. // TestFolderRestartZombies reproduces issue 5233, where multiple concurrent folder
  3103. // restarts would leave more than one folder runner alive.
  3104. func TestFolderRestartZombies(t *testing.T) {
  3105. wrapper := createTmpWrapper(defaultCfg.Copy())
  3106. defer os.Remove(wrapper.ConfigPath())
  3107. folderCfg, _ := wrapper.Folder("default")
  3108. folderCfg.FilesystemType = fs.FilesystemTypeFake
  3109. wrapper.SetFolder(folderCfg)
  3110. m := setupModel(wrapper)
  3111. defer m.Stop()
  3112. // Make sure the folder is up and running, because we want to count it.
  3113. m.ScanFolder("default")
  3114. // Check how many running folders we have running before the test.
  3115. if r := atomic.LoadInt32(&m.foldersRunning); r != 1 {
  3116. t.Error("Expected one running folder, not", r)
  3117. }
  3118. // Run a few parallel configuration changers for one second. Each waits
  3119. // for the commit to complete, but there are many of them.
  3120. var wg sync.WaitGroup
  3121. for i := 0; i < 25; i++ {
  3122. wg.Add(1)
  3123. go func() {
  3124. defer wg.Done()
  3125. t0 := time.Now()
  3126. for time.Since(t0) < time.Second {
  3127. cfg := folderCfg.Copy()
  3128. cfg.MaxConflicts = rand.Int() // safe change that should cause a folder restart
  3129. w, err := wrapper.SetFolder(cfg)
  3130. if err != nil {
  3131. panic(err)
  3132. }
  3133. w.Wait()
  3134. }
  3135. }()
  3136. }
  3137. // Wait for the above to complete and check how many folders we have
  3138. // running now. It should not have increased.
  3139. wg.Wait()
  3140. // Make sure the folder is up and running, because we want to count it.
  3141. m.ScanFolder("default")
  3142. if r := atomic.LoadInt32(&m.foldersRunning); r != 1 {
  3143. t.Error("Expected one running folder, not", r)
  3144. }
  3145. }
  3146. type fakeAddr struct{}
  3147. func (fakeAddr) Network() string {
  3148. return "network"
  3149. }
  3150. func (fakeAddr) String() string {
  3151. return "address"
  3152. }
  3153. type alwaysChangedKey struct {
  3154. fs fs.Filesystem
  3155. name string
  3156. }
  3157. // alwaysChanges is an ignore.ChangeDetector that always returns true on Changed()
  3158. type alwaysChanged struct {
  3159. seen map[alwaysChangedKey]struct{}
  3160. }
  3161. func newAlwaysChanged() *alwaysChanged {
  3162. return &alwaysChanged{
  3163. seen: make(map[alwaysChangedKey]struct{}),
  3164. }
  3165. }
  3166. func (c *alwaysChanged) Remember(fs fs.Filesystem, name string, _ time.Time) {
  3167. c.seen[alwaysChangedKey{fs, name}] = struct{}{}
  3168. }
  3169. func (c *alwaysChanged) Reset() {
  3170. c.seen = make(map[alwaysChangedKey]struct{})
  3171. }
  3172. func (c *alwaysChanged) Seen(fs fs.Filesystem, name string) bool {
  3173. _, ok := c.seen[alwaysChangedKey{fs, name}]
  3174. return ok
  3175. }
  3176. func (c *alwaysChanged) Changed() bool {
  3177. return true
  3178. }
  3179. func TestRequestLimit(t *testing.T) {
  3180. wrapper := createTmpWrapper(defaultCfg.Copy())
  3181. defer os.Remove(wrapper.ConfigPath())
  3182. dev, _ := wrapper.Device(device1)
  3183. dev.MaxRequestKiB = 1
  3184. wrapper.SetDevice(dev)
  3185. m, _ := setupModelWithConnectionFromWrapper(wrapper)
  3186. defer m.Stop()
  3187. file := "tmpfile"
  3188. befReq := time.Now()
  3189. first, err := m.Request(device1, "default", file, 2000, 0, nil, 0, false)
  3190. if err != nil {
  3191. t.Fatalf("First request failed: %v", err)
  3192. }
  3193. reqDur := time.Since(befReq)
  3194. returned := make(chan struct{})
  3195. go func() {
  3196. second, err := m.Request(device1, "default", file, 2000, 0, nil, 0, false)
  3197. if err != nil {
  3198. t.Fatalf("Second request failed: %v", err)
  3199. }
  3200. close(returned)
  3201. second.Close()
  3202. }()
  3203. time.Sleep(10 * reqDur)
  3204. select {
  3205. case <-returned:
  3206. t.Fatalf("Second request returned before first was done")
  3207. default:
  3208. }
  3209. first.Close()
  3210. select {
  3211. case <-returned:
  3212. case <-time.After(time.Second):
  3213. t.Fatalf("Second request did not return after first was done")
  3214. }
  3215. }
  3216. func TestSanitizePath(t *testing.T) {
  3217. cases := [][2]string{
  3218. {"", ""},
  3219. {"foo", "foo"},
  3220. {`\*/foo\?/bar[{!@$%^&*#()}]`, "foo bar ()"},
  3221. {"Räksmörgås", "Räksmörgås"},
  3222. {`Räk \/ smörgås`, "Räk smörgås"},
  3223. {"هذا هو *\x07?اسم الملف", "هذا هو اسم الملف"},
  3224. {`../foo.txt`, `.. foo.txt`},
  3225. }
  3226. for _, tc := range cases {
  3227. res := sanitizePath(tc[0])
  3228. if res != tc[1] {
  3229. t.Errorf("sanitizePath(%q) => %q, expected %q", tc[0], res, tc[1])
  3230. }
  3231. }
  3232. }