model_test.go 107 KB

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