model_test.go 106 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144
  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. "errors"
  12. "fmt"
  13. "io"
  14. mrand "math/rand"
  15. "os"
  16. "path/filepath"
  17. "runtime/pprof"
  18. "sort"
  19. "strconv"
  20. "strings"
  21. "sync"
  22. "testing"
  23. "time"
  24. "github.com/syncthing/syncthing/lib/build"
  25. "github.com/syncthing/syncthing/lib/config"
  26. "github.com/syncthing/syncthing/lib/db"
  27. "github.com/syncthing/syncthing/lib/db/backend"
  28. "github.com/syncthing/syncthing/lib/events"
  29. "github.com/syncthing/syncthing/lib/fs"
  30. "github.com/syncthing/syncthing/lib/ignore"
  31. "github.com/syncthing/syncthing/lib/osutil"
  32. "github.com/syncthing/syncthing/lib/protocol"
  33. protocolmocks "github.com/syncthing/syncthing/lib/protocol/mocks"
  34. srand "github.com/syncthing/syncthing/lib/rand"
  35. "github.com/syncthing/syncthing/lib/semaphore"
  36. "github.com/syncthing/syncthing/lib/testutil"
  37. "github.com/syncthing/syncthing/lib/versioner"
  38. )
  39. func newState(t testing.TB, cfg config.Configuration) (*testModel, context.CancelFunc) {
  40. wcfg, cancel := newConfigWrapper(cfg)
  41. m := setupModel(t, wcfg)
  42. for _, dev := range cfg.Devices {
  43. m.AddConnection(newFakeConnection(dev.DeviceID, m), protocol.Hello{})
  44. }
  45. return m, cancel
  46. }
  47. func createClusterConfig(remote protocol.DeviceID, ids ...string) protocol.ClusterConfig {
  48. cc := protocol.ClusterConfig{
  49. Folders: make([]protocol.Folder, len(ids)),
  50. }
  51. for i, id := range ids {
  52. cc.Folders[i] = protocol.Folder{
  53. ID: id,
  54. Label: id,
  55. }
  56. }
  57. return addFolderDevicesToClusterConfig(cc, remote)
  58. }
  59. func addFolderDevicesToClusterConfig(cc protocol.ClusterConfig, remote protocol.DeviceID) protocol.ClusterConfig {
  60. for i := range cc.Folders {
  61. cc.Folders[i].Devices = []protocol.Device{
  62. {ID: myID},
  63. {ID: remote},
  64. }
  65. }
  66. return cc
  67. }
  68. func TestRequest(t *testing.T) {
  69. wrapper, fcfg, cancel := newDefaultCfgWrapper()
  70. ffs := fcfg.Filesystem(nil)
  71. defer cancel()
  72. m := setupModel(t, wrapper)
  73. defer cleanupModel(m)
  74. fd, err := ffs.Create("foo")
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. if _, err := fd.Write([]byte("foobar")); err != nil {
  79. t.Fatal(err)
  80. }
  81. fd.Close()
  82. m.ScanFolder("default")
  83. // Existing, shared file
  84. res, err := m.Request(device1Conn, "default", "foo", 0, 6, 0, nil, 0, false)
  85. if err != nil {
  86. t.Fatal(err)
  87. }
  88. bs := res.Data()
  89. if !bytes.Equal(bs, []byte("foobar")) {
  90. t.Errorf("Incorrect data from request: %q", string(bs))
  91. }
  92. // Existing, nonshared file
  93. _, err = m.Request(device2Conn, "default", "foo", 0, 6, 0, nil, 0, false)
  94. if err == nil {
  95. t.Error("Unexpected nil error on insecure file read")
  96. }
  97. // Nonexistent file
  98. _, err = m.Request(device1Conn, "default", "nonexistent", 0, 6, 0, nil, 0, false)
  99. if err == nil {
  100. t.Error("Unexpected nil error on insecure file read")
  101. }
  102. // Shared folder, but disallowed file name
  103. _, err = m.Request(device1Conn, "default", "../walk.go", 0, 6, 0, nil, 0, false)
  104. if err == nil {
  105. t.Error("Unexpected nil error on insecure file read")
  106. }
  107. // Negative offset
  108. _, err = m.Request(device1Conn, "default", "foo", 0, -4, 0, nil, 0, false)
  109. if err == nil {
  110. t.Error("Unexpected nil error on insecure file read")
  111. }
  112. // Larger block than available
  113. _, err = m.Request(device1Conn, "default", "foo", 0, 42, 0, []byte("hash necessary but not checked"), 0, false)
  114. if err == nil {
  115. t.Error("Unexpected nil error on read past end of file")
  116. }
  117. _, err = m.Request(device1Conn, "default", "foo", 0, 42, 0, nil, 0, false)
  118. if err != nil {
  119. t.Error("Unexpected error when large read should be permitted")
  120. }
  121. }
  122. func genFiles(n int) []protocol.FileInfo {
  123. files := make([]protocol.FileInfo, n)
  124. t := time.Now().Unix()
  125. for i := 0; i < n; i++ {
  126. files[i] = protocol.FileInfo{
  127. Name: fmt.Sprintf("file%d", i),
  128. ModifiedS: t,
  129. Sequence: int64(i + 1),
  130. Blocks: []protocol.BlockInfo{{Offset: 0, Size: 100, Hash: []byte("some hash bytes")}},
  131. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}},
  132. }
  133. }
  134. return files
  135. }
  136. func BenchmarkIndex_10000(b *testing.B) {
  137. benchmarkIndex(b, 10000)
  138. }
  139. func BenchmarkIndex_100(b *testing.B) {
  140. benchmarkIndex(b, 100)
  141. }
  142. func benchmarkIndex(b *testing.B, nfiles int) {
  143. m, _, fcfg, wcfgCancel := setupModelWithConnection(b)
  144. defer wcfgCancel()
  145. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  146. files := genFiles(nfiles)
  147. must(b, m.Index(device1Conn, fcfg.ID, files))
  148. b.ResetTimer()
  149. for i := 0; i < b.N; i++ {
  150. must(b, m.Index(device1Conn, fcfg.ID, files))
  151. }
  152. b.ReportAllocs()
  153. }
  154. func BenchmarkIndexUpdate_10000_10000(b *testing.B) {
  155. benchmarkIndexUpdate(b, 10000, 10000)
  156. }
  157. func BenchmarkIndexUpdate_10000_100(b *testing.B) {
  158. benchmarkIndexUpdate(b, 10000, 100)
  159. }
  160. func BenchmarkIndexUpdate_10000_1(b *testing.B) {
  161. benchmarkIndexUpdate(b, 10000, 1)
  162. }
  163. func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
  164. m, _, fcfg, wcfgCancel := setupModelWithConnection(b)
  165. defer wcfgCancel()
  166. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  167. files := genFiles(nfiles)
  168. ufiles := genFiles(nufiles)
  169. must(b, m.Index(device1Conn, fcfg.ID, files))
  170. b.ResetTimer()
  171. for i := 0; i < b.N; i++ {
  172. must(b, m.IndexUpdate(device1Conn, fcfg.ID, ufiles))
  173. }
  174. b.ReportAllocs()
  175. }
  176. func BenchmarkRequestOut(b *testing.B) {
  177. m := setupModel(b, defaultCfgWrapper)
  178. defer cleanupModel(m)
  179. const n = 1000
  180. files := genFiles(n)
  181. fc := newFakeConnection(device1, m)
  182. for _, f := range files {
  183. fc.addFile(f.Name, 0o644, protocol.FileInfoTypeFile, []byte("some data to return"))
  184. }
  185. m.AddConnection(fc, protocol.Hello{})
  186. must(b, m.Index(device1Conn, "default", files))
  187. b.ResetTimer()
  188. for i := 0; i < b.N; i++ {
  189. data, err := m.requestGlobal(context.Background(), device1, "default", files[i%n].Name, 0, 0, 32, nil, 0, false)
  190. if err != nil {
  191. b.Error(err)
  192. }
  193. if data == nil {
  194. b.Error("nil data")
  195. }
  196. }
  197. }
  198. func BenchmarkRequestInSingleFile(b *testing.B) {
  199. w, cancel := newConfigWrapper(defaultCfg)
  200. defer cancel()
  201. ffs := w.FolderList()[0].Filesystem(nil)
  202. m := setupModel(b, w)
  203. defer cleanupModel(m)
  204. buf := make([]byte, 128<<10)
  205. srand.Read(buf)
  206. must(b, ffs.MkdirAll("request/for/a/file/in/a/couple/of/dirs", 0o755))
  207. writeFile(b, ffs, "request/for/a/file/in/a/couple/of/dirs/128k", buf)
  208. b.ResetTimer()
  209. for i := 0; i < b.N; i++ {
  210. if _, err := m.Request(device1Conn, "default", "request/for/a/file/in/a/couple/of/dirs/128k", 0, 128<<10, 0, nil, 0, false); err != nil {
  211. b.Error(err)
  212. }
  213. }
  214. b.SetBytes(128 << 10)
  215. }
  216. func TestDeviceRename(t *testing.T) {
  217. hello := protocol.Hello{
  218. ClientName: "syncthing",
  219. ClientVersion: "v0.9.4",
  220. }
  221. rawCfg := config.New(device1)
  222. rawCfg.Devices = []config.DeviceConfiguration{
  223. {
  224. DeviceID: device1,
  225. },
  226. }
  227. cfg, cfgCancel := newConfigWrapper(rawCfg)
  228. defer cfgCancel()
  229. m := newModel(t, cfg, myID, nil)
  230. if cfg.Devices()[device1].Name != "" {
  231. t.Errorf("Device already has a name")
  232. }
  233. conn := newFakeConnection(device1, m)
  234. m.AddConnection(conn, hello)
  235. m.ServeBackground()
  236. defer cleanupModel(m)
  237. if cfg.Devices()[device1].Name != "" {
  238. t.Errorf("Device already has a name")
  239. }
  240. m.Closed(conn, protocol.ErrTimeout)
  241. hello.DeviceName = "tester"
  242. m.AddConnection(conn, hello)
  243. if cfg.Devices()[device1].Name != "tester" {
  244. t.Errorf("Device did not get a name")
  245. }
  246. m.Closed(conn, protocol.ErrTimeout)
  247. hello.DeviceName = "tester2"
  248. m.AddConnection(conn, hello)
  249. if cfg.Devices()[device1].Name != "tester" {
  250. t.Errorf("Device name got overwritten")
  251. }
  252. ffs := fs.NewFilesystem(fs.FilesystemTypeFake, srand.String(32)+"?content=true")
  253. path := "someConfigfile"
  254. must(t, saveConfig(ffs, path, cfg.RawCopy()))
  255. cfgw, _, err := loadConfig(ffs, path, myID, events.NoopLogger)
  256. if err != nil {
  257. t.Error(err)
  258. return
  259. }
  260. if cfgw.Devices()[device1].Name != "tester" {
  261. t.Errorf("Device name not saved in config")
  262. }
  263. m.Closed(conn, protocol.ErrTimeout)
  264. waiter, err := cfg.Modify(func(cfg *config.Configuration) {
  265. cfg.Options.OverwriteRemoteDevNames = true
  266. })
  267. must(t, err)
  268. waiter.Wait()
  269. hello.DeviceName = "tester2"
  270. m.AddConnection(conn, hello)
  271. if cfg.Devices()[device1].Name != "tester2" {
  272. t.Errorf("Device name not overwritten")
  273. }
  274. }
  275. // Adjusted copy of the original function for testing purposes
  276. func saveConfig(ffs fs.Filesystem, path string, cfg config.Configuration) error {
  277. fd, err := ffs.Create(path)
  278. if err != nil {
  279. l.Debugln("Create:", err)
  280. return err
  281. }
  282. if err := cfg.WriteXML(osutil.LineEndingsWriter(fd)); err != nil {
  283. l.Debugln("WriteXML:", err)
  284. fd.Close()
  285. return err
  286. }
  287. if err := fd.Close(); err != nil {
  288. l.Debugln("Close:", err)
  289. return err
  290. }
  291. if _, err := ffs.Lstat(path); err != nil {
  292. return err
  293. }
  294. return nil
  295. }
  296. // Adjusted copy of the original function for testing purposes
  297. func loadConfig(ffs fs.Filesystem, path string, myID protocol.DeviceID, evLogger events.Logger) (config.Wrapper, int, error) {
  298. if _, err := ffs.Lstat(path); err != nil {
  299. return nil, 0, err
  300. }
  301. fd, err := ffs.OpenFile(path, fs.OptReadWrite, 0o666)
  302. if err != nil {
  303. return nil, 0, err
  304. }
  305. defer fd.Close()
  306. cfg, originalVersion, err := config.ReadXML(fd, myID)
  307. if err != nil {
  308. return nil, 0, err
  309. }
  310. return config.Wrap(path, cfg, myID, evLogger), originalVersion, nil
  311. }
  312. func TestClusterConfig(t *testing.T) {
  313. cfg := config.New(device1)
  314. cfg.Options.MinHomeDiskFree.Value = 0 // avoids unnecessary free space checks
  315. cfg.Devices = []config.DeviceConfiguration{
  316. {
  317. DeviceID: device1,
  318. Introducer: true,
  319. },
  320. {
  321. DeviceID: device2,
  322. },
  323. }
  324. cfg.Folders = []config.FolderConfiguration{
  325. {
  326. FilesystemType: fs.FilesystemTypeFake,
  327. ID: "folder1",
  328. Path: "testdata1",
  329. Devices: []config.FolderDeviceConfiguration{
  330. {DeviceID: device1},
  331. {DeviceID: device2},
  332. },
  333. },
  334. {
  335. FilesystemType: fs.FilesystemTypeFake,
  336. ID: "folder2",
  337. Path: "testdata2",
  338. Paused: true, // should still be included
  339. Devices: []config.FolderDeviceConfiguration{
  340. {DeviceID: device1},
  341. {DeviceID: device2},
  342. },
  343. },
  344. {
  345. FilesystemType: fs.FilesystemTypeFake,
  346. ID: "folder3",
  347. Path: "testdata3",
  348. Devices: []config.FolderDeviceConfiguration{
  349. {DeviceID: device1},
  350. // should not be included, does not include device2
  351. },
  352. },
  353. }
  354. wrapper, cancel := newConfigWrapper(cfg)
  355. defer cancel()
  356. m := newModel(t, wrapper, myID, nil)
  357. m.ServeBackground()
  358. defer cleanupModel(m)
  359. cm, _ := m.generateClusterConfig(device2)
  360. if l := len(cm.Folders); l != 2 {
  361. t.Fatalf("Incorrect number of folders %d != 2", l)
  362. }
  363. r := cm.Folders[0]
  364. if r.ID != "folder1" {
  365. t.Errorf("Incorrect folder %q != folder1", r.ID)
  366. }
  367. if l := len(r.Devices); l != 2 {
  368. t.Errorf("Incorrect number of devices %d != 2", l)
  369. }
  370. if id := r.Devices[0].ID; id != device1 {
  371. t.Errorf("Incorrect device ID %s != %s", id, device1)
  372. }
  373. if !r.Devices[0].Introducer {
  374. t.Error("Device1 should be flagged as Introducer")
  375. }
  376. if id := r.Devices[1].ID; id != device2 {
  377. t.Errorf("Incorrect device ID %s != %s", id, device2)
  378. }
  379. if r.Devices[1].Introducer {
  380. t.Error("Device2 should not be flagged as Introducer")
  381. }
  382. r = cm.Folders[1]
  383. if r.ID != "folder2" {
  384. t.Errorf("Incorrect folder %q != folder2", r.ID)
  385. }
  386. if l := len(r.Devices); l != 2 {
  387. t.Errorf("Incorrect number of devices %d != 2", l)
  388. }
  389. if id := r.Devices[0].ID; id != device1 {
  390. t.Errorf("Incorrect device ID %s != %s", id, device1)
  391. }
  392. if !r.Devices[0].Introducer {
  393. t.Error("Device1 should be flagged as Introducer")
  394. }
  395. if id := r.Devices[1].ID; id != device2 {
  396. t.Errorf("Incorrect device ID %s != %s", id, device2)
  397. }
  398. if r.Devices[1].Introducer {
  399. t.Error("Device2 should not be flagged as Introducer")
  400. }
  401. }
  402. func TestIntroducer(t *testing.T) {
  403. var introducedByAnyone protocol.DeviceID
  404. // LocalDeviceID is a magic value meaning don't check introducer
  405. contains := func(cfg config.FolderConfiguration, id, introducedBy protocol.DeviceID) bool {
  406. for _, dev := range cfg.Devices {
  407. if dev.DeviceID.Equals(id) {
  408. if introducedBy.Equals(introducedByAnyone) {
  409. return true
  410. }
  411. return dev.IntroducedBy.Equals(introducedBy)
  412. }
  413. }
  414. return false
  415. }
  416. m, cancel := newState(t, config.Configuration{
  417. Version: config.CurrentVersion,
  418. Devices: []config.DeviceConfiguration{
  419. {
  420. DeviceID: device1,
  421. Introducer: true,
  422. },
  423. },
  424. Folders: []config.FolderConfiguration{
  425. {
  426. FilesystemType: fs.FilesystemTypeFake,
  427. ID: "folder1",
  428. Path: "testdata",
  429. Devices: []config.FolderDeviceConfiguration{
  430. {DeviceID: device1},
  431. },
  432. },
  433. {
  434. FilesystemType: fs.FilesystemTypeFake,
  435. ID: "folder2",
  436. Path: "testdata",
  437. Devices: []config.FolderDeviceConfiguration{
  438. {DeviceID: device1},
  439. },
  440. },
  441. },
  442. })
  443. cc := basicClusterConfig(myID, device1, "folder1", "folder2")
  444. cc.Folders[0].Devices = append(cc.Folders[0].Devices, protocol.Device{
  445. ID: device2,
  446. Introducer: true,
  447. SkipIntroductionRemovals: true,
  448. })
  449. cc.Folders[1].Devices = append(cc.Folders[1].Devices, protocol.Device{
  450. ID: device2,
  451. Introducer: true,
  452. SkipIntroductionRemovals: true,
  453. EncryptionPasswordToken: []byte("faketoken"),
  454. })
  455. m.ClusterConfig(device1Conn, cc)
  456. if newDev, ok := m.cfg.Device(device2); !ok || !newDev.Introducer || !newDev.SkipIntroductionRemovals {
  457. t.Error("device 2 missing or wrong flags")
  458. }
  459. if !contains(m.cfg.Folders()["folder1"], device2, device1) {
  460. t.Error("expected folder 1 to have device2 introduced by device 1")
  461. }
  462. for _, devCfg := range m.cfg.Folders()["folder2"].Devices {
  463. if devCfg.DeviceID == device2 {
  464. t.Error("Device was added even though it's untrusted")
  465. }
  466. }
  467. cleanupModel(m)
  468. cancel()
  469. m, cancel = newState(t, config.Configuration{
  470. Version: config.CurrentVersion,
  471. Devices: []config.DeviceConfiguration{
  472. {
  473. DeviceID: device1,
  474. Introducer: true,
  475. },
  476. {
  477. DeviceID: device2,
  478. IntroducedBy: device1,
  479. },
  480. },
  481. Folders: []config.FolderConfiguration{
  482. {
  483. FilesystemType: fs.FilesystemTypeFake,
  484. ID: "folder1",
  485. Path: "testdata",
  486. Devices: []config.FolderDeviceConfiguration{
  487. {DeviceID: device1},
  488. {DeviceID: device2, IntroducedBy: device1},
  489. },
  490. },
  491. {
  492. FilesystemType: fs.FilesystemTypeFake,
  493. ID: "folder2",
  494. Path: "testdata",
  495. Devices: []config.FolderDeviceConfiguration{
  496. {DeviceID: device1},
  497. },
  498. },
  499. },
  500. })
  501. cc = basicClusterConfig(myID, device1, "folder2")
  502. cc.Folders[0].Devices = append(cc.Folders[0].Devices, protocol.Device{
  503. ID: device2,
  504. Introducer: true,
  505. SkipIntroductionRemovals: true,
  506. })
  507. m.ClusterConfig(device1Conn, cc)
  508. // Should not get introducer, as it's already unset, and it's an existing device.
  509. if newDev, ok := m.cfg.Device(device2); !ok || newDev.Introducer || newDev.SkipIntroductionRemovals {
  510. t.Error("device 2 missing or changed flags")
  511. }
  512. if contains(m.cfg.Folders()["folder1"], device2, introducedByAnyone) {
  513. t.Error("expected device 2 to be removed from folder 1")
  514. }
  515. if !contains(m.cfg.Folders()["folder2"], device2, device1) {
  516. t.Error("expected device 2 to be added to folder 2")
  517. }
  518. cleanupModel(m)
  519. cancel()
  520. m, cancel = newState(t, config.Configuration{
  521. Version: config.CurrentVersion,
  522. Devices: []config.DeviceConfiguration{
  523. {
  524. DeviceID: device1,
  525. Introducer: true,
  526. },
  527. {
  528. DeviceID: device2,
  529. IntroducedBy: device1,
  530. },
  531. },
  532. Folders: []config.FolderConfiguration{
  533. {
  534. FilesystemType: fs.FilesystemTypeFake,
  535. ID: "folder1",
  536. Path: "testdata",
  537. Devices: []config.FolderDeviceConfiguration{
  538. {DeviceID: device1},
  539. {DeviceID: device2, IntroducedBy: device1},
  540. },
  541. },
  542. {
  543. FilesystemType: fs.FilesystemTypeFake,
  544. ID: "folder2",
  545. Path: "testdata",
  546. Devices: []config.FolderDeviceConfiguration{
  547. {DeviceID: device1},
  548. {DeviceID: device2, IntroducedBy: device1},
  549. },
  550. },
  551. },
  552. })
  553. m.ClusterConfig(device1Conn, protocol.ClusterConfig{})
  554. if _, ok := m.cfg.Device(device2); ok {
  555. t.Error("device 2 should have been removed")
  556. }
  557. if contains(m.cfg.Folders()["folder1"], device2, introducedByAnyone) {
  558. t.Error("expected device 2 to be removed from folder 1")
  559. }
  560. if contains(m.cfg.Folders()["folder2"], device2, introducedByAnyone) {
  561. t.Error("expected device 2 to be removed from folder 2")
  562. }
  563. // Two cases when removals should not happen
  564. // 1. Introducer flag no longer set on device
  565. cleanupModel(m)
  566. cancel()
  567. m, cancel = newState(t, config.Configuration{
  568. Version: config.CurrentVersion,
  569. Devices: []config.DeviceConfiguration{
  570. {
  571. DeviceID: device1,
  572. Introducer: false,
  573. },
  574. {
  575. DeviceID: device2,
  576. IntroducedBy: device1,
  577. },
  578. },
  579. Folders: []config.FolderConfiguration{
  580. {
  581. FilesystemType: fs.FilesystemTypeFake,
  582. ID: "folder1",
  583. Path: "testdata",
  584. Devices: []config.FolderDeviceConfiguration{
  585. {DeviceID: device1},
  586. {DeviceID: device2, IntroducedBy: device1},
  587. },
  588. },
  589. {
  590. FilesystemType: fs.FilesystemTypeFake,
  591. ID: "folder2",
  592. Path: "testdata",
  593. Devices: []config.FolderDeviceConfiguration{
  594. {DeviceID: device1},
  595. {DeviceID: device2, IntroducedBy: device1},
  596. },
  597. },
  598. },
  599. })
  600. m.ClusterConfig(device1Conn, protocol.ClusterConfig{})
  601. if _, ok := m.cfg.Device(device2); !ok {
  602. t.Error("device 2 should not have been removed")
  603. }
  604. if !contains(m.cfg.Folders()["folder1"], device2, device1) {
  605. t.Error("expected device 2 not to be removed from folder 1")
  606. }
  607. if !contains(m.cfg.Folders()["folder2"], device2, device1) {
  608. t.Error("expected device 2 not to be removed from folder 2")
  609. }
  610. // 2. SkipIntroductionRemovals is set
  611. cleanupModel(m)
  612. cancel()
  613. m, cancel = newState(t, config.Configuration{
  614. Version: config.CurrentVersion,
  615. Devices: []config.DeviceConfiguration{
  616. {
  617. DeviceID: device1,
  618. Introducer: true,
  619. SkipIntroductionRemovals: true,
  620. },
  621. {
  622. DeviceID: device2,
  623. IntroducedBy: device1,
  624. },
  625. },
  626. Folders: []config.FolderConfiguration{
  627. {
  628. FilesystemType: fs.FilesystemTypeFake,
  629. ID: "folder1",
  630. Path: "testdata",
  631. Devices: []config.FolderDeviceConfiguration{
  632. {DeviceID: device1},
  633. {DeviceID: device2, IntroducedBy: device1},
  634. },
  635. },
  636. {
  637. FilesystemType: fs.FilesystemTypeFake,
  638. ID: "folder2",
  639. Path: "testdata",
  640. Devices: []config.FolderDeviceConfiguration{
  641. {DeviceID: device1},
  642. },
  643. },
  644. },
  645. })
  646. cc = basicClusterConfig(myID, device1, "folder2")
  647. cc.Folders[0].Devices = append(cc.Folders[0].Devices, protocol.Device{
  648. ID: device2,
  649. Introducer: true,
  650. SkipIntroductionRemovals: true,
  651. })
  652. m.ClusterConfig(device1Conn, cc)
  653. if _, ok := m.cfg.Device(device2); !ok {
  654. t.Error("device 2 should not have been removed")
  655. }
  656. if !contains(m.cfg.Folders()["folder1"], device2, device1) {
  657. t.Error("expected device 2 not to be removed from folder 1")
  658. }
  659. if !contains(m.cfg.Folders()["folder2"], device2, device1) {
  660. t.Error("expected device 2 not to be added to folder 2")
  661. }
  662. // Test device not being removed as it's shared without an introducer.
  663. cleanupModel(m)
  664. cancel()
  665. m, cancel = newState(t, config.Configuration{
  666. Version: config.CurrentVersion,
  667. Devices: []config.DeviceConfiguration{
  668. {
  669. DeviceID: device1,
  670. Introducer: true,
  671. },
  672. {
  673. DeviceID: device2,
  674. IntroducedBy: device1,
  675. },
  676. },
  677. Folders: []config.FolderConfiguration{
  678. {
  679. FilesystemType: fs.FilesystemTypeFake,
  680. ID: "folder1",
  681. Path: "testdata",
  682. Devices: []config.FolderDeviceConfiguration{
  683. {DeviceID: device1},
  684. {DeviceID: device2, IntroducedBy: device1},
  685. },
  686. },
  687. {
  688. FilesystemType: fs.FilesystemTypeFake,
  689. ID: "folder2",
  690. Path: "testdata",
  691. Devices: []config.FolderDeviceConfiguration{
  692. {DeviceID: device1},
  693. {DeviceID: device2},
  694. },
  695. },
  696. },
  697. })
  698. m.ClusterConfig(device1Conn, protocol.ClusterConfig{})
  699. if _, ok := m.cfg.Device(device2); !ok {
  700. t.Error("device 2 should not have been removed")
  701. }
  702. if contains(m.cfg.Folders()["folder1"], device2, introducedByAnyone) {
  703. t.Error("expected device 2 to be removed from folder 1")
  704. }
  705. if !contains(m.cfg.Folders()["folder2"], device2, introducedByAnyone) {
  706. t.Error("expected device 2 not to be removed from folder 2")
  707. }
  708. // Test device not being removed as it's shared by a different introducer.
  709. cleanupModel(m)
  710. cancel()
  711. m, cancel = newState(t, config.Configuration{
  712. Version: config.CurrentVersion,
  713. Devices: []config.DeviceConfiguration{
  714. {
  715. DeviceID: device1,
  716. Introducer: true,
  717. },
  718. {
  719. DeviceID: device2,
  720. IntroducedBy: device1,
  721. },
  722. },
  723. Folders: []config.FolderConfiguration{
  724. {
  725. FilesystemType: fs.FilesystemTypeFake,
  726. ID: "folder1",
  727. Path: "testdata",
  728. Devices: []config.FolderDeviceConfiguration{
  729. {DeviceID: device1},
  730. {DeviceID: device2, IntroducedBy: device1},
  731. },
  732. },
  733. {
  734. FilesystemType: fs.FilesystemTypeFake,
  735. ID: "folder2",
  736. Path: "testdata",
  737. Devices: []config.FolderDeviceConfiguration{
  738. {DeviceID: device1},
  739. {DeviceID: device2, IntroducedBy: myID},
  740. },
  741. },
  742. },
  743. })
  744. defer cleanupModel(m)
  745. defer cancel()
  746. m.ClusterConfig(device1Conn, protocol.ClusterConfig{})
  747. if _, ok := m.cfg.Device(device2); !ok {
  748. t.Error("device 2 should not have been removed")
  749. }
  750. if contains(m.cfg.Folders()["folder1"], device2, introducedByAnyone) {
  751. t.Error("expected device 2 to be removed from folder 1")
  752. }
  753. if !contains(m.cfg.Folders()["folder2"], device2, introducedByAnyone) {
  754. t.Error("expected device 2 not to be removed from folder 2")
  755. }
  756. }
  757. func TestIssue4897(t *testing.T) {
  758. m, cancel := newState(t, config.Configuration{
  759. Version: config.CurrentVersion,
  760. Devices: []config.DeviceConfiguration{
  761. {
  762. DeviceID: device1,
  763. Introducer: true,
  764. },
  765. },
  766. Folders: []config.FolderConfiguration{
  767. {
  768. FilesystemType: fs.FilesystemTypeFake,
  769. ID: "folder1",
  770. Path: "testdata",
  771. Devices: []config.FolderDeviceConfiguration{
  772. {DeviceID: device1},
  773. },
  774. Paused: true,
  775. },
  776. },
  777. })
  778. defer cleanupModel(m)
  779. cancel()
  780. cm, _ := m.generateClusterConfig(device1)
  781. if l := len(cm.Folders); l != 1 {
  782. t.Errorf("Cluster config contains %v folders, expected 1", l)
  783. }
  784. }
  785. // TestIssue5063 is about a panic in connection with modifying config in quick
  786. // succession, related with auto accepted folders. It's unclear what exactly, a
  787. // relevant bit seems to be here:
  788. // PR-comments: https://github.com/syncthing/syncthing/pull/5069/files#r203146546
  789. // Issue: https://github.com/syncthing/syncthing/pull/5509
  790. func TestIssue5063(t *testing.T) {
  791. m, cancel := newState(t, defaultAutoAcceptCfg)
  792. defer cleanupModel(m)
  793. defer cancel()
  794. m.pmut.Lock()
  795. for _, c := range m.connections {
  796. conn := c.(*fakeConnection)
  797. conn.CloseCalls(func(_ error) {})
  798. defer m.Closed(c, errStopped) // to unblock deferred m.Stop()
  799. }
  800. m.pmut.Unlock()
  801. wg := sync.WaitGroup{}
  802. addAndVerify := func(id string) {
  803. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  804. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  805. t.Error("expected shared", id)
  806. }
  807. wg.Done()
  808. }
  809. reps := 10
  810. ids := make([]string, reps)
  811. for i := 0; i < reps; i++ {
  812. wg.Add(1)
  813. ids[i] = srand.String(8)
  814. go addAndVerify(ids[i])
  815. }
  816. finished := make(chan struct{})
  817. go func() {
  818. wg.Wait()
  819. close(finished)
  820. }()
  821. select {
  822. case <-finished:
  823. case <-time.After(10 * time.Second):
  824. pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
  825. t.Fatal("Timed out before all devices were added")
  826. }
  827. }
  828. func TestAutoAcceptRejected(t *testing.T) {
  829. // Nothing happens if AutoAcceptFolders not set
  830. tcfg := defaultAutoAcceptCfg.Copy()
  831. for i := range tcfg.Devices {
  832. tcfg.Devices[i].AutoAcceptFolders = false
  833. }
  834. m, cancel := newState(t, tcfg)
  835. // defer cleanupModel(m)
  836. defer cancel()
  837. id := srand.String(8)
  838. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  839. if cfg, ok := m.cfg.Folder(id); ok && cfg.SharedWith(device1) {
  840. t.Error("unexpected shared", id)
  841. }
  842. }
  843. func TestAutoAcceptNewFolder(t *testing.T) {
  844. // New folder
  845. m, cancel := newState(t, defaultAutoAcceptCfg)
  846. defer cleanupModel(m)
  847. defer cancel()
  848. id := srand.String(8)
  849. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  850. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  851. t.Error("expected shared", id)
  852. }
  853. }
  854. func TestAutoAcceptNewFolderFromTwoDevices(t *testing.T) {
  855. m, cancel := newState(t, defaultAutoAcceptCfg)
  856. defer cleanupModel(m)
  857. defer cancel()
  858. id := srand.String(8)
  859. defer os.RemoveAll(id)
  860. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  861. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  862. t.Error("expected shared", id)
  863. }
  864. if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) {
  865. t.Error("unexpected expected shared", id)
  866. }
  867. m.ClusterConfig(device2Conn, createClusterConfig(device2, id))
  868. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device2) {
  869. t.Error("expected shared", id)
  870. }
  871. }
  872. func TestAutoAcceptNewFolderFromOnlyOneDevice(t *testing.T) {
  873. modifiedCfg := defaultAutoAcceptCfg.Copy()
  874. modifiedCfg.Devices[2].AutoAcceptFolders = false
  875. m, cancel := newState(t, modifiedCfg)
  876. id := srand.String(8)
  877. defer os.RemoveAll(id)
  878. defer cleanupModel(m)
  879. defer cancel()
  880. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  881. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  882. t.Error("expected shared", id)
  883. }
  884. if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) {
  885. t.Error("unexpected expected shared", id)
  886. }
  887. m.ClusterConfig(device2Conn, createClusterConfig(device2, id))
  888. if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) {
  889. t.Error("unexpected shared", id)
  890. }
  891. }
  892. func TestAutoAcceptNewFolderPremutationsNoPanic(t *testing.T) {
  893. if testing.Short() {
  894. t.Skip("short tests only")
  895. }
  896. id := srand.String(8)
  897. label := srand.String(8)
  898. premutations := []protocol.Folder{
  899. {ID: id, Label: id},
  900. {ID: id, Label: label},
  901. {ID: label, Label: id},
  902. {ID: label, Label: label},
  903. }
  904. localFolders := append(premutations, protocol.Folder{})
  905. for _, localFolder := range localFolders {
  906. for _, localFolderPaused := range []bool{false, true} {
  907. for _, dev1folder := range premutations {
  908. for _, dev2folder := range premutations {
  909. cfg := defaultAutoAcceptCfg.Copy()
  910. if localFolder.Label != "" {
  911. fcfg := newFolderConfiguration(defaultCfgWrapper, localFolder.ID, localFolder.Label, fs.FilesystemTypeFake, localFolder.ID)
  912. fcfg.Paused = localFolderPaused
  913. cfg.Folders = append(cfg.Folders, fcfg)
  914. }
  915. m, cancel := newState(t, cfg)
  916. m.ClusterConfig(device1Conn, protocol.ClusterConfig{
  917. Folders: []protocol.Folder{dev1folder},
  918. })
  919. m.ClusterConfig(device2Conn, protocol.ClusterConfig{
  920. Folders: []protocol.Folder{dev2folder},
  921. })
  922. cleanupModel(m)
  923. cancel()
  924. }
  925. }
  926. }
  927. }
  928. }
  929. func TestAutoAcceptMultipleFolders(t *testing.T) {
  930. // Multiple new folders
  931. id1 := srand.String(8)
  932. defer os.RemoveAll(id1)
  933. id2 := srand.String(8)
  934. defer os.RemoveAll(id2)
  935. m, cancel := newState(t, defaultAutoAcceptCfg)
  936. defer cleanupModel(m)
  937. defer cancel()
  938. m.ClusterConfig(device1Conn, createClusterConfig(device1, id1, id2))
  939. if fcfg, ok := m.cfg.Folder(id1); !ok || !fcfg.SharedWith(device1) {
  940. t.Error("expected shared", id1)
  941. }
  942. if fcfg, ok := m.cfg.Folder(id2); !ok || !fcfg.SharedWith(device1) {
  943. t.Error("expected shared", id2)
  944. }
  945. }
  946. func TestAutoAcceptExistingFolder(t *testing.T) {
  947. // Existing folder
  948. id := srand.String(8)
  949. idOther := srand.String(8) // To check that path does not get changed.
  950. tcfg := defaultAutoAcceptCfg.Copy()
  951. tcfg.Folders = []config.FolderConfiguration{
  952. {
  953. FilesystemType: fs.FilesystemTypeFake,
  954. ID: id,
  955. Path: idOther, // To check that path does not get changed.
  956. },
  957. }
  958. m, cancel := newState(t, tcfg)
  959. defer cleanupModel(m)
  960. defer cancel()
  961. if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device1) {
  962. t.Error("missing folder, or shared", id)
  963. }
  964. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  965. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) || fcfg.Path != idOther {
  966. t.Error("missing folder, or unshared, or path changed", id)
  967. }
  968. }
  969. func TestAutoAcceptNewAndExistingFolder(t *testing.T) {
  970. // New and existing folder
  971. id1 := srand.String(8)
  972. id2 := srand.String(8)
  973. tcfg := defaultAutoAcceptCfg.Copy()
  974. tcfg.Folders = []config.FolderConfiguration{
  975. {
  976. FilesystemType: fs.FilesystemTypeFake,
  977. ID: id1,
  978. Path: id1, // from previous test case, to verify that path doesn't get changed.
  979. },
  980. }
  981. m, cancel := newState(t, tcfg)
  982. defer cleanupModel(m)
  983. defer cancel()
  984. if fcfg, ok := m.cfg.Folder(id1); !ok || fcfg.SharedWith(device1) {
  985. t.Error("missing folder, or shared", id1)
  986. }
  987. m.ClusterConfig(device1Conn, createClusterConfig(device1, id1, id2))
  988. for i, id := range []string{id1, id2} {
  989. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  990. t.Error("missing folder, or unshared", i, id)
  991. }
  992. }
  993. }
  994. func TestAutoAcceptAlreadyShared(t *testing.T) {
  995. // Already shared
  996. id := srand.String(8)
  997. tcfg := defaultAutoAcceptCfg.Copy()
  998. tcfg.Folders = []config.FolderConfiguration{
  999. {
  1000. FilesystemType: fs.FilesystemTypeFake,
  1001. ID: id,
  1002. Path: id,
  1003. Devices: []config.FolderDeviceConfiguration{
  1004. {
  1005. DeviceID: device1,
  1006. },
  1007. },
  1008. },
  1009. }
  1010. m, cancel := newState(t, tcfg)
  1011. defer cleanupModel(m)
  1012. defer cancel()
  1013. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1014. t.Error("missing folder, or not shared", id)
  1015. }
  1016. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  1017. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1018. t.Error("missing folder, or not shared", id)
  1019. }
  1020. }
  1021. func TestAutoAcceptNameConflict(t *testing.T) {
  1022. ffs := fs.NewFilesystem(fs.FilesystemTypeFake, srand.String(32))
  1023. id := srand.String(8)
  1024. label := srand.String(8)
  1025. ffs.MkdirAll(id, 0o777)
  1026. ffs.MkdirAll(label, 0o777)
  1027. m, cancel := newState(t, defaultAutoAcceptCfg)
  1028. defer cleanupModel(m)
  1029. defer cancel()
  1030. m.ClusterConfig(device1Conn, protocol.ClusterConfig{
  1031. Folders: []protocol.Folder{
  1032. {
  1033. ID: id,
  1034. Label: label,
  1035. },
  1036. },
  1037. })
  1038. if fcfg, ok := m.cfg.Folder(id); ok && fcfg.SharedWith(device1) {
  1039. t.Error("unexpected folder", id)
  1040. }
  1041. }
  1042. func TestAutoAcceptPrefersLabel(t *testing.T) {
  1043. // Prefers label, falls back to ID.
  1044. m, cancel := newState(t, defaultAutoAcceptCfg)
  1045. id := srand.String(8)
  1046. label := srand.String(8)
  1047. defer cleanupModel(m)
  1048. defer cancel()
  1049. m.ClusterConfig(device1Conn, addFolderDevicesToClusterConfig(protocol.ClusterConfig{
  1050. Folders: []protocol.Folder{
  1051. {
  1052. ID: id,
  1053. Label: label,
  1054. },
  1055. },
  1056. }, device1))
  1057. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) || !strings.HasSuffix(fcfg.Path, label) {
  1058. t.Error("expected shared, or wrong path", id, label, fcfg.Path)
  1059. }
  1060. }
  1061. func TestAutoAcceptFallsBackToID(t *testing.T) {
  1062. // Prefers label, falls back to ID.
  1063. m, cancel := newState(t, defaultAutoAcceptCfg)
  1064. ffs := defaultFolderConfig.Filesystem(nil)
  1065. id := srand.String(8)
  1066. label := srand.String(8)
  1067. if err := ffs.MkdirAll(label, 0o777); err != nil {
  1068. t.Error(err)
  1069. }
  1070. defer cleanupModel(m)
  1071. defer cancel()
  1072. m.ClusterConfig(device1Conn, addFolderDevicesToClusterConfig(protocol.ClusterConfig{
  1073. Folders: []protocol.Folder{
  1074. {
  1075. ID: id,
  1076. Label: label,
  1077. },
  1078. },
  1079. }, device1))
  1080. fcfg, ok := m.cfg.Folder(id)
  1081. if !ok {
  1082. t.Error("folder configuration missing")
  1083. }
  1084. if !fcfg.SharedWith(device1) {
  1085. t.Error("folder is not shared with device1")
  1086. }
  1087. }
  1088. func TestAutoAcceptPausedWhenFolderConfigChanged(t *testing.T) {
  1089. // Existing folder
  1090. id := srand.String(8)
  1091. idOther := srand.String(8) // To check that path does not get changed.
  1092. tcfg := defaultAutoAcceptCfg.Copy()
  1093. fcfg := newFolderConfiguration(defaultCfgWrapper, id, "", fs.FilesystemTypeFake, idOther)
  1094. fcfg.Paused = true
  1095. // The order of devices here is wrong (cfg.clean() sorts them), which will cause the folder to restart.
  1096. // Because of the restart, folder gets removed from m.deviceFolder, which means that generateClusterConfig will not panic.
  1097. // This wasn't an issue before, yet keeping the test case to prove that it still isn't.
  1098. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{
  1099. DeviceID: device1,
  1100. })
  1101. tcfg.Folders = []config.FolderConfiguration{fcfg}
  1102. m, cancel := newState(t, tcfg)
  1103. defer cleanupModel(m)
  1104. defer cancel()
  1105. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1106. t.Error("missing folder, or not shared", id)
  1107. }
  1108. if _, ok := m.folderRunners.Get(id); ok {
  1109. t.Fatal("folder running?")
  1110. }
  1111. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  1112. m.generateClusterConfig(device1)
  1113. if fcfg, ok := m.cfg.Folder(id); !ok {
  1114. t.Error("missing folder")
  1115. } else if fcfg.Path != idOther {
  1116. t.Error("folder path changed")
  1117. } else {
  1118. for _, dev := range fcfg.DeviceIDs() {
  1119. if dev == device1 {
  1120. return
  1121. }
  1122. }
  1123. t.Error("device missing")
  1124. }
  1125. if _, ok := m.folderRunners.Get(id); ok {
  1126. t.Error("folder started")
  1127. }
  1128. }
  1129. func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) {
  1130. // Existing folder
  1131. id := srand.String(8)
  1132. idOther := srand.String(8) // To check that path does not get changed.
  1133. tcfg := defaultAutoAcceptCfg.Copy()
  1134. fcfg := newFolderConfiguration(defaultCfgWrapper, id, "", fs.FilesystemTypeFake, idOther)
  1135. fcfg.Paused = true
  1136. // The new folder is exactly the same as the one constructed by handleAutoAccept, which means
  1137. // the folder will not be restarted (even if it's paused), yet handleAutoAccept used to add the folder
  1138. // to m.deviceFolders which had caused panics when calling generateClusterConfig, as the folder
  1139. // did not have a file set.
  1140. fcfg.Devices = append([]config.FolderDeviceConfiguration{
  1141. {
  1142. DeviceID: device1,
  1143. },
  1144. }, fcfg.Devices...) // Need to ensure this device order to avoid folder restart.
  1145. tcfg.Folders = []config.FolderConfiguration{fcfg}
  1146. m, cancel := newState(t, tcfg)
  1147. defer cleanupModel(m)
  1148. defer cancel()
  1149. if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
  1150. t.Error("missing folder, or not shared", id)
  1151. }
  1152. if _, ok := m.folderRunners.Get(id); ok {
  1153. t.Fatal("folder running?")
  1154. }
  1155. m.ClusterConfig(device1Conn, createClusterConfig(device1, id))
  1156. m.generateClusterConfig(device1)
  1157. if fcfg, ok := m.cfg.Folder(id); !ok {
  1158. t.Error("missing folder")
  1159. } else if fcfg.Path != idOther {
  1160. t.Error("folder path changed")
  1161. } else {
  1162. for _, dev := range fcfg.DeviceIDs() {
  1163. if dev == device1 {
  1164. return
  1165. }
  1166. }
  1167. t.Error("device missing")
  1168. }
  1169. if _, ok := m.folderRunners.Get(id); ok {
  1170. t.Error("folder started")
  1171. }
  1172. }
  1173. func TestAutoAcceptEnc(t *testing.T) {
  1174. tcfg := defaultAutoAcceptCfg.Copy()
  1175. m, cancel := newState(t, tcfg)
  1176. defer cleanupModel(m)
  1177. defer cancel()
  1178. id := srand.String(8)
  1179. defer os.RemoveAll(id)
  1180. token := []byte("token")
  1181. basicCC := func() protocol.ClusterConfig {
  1182. return protocol.ClusterConfig{
  1183. Folders: []protocol.Folder{{
  1184. ID: id,
  1185. Label: id,
  1186. }},
  1187. }
  1188. }
  1189. // Earlier tests might cause the connection to get closed, thus ClusterConfig
  1190. // would panic.
  1191. clusterConfig := func(deviceID protocol.DeviceID, cm protocol.ClusterConfig) {
  1192. conn := newFakeConnection(deviceID, m)
  1193. m.AddConnection(conn, protocol.Hello{})
  1194. m.ClusterConfig(conn, cm)
  1195. }
  1196. clusterConfig(device1, basicCC())
  1197. if _, ok := m.cfg.Folder(id); ok {
  1198. t.Fatal("unexpected added")
  1199. }
  1200. cc := basicCC()
  1201. cc.Folders[0].Devices = []protocol.Device{{ID: device1}}
  1202. clusterConfig(device1, cc)
  1203. if _, ok := m.cfg.Folder(id); ok {
  1204. t.Fatal("unexpected added")
  1205. }
  1206. cc = basicCC()
  1207. cc.Folders[0].Devices = []protocol.Device{{ID: myID}}
  1208. clusterConfig(device1, cc)
  1209. if _, ok := m.cfg.Folder(id); ok {
  1210. t.Fatal("unexpected added")
  1211. }
  1212. // New folder, encrypted -> add as enc
  1213. cc = createClusterConfig(device1, id)
  1214. cc.Folders[0].Devices[1].EncryptionPasswordToken = token
  1215. clusterConfig(device1, cc)
  1216. if cfg, ok := m.cfg.Folder(id); !ok {
  1217. t.Fatal("unexpected unadded")
  1218. } else {
  1219. if !cfg.SharedWith(device1) {
  1220. t.Fatal("unexpected unshared")
  1221. }
  1222. if cfg.Type != config.FolderTypeReceiveEncrypted {
  1223. t.Fatal("Folder not added as receiveEncrypted")
  1224. }
  1225. }
  1226. // New device, unencrypted on encrypted folder -> reject
  1227. clusterConfig(device2, createClusterConfig(device2, id))
  1228. if cfg, _ := m.cfg.Folder(id); cfg.SharedWith(device2) {
  1229. t.Fatal("unexpected shared")
  1230. }
  1231. // New device, encrypted on encrypted folder -> share
  1232. cc = createClusterConfig(device2, id)
  1233. cc.Folders[0].Devices[1].EncryptionPasswordToken = token
  1234. clusterConfig(device2, cc)
  1235. if cfg, _ := m.cfg.Folder(id); !cfg.SharedWith(device2) {
  1236. t.Fatal("unexpected unshared")
  1237. }
  1238. // New folder, no encrypted -> add "normal"
  1239. id = srand.String(8)
  1240. defer os.RemoveAll(id)
  1241. clusterConfig(device1, createClusterConfig(device1, id))
  1242. if cfg, ok := m.cfg.Folder(id); !ok {
  1243. t.Fatal("unexpected unadded")
  1244. } else {
  1245. if !cfg.SharedWith(device1) {
  1246. t.Fatal("unexpected unshared")
  1247. }
  1248. if cfg.Type != config.FolderTypeSendReceive {
  1249. t.Fatal("Folder not added as send-receive")
  1250. }
  1251. }
  1252. // New device, encrypted on unencrypted folder -> reject
  1253. cc = createClusterConfig(device2, id)
  1254. cc.Folders[0].Devices[1].EncryptionPasswordToken = token
  1255. clusterConfig(device2, cc)
  1256. if cfg, _ := m.cfg.Folder(id); cfg.SharedWith(device2) {
  1257. t.Fatal("unexpected shared")
  1258. }
  1259. // New device, unencrypted on unencrypted folder -> share
  1260. clusterConfig(device2, createClusterConfig(device2, id))
  1261. if cfg, _ := m.cfg.Folder(id); !cfg.SharedWith(device2) {
  1262. t.Fatal("unexpected unshared")
  1263. }
  1264. }
  1265. func changeIgnores(t *testing.T, m *testModel, expected []string) {
  1266. arrEqual := func(a, b []string) bool {
  1267. if len(a) != len(b) {
  1268. return false
  1269. }
  1270. for i := range a {
  1271. if a[i] != b[i] {
  1272. return false
  1273. }
  1274. }
  1275. return true
  1276. }
  1277. ignores, _, err := m.LoadIgnores("default")
  1278. if err != nil {
  1279. t.Error(err)
  1280. }
  1281. if !arrEqual(ignores, expected) {
  1282. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  1283. }
  1284. ignores = append(ignores, "pox")
  1285. err = m.SetIgnores("default", ignores)
  1286. if err != nil {
  1287. t.Error(err)
  1288. }
  1289. ignores2, _, err := m.LoadIgnores("default")
  1290. if err != nil {
  1291. t.Error(err)
  1292. }
  1293. if !arrEqual(ignores, ignores2) {
  1294. t.Errorf("Incorrect ignores: %v != %v", ignores2, ignores)
  1295. }
  1296. if build.IsDarwin {
  1297. // see above
  1298. time.Sleep(time.Second)
  1299. } else {
  1300. time.Sleep(time.Millisecond)
  1301. }
  1302. err = m.SetIgnores("default", expected)
  1303. if err != nil {
  1304. t.Error(err)
  1305. }
  1306. ignores, _, err = m.LoadIgnores("default")
  1307. if err != nil {
  1308. t.Error(err)
  1309. }
  1310. if !arrEqual(ignores, expected) {
  1311. t.Errorf("Incorrect ignores: %v != %v", ignores, expected)
  1312. }
  1313. }
  1314. func TestIgnores(t *testing.T) {
  1315. w, cancel := newConfigWrapper(defaultCfg)
  1316. defer cancel()
  1317. ffs := w.FolderList()[0].Filesystem(nil)
  1318. m := setupModel(t, w)
  1319. defer cleanupModel(m)
  1320. // Assure a clean start state
  1321. must(t, ffs.MkdirAll(config.DefaultMarkerName, 0o644))
  1322. writeFile(t, ffs, ".stignore", []byte(".*\nquux\n"))
  1323. folderIgnoresAlwaysReload(t, m, defaultFolderConfig)
  1324. // Make sure the initial scan has finished (ScanFolders is blocking)
  1325. m.ScanFolders()
  1326. expected := []string{
  1327. ".*",
  1328. "quux",
  1329. }
  1330. changeIgnores(t, m, expected)
  1331. _, _, err := m.LoadIgnores("doesnotexist")
  1332. if err == nil {
  1333. t.Error("No error")
  1334. }
  1335. err = m.SetIgnores("doesnotexist", expected)
  1336. if err == nil {
  1337. t.Error("No error")
  1338. }
  1339. // Invalid path, treated like no patterns at all.
  1340. fcfg := config.FolderConfiguration{
  1341. ID: "fresh", Path: "XXX",
  1342. FilesystemType: fs.FilesystemTypeFake,
  1343. }
  1344. ignores := ignore.New(fcfg.Filesystem(nil), ignore.WithCache(m.cfg.Options().CacheIgnoredFiles))
  1345. m.fmut.Lock()
  1346. m.folderCfgs[fcfg.ID] = fcfg
  1347. m.folderIgnores[fcfg.ID] = ignores
  1348. m.fmut.Unlock()
  1349. _, _, err = m.LoadIgnores("fresh")
  1350. if err != nil {
  1351. t.Error("Got error for inexistent folder path")
  1352. }
  1353. // Repeat tests with paused folder
  1354. pausedDefaultFolderConfig := defaultFolderConfig
  1355. pausedDefaultFolderConfig.Paused = true
  1356. m.restartFolder(defaultFolderConfig, pausedDefaultFolderConfig, false)
  1357. // Here folder initialization is not an issue as a paused folder isn't
  1358. // added to the model and thus there is no initial scan happening.
  1359. changeIgnores(t, m, expected)
  1360. // Make sure no .stignore file is considered valid
  1361. defer func() {
  1362. must(t, ffs.Rename(".stignore.bak", ".stignore"))
  1363. }()
  1364. must(t, ffs.Rename(".stignore", ".stignore.bak"))
  1365. changeIgnores(t, m, []string{})
  1366. }
  1367. func TestEmptyIgnores(t *testing.T) {
  1368. w, cancel := newConfigWrapper(defaultCfg)
  1369. defer cancel()
  1370. ffs := w.FolderList()[0].Filesystem(nil)
  1371. m := setupModel(t, w)
  1372. defer cleanupModel(m)
  1373. if err := m.SetIgnores("default", []string{}); err != nil {
  1374. t.Error(err)
  1375. }
  1376. if _, err := ffs.Stat(".stignore"); err == nil {
  1377. t.Error(".stignore was created despite being empty")
  1378. }
  1379. if err := m.SetIgnores("default", []string{".*", "quux"}); err != nil {
  1380. t.Error(err)
  1381. }
  1382. if _, err := ffs.Stat(".stignore"); os.IsNotExist(err) {
  1383. t.Error(".stignore does not exist")
  1384. }
  1385. if err := m.SetIgnores("default", []string{}); err != nil {
  1386. t.Error(err)
  1387. }
  1388. if _, err := ffs.Stat(".stignore"); err == nil {
  1389. t.Error(".stignore should have been deleted because it is empty")
  1390. }
  1391. }
  1392. func waitForState(t *testing.T, sub events.Subscription, folder, expected string) {
  1393. t.Helper()
  1394. timeout := time.After(5 * time.Second)
  1395. var err string
  1396. for {
  1397. select {
  1398. case ev := <-sub.C():
  1399. data := ev.Data.(map[string]interface{})
  1400. if data["folder"].(string) == folder {
  1401. if data["error"] == nil {
  1402. err = ""
  1403. } else {
  1404. err = data["error"].(string)
  1405. }
  1406. if err == expected {
  1407. return
  1408. } else {
  1409. t.Error(ev)
  1410. }
  1411. }
  1412. case <-timeout:
  1413. t.Fatalf("Timed out waiting for status: %s, current status: %v", expected, err)
  1414. }
  1415. }
  1416. }
  1417. func TestROScanRecovery(t *testing.T) {
  1418. fcfg := config.FolderConfiguration{
  1419. FilesystemType: fs.FilesystemTypeFake,
  1420. ID: "default",
  1421. Path: srand.String(32),
  1422. Type: config.FolderTypeSendOnly,
  1423. RescanIntervalS: 1,
  1424. MarkerName: config.DefaultMarkerName,
  1425. }
  1426. cfg, cancel := newConfigWrapper(config.Configuration{
  1427. Version: config.CurrentVersion,
  1428. Folders: []config.FolderConfiguration{fcfg},
  1429. Devices: []config.DeviceConfiguration{
  1430. {
  1431. DeviceID: device1,
  1432. },
  1433. },
  1434. })
  1435. defer cancel()
  1436. m := newModel(t, cfg, myID, nil)
  1437. set := newFileSet(t, "default", m.db)
  1438. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  1439. {Name: "dummyfile", Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}}},
  1440. })
  1441. ffs := fcfg.Filesystem(nil)
  1442. // Remove marker to generate an error
  1443. ffs.Remove(fcfg.MarkerName)
  1444. sub := m.evLogger.Subscribe(events.StateChanged)
  1445. defer sub.Unsubscribe()
  1446. m.ServeBackground()
  1447. defer cleanupModel(m)
  1448. waitForState(t, sub, "default", config.ErrMarkerMissing.Error())
  1449. fd, err := ffs.Create(config.DefaultMarkerName)
  1450. if err != nil {
  1451. t.Fatal(err)
  1452. }
  1453. fd.Close()
  1454. waitForState(t, sub, "default", "")
  1455. }
  1456. func TestRWScanRecovery(t *testing.T) {
  1457. fcfg := config.FolderConfiguration{
  1458. FilesystemType: fs.FilesystemTypeFake,
  1459. ID: "default",
  1460. Path: srand.String(32),
  1461. Type: config.FolderTypeSendReceive,
  1462. RescanIntervalS: 1,
  1463. MarkerName: config.DefaultMarkerName,
  1464. }
  1465. cfg, cancel := newConfigWrapper(config.Configuration{
  1466. Version: config.CurrentVersion,
  1467. Folders: []config.FolderConfiguration{fcfg},
  1468. Devices: []config.DeviceConfiguration{
  1469. {
  1470. DeviceID: device1,
  1471. },
  1472. },
  1473. })
  1474. defer cancel()
  1475. m := newModel(t, cfg, myID, nil)
  1476. set := newFileSet(t, "default", m.db)
  1477. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  1478. {Name: "dummyfile", Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1}}}},
  1479. })
  1480. ffs := fcfg.Filesystem(nil)
  1481. // Generate error
  1482. if err := ffs.Remove(config.DefaultMarkerName); err != nil {
  1483. t.Fatal(err)
  1484. }
  1485. sub := m.evLogger.Subscribe(events.StateChanged)
  1486. defer sub.Unsubscribe()
  1487. m.ServeBackground()
  1488. defer cleanupModel(m)
  1489. waitForState(t, sub, "default", config.ErrMarkerMissing.Error())
  1490. fd, err := ffs.Create(config.DefaultMarkerName)
  1491. if err != nil {
  1492. t.Error(err)
  1493. }
  1494. fd.Close()
  1495. waitForState(t, sub, "default", "")
  1496. }
  1497. func TestGlobalDirectoryTree(t *testing.T) {
  1498. m, conn, fcfg, wCancel := setupModelWithConnection(t)
  1499. defer wCancel()
  1500. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  1501. b := func(isfile bool, path ...string) protocol.FileInfo {
  1502. typ := protocol.FileInfoTypeDirectory
  1503. var blocks []protocol.BlockInfo
  1504. if isfile {
  1505. typ = protocol.FileInfoTypeFile
  1506. 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}}}
  1507. }
  1508. return protocol.FileInfo{
  1509. Name: filepath.Join(path...),
  1510. Type: typ,
  1511. ModifiedS: 0x666,
  1512. Blocks: blocks,
  1513. Size: 0xa,
  1514. }
  1515. }
  1516. f := func(name string) *TreeEntry {
  1517. return &TreeEntry{
  1518. Name: name,
  1519. ModTime: time.Unix(0x666, 0),
  1520. Size: 0xa,
  1521. Type: protocol.FileInfoTypeFile,
  1522. }
  1523. }
  1524. d := func(name string, entries ...*TreeEntry) *TreeEntry {
  1525. return &TreeEntry{
  1526. Name: name,
  1527. ModTime: time.Unix(0x666, 0),
  1528. Size: 128,
  1529. Type: protocol.FileInfoTypeDirectory,
  1530. Children: entries,
  1531. }
  1532. }
  1533. testdata := []protocol.FileInfo{
  1534. b(false, "another"),
  1535. b(false, "another", "directory"),
  1536. b(true, "another", "directory", "afile"),
  1537. b(false, "another", "directory", "with"),
  1538. b(false, "another", "directory", "with", "a"),
  1539. b(true, "another", "directory", "with", "a", "file"),
  1540. b(true, "another", "directory", "with", "file"),
  1541. b(true, "another", "file"),
  1542. b(false, "other"),
  1543. b(false, "other", "rand"),
  1544. b(false, "other", "random"),
  1545. b(false, "other", "random", "dir"),
  1546. b(false, "other", "random", "dirx"),
  1547. b(false, "other", "randomx"),
  1548. b(false, "some"),
  1549. b(false, "some", "directory"),
  1550. b(false, "some", "directory", "with"),
  1551. b(false, "some", "directory", "with", "a"),
  1552. b(true, "some", "directory", "with", "a", "file"),
  1553. b(true, "zzrootfile"),
  1554. }
  1555. expectedResult := []*TreeEntry{
  1556. d("another",
  1557. d("directory",
  1558. f("afile"),
  1559. d("with",
  1560. d("a",
  1561. f("file"),
  1562. ),
  1563. f("file"),
  1564. ),
  1565. ),
  1566. f("file"),
  1567. ),
  1568. d("other",
  1569. d("rand"),
  1570. d("random",
  1571. d("dir"),
  1572. d("dirx"),
  1573. ),
  1574. d("randomx"),
  1575. ),
  1576. d("some",
  1577. d("directory",
  1578. d("with",
  1579. d("a",
  1580. f("file"),
  1581. ),
  1582. ),
  1583. ),
  1584. ),
  1585. f("zzrootfile"),
  1586. }
  1587. mm := func(data interface{}) string {
  1588. bytes, err := json.MarshalIndent(data, "", " ")
  1589. if err != nil {
  1590. panic(err)
  1591. }
  1592. return string(bytes)
  1593. }
  1594. must(t, m.Index(conn, "default", testdata))
  1595. result, _ := m.GlobalDirectoryTree("default", "", -1, false)
  1596. if mm(result) != mm(expectedResult) {
  1597. t.Errorf("Does not match:\n%s\n============\n%s", mm(result), mm(expectedResult))
  1598. }
  1599. result, _ = m.GlobalDirectoryTree("default", "another", -1, false)
  1600. if mm(result) != mm(findByName(expectedResult, "another").Children) {
  1601. t.Errorf("Does not match:\n%s\n============\n%s", mm(result), mm(findByName(expectedResult, "another").Children))
  1602. }
  1603. result, _ = m.GlobalDirectoryTree("default", "", 0, false)
  1604. currentResult := []*TreeEntry{
  1605. d("another"),
  1606. d("other"),
  1607. d("some"),
  1608. f("zzrootfile"),
  1609. }
  1610. if mm(result) != mm(currentResult) {
  1611. t.Errorf("Does not match:\n%s\n============\n%s", mm(result), mm(currentResult))
  1612. }
  1613. result, _ = m.GlobalDirectoryTree("default", "", 1, false)
  1614. currentResult = []*TreeEntry{
  1615. d("another",
  1616. d("directory"),
  1617. f("file"),
  1618. ),
  1619. d("other",
  1620. d("rand"),
  1621. d("random"),
  1622. d("randomx"),
  1623. ),
  1624. d("some",
  1625. d("directory"),
  1626. ),
  1627. f("zzrootfile"),
  1628. }
  1629. if mm(result) != mm(currentResult) {
  1630. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1631. }
  1632. result, _ = m.GlobalDirectoryTree("default", "", -1, true)
  1633. currentResult = []*TreeEntry{
  1634. d("another",
  1635. d("directory",
  1636. d("with",
  1637. d("a"),
  1638. ),
  1639. ),
  1640. ),
  1641. d("other",
  1642. d("rand"),
  1643. d("random",
  1644. d("dir"),
  1645. d("dirx"),
  1646. ),
  1647. d("randomx"),
  1648. ),
  1649. d("some",
  1650. d("directory",
  1651. d("with",
  1652. d("a"),
  1653. ),
  1654. ),
  1655. ),
  1656. }
  1657. if mm(result) != mm(currentResult) {
  1658. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1659. }
  1660. result, _ = m.GlobalDirectoryTree("default", "", 1, true)
  1661. currentResult = []*TreeEntry{
  1662. d("another",
  1663. d("directory"),
  1664. ),
  1665. d("other",
  1666. d("rand"),
  1667. d("random"),
  1668. d("randomx"),
  1669. ),
  1670. d("some",
  1671. d("directory"),
  1672. ),
  1673. }
  1674. if mm(result) != mm(currentResult) {
  1675. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1676. }
  1677. result, _ = m.GlobalDirectoryTree("default", "another", 0, false)
  1678. currentResult = []*TreeEntry{
  1679. d("directory"),
  1680. f("file"),
  1681. }
  1682. if mm(result) != mm(currentResult) {
  1683. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1684. }
  1685. result, _ = m.GlobalDirectoryTree("default", "some/directory", 0, false)
  1686. currentResult = []*TreeEntry{
  1687. d("with"),
  1688. }
  1689. if mm(result) != mm(currentResult) {
  1690. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1691. }
  1692. result, _ = m.GlobalDirectoryTree("default", "some/directory", 1, false)
  1693. currentResult = []*TreeEntry{
  1694. d("with",
  1695. d("a"),
  1696. ),
  1697. }
  1698. if mm(result) != mm(currentResult) {
  1699. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1700. }
  1701. result, _ = m.GlobalDirectoryTree("default", "some/directory", 2, false)
  1702. currentResult = []*TreeEntry{
  1703. d("with",
  1704. d("a",
  1705. f("file"),
  1706. ),
  1707. ),
  1708. }
  1709. if mm(result) != mm(currentResult) {
  1710. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1711. }
  1712. result, _ = m.GlobalDirectoryTree("default", "another", -1, true)
  1713. currentResult = []*TreeEntry{
  1714. d("directory",
  1715. d("with",
  1716. d("a"),
  1717. ),
  1718. ),
  1719. }
  1720. if mm(result) != mm(currentResult) {
  1721. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1722. }
  1723. // No prefix matching!
  1724. result, _ = m.GlobalDirectoryTree("default", "som", -1, false)
  1725. currentResult = []*TreeEntry{}
  1726. if mm(result) != mm(currentResult) {
  1727. t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult))
  1728. }
  1729. }
  1730. func genDeepFiles(n, d int) []protocol.FileInfo {
  1731. mrand.Seed(int64(n))
  1732. files := make([]protocol.FileInfo, n)
  1733. t := time.Now().Unix()
  1734. for i := 0; i < n; i++ {
  1735. path := ""
  1736. for i := 0; i <= d; i++ {
  1737. path = filepath.Join(path, strconv.Itoa(mrand.Int()))
  1738. }
  1739. sofar := ""
  1740. for _, path := range filepath.SplitList(path) {
  1741. sofar = filepath.Join(sofar, path)
  1742. files[i] = protocol.FileInfo{
  1743. Name: sofar,
  1744. }
  1745. i++
  1746. }
  1747. files[i].ModifiedS = t
  1748. files[i].Blocks = []protocol.BlockInfo{{Offset: 0, Size: 100, Hash: []byte("some hash bytes")}}
  1749. }
  1750. return files
  1751. }
  1752. func BenchmarkTree_10000_50(b *testing.B) {
  1753. benchmarkTree(b, 10000, 50)
  1754. }
  1755. func BenchmarkTree_100_50(b *testing.B) {
  1756. benchmarkTree(b, 100, 50)
  1757. }
  1758. func BenchmarkTree_100_10(b *testing.B) {
  1759. benchmarkTree(b, 100, 10)
  1760. }
  1761. func benchmarkTree(b *testing.B, n1, n2 int) {
  1762. m, _, fcfg, wcfgCancel := setupModelWithConnection(b)
  1763. defer wcfgCancel()
  1764. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  1765. m.ScanFolder(fcfg.ID)
  1766. files := genDeepFiles(n1, n2)
  1767. must(b, m.Index(device1Conn, fcfg.ID, files))
  1768. b.ResetTimer()
  1769. for i := 0; i < b.N; i++ {
  1770. m.GlobalDirectoryTree(fcfg.ID, "", -1, false)
  1771. }
  1772. b.ReportAllocs()
  1773. }
  1774. func TestIssue3028(t *testing.T) {
  1775. w, cancel := newConfigWrapper(defaultCfg)
  1776. defer cancel()
  1777. ffs := w.FolderList()[0].Filesystem(nil)
  1778. m := setupModel(t, w)
  1779. defer cleanupModel(m)
  1780. // Create two files that we'll delete, one with a name that is a prefix of the other.
  1781. writeFile(t, ffs, "testrm", []byte("Hello"))
  1782. writeFile(t, ffs, "testrm2", []byte("Hello"))
  1783. // Scan, and get a count of how many files are there now
  1784. m.ScanFolderSubdirs("default", []string{"testrm", "testrm2"})
  1785. locorigfiles := localSize(t, m, "default").Files
  1786. globorigfiles := globalSize(t, m, "default").Files
  1787. // Delete
  1788. must(t, ffs.Remove("testrm"))
  1789. must(t, ffs.Remove("testrm2"))
  1790. // Verify that the number of files decreased by two and the number of
  1791. // deleted files increases by two
  1792. m.ScanFolderSubdirs("default", []string{"testrm", "testrm2"})
  1793. loc := localSize(t, m, "default")
  1794. glob := globalSize(t, m, "default")
  1795. if loc.Files != locorigfiles-2 {
  1796. t.Errorf("Incorrect local accounting; got %d current files, expected %d", loc.Files, locorigfiles-2)
  1797. }
  1798. if glob.Files != globorigfiles-2 {
  1799. t.Errorf("Incorrect global accounting; got %d current files, expected %d", glob.Files, globorigfiles-2)
  1800. }
  1801. if loc.Deleted != 2 {
  1802. t.Errorf("Incorrect local accounting; got %d deleted files, expected 2", loc.Deleted)
  1803. }
  1804. if glob.Deleted != 2 {
  1805. t.Errorf("Incorrect global accounting; got %d deleted files, expected 2", glob.Deleted)
  1806. }
  1807. }
  1808. func TestIssue4357(t *testing.T) {
  1809. cfg := defaultCfgWrapper.RawCopy()
  1810. // Create a separate wrapper not to pollute other tests.
  1811. wrapper, cancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
  1812. defer cancel()
  1813. m := newModel(t, wrapper, myID, nil)
  1814. m.ServeBackground()
  1815. defer cleanupModel(m)
  1816. // Force the model to wire itself and add the folders
  1817. replace(t, wrapper, cfg)
  1818. if _, ok := m.folderCfgs["default"]; !ok {
  1819. t.Error("Folder should be running")
  1820. }
  1821. newCfg := wrapper.RawCopy()
  1822. newCfg.Folders[0].Paused = true
  1823. replace(t, wrapper, newCfg)
  1824. if _, ok := m.folderCfgs["default"]; ok {
  1825. t.Error("Folder should not be running")
  1826. }
  1827. if _, ok := m.cfg.Folder("default"); !ok {
  1828. t.Error("should still have folder in config")
  1829. }
  1830. replace(t, wrapper, config.Configuration{Version: config.CurrentVersion})
  1831. if _, ok := m.cfg.Folder("default"); ok {
  1832. t.Error("should not have folder in config")
  1833. }
  1834. // Add the folder back, should be running
  1835. replace(t, wrapper, cfg)
  1836. if _, ok := m.folderCfgs["default"]; !ok {
  1837. t.Error("Folder should be running")
  1838. }
  1839. if _, ok := m.cfg.Folder("default"); !ok {
  1840. t.Error("should still have folder in config")
  1841. }
  1842. // Should not panic when removing a running folder.
  1843. replace(t, wrapper, config.Configuration{Version: config.CurrentVersion})
  1844. if _, ok := m.folderCfgs["default"]; ok {
  1845. t.Error("Folder should not be running")
  1846. }
  1847. if _, ok := m.cfg.Folder("default"); ok {
  1848. t.Error("should not have folder in config")
  1849. }
  1850. }
  1851. func TestIndexesForUnknownDevicesDropped(t *testing.T) {
  1852. m := newModel(t, defaultCfgWrapper, myID, nil)
  1853. files := newFileSet(t, "default", m.db)
  1854. files.Drop(device1)
  1855. files.Update(device1, genFiles(1))
  1856. files.Drop(device2)
  1857. files.Update(device2, genFiles(1))
  1858. if len(files.ListDevices()) != 2 {
  1859. t.Error("expected two devices")
  1860. }
  1861. m.newFolder(defaultFolderConfig, false)
  1862. defer cleanupModel(m)
  1863. // Remote sequence is cached, hence need to recreated.
  1864. files = newFileSet(t, "default", m.db)
  1865. if l := len(files.ListDevices()); l != 1 {
  1866. t.Errorf("Expected one device got %v", l)
  1867. }
  1868. }
  1869. func TestSharedWithClearedOnDisconnect(t *testing.T) {
  1870. wcfg, cancel := newConfigWrapper(defaultCfg)
  1871. defer cancel()
  1872. addDevice2(t, wcfg, wcfg.FolderList()[0])
  1873. m := setupModel(t, wcfg)
  1874. defer cleanupModel(m)
  1875. conn1 := newFakeConnection(device1, m)
  1876. m.AddConnection(conn1, protocol.Hello{})
  1877. conn2 := newFakeConnection(device2, m)
  1878. m.AddConnection(conn2, protocol.Hello{})
  1879. m.ClusterConfig(conn1, protocol.ClusterConfig{
  1880. Folders: []protocol.Folder{
  1881. {
  1882. ID: "default",
  1883. Devices: []protocol.Device{
  1884. {ID: myID},
  1885. {ID: device1},
  1886. {ID: device2},
  1887. },
  1888. },
  1889. },
  1890. })
  1891. m.ClusterConfig(conn2, protocol.ClusterConfig{
  1892. Folders: []protocol.Folder{
  1893. {
  1894. ID: "default",
  1895. Devices: []protocol.Device{
  1896. {ID: myID},
  1897. {ID: device1},
  1898. {ID: device2},
  1899. },
  1900. },
  1901. },
  1902. })
  1903. if fcfg, ok := m.cfg.Folder("default"); !ok || !fcfg.SharedWith(device1) {
  1904. t.Error("not shared with device1")
  1905. }
  1906. if fcfg, ok := m.cfg.Folder("default"); !ok || !fcfg.SharedWith(device2) {
  1907. t.Error("not shared with device2")
  1908. }
  1909. select {
  1910. case <-conn2.Closed():
  1911. t.Error("conn already closed")
  1912. default:
  1913. }
  1914. if _, err := wcfg.RemoveDevice(device2); err != nil {
  1915. t.Error(err)
  1916. }
  1917. time.Sleep(100 * time.Millisecond) // Committer notification happens in a separate routine
  1918. fcfg, ok := m.cfg.Folder("default")
  1919. if !ok {
  1920. t.Fatal("default folder missing")
  1921. }
  1922. if !fcfg.SharedWith(device1) {
  1923. t.Error("not shared with device1")
  1924. }
  1925. if fcfg.SharedWith(device2) {
  1926. t.Error("shared with device2")
  1927. }
  1928. for _, dev := range fcfg.Devices {
  1929. if dev.DeviceID == device2 {
  1930. t.Error("still there")
  1931. }
  1932. }
  1933. select {
  1934. case <-conn2.Closed():
  1935. default:
  1936. t.Error("connection not closed")
  1937. }
  1938. if _, ok := wcfg.Devices()[device2]; ok {
  1939. t.Error("device still in config")
  1940. }
  1941. if _, ok := m.deviceConnIDs[device2]; ok {
  1942. t.Error("conn not missing")
  1943. }
  1944. if _, ok := m.helloMessages[device2]; ok {
  1945. t.Error("hello not missing")
  1946. }
  1947. if _, ok := m.deviceDownloads[device2]; ok {
  1948. t.Error("downloads not missing")
  1949. }
  1950. }
  1951. func TestIssue3804(t *testing.T) {
  1952. m := setupModel(t, defaultCfgWrapper)
  1953. defer cleanupModel(m)
  1954. // Subdirs ending in slash should be accepted
  1955. if err := m.ScanFolderSubdirs("default", []string{"baz/", "foo"}); err != nil {
  1956. t.Error("Unexpected error:", err)
  1957. }
  1958. }
  1959. func TestIssue3829(t *testing.T) {
  1960. m := setupModel(t, defaultCfgWrapper)
  1961. defer cleanupModel(m)
  1962. // Empty subdirs should be accepted
  1963. if err := m.ScanFolderSubdirs("default", []string{""}); err != nil {
  1964. t.Error("Unexpected error:", err)
  1965. }
  1966. }
  1967. // TestIssue4573 tests that contents of an unavailable dir aren't marked deleted
  1968. func TestIssue4573(t *testing.T) {
  1969. w, fcfg, wCancel := newDefaultCfgWrapper()
  1970. defer wCancel()
  1971. testFs := fcfg.Filesystem(nil)
  1972. defer os.RemoveAll(testFs.URI())
  1973. must(t, testFs.MkdirAll("inaccessible", 0o755))
  1974. defer testFs.Chmod("inaccessible", 0o777)
  1975. file := filepath.Join("inaccessible", "a")
  1976. fd, err := testFs.Create(file)
  1977. must(t, err)
  1978. fd.Close()
  1979. m := setupModel(t, w)
  1980. defer cleanupModel(m)
  1981. must(t, testFs.Chmod("inaccessible", 0o000))
  1982. m.ScanFolder("default")
  1983. if file, ok := m.testCurrentFolderFile("default", file); !ok {
  1984. t.Fatalf("File missing in db")
  1985. } else if file.Deleted {
  1986. t.Errorf("Inaccessible file has been marked as deleted.")
  1987. }
  1988. }
  1989. // TestInternalScan checks whether various fs operations are correctly represented
  1990. // in the db after scanning.
  1991. func TestInternalScan(t *testing.T) {
  1992. w, fcfg, wCancel := newDefaultCfgWrapper()
  1993. defer wCancel()
  1994. testFs := fcfg.Filesystem(nil)
  1995. defer os.RemoveAll(testFs.URI())
  1996. testCases := map[string]func(protocol.FileInfo) bool{
  1997. "removeDir": func(f protocol.FileInfo) bool {
  1998. return !f.Deleted
  1999. },
  2000. "dirToFile": func(f protocol.FileInfo) bool {
  2001. return f.Deleted || f.IsDirectory()
  2002. },
  2003. }
  2004. baseDirs := []string{"dirToFile", "removeDir"}
  2005. for _, dir := range baseDirs {
  2006. sub := filepath.Join(dir, "subDir")
  2007. for _, dir := range []string{dir, sub} {
  2008. if err := testFs.MkdirAll(dir, 0o775); err != nil {
  2009. t.Fatalf("%v: %v", dir, err)
  2010. }
  2011. }
  2012. testCases[sub] = func(f protocol.FileInfo) bool {
  2013. return !f.Deleted
  2014. }
  2015. for _, dir := range []string{dir, sub} {
  2016. file := filepath.Join(dir, "a")
  2017. fd, err := testFs.Create(file)
  2018. must(t, err)
  2019. fd.Close()
  2020. testCases[file] = func(f protocol.FileInfo) bool {
  2021. return !f.Deleted
  2022. }
  2023. }
  2024. }
  2025. m := setupModel(t, w)
  2026. defer cleanupModel(m)
  2027. for _, dir := range baseDirs {
  2028. must(t, testFs.RemoveAll(dir))
  2029. }
  2030. fd, err := testFs.Create("dirToFile")
  2031. must(t, err)
  2032. fd.Close()
  2033. m.ScanFolder("default")
  2034. for path, cond := range testCases {
  2035. if f, ok := m.testCurrentFolderFile("default", path); !ok {
  2036. t.Fatalf("%v missing in db", path)
  2037. } else if cond(f) {
  2038. t.Errorf("Incorrect db entry for %v", path)
  2039. }
  2040. }
  2041. }
  2042. func TestCustomMarkerName(t *testing.T) {
  2043. fcfg := newFolderConfig()
  2044. fcfg.ID = "default"
  2045. fcfg.RescanIntervalS = 1
  2046. fcfg.MarkerName = "myfile"
  2047. cfg, cancel := newConfigWrapper(config.Configuration{
  2048. Version: config.CurrentVersion,
  2049. Folders: []config.FolderConfiguration{fcfg},
  2050. Devices: []config.DeviceConfiguration{
  2051. {
  2052. DeviceID: device1,
  2053. },
  2054. },
  2055. })
  2056. defer cancel()
  2057. ffs := fcfg.Filesystem(nil)
  2058. m := newModel(t, cfg, myID, nil)
  2059. set := newFileSet(t, "default", m.db)
  2060. set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  2061. {Name: "dummyfile"},
  2062. })
  2063. if err := ffs.Remove(config.DefaultMarkerName); err != nil {
  2064. t.Fatal(err)
  2065. }
  2066. sub := m.evLogger.Subscribe(events.StateChanged)
  2067. defer sub.Unsubscribe()
  2068. m.ServeBackground()
  2069. defer cleanupModel(m)
  2070. waitForState(t, sub, "default", config.ErrMarkerMissing.Error())
  2071. fd, _ := ffs.Create("myfile")
  2072. fd.Close()
  2073. waitForState(t, sub, "default", "")
  2074. }
  2075. func TestRemoveDirWithContent(t *testing.T) {
  2076. m, conn, fcfg, wcfgCancel := setupModelWithConnection(t)
  2077. defer wcfgCancel()
  2078. tfs := fcfg.Filesystem(nil)
  2079. defer cleanupModelAndRemoveDir(m, tfs.URI())
  2080. tfs.MkdirAll("dirwith", 0o755)
  2081. content := filepath.Join("dirwith", "content")
  2082. fd, err := tfs.Create(content)
  2083. must(t, err)
  2084. fd.Close()
  2085. must(t, m.ScanFolder(fcfg.ID))
  2086. dir, ok := m.testCurrentFolderFile(fcfg.ID, "dirwith")
  2087. if !ok {
  2088. t.Fatalf("Can't get dir \"dirwith\" after initial scan")
  2089. }
  2090. dir.Deleted = true
  2091. dir.Version = dir.Version.Update(device1.Short()).Update(device1.Short())
  2092. file, ok := m.testCurrentFolderFile(fcfg.ID, content)
  2093. if !ok {
  2094. t.Fatalf("Can't get file \"%v\" after initial scan", content)
  2095. }
  2096. file.Deleted = true
  2097. file.Version = file.Version.Update(device1.Short()).Update(device1.Short())
  2098. must(t, m.IndexUpdate(conn, fcfg.ID, []protocol.FileInfo{dir, file}))
  2099. // Is there something we could trigger on instead of just waiting?
  2100. timeout := time.NewTimer(5 * time.Second)
  2101. for {
  2102. dir, ok := m.testCurrentFolderFile(fcfg.ID, "dirwith")
  2103. if !ok {
  2104. t.Fatalf("Can't get dir \"dirwith\" after index update")
  2105. }
  2106. file, ok := m.testCurrentFolderFile(fcfg.ID, content)
  2107. if !ok {
  2108. t.Fatalf("Can't get file \"%v\" after index update", content)
  2109. }
  2110. if dir.Deleted && file.Deleted {
  2111. return
  2112. }
  2113. select {
  2114. case <-timeout.C:
  2115. if !dir.Deleted && !file.Deleted {
  2116. t.Errorf("Neither the dir nor its content was deleted before timing out.")
  2117. } else if !dir.Deleted {
  2118. t.Errorf("The dir was not deleted before timing out.")
  2119. } else {
  2120. t.Errorf("The content of the dir was not deleted before timing out.")
  2121. }
  2122. return
  2123. default:
  2124. time.Sleep(100 * time.Millisecond)
  2125. }
  2126. }
  2127. }
  2128. func TestIssue4475(t *testing.T) {
  2129. m, conn, fcfg, wcfgCancel := setupModelWithConnection(t)
  2130. defer wcfgCancel()
  2131. defer cleanupModel(m)
  2132. testFs := fcfg.Filesystem(nil)
  2133. // Scenario: Dir is deleted locally and before syncing/index exchange
  2134. // happens, a file is create in that dir on the remote.
  2135. // This should result in the directory being recreated and added to the
  2136. // db locally.
  2137. must(t, testFs.MkdirAll("delDir", 0o755))
  2138. m.ScanFolder("default")
  2139. if fcfg, ok := m.cfg.Folder("default"); !ok || !fcfg.SharedWith(device1) {
  2140. t.Fatal("not shared with device1")
  2141. }
  2142. fileName := filepath.Join("delDir", "file")
  2143. conn.addFile(fileName, 0o644, protocol.FileInfoTypeFile, nil)
  2144. conn.sendIndexUpdate()
  2145. // Is there something we could trigger on instead of just waiting?
  2146. timeout := time.NewTimer(5 * time.Second)
  2147. created := false
  2148. for {
  2149. if !created {
  2150. if _, ok := m.testCurrentFolderFile("default", fileName); ok {
  2151. created = true
  2152. }
  2153. } else {
  2154. dir, ok := m.testCurrentFolderFile("default", "delDir")
  2155. if !ok {
  2156. t.Fatalf("can't get dir from db")
  2157. }
  2158. if !dir.Deleted {
  2159. return
  2160. }
  2161. }
  2162. select {
  2163. case <-timeout.C:
  2164. if created {
  2165. t.Errorf("Timed out before file from remote was created")
  2166. } else {
  2167. t.Errorf("Timed out before directory was resurrected in db")
  2168. }
  2169. return
  2170. default:
  2171. time.Sleep(100 * time.Millisecond)
  2172. }
  2173. }
  2174. }
  2175. func TestVersionRestore(t *testing.T) {
  2176. t.Skip("incompatible with fakefs")
  2177. // We create a bunch of files which we restore
  2178. // In each file, we write the filename as the content
  2179. // We verify that the content matches at the expected filenames
  2180. // after the restore operation.
  2181. fcfg := newFolderConfiguration(defaultCfgWrapper, "default", "default", fs.FilesystemTypeFake, srand.String(32))
  2182. fcfg.Versioning.Type = "simple"
  2183. fcfg.FSWatcherEnabled = false
  2184. filesystem := fcfg.Filesystem(nil)
  2185. rawConfig := config.Configuration{
  2186. Version: config.CurrentVersion,
  2187. Folders: []config.FolderConfiguration{fcfg},
  2188. }
  2189. cfg, cancel := newConfigWrapper(rawConfig)
  2190. defer cancel()
  2191. m := setupModel(t, cfg)
  2192. defer cleanupModel(m)
  2193. m.ScanFolder("default")
  2194. sentinel, err := time.ParseInLocation(versioner.TimeFormat, "20180101-010101", time.Local)
  2195. if err != nil {
  2196. t.Fatal(err)
  2197. }
  2198. for _, file := range []string{
  2199. // Versions directory
  2200. ".stversions/file~20171210-040404.txt", // will be restored
  2201. ".stversions/existing~20171210-040404", // exists, should expect to be archived.
  2202. ".stversions/something~20171210-040404", // will become directory, hence error
  2203. ".stversions/dir/file~20171210-040404.txt",
  2204. ".stversions/dir/file~20171210-040405.txt",
  2205. ".stversions/dir/file~20171210-040406.txt",
  2206. ".stversions/very/very/deep/one~20171210-040406.txt", // lives deep down, no directory exists.
  2207. ".stversions/dir/existing~20171210-040406.txt", // exists, should expect to be archived.
  2208. ".stversions/dir/cat", // untagged which was used by trashcan, supported
  2209. // "file.txt" will be restored
  2210. "existing",
  2211. "something/file", // Becomes directory
  2212. "dir/file.txt",
  2213. "dir/existing.txt",
  2214. } {
  2215. if build.IsWindows {
  2216. file = filepath.FromSlash(file)
  2217. }
  2218. dir := filepath.Dir(file)
  2219. must(t, filesystem.MkdirAll(dir, 0o755))
  2220. if fd, err := filesystem.Create(file); err != nil {
  2221. t.Fatal(err)
  2222. } else if _, err := fd.Write([]byte(file)); err != nil {
  2223. t.Fatal(err)
  2224. } else if err := fd.Close(); err != nil {
  2225. t.Fatal(err)
  2226. } else if err := filesystem.Chtimes(file, sentinel, sentinel); err != nil {
  2227. t.Fatal(err)
  2228. }
  2229. }
  2230. versions, err := m.GetFolderVersions("default")
  2231. must(t, err)
  2232. expectedVersions := map[string]int{
  2233. "file.txt": 1,
  2234. "existing": 1,
  2235. "something": 1,
  2236. "dir/file.txt": 3,
  2237. "dir/existing.txt": 1,
  2238. "very/very/deep/one.txt": 1,
  2239. "dir/cat": 1,
  2240. }
  2241. for name, vers := range versions {
  2242. cnt, ok := expectedVersions[name]
  2243. if !ok {
  2244. t.Errorf("unexpected %s", name)
  2245. }
  2246. if len(vers) != cnt {
  2247. t.Errorf("%s: %d != %d", name, cnt, len(vers))
  2248. }
  2249. // Delete, so we can check if we didn't hit something we expect afterwards.
  2250. delete(expectedVersions, name)
  2251. }
  2252. for name := range expectedVersions {
  2253. t.Errorf("not found expected %s", name)
  2254. }
  2255. // Restoring non existing folder fails.
  2256. _, err = m.RestoreFolderVersions("does not exist", nil)
  2257. if err == nil {
  2258. t.Errorf("expected an error")
  2259. }
  2260. makeTime := func(s string) time.Time {
  2261. tm, err := time.ParseInLocation(versioner.TimeFormat, s, time.Local)
  2262. if err != nil {
  2263. t.Error(err)
  2264. }
  2265. return tm.Truncate(time.Second)
  2266. }
  2267. restore := map[string]time.Time{
  2268. "file.txt": makeTime("20171210-040404"),
  2269. "existing": makeTime("20171210-040404"),
  2270. "something": makeTime("20171210-040404"),
  2271. "dir/file.txt": makeTime("20171210-040406"),
  2272. "dir/existing.txt": makeTime("20171210-040406"),
  2273. "very/very/deep/one.txt": makeTime("20171210-040406"),
  2274. }
  2275. beforeRestore := time.Now().Truncate(time.Second)
  2276. ferr, err := m.RestoreFolderVersions("default", restore)
  2277. must(t, err)
  2278. if err, ok := ferr["something"]; len(ferr) > 1 || !ok || !errors.Is(err, versioner.ErrDirectory) {
  2279. t.Fatalf("incorrect error or count: %d %s", len(ferr), ferr)
  2280. }
  2281. // Failed items are not expected to be restored.
  2282. // Remove them from expectations
  2283. for name := range ferr {
  2284. delete(restore, name)
  2285. }
  2286. // Check that content of files matches to the version they've been restored.
  2287. for file, version := range restore {
  2288. if build.IsWindows {
  2289. file = filepath.FromSlash(file)
  2290. }
  2291. tag := version.In(time.Local).Truncate(time.Second).Format(versioner.TimeFormat)
  2292. taggedName := filepath.Join(versioner.DefaultPath, versioner.TagFilename(file, tag))
  2293. fd, err := filesystem.Open(file)
  2294. if err != nil {
  2295. t.Error(err)
  2296. }
  2297. defer fd.Close()
  2298. content, err := io.ReadAll(fd)
  2299. if err != nil {
  2300. t.Error(err)
  2301. }
  2302. if !bytes.Equal(content, []byte(taggedName)) {
  2303. t.Errorf("%s: %s != %s", file, string(content), taggedName)
  2304. }
  2305. }
  2306. // Simple versioner uses now for timestamp generation, so we can check
  2307. // if existing stuff was correctly archived as we restored (oppose to deleteD), and version time as after beforeRestore
  2308. expectArchived := map[string]struct{}{
  2309. "existing": {},
  2310. "dir/file.txt": {},
  2311. "dir/existing.txt": {},
  2312. }
  2313. allFileVersions, err := m.GetFolderVersions("default")
  2314. must(t, err)
  2315. for file, versions := range allFileVersions {
  2316. key := file
  2317. if build.IsWindows {
  2318. file = filepath.FromSlash(file)
  2319. }
  2320. for _, version := range versions {
  2321. if version.VersionTime.Equal(beforeRestore) || version.VersionTime.After(beforeRestore) {
  2322. fd, err := filesystem.Open(versioner.DefaultPath + "/" + versioner.TagFilename(file, version.VersionTime.Format(versioner.TimeFormat)))
  2323. must(t, err)
  2324. defer fd.Close()
  2325. content, err := io.ReadAll(fd)
  2326. if err != nil {
  2327. t.Error(err)
  2328. }
  2329. // Even if they are at the archived path, content should have the non
  2330. // archived name.
  2331. if !bytes.Equal(content, []byte(file)) {
  2332. t.Errorf("%s (%s): %s != %s", file, fd.Name(), string(content), file)
  2333. }
  2334. _, ok := expectArchived[key]
  2335. if !ok {
  2336. t.Error("unexpected archived file with future timestamp", file, version.VersionTime)
  2337. }
  2338. delete(expectArchived, key)
  2339. }
  2340. }
  2341. }
  2342. if len(expectArchived) != 0 {
  2343. t.Fatal("missed some archived files", expectArchived)
  2344. }
  2345. }
  2346. func TestPausedFolders(t *testing.T) {
  2347. // Create a separate wrapper not to pollute other tests.
  2348. wrapper, cancel := newConfigWrapper(defaultCfgWrapper.RawCopy())
  2349. defer cancel()
  2350. m := setupModel(t, wrapper)
  2351. defer cleanupModel(m)
  2352. if err := m.ScanFolder("default"); err != nil {
  2353. t.Error(err)
  2354. }
  2355. pausedConfig := wrapper.RawCopy()
  2356. pausedConfig.Folders[0].Paused = true
  2357. replace(t, wrapper, pausedConfig)
  2358. if err := m.ScanFolder("default"); err != ErrFolderPaused {
  2359. t.Errorf("Expected folder paused error, received: %v", err)
  2360. }
  2361. if err := m.ScanFolder("nonexistent"); err != ErrFolderMissing {
  2362. t.Errorf("Expected missing folder error, received: %v", err)
  2363. }
  2364. }
  2365. func TestIssue4094(t *testing.T) {
  2366. // Create a separate wrapper not to pollute other tests.
  2367. wrapper, cancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
  2368. defer cancel()
  2369. m := newModel(t, wrapper, myID, nil)
  2370. m.ServeBackground()
  2371. defer cleanupModel(m)
  2372. // Force the model to wire itself and add the folders
  2373. folderPath := "nonexistent"
  2374. cfg := defaultCfgWrapper.RawCopy()
  2375. fcfg := config.FolderConfiguration{
  2376. FilesystemType: fs.FilesystemTypeFake,
  2377. ID: "folder1",
  2378. Path: folderPath,
  2379. Paused: true,
  2380. Devices: []config.FolderDeviceConfiguration{
  2381. {DeviceID: device1},
  2382. },
  2383. }
  2384. cfg.Folders = []config.FolderConfiguration{fcfg}
  2385. replace(t, wrapper, cfg)
  2386. if err := m.SetIgnores(fcfg.ID, []string{"foo"}); err != nil {
  2387. t.Fatalf("failed setting ignores: %v", err)
  2388. }
  2389. if _, err := fcfg.Filesystem(nil).Lstat(".stignore"); err != nil {
  2390. t.Fatalf("failed stating .stignore: %v", err)
  2391. }
  2392. }
  2393. func TestIssue4903(t *testing.T) {
  2394. wrapper, cancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
  2395. defer cancel()
  2396. m := setupModel(t, wrapper)
  2397. defer cleanupModel(m)
  2398. // Force the model to wire itself and add the folders
  2399. folderPath := "nonexistent"
  2400. cfg := defaultCfgWrapper.RawCopy()
  2401. fcfg := config.FolderConfiguration{
  2402. ID: "folder1",
  2403. Path: folderPath,
  2404. Paused: true,
  2405. Devices: []config.FolderDeviceConfiguration{
  2406. {DeviceID: device1},
  2407. },
  2408. }
  2409. cfg.Folders = []config.FolderConfiguration{fcfg}
  2410. replace(t, wrapper, cfg)
  2411. if err := fcfg.CheckPath(); err != config.ErrPathMissing {
  2412. t.Fatalf("expected path missing error, got: %v, debug: %s", err, fcfg.CheckPath())
  2413. }
  2414. if _, err := fcfg.Filesystem(nil).Lstat("."); !fs.IsNotExist(err) {
  2415. t.Fatalf("Expected missing path error, got: %v", err)
  2416. }
  2417. }
  2418. func TestIssue5002(t *testing.T) {
  2419. // recheckFile should not panic when given an index equal to the number of blocks
  2420. w, fcfg, wCancel := newDefaultCfgWrapper()
  2421. defer wCancel()
  2422. ffs := fcfg.Filesystem(nil)
  2423. fd, err := ffs.Create("foo")
  2424. must(t, err)
  2425. _, err = fd.Write([]byte("foobar"))
  2426. must(t, err)
  2427. fd.Close()
  2428. m := setupModel(t, w)
  2429. defer cleanupModel(m)
  2430. if err := m.ScanFolder("default"); err != nil {
  2431. t.Error(err)
  2432. }
  2433. file, ok := m.testCurrentFolderFile("default", "foo")
  2434. if !ok {
  2435. t.Fatal("test file should exist")
  2436. }
  2437. blockSize := int32(file.BlockSize())
  2438. m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size-int64(blockSize), []byte{1, 2, 3, 4}, 0)
  2439. m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size, []byte{1, 2, 3, 4}, 0) // panic
  2440. m.recheckFile(protocol.LocalDeviceID, "default", "foo", file.Size+int64(blockSize), []byte{1, 2, 3, 4}, 0)
  2441. }
  2442. func TestParentOfUnignored(t *testing.T) {
  2443. w, fcfg, wCancel := newDefaultCfgWrapper()
  2444. defer wCancel()
  2445. ffs := fcfg.Filesystem(nil)
  2446. must(t, ffs.Mkdir("bar", 0o755))
  2447. must(t, ffs.Mkdir("baz", 0o755))
  2448. must(t, ffs.Mkdir("baz/quux", 0o755))
  2449. m := setupModel(t, w)
  2450. defer cleanupModel(m)
  2451. m.SetIgnores("default", []string{"!quux", "*"})
  2452. m.ScanFolder("default")
  2453. if bar, ok := m.testCurrentFolderFile("default", "bar"); !ok {
  2454. t.Error(`Directory "bar" missing in db`)
  2455. } else if !bar.IsIgnored() {
  2456. t.Error(`Directory "bar" is not ignored`)
  2457. }
  2458. if baz, ok := m.testCurrentFolderFile("default", "baz"); !ok {
  2459. t.Error(`Directory "baz" missing in db`)
  2460. } else if baz.IsIgnored() {
  2461. t.Error(`Directory "baz" is ignored`)
  2462. }
  2463. }
  2464. // TestFolderRestartZombies reproduces issue 5233, where multiple concurrent folder
  2465. // restarts would leave more than one folder runner alive.
  2466. func TestFolderRestartZombies(t *testing.T) {
  2467. wrapper, cancel := newConfigWrapper(defaultCfg.Copy())
  2468. defer cancel()
  2469. waiter, err := wrapper.Modify(func(cfg *config.Configuration) {
  2470. cfg.Options.RawMaxFolderConcurrency = -1
  2471. _, i, _ := cfg.Folder("default")
  2472. cfg.Folders[i].FilesystemType = fs.FilesystemTypeFake
  2473. })
  2474. must(t, err)
  2475. waiter.Wait()
  2476. folderCfg, _ := wrapper.Folder("default")
  2477. m := setupModel(t, wrapper)
  2478. defer cleanupModel(m)
  2479. // Make sure the folder is up and running, because we want to count it.
  2480. m.ScanFolder("default")
  2481. // Check how many running folders we have running before the test.
  2482. if r := m.foldersRunning.Load(); r != 1 {
  2483. t.Error("Expected one running folder, not", r)
  2484. }
  2485. // Run a few parallel configuration changers for one second. Each waits
  2486. // for the commit to complete, but there are many of them.
  2487. var wg sync.WaitGroup
  2488. for i := 0; i < 25; i++ {
  2489. wg.Add(1)
  2490. go func() {
  2491. defer wg.Done()
  2492. t0 := time.Now()
  2493. for time.Since(t0) < time.Second {
  2494. fcfg := folderCfg.Copy()
  2495. fcfg.MaxConflicts = mrand.Int() // safe change that should cause a folder restart
  2496. setFolder(t, wrapper, fcfg)
  2497. }
  2498. }()
  2499. }
  2500. // Wait for the above to complete and check how many folders we have
  2501. // running now. It should not have increased.
  2502. wg.Wait()
  2503. // Make sure the folder is up and running, because we want to count it.
  2504. m.ScanFolder("default")
  2505. if r := m.foldersRunning.Load(); r != 1 {
  2506. t.Error("Expected one running folder, not", r)
  2507. }
  2508. }
  2509. func TestRequestLimit(t *testing.T) {
  2510. wrapper, fcfg, cancel := newDefaultCfgWrapper()
  2511. ffs := fcfg.Filesystem(nil)
  2512. file := "tmpfile"
  2513. fd, err := ffs.Create(file)
  2514. must(t, err)
  2515. fd.Close()
  2516. defer cancel()
  2517. waiter, err := wrapper.Modify(func(cfg *config.Configuration) {
  2518. _, i, _ := cfg.Device(device1)
  2519. cfg.Devices[i].MaxRequestKiB = 1
  2520. })
  2521. must(t, err)
  2522. waiter.Wait()
  2523. m, conn := setupModelWithConnectionFromWrapper(t, wrapper)
  2524. defer cleanupModel(m)
  2525. m.ScanFolder("default")
  2526. befReq := time.Now()
  2527. first, err := m.Request(conn, "default", file, 0, 2000, 0, nil, 0, false)
  2528. if err != nil {
  2529. t.Fatalf("First request failed: %v", err)
  2530. }
  2531. reqDur := time.Since(befReq)
  2532. returned := make(chan struct{})
  2533. go func() {
  2534. second, err := m.Request(conn, "default", file, 0, 2000, 0, nil, 0, false)
  2535. if err != nil {
  2536. t.Errorf("Second request failed: %v", err)
  2537. }
  2538. close(returned)
  2539. second.Close()
  2540. }()
  2541. time.Sleep(10 * reqDur)
  2542. select {
  2543. case <-returned:
  2544. t.Fatalf("Second request returned before first was done")
  2545. default:
  2546. }
  2547. first.Close()
  2548. select {
  2549. case <-returned:
  2550. case <-time.After(time.Second):
  2551. t.Fatalf("Second request did not return after first was done")
  2552. }
  2553. }
  2554. // TestConnCloseOnRestart checks that there is no deadlock when calling Close
  2555. // on a protocol connection that has a blocking reader (blocking writer can't
  2556. // be done as the test requires clusterconfigs to go through).
  2557. func TestConnCloseOnRestart(t *testing.T) {
  2558. oldCloseTimeout := protocol.CloseTimeout
  2559. protocol.CloseTimeout = 100 * time.Millisecond
  2560. defer func() {
  2561. protocol.CloseTimeout = oldCloseTimeout
  2562. }()
  2563. w, fcfg, wCancel := newDefaultCfgWrapper()
  2564. defer wCancel()
  2565. m := setupModel(t, w)
  2566. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  2567. br := &testutil.BlockingRW{}
  2568. nw := &testutil.NoopRW{}
  2569. ci := &protocolmocks.ConnectionInfo{}
  2570. ci.ConnectionIDReturns(srand.String(16))
  2571. m.AddConnection(protocol.NewConnection(device1, br, nw, testutil.NoopCloser{}, m, ci, protocol.CompressionNever, nil, m.keyGen), protocol.Hello{})
  2572. m.pmut.RLock()
  2573. if len(m.closed) != 1 {
  2574. t.Fatalf("Expected just one conn (len(m.closed) == %v)", len(m.closed))
  2575. }
  2576. var closed chan struct{}
  2577. for _, c := range m.closed {
  2578. closed = c
  2579. }
  2580. m.pmut.RUnlock()
  2581. waiter, err := w.RemoveDevice(device1)
  2582. if err != nil {
  2583. t.Fatal(err)
  2584. }
  2585. done := make(chan struct{})
  2586. go func() {
  2587. waiter.Wait()
  2588. close(done)
  2589. }()
  2590. select {
  2591. case <-done:
  2592. case <-time.After(5 * time.Second):
  2593. t.Fatal("Timed out before config took effect")
  2594. }
  2595. select {
  2596. case <-closed:
  2597. case <-time.After(5 * time.Second):
  2598. t.Fatal("Timed out before connection was closed")
  2599. }
  2600. }
  2601. func TestModTimeWindow(t *testing.T) {
  2602. w, fcfg, wCancel := newDefaultCfgWrapper()
  2603. defer wCancel()
  2604. tfs := modtimeTruncatingFS{
  2605. trunc: 0,
  2606. Filesystem: fcfg.Filesystem(nil),
  2607. }
  2608. // fcfg.RawModTimeWindowS = 2
  2609. setFolder(t, w, fcfg)
  2610. m := setupModel(t, w)
  2611. defer cleanupModelAndRemoveDir(m, tfs.URI())
  2612. name := "foo"
  2613. fd, err := tfs.Create(name)
  2614. must(t, err)
  2615. stat, err := fd.Stat()
  2616. must(t, err)
  2617. modTime := stat.ModTime()
  2618. fd.Close()
  2619. m.ScanFolders()
  2620. // Get current version
  2621. fi, ok := m.testCurrentFolderFile("default", name)
  2622. if !ok {
  2623. t.Fatal("File missing")
  2624. }
  2625. v := fi.Version
  2626. // Change the filesystem to only return modtimes to the closest two
  2627. // seconds, like FAT.
  2628. tfs.trunc = 2 * time.Second
  2629. // Scan again
  2630. m.ScanFolders()
  2631. // No change due to within window
  2632. fi, _ = m.testCurrentFolderFile("default", name)
  2633. if !fi.Version.Equal(v) {
  2634. t.Fatalf("Got version %v, expected %v", fi.Version, v)
  2635. }
  2636. // Update to be outside window
  2637. err = tfs.Chtimes(name, time.Now(), modTime.Add(2*time.Second))
  2638. must(t, err)
  2639. m.ScanFolders()
  2640. // Version should have updated
  2641. fi, _ = m.testCurrentFolderFile("default", name)
  2642. if fi.Version.Compare(v) != protocol.Greater {
  2643. t.Fatalf("Got result %v, expected %v", fi.Version.Compare(v), protocol.Greater)
  2644. }
  2645. }
  2646. func TestDevicePause(t *testing.T) {
  2647. m, _, fcfg, wcfgCancel := setupModelWithConnection(t)
  2648. defer wcfgCancel()
  2649. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  2650. sub := m.evLogger.Subscribe(events.DevicePaused)
  2651. defer sub.Unsubscribe()
  2652. m.pmut.RLock()
  2653. var closed chan struct{}
  2654. for _, c := range m.closed {
  2655. closed = c
  2656. }
  2657. m.pmut.RUnlock()
  2658. pauseDevice(t, m.cfg, device1, true)
  2659. timeout := time.NewTimer(5 * time.Second)
  2660. select {
  2661. case <-sub.C():
  2662. select {
  2663. case <-closed:
  2664. case <-timeout.C:
  2665. t.Fatal("Timed out before connection was closed")
  2666. }
  2667. case <-timeout.C:
  2668. t.Fatal("Timed out before device was paused")
  2669. }
  2670. }
  2671. func TestDeviceWasSeen(t *testing.T) {
  2672. m, _, fcfg, wcfgCancel := setupModelWithConnection(t)
  2673. defer wcfgCancel()
  2674. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  2675. m.deviceWasSeen(device1)
  2676. stats, err := m.DeviceStatistics()
  2677. if err != nil {
  2678. t.Error("Unexpected error:", err)
  2679. }
  2680. entry := stats[device1]
  2681. if time.Since(entry.LastSeen) > time.Second {
  2682. t.Error("device should have been seen now")
  2683. }
  2684. }
  2685. func TestNewLimitedRequestResponse(t *testing.T) {
  2686. l0 := semaphore.New(0)
  2687. l1 := semaphore.New(1024)
  2688. l2 := (*semaphore.Semaphore)(nil)
  2689. // Should take 500 bytes from any non-unlimited non-nil limiters.
  2690. res := newLimitedRequestResponse(500, l0, l1, l2)
  2691. if l1.Available() != 1024-500 {
  2692. t.Error("should have taken bytes from limited limiter")
  2693. }
  2694. // Closing the result should return the bytes.
  2695. res.Close()
  2696. // Try to take 1024 bytes to make sure the bytes were returned.
  2697. done := make(chan struct{})
  2698. go func() {
  2699. l1.Take(1024)
  2700. close(done)
  2701. }()
  2702. select {
  2703. case <-done:
  2704. case <-time.After(time.Second):
  2705. t.Error("Bytes weren't returned in a timely fashion")
  2706. }
  2707. }
  2708. func TestSummaryPausedNoError(t *testing.T) {
  2709. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  2710. defer wcfgCancel()
  2711. pauseFolder(t, wcfg, fcfg.ID, true)
  2712. m := setupModel(t, wcfg)
  2713. defer cleanupModel(m)
  2714. fss := NewFolderSummaryService(wcfg, m, myID, events.NoopLogger)
  2715. if _, err := fss.Summary(fcfg.ID); err != nil {
  2716. t.Error("Expected no error getting a summary for a paused folder:", err)
  2717. }
  2718. }
  2719. func TestFolderAPIErrors(t *testing.T) {
  2720. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  2721. defer wcfgCancel()
  2722. pauseFolder(t, wcfg, fcfg.ID, true)
  2723. m := setupModel(t, wcfg)
  2724. defer cleanupModel(m)
  2725. methods := []func(folder string) error{
  2726. m.ScanFolder,
  2727. func(folder string) error {
  2728. return m.ScanFolderSubdirs(folder, nil)
  2729. },
  2730. func(folder string) error {
  2731. _, err := m.GetFolderVersions(folder)
  2732. return err
  2733. },
  2734. func(folder string) error {
  2735. _, err := m.RestoreFolderVersions(folder, nil)
  2736. return err
  2737. },
  2738. }
  2739. for i, method := range methods {
  2740. if err := method(fcfg.ID); err != ErrFolderPaused {
  2741. t.Errorf(`Expected "%v", got "%v" (method no %v)`, ErrFolderPaused, err, i)
  2742. }
  2743. if err := method("notexisting"); err != ErrFolderMissing {
  2744. t.Errorf(`Expected "%v", got "%v" (method no %v)`, ErrFolderMissing, err, i)
  2745. }
  2746. }
  2747. }
  2748. func TestRenameSequenceOrder(t *testing.T) {
  2749. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  2750. defer wcfgCancel()
  2751. m := setupModel(t, wcfg)
  2752. defer cleanupModel(m)
  2753. numFiles := 20
  2754. ffs := fcfg.Filesystem(nil)
  2755. for i := 0; i < numFiles; i++ {
  2756. v := fmt.Sprintf("%d", i)
  2757. writeFile(t, ffs, v, []byte(v))
  2758. }
  2759. m.ScanFolders()
  2760. count := 0
  2761. snap := dbSnapshot(t, m, "default")
  2762. snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileIntf) bool {
  2763. count++
  2764. return true
  2765. })
  2766. snap.Release()
  2767. if count != numFiles {
  2768. t.Errorf("Unexpected count: %d != %d", count, numFiles)
  2769. }
  2770. // Modify all the files, other than the ones we expect to rename
  2771. for i := 0; i < numFiles; i++ {
  2772. if i == 3 || i == 17 || i == 16 || i == 4 {
  2773. continue
  2774. }
  2775. v := fmt.Sprintf("%d", i)
  2776. writeFile(t, ffs, v, []byte(v+"-new"))
  2777. }
  2778. // Rename
  2779. must(t, ffs.Rename("3", "17"))
  2780. must(t, ffs.Rename("16", "4"))
  2781. // Scan
  2782. m.ScanFolders()
  2783. // Verify sequence of a appearing is followed by c disappearing.
  2784. snap = dbSnapshot(t, m, "default")
  2785. defer snap.Release()
  2786. var firstExpectedSequence int64
  2787. var secondExpectedSequence int64
  2788. failed := false
  2789. snap.WithHaveSequence(0, func(i protocol.FileIntf) bool {
  2790. t.Log(i)
  2791. if i.FileName() == "17" {
  2792. firstExpectedSequence = i.SequenceNo() + 1
  2793. }
  2794. if i.FileName() == "4" {
  2795. secondExpectedSequence = i.SequenceNo() + 1
  2796. }
  2797. if i.FileName() == "3" {
  2798. failed = i.SequenceNo() != firstExpectedSequence || failed
  2799. }
  2800. if i.FileName() == "16" {
  2801. failed = i.SequenceNo() != secondExpectedSequence || failed
  2802. }
  2803. return true
  2804. })
  2805. if failed {
  2806. t.Fail()
  2807. }
  2808. }
  2809. func TestRenameSameFile(t *testing.T) {
  2810. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  2811. defer wcfgCancel()
  2812. m := setupModel(t, wcfg)
  2813. defer cleanupModel(m)
  2814. ffs := fcfg.Filesystem(nil)
  2815. writeFile(t, ffs, "file", []byte("file"))
  2816. m.ScanFolders()
  2817. count := 0
  2818. snap := dbSnapshot(t, m, "default")
  2819. snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileIntf) bool {
  2820. count++
  2821. return true
  2822. })
  2823. snap.Release()
  2824. if count != 1 {
  2825. t.Errorf("Unexpected count: %d != %d", count, 1)
  2826. }
  2827. must(t, ffs.Rename("file", "file1"))
  2828. must(t, osutil.Copy(fs.CopyRangeMethodStandard, ffs, ffs, "file1", "file0"))
  2829. must(t, osutil.Copy(fs.CopyRangeMethodStandard, ffs, ffs, "file1", "file2"))
  2830. must(t, osutil.Copy(fs.CopyRangeMethodStandard, ffs, ffs, "file1", "file3"))
  2831. must(t, osutil.Copy(fs.CopyRangeMethodStandard, ffs, ffs, "file1", "file4"))
  2832. m.ScanFolders()
  2833. snap = dbSnapshot(t, m, "default")
  2834. defer snap.Release()
  2835. prevSeq := int64(0)
  2836. seen := false
  2837. snap.WithHaveSequence(0, func(i protocol.FileIntf) bool {
  2838. if i.SequenceNo() <= prevSeq {
  2839. t.Fatalf("non-increasing sequences: %d <= %d", i.SequenceNo(), prevSeq)
  2840. }
  2841. if i.FileName() == "file" {
  2842. if seen {
  2843. t.Fatal("already seen file")
  2844. }
  2845. seen = true
  2846. }
  2847. prevSeq = i.SequenceNo()
  2848. return true
  2849. })
  2850. }
  2851. func TestRenameEmptyFile(t *testing.T) {
  2852. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  2853. defer wcfgCancel()
  2854. m := setupModel(t, wcfg)
  2855. defer cleanupModel(m)
  2856. ffs := fcfg.Filesystem(nil)
  2857. writeFile(t, ffs, "file", []byte("data"))
  2858. writeFile(t, ffs, "empty", nil)
  2859. m.ScanFolders()
  2860. snap := dbSnapshot(t, m, "default")
  2861. defer snap.Release()
  2862. empty, eok := snap.Get(protocol.LocalDeviceID, "empty")
  2863. if !eok {
  2864. t.Fatal("failed to find empty file")
  2865. }
  2866. file, fok := snap.Get(protocol.LocalDeviceID, "file")
  2867. if !fok {
  2868. t.Fatal("failed to find non-empty file")
  2869. }
  2870. count := 0
  2871. snap.WithBlocksHash(empty.BlocksHash, func(_ protocol.FileIntf) bool {
  2872. count++
  2873. return true
  2874. })
  2875. if count != 0 {
  2876. t.Fatalf("Found %d entries for empty file, expected 0", count)
  2877. }
  2878. count = 0
  2879. snap.WithBlocksHash(file.BlocksHash, func(_ protocol.FileIntf) bool {
  2880. count++
  2881. return true
  2882. })
  2883. if count != 1 {
  2884. t.Fatalf("Found %d entries for non-empty file, expected 1", count)
  2885. }
  2886. must(t, ffs.Rename("file", "new-file"))
  2887. must(t, ffs.Rename("empty", "new-empty"))
  2888. // Scan
  2889. m.ScanFolders()
  2890. snap = dbSnapshot(t, m, "default")
  2891. defer snap.Release()
  2892. count = 0
  2893. snap.WithBlocksHash(empty.BlocksHash, func(_ protocol.FileIntf) bool {
  2894. count++
  2895. return true
  2896. })
  2897. if count != 0 {
  2898. t.Fatalf("Found %d entries for empty file, expected 0", count)
  2899. }
  2900. count = 0
  2901. snap.WithBlocksHash(file.BlocksHash, func(i protocol.FileIntf) bool {
  2902. count++
  2903. if i.FileName() != "new-file" {
  2904. t.Fatalf("unexpected file name %s, expected new-file", i.FileName())
  2905. }
  2906. return true
  2907. })
  2908. if count != 1 {
  2909. t.Fatalf("Found %d entries for non-empty file, expected 1", count)
  2910. }
  2911. }
  2912. func TestBlockListMap(t *testing.T) {
  2913. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  2914. defer wcfgCancel()
  2915. m := setupModel(t, wcfg)
  2916. defer cleanupModel(m)
  2917. ffs := fcfg.Filesystem(nil)
  2918. writeFile(t, ffs, "one", []byte("content"))
  2919. writeFile(t, ffs, "two", []byte("content"))
  2920. writeFile(t, ffs, "three", []byte("content"))
  2921. writeFile(t, ffs, "four", []byte("content"))
  2922. writeFile(t, ffs, "five", []byte("content"))
  2923. m.ScanFolders()
  2924. snap := dbSnapshot(t, m, "default")
  2925. defer snap.Release()
  2926. fi, ok := snap.Get(protocol.LocalDeviceID, "one")
  2927. if !ok {
  2928. t.Error("failed to find existing file")
  2929. }
  2930. var paths []string
  2931. snap.WithBlocksHash(fi.BlocksHash, func(fi protocol.FileIntf) bool {
  2932. paths = append(paths, fi.FileName())
  2933. return true
  2934. })
  2935. snap.Release()
  2936. expected := []string{"one", "two", "three", "four", "five"}
  2937. if !equalStringsInAnyOrder(paths, expected) {
  2938. t.Errorf("expected %q got %q", expected, paths)
  2939. }
  2940. // Fudge the files around
  2941. // Remove
  2942. must(t, ffs.Remove("one"))
  2943. // Modify
  2944. must(t, ffs.Remove("two"))
  2945. writeFile(t, ffs, "two", []byte("mew-content"))
  2946. // Rename
  2947. must(t, ffs.Rename("three", "new-three"))
  2948. // Change type
  2949. must(t, ffs.Remove("four"))
  2950. must(t, ffs.Mkdir("four", 0o644))
  2951. m.ScanFolders()
  2952. // Check we're left with 2 of the 5
  2953. snap = dbSnapshot(t, m, "default")
  2954. defer snap.Release()
  2955. paths = paths[:0]
  2956. snap.WithBlocksHash(fi.BlocksHash, func(fi protocol.FileIntf) bool {
  2957. paths = append(paths, fi.FileName())
  2958. return true
  2959. })
  2960. snap.Release()
  2961. expected = []string{"new-three", "five"}
  2962. if !equalStringsInAnyOrder(paths, expected) {
  2963. t.Errorf("expected %q got %q", expected, paths)
  2964. }
  2965. }
  2966. func TestScanRenameCaseOnly(t *testing.T) {
  2967. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  2968. defer wcfgCancel()
  2969. m := setupModel(t, wcfg)
  2970. defer cleanupModel(m)
  2971. ffs := fcfg.Filesystem(nil)
  2972. name := "foo"
  2973. writeFile(t, ffs, name, []byte("contents"))
  2974. m.ScanFolders()
  2975. snap := dbSnapshot(t, m, fcfg.ID)
  2976. defer snap.Release()
  2977. found := false
  2978. snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileIntf) bool {
  2979. if found {
  2980. t.Fatal("got more than one file")
  2981. }
  2982. if i.FileName() != name {
  2983. t.Fatalf("got file %v, expected %v", i.FileName(), name)
  2984. }
  2985. found = true
  2986. return true
  2987. })
  2988. snap.Release()
  2989. upper := strings.ToUpper(name)
  2990. must(t, ffs.Rename(name, upper))
  2991. m.ScanFolders()
  2992. snap = dbSnapshot(t, m, fcfg.ID)
  2993. defer snap.Release()
  2994. found = false
  2995. snap.WithHave(protocol.LocalDeviceID, func(i protocol.FileIntf) bool {
  2996. if i.FileName() == name {
  2997. if i.IsDeleted() {
  2998. return true
  2999. }
  3000. t.Fatal("renamed file not deleted")
  3001. }
  3002. if i.FileName() != upper {
  3003. t.Fatalf("got file %v, expected %v", i.FileName(), upper)
  3004. }
  3005. if found {
  3006. t.Fatal("got more than the expected files")
  3007. }
  3008. found = true
  3009. return true
  3010. })
  3011. }
  3012. func TestClusterConfigOnFolderAdd(t *testing.T) {
  3013. testConfigChangeTriggersClusterConfigs(t, false, true, nil, func(wrapper config.Wrapper) {
  3014. fcfg := newFolderConfig()
  3015. fcfg.ID = "second"
  3016. fcfg.Label = "second"
  3017. fcfg.Devices = []config.FolderDeviceConfiguration{{
  3018. DeviceID: device2,
  3019. IntroducedBy: protocol.EmptyDeviceID,
  3020. }}
  3021. setFolder(t, wrapper, fcfg)
  3022. })
  3023. }
  3024. func TestClusterConfigOnFolderShare(t *testing.T) {
  3025. testConfigChangeTriggersClusterConfigs(t, true, true, nil, func(cfg config.Wrapper) {
  3026. fcfg := cfg.FolderList()[0]
  3027. fcfg.Devices = []config.FolderDeviceConfiguration{{
  3028. DeviceID: device2,
  3029. IntroducedBy: protocol.EmptyDeviceID,
  3030. }}
  3031. setFolder(t, cfg, fcfg)
  3032. })
  3033. }
  3034. func TestClusterConfigOnFolderUnshare(t *testing.T) {
  3035. testConfigChangeTriggersClusterConfigs(t, true, false, nil, func(cfg config.Wrapper) {
  3036. fcfg := cfg.FolderList()[0]
  3037. fcfg.Devices = nil
  3038. setFolder(t, cfg, fcfg)
  3039. })
  3040. }
  3041. func TestClusterConfigOnFolderRemove(t *testing.T) {
  3042. testConfigChangeTriggersClusterConfigs(t, true, false, nil, func(cfg config.Wrapper) {
  3043. rcfg := cfg.RawCopy()
  3044. rcfg.Folders = nil
  3045. replace(t, cfg, rcfg)
  3046. })
  3047. }
  3048. func TestClusterConfigOnFolderPause(t *testing.T) {
  3049. testConfigChangeTriggersClusterConfigs(t, true, false, nil, func(cfg config.Wrapper) {
  3050. pauseFolder(t, cfg, cfg.FolderList()[0].ID, true)
  3051. })
  3052. }
  3053. func TestClusterConfigOnFolderUnpause(t *testing.T) {
  3054. testConfigChangeTriggersClusterConfigs(t, true, false, func(cfg config.Wrapper) {
  3055. pauseFolder(t, cfg, cfg.FolderList()[0].ID, true)
  3056. }, func(cfg config.Wrapper) {
  3057. pauseFolder(t, cfg, cfg.FolderList()[0].ID, false)
  3058. })
  3059. }
  3060. func TestAddFolderCompletion(t *testing.T) {
  3061. // Empty folders are always 100% complete.
  3062. comp := newFolderCompletion(db.Counts{}, db.Counts{}, 0, remoteFolderValid)
  3063. comp.add(newFolderCompletion(db.Counts{}, db.Counts{}, 0, remoteFolderPaused))
  3064. if comp.CompletionPct != 100 {
  3065. t.Error(comp.CompletionPct)
  3066. }
  3067. // Completion is of the whole
  3068. comp = newFolderCompletion(db.Counts{Bytes: 100}, db.Counts{}, 0, remoteFolderValid) // 100% complete
  3069. comp.add(newFolderCompletion(db.Counts{Bytes: 400}, db.Counts{Bytes: 50}, 0, remoteFolderValid)) // 82.5% complete
  3070. if comp.CompletionPct != 90 { // 100 * (1 - 50/500)
  3071. t.Error(comp.CompletionPct)
  3072. }
  3073. }
  3074. func TestScanDeletedROChangedOnSR(t *testing.T) {
  3075. m, conn, fcfg, wCancel := setupModelWithConnection(t)
  3076. ffs := fcfg.Filesystem(nil)
  3077. defer wCancel()
  3078. defer cleanupModelAndRemoveDir(m, ffs.URI())
  3079. fcfg.Type = config.FolderTypeReceiveOnly
  3080. setFolder(t, m.cfg, fcfg)
  3081. name := "foo"
  3082. writeFile(t, ffs, name, []byte(name))
  3083. m.ScanFolders()
  3084. file, ok := m.testCurrentFolderFile(fcfg.ID, name)
  3085. if !ok {
  3086. t.Fatal("file missing in db")
  3087. }
  3088. // A remote must have the file, otherwise the deletion below is
  3089. // automatically resolved as not a ro-changed item.
  3090. must(t, m.IndexUpdate(conn, fcfg.ID, []protocol.FileInfo{file}))
  3091. must(t, ffs.Remove(name))
  3092. m.ScanFolders()
  3093. if receiveOnlyChangedSize(t, m, fcfg.ID).Deleted != 1 {
  3094. t.Fatal("expected one receive only changed deleted item")
  3095. }
  3096. fcfg.Type = config.FolderTypeSendReceive
  3097. setFolder(t, m.cfg, fcfg)
  3098. m.ScanFolders()
  3099. if receiveOnlyChangedSize(t, m, fcfg.ID).Deleted != 0 {
  3100. t.Fatal("expected no receive only changed deleted item")
  3101. }
  3102. if localSize(t, m, fcfg.ID).Deleted != 1 {
  3103. t.Fatal("expected one local deleted item")
  3104. }
  3105. }
  3106. func testConfigChangeTriggersClusterConfigs(t *testing.T, expectFirst, expectSecond bool, pre func(config.Wrapper), fn func(config.Wrapper)) {
  3107. t.Helper()
  3108. wcfg, _, wcfgCancel := newDefaultCfgWrapper()
  3109. defer wcfgCancel()
  3110. m := setupModel(t, wcfg)
  3111. defer cleanupModel(m)
  3112. setDevice(t, wcfg, newDeviceConfiguration(wcfg.DefaultDevice(), device2, "device2"))
  3113. if pre != nil {
  3114. pre(wcfg)
  3115. }
  3116. cc1 := make(chan struct{}, 1)
  3117. cc2 := make(chan struct{}, 1)
  3118. fc1 := newFakeConnection(device1, m)
  3119. fc1.ClusterConfigCalls(func(_ protocol.ClusterConfig) {
  3120. cc1 <- struct{}{}
  3121. })
  3122. fc2 := newFakeConnection(device2, m)
  3123. fc2.ClusterConfigCalls(func(_ protocol.ClusterConfig) {
  3124. cc2 <- struct{}{}
  3125. })
  3126. m.AddConnection(fc1, protocol.Hello{})
  3127. m.AddConnection(fc2, protocol.Hello{})
  3128. m.promoteConnections()
  3129. // Initial CCs
  3130. select {
  3131. case <-cc1:
  3132. default:
  3133. t.Fatal("missing initial CC from device1")
  3134. }
  3135. select {
  3136. case <-cc2:
  3137. default:
  3138. t.Fatal("missing initial CC from device2")
  3139. }
  3140. t.Log("Applying config change")
  3141. fn(wcfg)
  3142. timeout := time.NewTimer(time.Second)
  3143. if expectFirst {
  3144. select {
  3145. case <-cc1:
  3146. case <-timeout.C:
  3147. t.Errorf("timed out before receiving cluste rconfig for first device")
  3148. }
  3149. }
  3150. if expectSecond {
  3151. select {
  3152. case <-cc2:
  3153. case <-timeout.C:
  3154. t.Errorf("timed out before receiving cluste rconfig for second device")
  3155. }
  3156. }
  3157. }
  3158. // The end result of the tested scenario is that the global version entry has an
  3159. // empty version vector and is not deleted, while everything is actually deleted.
  3160. // That then causes these files to be considered as needed, while they are not.
  3161. // https://github.com/syncthing/syncthing/issues/6961
  3162. func TestIssue6961(t *testing.T) {
  3163. wcfg, fcfg, wcfgCancel := newDefaultCfgWrapper()
  3164. defer wcfgCancel()
  3165. tfs := fcfg.Filesystem(nil)
  3166. waiter, err := wcfg.Modify(func(cfg *config.Configuration) {
  3167. cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device2, "device2"))
  3168. fcfg.Type = config.FolderTypeReceiveOnly
  3169. fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
  3170. cfg.SetFolder(fcfg)
  3171. })
  3172. must(t, err)
  3173. waiter.Wait()
  3174. // Always recalc/repair when opening a fileset.
  3175. m := newModel(t, wcfg, myID, nil)
  3176. m.db.Close()
  3177. m.db, err = db.NewLowlevel(backend.OpenMemory(), m.evLogger, db.WithRecheckInterval(time.Millisecond))
  3178. if err != nil {
  3179. t.Fatal(err)
  3180. }
  3181. m.ServeBackground()
  3182. defer cleanupModelAndRemoveDir(m, tfs.URI())
  3183. conn1 := addFakeConn(m, device1, fcfg.ID)
  3184. conn2 := addFakeConn(m, device2, fcfg.ID)
  3185. m.ScanFolders()
  3186. name := "foo"
  3187. version := protocol.Vector{}.Update(device1.Short())
  3188. // Remote, valid and existing file
  3189. must(t, m.Index(conn1, fcfg.ID, []protocol.FileInfo{{Name: name, Version: version, Sequence: 1}}))
  3190. // Remote, invalid (receive-only) and existing file
  3191. must(t, m.Index(conn2, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}}))
  3192. // Create a local file
  3193. if fd, err := tfs.OpenFile(name, fs.OptCreate, 0o666); err != nil {
  3194. t.Fatal(err)
  3195. } else {
  3196. fd.Close()
  3197. }
  3198. if info, err := tfs.Lstat(name); err != nil {
  3199. t.Fatal(err)
  3200. } else {
  3201. l.Infoln("intest", info.Mode)
  3202. }
  3203. m.ScanFolders()
  3204. // Get rid of valid global
  3205. waiter, err = wcfg.RemoveDevice(device1)
  3206. if err != nil {
  3207. t.Fatal(err)
  3208. }
  3209. waiter.Wait()
  3210. // Delete the local file
  3211. must(t, tfs.Remove(name))
  3212. m.ScanFolders()
  3213. // Drop the remote index, add some other file.
  3214. must(t, m.Index(conn2, fcfg.ID, []protocol.FileInfo{{Name: "bar", RawInvalid: true, Sequence: 1}}))
  3215. // Pause and unpause folder to create new db.FileSet and thus recalculate everything
  3216. pauseFolder(t, wcfg, fcfg.ID, true)
  3217. pauseFolder(t, wcfg, fcfg.ID, false)
  3218. if comp := m.testCompletion(device2, fcfg.ID); comp.NeedDeletes != 0 {
  3219. t.Error("Expected 0 needed deletes, got", comp.NeedDeletes)
  3220. } else {
  3221. t.Log(comp)
  3222. }
  3223. }
  3224. func TestCompletionEmptyGlobal(t *testing.T) {
  3225. m, conn, fcfg, wcfgCancel := setupModelWithConnection(t)
  3226. defer wcfgCancel()
  3227. defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI())
  3228. files := []protocol.FileInfo{{Name: "foo", Version: protocol.Vector{}.Update(myID.Short()), Sequence: 1}}
  3229. m.fmut.Lock()
  3230. m.folderFiles[fcfg.ID].Update(protocol.LocalDeviceID, files)
  3231. m.fmut.Unlock()
  3232. files[0].Deleted = true
  3233. files[0].Version = files[0].Version.Update(device1.Short())
  3234. must(t, m.IndexUpdate(conn, fcfg.ID, files))
  3235. comp := m.testCompletion(protocol.LocalDeviceID, fcfg.ID)
  3236. if comp.CompletionPct != 95 {
  3237. t.Error("Expected completion of 95%, got", comp.CompletionPct)
  3238. }
  3239. }
  3240. func TestNeedMetaAfterIndexReset(t *testing.T) {
  3241. w, fcfg, wCancel := newDefaultCfgWrapper()
  3242. defer wCancel()
  3243. addDevice2(t, w, fcfg)
  3244. m := setupModel(t, w)
  3245. defer cleanupModelAndRemoveDir(m, fcfg.Path)
  3246. conn1 := addFakeConn(m, device1, fcfg.ID)
  3247. conn2 := addFakeConn(m, device2, fcfg.ID)
  3248. var seq int64 = 1
  3249. files := []protocol.FileInfo{{Name: "foo", Size: 10, Version: protocol.Vector{}.Update(device1.Short()), Sequence: seq}}
  3250. // Start with two remotes having one file, then both deleting it, then
  3251. // only one adding it again.
  3252. must(t, m.Index(conn1, fcfg.ID, files))
  3253. must(t, m.Index(conn2, fcfg.ID, files))
  3254. seq++
  3255. files[0].SetDeleted(device2.Short())
  3256. files[0].Sequence = seq
  3257. must(t, m.IndexUpdate(conn1, fcfg.ID, files))
  3258. must(t, m.IndexUpdate(conn2, fcfg.ID, files))
  3259. seq++
  3260. files[0].Deleted = false
  3261. files[0].Size = 20
  3262. files[0].Version = files[0].Version.Update(device1.Short())
  3263. files[0].Sequence = seq
  3264. must(t, m.IndexUpdate(conn1, fcfg.ID, files))
  3265. if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 {
  3266. t.Error("Expected one needed item for device2, got", comp.NeedItems)
  3267. }
  3268. // Pretend we had an index reset on device 1
  3269. must(t, m.Index(conn1, fcfg.ID, files))
  3270. if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 {
  3271. t.Error("Expected one needed item for device2, got", comp.NeedItems)
  3272. }
  3273. }
  3274. func TestCcCheckEncryption(t *testing.T) {
  3275. if testing.Short() {
  3276. t.Skip("skipping on short testing - generating encryption tokens is slow")
  3277. }
  3278. w, fcfg, wCancel := newDefaultCfgWrapper()
  3279. defer wCancel()
  3280. m := setupModel(t, w)
  3281. m.cancel()
  3282. defer cleanupModel(m)
  3283. pw := "foo"
  3284. token := protocol.PasswordToken(m.keyGen, fcfg.ID, pw)
  3285. m.folderEncryptionPasswordTokens[fcfg.ID] = token
  3286. testCases := []struct {
  3287. tokenRemote, tokenLocal []byte
  3288. isEncryptedRemote, isEncryptedLocal bool
  3289. expectedErr error
  3290. }{
  3291. {
  3292. tokenRemote: token,
  3293. tokenLocal: token,
  3294. expectedErr: errEncryptionInvConfigRemote,
  3295. },
  3296. {
  3297. isEncryptedRemote: true,
  3298. isEncryptedLocal: true,
  3299. expectedErr: errEncryptionInvConfigLocal,
  3300. },
  3301. {
  3302. tokenRemote: token,
  3303. tokenLocal: nil,
  3304. isEncryptedRemote: false,
  3305. isEncryptedLocal: false,
  3306. expectedErr: errEncryptionNotEncryptedLocal,
  3307. },
  3308. {
  3309. tokenRemote: token,
  3310. tokenLocal: nil,
  3311. isEncryptedRemote: true,
  3312. isEncryptedLocal: false,
  3313. expectedErr: nil,
  3314. },
  3315. {
  3316. tokenRemote: token,
  3317. tokenLocal: nil,
  3318. isEncryptedRemote: false,
  3319. isEncryptedLocal: true,
  3320. expectedErr: nil,
  3321. },
  3322. {
  3323. tokenRemote: nil,
  3324. tokenLocal: token,
  3325. isEncryptedRemote: true,
  3326. isEncryptedLocal: false,
  3327. expectedErr: nil,
  3328. },
  3329. {
  3330. tokenRemote: nil,
  3331. tokenLocal: token,
  3332. isEncryptedRemote: false,
  3333. isEncryptedLocal: true,
  3334. expectedErr: nil,
  3335. },
  3336. {
  3337. tokenRemote: nil,
  3338. tokenLocal: token,
  3339. isEncryptedRemote: false,
  3340. isEncryptedLocal: false,
  3341. expectedErr: errEncryptionNotEncryptedLocal,
  3342. },
  3343. {
  3344. tokenRemote: nil,
  3345. tokenLocal: nil,
  3346. isEncryptedRemote: true,
  3347. isEncryptedLocal: false,
  3348. expectedErr: errEncryptionPlainForRemoteEncrypted,
  3349. },
  3350. {
  3351. tokenRemote: nil,
  3352. tokenLocal: nil,
  3353. isEncryptedRemote: false,
  3354. isEncryptedLocal: true,
  3355. expectedErr: errEncryptionPlainForReceiveEncrypted,
  3356. },
  3357. {
  3358. tokenRemote: nil,
  3359. tokenLocal: nil,
  3360. isEncryptedRemote: false,
  3361. isEncryptedLocal: false,
  3362. expectedErr: nil,
  3363. },
  3364. }
  3365. for i, tc := range testCases {
  3366. tfcfg := fcfg.Copy()
  3367. if tc.isEncryptedLocal {
  3368. tfcfg.Type = config.FolderTypeReceiveEncrypted
  3369. m.folderEncryptionPasswordTokens[fcfg.ID] = token
  3370. }
  3371. dcfg := config.FolderDeviceConfiguration{DeviceID: device1}
  3372. if tc.isEncryptedRemote {
  3373. dcfg.EncryptionPassword = pw
  3374. }
  3375. deviceInfos := &clusterConfigDeviceInfo{
  3376. remote: protocol.Device{ID: device1, EncryptionPasswordToken: tc.tokenRemote},
  3377. local: protocol.Device{ID: myID, EncryptionPasswordToken: tc.tokenLocal},
  3378. }
  3379. err := m.ccCheckEncryption(tfcfg, dcfg, deviceInfos, false)
  3380. if err != tc.expectedErr {
  3381. t.Errorf("Testcase %v: Expected error %v, got %v", i, tc.expectedErr, err)
  3382. }
  3383. if tc.expectedErr == nil {
  3384. err := m.ccCheckEncryption(tfcfg, dcfg, deviceInfos, true)
  3385. if tc.isEncryptedRemote || tc.isEncryptedLocal {
  3386. if err != nil {
  3387. t.Errorf("Testcase %v: Expected no error, got %v", i, err)
  3388. }
  3389. } else {
  3390. if err != errEncryptionNotEncryptedUntrusted {
  3391. t.Errorf("Testcase %v: Expected error %v, got %v", i, errEncryptionNotEncryptedUntrusted, err)
  3392. }
  3393. }
  3394. }
  3395. if err != nil || (!tc.isEncryptedRemote && !tc.isEncryptedLocal) {
  3396. continue
  3397. }
  3398. if tc.isEncryptedLocal {
  3399. m.folderEncryptionPasswordTokens[fcfg.ID] = []byte("notAMatch")
  3400. } else {
  3401. dcfg.EncryptionPassword = "notAMatch"
  3402. }
  3403. err = m.ccCheckEncryption(tfcfg, dcfg, deviceInfos, false)
  3404. if err != errEncryptionPassword {
  3405. t.Errorf("Testcase %v: Expected error %v, got %v", i, errEncryptionPassword, err)
  3406. }
  3407. }
  3408. }
  3409. func TestCCFolderNotRunning(t *testing.T) {
  3410. // Create the folder, but don't start it.
  3411. w, fcfg, wCancel := newDefaultCfgWrapper()
  3412. defer wCancel()
  3413. tfs := fcfg.Filesystem(nil)
  3414. m := newModel(t, w, myID, nil)
  3415. defer cleanupModelAndRemoveDir(m, tfs.URI())
  3416. // A connection can happen before all the folders are started.
  3417. cc, _ := m.generateClusterConfig(device1)
  3418. if l := len(cc.Folders); l != 1 {
  3419. t.Fatalf("Expected 1 folder in CC, got %v", l)
  3420. }
  3421. folder := cc.Folders[0]
  3422. if id := folder.ID; id != fcfg.ID {
  3423. t.Fatalf("Expected folder %v, got %v", fcfg.ID, id)
  3424. }
  3425. if l := len(folder.Devices); l != 2 {
  3426. t.Fatalf("Expected 2 devices in CC, got %v", l)
  3427. }
  3428. local := folder.Devices[1]
  3429. if local.ID != myID {
  3430. local = folder.Devices[0]
  3431. }
  3432. if !folder.Paused && local.IndexID == 0 {
  3433. t.Errorf("Folder isn't paused, but index-id is zero")
  3434. }
  3435. }
  3436. func TestPendingFolder(t *testing.T) {
  3437. w, _, wCancel := newDefaultCfgWrapper()
  3438. defer wCancel()
  3439. m := setupModel(t, w)
  3440. defer cleanupModel(m)
  3441. setDevice(t, w, config.DeviceConfiguration{DeviceID: device2})
  3442. pfolder := "default"
  3443. of := db.ObservedFolder{
  3444. Time: time.Now().Truncate(time.Second),
  3445. Label: pfolder,
  3446. }
  3447. if err := m.db.AddOrUpdatePendingFolder(pfolder, of, device2); err != nil {
  3448. t.Fatal(err)
  3449. }
  3450. deviceFolders, err := m.PendingFolders(protocol.EmptyDeviceID)
  3451. if err != nil {
  3452. t.Fatal(err)
  3453. } else if pf, ok := deviceFolders[pfolder]; !ok {
  3454. t.Errorf("folder %v not pending", pfolder)
  3455. } else if _, ok := pf.OfferedBy[device2]; !ok {
  3456. t.Errorf("folder %v not pending for device %v", pfolder, device2)
  3457. } else if len(pf.OfferedBy) > 1 {
  3458. t.Errorf("folder %v pending for too many devices %v", pfolder, pf.OfferedBy)
  3459. }
  3460. device3, err := protocol.DeviceIDFromString("AIBAEAQ-CAIBAEC-AQCAIBA-EAQCAIA-BAEAQCA-IBAEAQC-CAIBAEA-QCAIBA7")
  3461. if err != nil {
  3462. t.Fatal(err)
  3463. }
  3464. setDevice(t, w, config.DeviceConfiguration{DeviceID: device3})
  3465. if err := m.db.AddOrUpdatePendingFolder(pfolder, of, device3); err != nil {
  3466. t.Fatal(err)
  3467. }
  3468. deviceFolders, err = m.PendingFolders(device2)
  3469. if err != nil {
  3470. t.Fatal(err)
  3471. } else if pf, ok := deviceFolders[pfolder]; !ok {
  3472. t.Errorf("folder %v not pending when filtered", pfolder)
  3473. } else if _, ok := pf.OfferedBy[device2]; !ok {
  3474. t.Errorf("folder %v not pending for device %v when filtered", pfolder, device2)
  3475. } else if _, ok := pf.OfferedBy[device3]; ok {
  3476. t.Errorf("folder %v pending for device %v, but not filtered out", pfolder, device3)
  3477. }
  3478. waiter, err := w.RemoveDevice(device3)
  3479. if err != nil {
  3480. t.Fatal(err)
  3481. }
  3482. waiter.Wait()
  3483. deviceFolders, err = m.PendingFolders(protocol.EmptyDeviceID)
  3484. if err != nil {
  3485. t.Fatal(err)
  3486. } else if pf, ok := deviceFolders[pfolder]; !ok {
  3487. t.Errorf("folder %v not pending", pfolder)
  3488. } else if _, ok := pf.OfferedBy[device3]; ok {
  3489. t.Errorf("folder %v pending for removed device %v", pfolder, device3)
  3490. }
  3491. waiter, err = w.RemoveFolder(pfolder)
  3492. if err != nil {
  3493. t.Fatal(err)
  3494. }
  3495. waiter.Wait()
  3496. deviceFolders, err = m.PendingFolders(protocol.EmptyDeviceID)
  3497. if err != nil {
  3498. t.Fatal(err)
  3499. } else if _, ok := deviceFolders[pfolder]; ok {
  3500. t.Errorf("folder %v still pending after local removal", pfolder)
  3501. }
  3502. }
  3503. func TestDeletedNotLocallyChangedReceiveOnly(t *testing.T) {
  3504. deletedNotLocallyChanged(t, config.FolderTypeReceiveOnly)
  3505. }
  3506. func TestDeletedNotLocallyChangedReceiveEncrypted(t *testing.T) {
  3507. deletedNotLocallyChanged(t, config.FolderTypeReceiveEncrypted)
  3508. }
  3509. func deletedNotLocallyChanged(t *testing.T, ft config.FolderType) {
  3510. w, fcfg, wCancel := newDefaultCfgWrapper()
  3511. tfs := fcfg.Filesystem(nil)
  3512. fcfg.Type = ft
  3513. setFolder(t, w, fcfg)
  3514. defer wCancel()
  3515. m := setupModel(t, w)
  3516. defer cleanupModelAndRemoveDir(m, tfs.URI())
  3517. name := "foo"
  3518. writeFile(t, tfs, name, nil)
  3519. must(t, m.ScanFolder(fcfg.ID))
  3520. fi, ok, err := m.CurrentFolderFile(fcfg.ID, name)
  3521. must(t, err)
  3522. if !ok {
  3523. t.Fatal("File hasn't been added")
  3524. }
  3525. if !fi.IsReceiveOnlyChanged() {
  3526. t.Fatal("File isn't receive-only-changed")
  3527. }
  3528. must(t, tfs.Remove(name))
  3529. must(t, m.ScanFolder(fcfg.ID))
  3530. _, ok, err = m.CurrentFolderFile(fcfg.ID, name)
  3531. must(t, err)
  3532. if ok {
  3533. t.Error("Expected file to be removed from db")
  3534. }
  3535. }
  3536. func equalStringsInAnyOrder(a, b []string) bool {
  3537. if len(a) != len(b) {
  3538. return false
  3539. }
  3540. sort.Strings(a)
  3541. sort.Strings(b)
  3542. for i := range a {
  3543. if a[i] != b[i] {
  3544. return false
  3545. }
  3546. }
  3547. return true
  3548. }
  3549. // modtimeTruncatingFS is a FileSystem that returns modification times only
  3550. // to the closest two `trunc` interval.
  3551. type modtimeTruncatingFS struct {
  3552. trunc time.Duration
  3553. fs.Filesystem
  3554. }
  3555. func (f modtimeTruncatingFS) Lstat(name string) (fs.FileInfo, error) {
  3556. fmt.Println("lstat", name)
  3557. info, err := f.Filesystem.Lstat(name)
  3558. return modtimeTruncatingFileInfo{trunc: f.trunc, FileInfo: info}, err
  3559. }
  3560. func (f modtimeTruncatingFS) Stat(name string) (fs.FileInfo, error) {
  3561. fmt.Println("stat", name)
  3562. info, err := f.Filesystem.Stat(name)
  3563. return modtimeTruncatingFileInfo{trunc: f.trunc, FileInfo: info}, err
  3564. }
  3565. func (f modtimeTruncatingFS) Walk(root string, walkFn fs.WalkFunc) error {
  3566. return f.Filesystem.Walk(root, func(path string, info fs.FileInfo, err error) error {
  3567. if err != nil {
  3568. return walkFn(path, nil, err)
  3569. }
  3570. fmt.Println("walk", info.Name())
  3571. return walkFn(path, modtimeTruncatingFileInfo{trunc: f.trunc, FileInfo: info}, nil)
  3572. })
  3573. }
  3574. type modtimeTruncatingFileInfo struct {
  3575. trunc time.Duration
  3576. fs.FileInfo
  3577. }
  3578. func (fi modtimeTruncatingFileInfo) ModTime() time.Time {
  3579. return fi.FileInfo.ModTime().Truncate(fi.trunc)
  3580. }