lib/config: Fix aliased append, copy config inputs and outputs (fixes #5063) (#5069)

This commit is contained in:
Audrius Butkevicius 2018-07-26 22:14:12 +01:00 committed by Jakob Borg
parent 682ffcb8ed
commit 5161f03f02
4 changed files with 61 additions and 32 deletions

View File

@ -154,6 +154,7 @@ func (cfg Configuration) Copy() Configuration {
} }
newCfg.Options = cfg.Options.Copy() newCfg.Options = cfg.Options.Copy()
newCfg.GUI = cfg.GUI.Copy()
// DeviceIDs are values // DeviceIDs are values
newCfg.IgnoredDevices = make([]protocol.DeviceID, len(cfg.IgnoredDevices)) newCfg.IgnoredDevices = make([]protocol.DeviceID, len(cfg.IgnoredDevices))

View File

@ -93,3 +93,7 @@ func (c GUIConfiguration) IsValidAPIKey(apiKey string) bool {
return false return false
} }
} }
func (c GUIConfiguration) Copy() GUIConfiguration {
return c
}

View File

@ -142,7 +142,7 @@ func (w *Wrapper) RawCopy() Configuration {
func (w *Wrapper) Replace(cfg Configuration) (Waiter, error) { func (w *Wrapper) Replace(cfg Configuration) (Waiter, error) {
w.mut.Lock() w.mut.Lock()
defer w.mut.Unlock() defer w.mut.Unlock()
return w.replaceLocked(cfg) return w.replaceLocked(cfg.Copy())
} }
func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) { func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) {
@ -154,7 +154,7 @@ func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) {
for _, sub := range w.subs { for _, sub := range w.subs {
l.Debugln(sub, "verifying configuration") l.Debugln(sub, "verifying configuration")
if err := sub.VerifyConfiguration(from, to); err != nil { if err := sub.VerifyConfiguration(from.Copy(), to.Copy()); err != nil {
l.Debugln(sub, "rejected config:", err) l.Debugln(sub, "rejected config:", err)
return noopWaiter{}, err return noopWaiter{}, err
} }
@ -164,7 +164,7 @@ func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) {
w.deviceMap = nil w.deviceMap = nil
w.folderMap = nil w.folderMap = nil
return w.notifyListeners(from, to), nil return w.notifyListeners(from.Copy(), to.Copy()), nil
} }
func (w *Wrapper) notifyListeners(from, to Configuration) Waiter { func (w *Wrapper) notifyListeners(from, to Configuration) Waiter {
@ -172,7 +172,7 @@ func (w *Wrapper) notifyListeners(from, to Configuration) Waiter {
wg.Add(len(w.subs)) wg.Add(len(w.subs))
for _, sub := range w.subs { for _, sub := range w.subs {
go func(commiter Committer) { go func(commiter Committer) {
w.notifyListener(commiter, from.Copy(), to.Copy()) w.notifyListener(commiter, from, to)
wg.Done() wg.Done()
}(sub) }(sub)
} }
@ -187,15 +187,14 @@ func (w *Wrapper) notifyListener(sub Committer, from, to Configuration) {
} }
} }
// Devices returns a map of devices. Device structures should not be changed, // Devices returns a map of devices.
// other than for the purpose of updating via SetDevice().
func (w *Wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration { func (w *Wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration {
w.mut.Lock() w.mut.Lock()
defer w.mut.Unlock() defer w.mut.Unlock()
if w.deviceMap == nil { if w.deviceMap == nil {
w.deviceMap = make(map[protocol.DeviceID]DeviceConfiguration, len(w.cfg.Devices)) w.deviceMap = make(map[protocol.DeviceID]DeviceConfiguration, len(w.cfg.Devices))
for _, dev := range w.cfg.Devices { for _, dev := range w.cfg.Devices {
w.deviceMap[dev.DeviceID] = dev w.deviceMap[dev.DeviceID] = dev.Copy()
} }
} }
return w.deviceMap return w.deviceMap
@ -213,13 +212,13 @@ func (w *Wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) {
replaced = false replaced = false
for newIndex := range newCfg.Devices { for newIndex := range newCfg.Devices {
if newCfg.Devices[newIndex].DeviceID == devs[oldIndex].DeviceID { if newCfg.Devices[newIndex].DeviceID == devs[oldIndex].DeviceID {
newCfg.Devices[newIndex] = devs[oldIndex] newCfg.Devices[newIndex] = devs[oldIndex].Copy()
replaced = true replaced = true
break break
} }
} }
if !replaced { if !replaced {
newCfg.Devices = append(newCfg.Devices, devs[oldIndex]) newCfg.Devices = append(newCfg.Devices, devs[oldIndex].Copy())
} }
} }
@ -238,19 +237,14 @@ func (w *Wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) {
defer w.mut.Unlock() defer w.mut.Unlock()
newCfg := w.cfg.Copy() newCfg := w.cfg.Copy()
removed := false
for i := range newCfg.Devices { for i := range newCfg.Devices {
if newCfg.Devices[i].DeviceID == id { if newCfg.Devices[i].DeviceID == id {
newCfg.Devices = append(newCfg.Devices[:i], newCfg.Devices[i+1:]...) newCfg.Devices = append(newCfg.Devices[:i], newCfg.Devices[i+1:]...)
removed = true return w.replaceLocked(newCfg)
break
} }
} }
if !removed {
return noopWaiter{}, nil
}
return w.replaceLocked(newCfg) return noopWaiter{}, nil
} }
// Folders returns a map of folders. Folder structures should not be changed, // Folders returns a map of folders. Folder structures should not be changed,
@ -261,7 +255,7 @@ func (w *Wrapper) Folders() map[string]FolderConfiguration {
if w.folderMap == nil { if w.folderMap == nil {
w.folderMap = make(map[string]FolderConfiguration, len(w.cfg.Folders)) w.folderMap = make(map[string]FolderConfiguration, len(w.cfg.Folders))
for _, fld := range w.cfg.Folders { for _, fld := range w.cfg.Folders {
w.folderMap[fld.ID] = fld w.folderMap[fld.ID] = fld.Copy()
} }
} }
return w.folderMap return w.folderMap
@ -271,7 +265,7 @@ func (w *Wrapper) Folders() map[string]FolderConfiguration {
func (w *Wrapper) FolderList() []FolderConfiguration { func (w *Wrapper) FolderList() []FolderConfiguration {
w.mut.Lock() w.mut.Lock()
defer w.mut.Unlock() defer w.mut.Unlock()
return append([]FolderConfiguration(nil), w.cfg.Folders...) return w.cfg.Copy().Folders
} }
// SetFolder adds a new folder to the configuration, or overwrites an existing // SetFolder adds a new folder to the configuration, or overwrites an existing
@ -281,17 +275,15 @@ func (w *Wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) {
defer w.mut.Unlock() defer w.mut.Unlock()
newCfg := w.cfg.Copy() newCfg := w.cfg.Copy()
replaced := false
for i := range newCfg.Folders { for i := range newCfg.Folders {
if newCfg.Folders[i].ID == fld.ID { if newCfg.Folders[i].ID == fld.ID {
newCfg.Folders[i] = fld newCfg.Folders[i] = fld
replaced = true return w.replaceLocked(newCfg)
break
} }
} }
if !replaced {
newCfg.Folders = append(w.cfg.Folders, fld) newCfg.Folders = append(newCfg.Folders, fld)
}
return w.replaceLocked(newCfg) return w.replaceLocked(newCfg)
} }
@ -300,7 +292,7 @@ func (w *Wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) {
func (w *Wrapper) Options() OptionsConfiguration { func (w *Wrapper) Options() OptionsConfiguration {
w.mut.Lock() w.mut.Lock()
defer w.mut.Unlock() defer w.mut.Unlock()
return w.cfg.Options return w.cfg.Options.Copy()
} }
// SetOptions replaces the current options configuration object. // SetOptions replaces the current options configuration object.
@ -308,7 +300,7 @@ func (w *Wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) {
w.mut.Lock() w.mut.Lock()
defer w.mut.Unlock() defer w.mut.Unlock()
newCfg := w.cfg.Copy() newCfg := w.cfg.Copy()
newCfg.Options = opts newCfg.Options = opts.Copy()
return w.replaceLocked(newCfg) return w.replaceLocked(newCfg)
} }
@ -316,7 +308,7 @@ func (w *Wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) {
func (w *Wrapper) GUI() GUIConfiguration { func (w *Wrapper) GUI() GUIConfiguration {
w.mut.Lock() w.mut.Lock()
defer w.mut.Unlock() defer w.mut.Unlock()
return w.cfg.GUI return w.cfg.GUI.Copy()
} }
// SetGUI replaces the current GUI configuration object. // SetGUI replaces the current GUI configuration object.
@ -324,7 +316,7 @@ func (w *Wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) {
w.mut.Lock() w.mut.Lock()
defer w.mut.Unlock() defer w.mut.Unlock()
newCfg := w.cfg.Copy() newCfg := w.cfg.Copy()
newCfg.GUI = gui newCfg.GUI = gui.Copy()
return w.replaceLocked(newCfg) return w.replaceLocked(newCfg)
} }
@ -360,7 +352,7 @@ func (w *Wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) {
defer w.mut.Unlock() defer w.mut.Unlock()
for _, device := range w.cfg.Devices { for _, device := range w.cfg.Devices {
if device.DeviceID == id { if device.DeviceID == id {
return device, true return device.Copy(), true
} }
} }
return DeviceConfiguration{}, false return DeviceConfiguration{}, false
@ -372,7 +364,7 @@ func (w *Wrapper) Folder(id string) (FolderConfiguration, bool) {
defer w.mut.Unlock() defer w.mut.Unlock()
for _, folder := range w.cfg.Folders { for _, folder := range w.cfg.Folders {
if folder.ID == id { if folder.ID == id {
return folder, true return folder.Copy(), true
} }
} }
return FolderConfiguration{}, false return FolderConfiguration{}, false
@ -380,6 +372,9 @@ func (w *Wrapper) Folder(id string) (FolderConfiguration, bool) {
// Save writes the configuration to disk, and generates a ConfigSaved event. // Save writes the configuration to disk, and generates a ConfigSaved event.
func (w *Wrapper) Save() error { func (w *Wrapper) Save() error {
w.mut.Lock()
defer w.mut.Unlock()
fd, err := osutil.CreateAtomic(w.path) fd, err := osutil.CreateAtomic(w.path)
if err != nil { if err != nil {
l.Debugln("CreateAtomic:", err) l.Debugln("CreateAtomic:", err)
@ -403,7 +398,7 @@ func (w *Wrapper) Save() error {
func (w *Wrapper) GlobalDiscoveryServers() []string { func (w *Wrapper) GlobalDiscoveryServers() []string {
var servers []string var servers []string
for _, srv := range w.cfg.Options.GlobalAnnServers { for _, srv := range w.Options().GlobalAnnServers {
switch srv { switch srv {
case "default": case "default":
servers = append(servers, DefaultDiscoveryServers...) servers = append(servers, DefaultDiscoveryServers...)
@ -420,7 +415,7 @@ func (w *Wrapper) GlobalDiscoveryServers() []string {
func (w *Wrapper) ListenAddresses() []string { func (w *Wrapper) ListenAddresses() []string {
var addresses []string var addresses []string
for _, addr := range w.cfg.Options.ListenAddresses { for _, addr := range w.Options().ListenAddresses {
switch addr { switch addr {
case "default": case "default":
addresses = append(addresses, DefaultListenAddresses...) addresses = append(addresses, DefaultListenAddresses...)

View File

@ -1101,6 +1101,35 @@ func TestIssue4897(t *testing.T) {
} }
} }
func TestIssue5063(t *testing.T) {
wcfg, m := newState(defaultAutoAcceptCfg)
addAndVerify := func(wg *sync.WaitGroup) {
id := srand.String(8)
m.ClusterConfig(device1, protocol.ClusterConfig{
Folders: []protocol.Folder{
{
ID: id,
Label: id,
},
},
})
os.RemoveAll(filepath.Join("testdata", id))
wg.Done()
if fcfg, ok := wcfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
t.Error("expected shared", id)
}
}
wg := &sync.WaitGroup{}
for i := 0; i <= 10; i++ {
wg.Add(1)
go addAndVerify(wg)
}
wg.Wait()
}
func TestAutoAcceptRejected(t *testing.T) { func TestAutoAcceptRejected(t *testing.T) {
// Nothing happens if AutoAcceptFolders not set // Nothing happens if AutoAcceptFolders not set
tcfg := defaultAutoAcceptCfg.Copy() tcfg := defaultAutoAcceptCfg.Copy()