model_test.go 93 KB

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