mirror of
https://github.com/octoleo/syncthing.git
synced 2025-02-02 11:58:28 +00:00
Copy configuration struct when sending Changed() events
Avoids data race. Copy() must be called with lock held.
This commit is contained in:
parent
a5edb6807e
commit
bf4eb4b269
@ -44,6 +44,30 @@ type Configuration struct {
|
|||||||
OriginalVersion int `xml:"-" json:"-"` // The version we read from disk, before any conversion
|
OriginalVersion int `xml:"-" json:"-"` // The version we read from disk, before any conversion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (orig Configuration) Copy() Configuration {
|
||||||
|
c := orig
|
||||||
|
|
||||||
|
// Deep copy FolderConfigurations
|
||||||
|
c.Folders = make([]FolderConfiguration, len(orig.Folders))
|
||||||
|
for i := range c.Folders {
|
||||||
|
c.Folders[i] = orig.Folders[i].Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep copy DeviceConfigurations
|
||||||
|
c.Devices = make([]DeviceConfiguration, len(orig.Devices))
|
||||||
|
for i := range c.Devices {
|
||||||
|
c.Devices[i] = orig.Devices[i].Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Options = orig.Options.Copy()
|
||||||
|
|
||||||
|
// DeviceIDs are values
|
||||||
|
c.IgnoredDevices = make([]protocol.DeviceID, len(orig.IgnoredDevices))
|
||||||
|
copy(c.IgnoredDevices, orig.IgnoredDevices)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
type FolderConfiguration struct {
|
type FolderConfiguration struct {
|
||||||
ID string `xml:"id,attr" json:"id"`
|
ID string `xml:"id,attr" json:"id"`
|
||||||
Path string `xml:"path,attr" json:"path"`
|
Path string `xml:"path,attr" json:"path"`
|
||||||
@ -63,6 +87,13 @@ type FolderConfiguration struct {
|
|||||||
deviceIDs []protocol.DeviceID
|
deviceIDs []protocol.DeviceID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (orig FolderConfiguration) Copy() FolderConfiguration {
|
||||||
|
c := orig
|
||||||
|
c.Devices = make([]FolderDeviceConfiguration, len(orig.Devices))
|
||||||
|
copy(c.Devices, orig.Devices)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FolderConfiguration) CreateMarker() error {
|
func (f *FolderConfiguration) CreateMarker() error {
|
||||||
if !f.HasMarker() {
|
if !f.HasMarker() {
|
||||||
marker := filepath.Join(f.Path, ".stfolder")
|
marker := filepath.Join(f.Path, ".stfolder")
|
||||||
@ -144,11 +175,15 @@ type DeviceConfiguration struct {
|
|||||||
Introducer bool `xml:"introducer,attr" json:"introducer"`
|
Introducer bool `xml:"introducer,attr" json:"introducer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (orig DeviceConfiguration) Copy() DeviceConfiguration {
|
||||||
|
c := orig
|
||||||
|
c.Addresses = make([]string, len(orig.Addresses))
|
||||||
|
copy(c.Addresses, orig.Addresses)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
type FolderDeviceConfiguration struct {
|
type FolderDeviceConfiguration struct {
|
||||||
DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
|
DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
|
||||||
|
|
||||||
Deprecated_Name string `xml:"name,attr,omitempty" json:"-"`
|
|
||||||
Deprecated_Addresses []string `xml:"address,omitempty" json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type OptionsConfiguration struct {
|
type OptionsConfiguration struct {
|
||||||
@ -176,6 +211,15 @@ type OptionsConfiguration struct {
|
|||||||
LimitBandwidthInLan bool `xml:"limitBandwidthInLan" json:"limitBandwidthInLan" default:"false"`
|
LimitBandwidthInLan bool `xml:"limitBandwidthInLan" json:"limitBandwidthInLan" default:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (orig OptionsConfiguration) Copy() OptionsConfiguration {
|
||||||
|
c := orig
|
||||||
|
c.ListenAddress = make([]string, len(orig.ListenAddress))
|
||||||
|
copy(c.ListenAddress, orig.ListenAddress)
|
||||||
|
c.GlobalAnnServers = make([]string, len(orig.GlobalAnnServers))
|
||||||
|
copy(c.GlobalAnnServers, orig.GlobalAnnServers)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
type GUIConfiguration struct {
|
type GUIConfiguration struct {
|
||||||
Enabled bool `xml:"enabled,attr" json:"enabled" default:"true"`
|
Enabled bool `xml:"enabled,attr" json:"enabled" default:"true"`
|
||||||
Address string `xml:"address" json:"address" default:"127.0.0.1:8384"`
|
Address string `xml:"address" json:"address" default:"127.0.0.1:8384"`
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -438,3 +440,42 @@ func TestRequiresRestart(t *testing.T) {
|
|||||||
t.Error("Changing GUI options requires restart")
|
t.Error("Changing GUI options requires restart")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
wrapper, err := Load("testdata/example.xml", device1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cfg := wrapper.Raw()
|
||||||
|
|
||||||
|
bsOrig, err := json.MarshalIndent(cfg, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy := cfg.Copy()
|
||||||
|
|
||||||
|
cfg.Devices[0].Addresses[0] = "wrong"
|
||||||
|
cfg.Folders[0].Devices[0].DeviceID = protocol.DeviceID{0, 1, 2, 3}
|
||||||
|
cfg.Options.ListenAddress[0] = "wrong"
|
||||||
|
cfg.GUI.APIKey = "wrong"
|
||||||
|
|
||||||
|
bsChanged, err := json.MarshalIndent(cfg, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bsCopy, err := json.MarshalIndent(copy, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(bsOrig, bsChanged) == 0 {
|
||||||
|
t.Error("Config should have changed")
|
||||||
|
}
|
||||||
|
if bytes.Compare(bsOrig, bsCopy) != 0 {
|
||||||
|
//ioutil.WriteFile("a", bsOrig, 0644)
|
||||||
|
//ioutil.WriteFile("b", bsCopy, 0644)
|
||||||
|
t.Error("Copy should be unchanged")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
50
internal/config/testdata/example.xml
vendored
Normal file
50
internal/config/testdata/example.xml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<configuration version="10">
|
||||||
|
<folder id="default" path="~/Sync" ro="false" rescanIntervalS="60" ignorePerms="false">
|
||||||
|
<device id="GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY"></device>
|
||||||
|
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"></device>
|
||||||
|
<device id="ZJCXQL7-M3NP4IC-4KQ7WFU-3NANYUX-AD74QRL-Q5LJ7BH-72KYZHK-GHTAOAK"></device>
|
||||||
|
<versioning></versioning>
|
||||||
|
<lenientMtimes>false</lenientMtimes>
|
||||||
|
<copiers>1</copiers>
|
||||||
|
<pullers>16</pullers>
|
||||||
|
<hashers>0</hashers>
|
||||||
|
</folder>
|
||||||
|
<device id="GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY" name="win7" compression="metadata" introducer="false">
|
||||||
|
<address>dynamic</address>
|
||||||
|
</device>
|
||||||
|
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" name="jborg-mbp" compression="metadata" introducer="false">
|
||||||
|
<address>dynamic</address>
|
||||||
|
</device>
|
||||||
|
<device id="ZJCXQL7-M3NP4IC-4KQ7WFU-3NANYUX-AD74QRL-Q5LJ7BH-72KYZHK-GHTAOAK" name="anto-syncer" compression="never" introducer="false">
|
||||||
|
<address>dynamic</address>
|
||||||
|
</device>
|
||||||
|
<gui enabled="true" tls="false">
|
||||||
|
<address>0.0.0.0:8080</address>
|
||||||
|
<apikey>136020D511BF136020D511BF136020D511BF</apikey>
|
||||||
|
</gui>
|
||||||
|
<options>
|
||||||
|
<listenAddress>0.0.0.0:22000</listenAddress>
|
||||||
|
<globalAnnounceServer>udp4://announce.syncthing.net:22026</globalAnnounceServer>
|
||||||
|
<globalAnnounceServer>udp6://announce-v6.syncthing.net:22026</globalAnnounceServer>
|
||||||
|
<globalAnnounceEnabled>true</globalAnnounceEnabled>
|
||||||
|
<localAnnounceEnabled>true</localAnnounceEnabled>
|
||||||
|
<localAnnouncePort>21025</localAnnouncePort>
|
||||||
|
<localAnnounceMCAddr>[ff32::5222]:21026</localAnnounceMCAddr>
|
||||||
|
<maxSendKbps>0</maxSendKbps>
|
||||||
|
<maxRecvKbps>0</maxRecvKbps>
|
||||||
|
<reconnectionIntervalS>60</reconnectionIntervalS>
|
||||||
|
<startBrowser>false</startBrowser>
|
||||||
|
<upnpEnabled>true</upnpEnabled>
|
||||||
|
<upnpLeaseMinutes>0</upnpLeaseMinutes>
|
||||||
|
<upnpRenewalMinutes>30</upnpRenewalMinutes>
|
||||||
|
<urAccepted>-1</urAccepted>
|
||||||
|
<urUniqueID></urUniqueID>
|
||||||
|
<restartOnWakeup>true</restartOnWakeup>
|
||||||
|
<autoUpgradeIntervalH>0</autoUpgradeIntervalH>
|
||||||
|
<keepTemporariesH>24</keepTemporariesH>
|
||||||
|
<cacheIgnoredFiles>true</cacheIgnoredFiles>
|
||||||
|
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||||
|
<symlinksEnabled>true</symlinksEnabled>
|
||||||
|
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||||
|
</options>
|
||||||
|
</configuration>
|
@ -80,6 +80,7 @@ func (w *Wrapper) Serve() {
|
|||||||
w.sMut.Lock()
|
w.sMut.Lock()
|
||||||
subs := w.subs
|
subs := w.subs
|
||||||
w.sMut.Unlock()
|
w.sMut.Unlock()
|
||||||
|
|
||||||
for _, h := range subs {
|
for _, h := range subs {
|
||||||
h.Changed(cfg)
|
h.Changed(cfg)
|
||||||
}
|
}
|
||||||
@ -113,7 +114,7 @@ func (w *Wrapper) Replace(cfg Configuration) {
|
|||||||
w.cfg = cfg
|
w.cfg = cfg
|
||||||
w.deviceMap = nil
|
w.deviceMap = nil
|
||||||
w.folderMap = nil
|
w.folderMap = nil
|
||||||
w.replaces <- cfg
|
w.replaces <- cfg.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Devices returns a map of devices. Device structures should not be changed,
|
// Devices returns a map of devices. Device structures should not be changed,
|
||||||
@ -141,13 +142,13 @@ func (w *Wrapper) SetDevice(dev DeviceConfiguration) {
|
|||||||
for i := range w.cfg.Devices {
|
for i := range w.cfg.Devices {
|
||||||
if w.cfg.Devices[i].DeviceID == dev.DeviceID {
|
if w.cfg.Devices[i].DeviceID == dev.DeviceID {
|
||||||
w.cfg.Devices[i] = dev
|
w.cfg.Devices[i] = dev
|
||||||
w.replaces <- w.cfg
|
w.replaces <- w.cfg.Copy()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.cfg.Devices = append(w.cfg.Devices, dev)
|
w.cfg.Devices = append(w.cfg.Devices, dev)
|
||||||
w.replaces <- w.cfg
|
w.replaces <- w.cfg.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Devices returns a map of folders. Folder structures should not be changed,
|
// Devices returns a map of folders. Folder structures should not be changed,
|
||||||
@ -181,13 +182,13 @@ func (w *Wrapper) SetFolder(fld FolderConfiguration) {
|
|||||||
for i := range w.cfg.Folders {
|
for i := range w.cfg.Folders {
|
||||||
if w.cfg.Folders[i].ID == fld.ID {
|
if w.cfg.Folders[i].ID == fld.ID {
|
||||||
w.cfg.Folders[i] = fld
|
w.cfg.Folders[i] = fld
|
||||||
w.replaces <- w.cfg
|
w.replaces <- w.cfg.Copy()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.cfg.Folders = append(w.cfg.Folders, fld)
|
w.cfg.Folders = append(w.cfg.Folders, fld)
|
||||||
w.replaces <- w.cfg
|
w.replaces <- w.cfg.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options returns the current options configuration object.
|
// Options returns the current options configuration object.
|
||||||
@ -202,7 +203,7 @@ func (w *Wrapper) SetOptions(opts OptionsConfiguration) {
|
|||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
w.cfg.Options = opts
|
w.cfg.Options = opts
|
||||||
w.replaces <- w.cfg
|
w.replaces <- w.cfg.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GUI returns the current GUI configuration object.
|
// GUI returns the current GUI configuration object.
|
||||||
@ -217,7 +218,7 @@ func (w *Wrapper) SetGUI(gui GUIConfiguration) {
|
|||||||
w.mut.Lock()
|
w.mut.Lock()
|
||||||
defer w.mut.Unlock()
|
defer w.mut.Unlock()
|
||||||
w.cfg.GUI = gui
|
w.cfg.GUI = gui
|
||||||
w.replaces <- w.cfg
|
w.replaces <- w.cfg.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the folder error state. Emits ConfigSaved to cause a GUI refresh.
|
// Sets the folder error state. Emits ConfigSaved to cause a GUI refresh.
|
||||||
@ -236,7 +237,7 @@ func (w *Wrapper) SetFolderError(id string, err error) {
|
|||||||
if errstr != w.cfg.Folders[i].Invalid {
|
if errstr != w.cfg.Folders[i].Invalid {
|
||||||
w.cfg.Folders[i].Invalid = errstr
|
w.cfg.Folders[i].Invalid = errstr
|
||||||
events.Default.Log(events.ConfigSaved, w.cfg)
|
events.Default.Log(events.ConfigSaved, w.cfg)
|
||||||
w.replaces <- w.cfg
|
w.replaces <- w.cfg.Copy()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user