diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index fc3068717..bf7582e5d 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -40,11 +40,9 @@ import ( "github.com/syncthing/syncthing/lib/model" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/rand" - "github.com/syncthing/syncthing/lib/stats" "github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/upgrade" - "github.com/syncthing/syncthing/lib/versioner" "github.com/vitrun/qart/qr" "golang.org/x/crypto/bcrypt" ) @@ -64,15 +62,15 @@ const ( type apiService struct { id protocol.DeviceID - cfg configIntf + cfg config.Wrapper httpsCertFile string httpsKeyFile string statics *staticsServer - model modelIntf + model model.Model eventSubs map[events.EventType]events.BufferedSubscription eventSubsMut sync.Mutex discoverer discover.CachingMux - connectionsService connectionsIntf + connectionsService connections.Service fss *folderSummaryService systemConfigMut sync.Mutex // serializes posts to /rest/system/config stop chan struct{} // signals intentional stop @@ -86,69 +84,11 @@ type apiService struct { systemLog logger.Recorder } -type modelIntf interface { - GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} - Completion(device protocol.DeviceID, folder string) model.FolderCompletion - Override(folder string) - Revert(folder string) - NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated) - RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error) - LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated - NeedSize(folder string) db.Counts - ConnectionStats() map[string]interface{} - DeviceStatistics() map[string]stats.DeviceStatistics - FolderStatistics() map[string]stats.FolderStatistics - CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) - CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool) - ResetFolder(folder string) - Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []model.Availability - GetIgnores(folder string) ([]string, []string, error) - GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error) - RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error) - SetIgnores(folder string, content []string) error - DelayScan(folder string, next time.Duration) - ScanFolder(folder string) error - ScanFolders() map[string]error - ScanFolderSubdirs(folder string, subs []string) error - BringToFront(folder, file string) - Connection(deviceID protocol.DeviceID) (connections.Connection, bool) - GlobalSize(folder string) db.Counts - LocalSize(folder string) db.Counts - ReceiveOnlyChangedSize(folder string) db.Counts - CurrentSequence(folder string) (int64, bool) - RemoteSequence(folder string) (int64, bool) - State(folder string) (string, time.Time, error) - UsageReportingStats(version int, preview bool) map[string]interface{} - FolderErrors(folder string) ([]model.FileError, error) - WatchError(folder string) error -} - -type configIntf interface { - GUI() config.GUIConfiguration - LDAP() config.LDAPConfiguration - RawCopy() config.Configuration - Options() config.OptionsConfiguration - Replace(cfg config.Configuration) (config.Waiter, error) - Subscribe(c config.Committer) - Folders() map[string]config.FolderConfiguration - Devices() map[protocol.DeviceID]config.DeviceConfiguration - SetDevice(config.DeviceConfiguration) (config.Waiter, error) - SetDevices([]config.DeviceConfiguration) (config.Waiter, error) - Save() error - ListenAddresses() []string - RequiresRestart() bool -} - -type connectionsIntf interface { - Status() map[string]interface{} - NATType() string -} - type rater interface { Rate() float64 } -func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKeyFile, assetDir string, m modelIntf, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connectionsIntf, errors, systemLog logger.Recorder, cpu rater) *apiService { +func newAPIService(id protocol.DeviceID, cfg config.Wrapper, httpsCertFile, httpsKeyFile, assetDir string, m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connections.Service, errors, systemLog logger.Recorder, cpu rater) *apiService { service := &apiService{ id: id, cfg: cfg, @@ -719,7 +659,7 @@ func (s *apiService) getDBStatus(w http.ResponseWriter, r *http.Request) { } } -func folderSummary(cfg configIntf, m modelIntf, folder string) (map[string]interface{}, error) { +func folderSummary(cfg config.Wrapper, m model.Model, folder string) (map[string]interface{}, error) { var res = make(map[string]interface{}) errors, err := m.FolderErrors(folder) diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 37da39f79..368957b6c 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -956,7 +956,7 @@ func setupSignalHandling() { }() } -func loadOrDefaultConfig() (*config.Wrapper, error) { +func loadOrDefaultConfig() (config.Wrapper, error) { cfgFile := locations.Get(locations.ConfigFile) cfg, err := config.Load(cfgFile, myID) @@ -967,7 +967,7 @@ func loadOrDefaultConfig() (*config.Wrapper, error) { return cfg, err } -func loadConfigAtStartup() (*config.Wrapper, error) { +func loadConfigAtStartup() (config.Wrapper, error) { cfgFile := locations.Get(locations.ConfigFile) cfg, err := config.Load(cfgFile, myID) if os.IsNotExist(err) { @@ -996,7 +996,7 @@ func loadConfigAtStartup() (*config.Wrapper, error) { return cfg, nil } -func archiveAndSaveConfig(cfg *config.Wrapper) error { +func archiveAndSaveConfig(cfg config.Wrapper) error { // Copy the existing config to an archive copy archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", cfg.RawCopy().OriginalVersion) l.Infoln("Archiving a copy of old config file format at:", archivePath) @@ -1061,7 +1061,7 @@ func startAuditing(mainService *suture.Supervisor, auditFile string) { l.Infoln("Audit log in", auditDest) } -func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService *connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) { +func setupGUI(mainService *suture.Supervisor, cfg config.Wrapper, m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) { guiCfg := cfg.GUI() if !guiCfg.Enabled { @@ -1091,7 +1091,7 @@ func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Mode } } -func defaultConfig(cfgFile string) (*config.Wrapper, error) { +func defaultConfig(cfgFile string) (config.Wrapper, error) { newCfg, err := config.NewWithFreePorts(myID) if err != nil { return nil, err @@ -1154,7 +1154,7 @@ func standbyMonitor() { } } -func autoUpgrade(cfg *config.Wrapper) { +func autoUpgrade(cfg config.Wrapper) { timer := time.NewTimer(0) sub := events.Default.Subscribe(events.DeviceConnected) for { @@ -1256,7 +1256,7 @@ func cleanConfigDirectory() { // checkShortIDs verifies that the configuration won't result in duplicate // short ID:s; that is, that the devices in the cluster all have unique // initial 64 bits. -func checkShortIDs(cfg *config.Wrapper) error { +func checkShortIDs(cfg config.Wrapper) error { exists := make(map[protocol.ShortID]protocol.DeviceID) for deviceID := range cfg.Devices() { shortID := deviceID.Short() @@ -1278,7 +1278,7 @@ func showPaths(options RuntimeOptions) { fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder)) } -func setPauseState(cfg *config.Wrapper, paused bool) { +func setPauseState(cfg config.Wrapper, paused bool) { raw := cfg.RawCopy() for i := range raw.Devices { raw.Devices[i].Paused = paused diff --git a/cmd/syncthing/mocked_config_test.go b/cmd/syncthing/mocked_config_test.go index bf97087b4..8bdb8ebe1 100644 --- a/cmd/syncthing/mocked_config_test.go +++ b/cmd/syncthing/mocked_config_test.go @@ -44,6 +44,8 @@ func (c *mockedConfig) Replace(cfg config.Configuration) (config.Waiter, error) func (c *mockedConfig) Subscribe(cm config.Committer) {} +func (c *mockedConfig) Unsubscribe(cm config.Committer) {} + func (c *mockedConfig) Folders() map[string]config.FolderConfiguration { return nil } @@ -68,6 +70,58 @@ func (c *mockedConfig) RequiresRestart() bool { return false } +func (c *mockedConfig) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) {} + +func (c *mockedConfig) AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) {} + +func (m *mockedConfig) MyName() string { + return "" +} + +func (m *mockedConfig) ConfigPath() string { + return "" +} + +func (m *mockedConfig) SetGUI(gui config.GUIConfiguration) (config.Waiter, error) { + return noopWaiter{}, nil +} + +func (m *mockedConfig) SetOptions(opts config.OptionsConfiguration) (config.Waiter, error) { + return noopWaiter{}, nil +} + +func (m *mockedConfig) Folder(id string) (config.FolderConfiguration, bool) { + return config.FolderConfiguration{}, false +} + +func (m *mockedConfig) FolderList() []config.FolderConfiguration { + return nil +} + +func (m *mockedConfig) SetFolder(fld config.FolderConfiguration) (config.Waiter, error) { + return noopWaiter{}, nil +} + +func (m *mockedConfig) Device(id protocol.DeviceID) (config.DeviceConfiguration, bool) { + return config.DeviceConfiguration{}, false +} + +func (m *mockedConfig) RemoveDevice(id protocol.DeviceID) (config.Waiter, error) { + return noopWaiter{}, nil +} + +func (m *mockedConfig) IgnoredDevice(id protocol.DeviceID) bool { + return false +} + +func (m *mockedConfig) IgnoredFolder(device protocol.DeviceID, folder string) bool { + return false +} + +func (m *mockedConfig) GlobalDiscoveryServers() []string { + return nil +} + type noopWaiter struct{} func (noopWaiter) Wait() {} diff --git a/cmd/syncthing/mocked_connections_test.go b/cmd/syncthing/mocked_connections_test.go index ebb90503b..aaf070293 100644 --- a/cmd/syncthing/mocked_connections_test.go +++ b/cmd/syncthing/mocked_connections_test.go @@ -15,3 +15,7 @@ func (m *mockedConnections) Status() map[string]interface{} { func (m *mockedConnections) NATType() string { return "" } + +func (m *mockedConnections) Serve() {} + +func (m *mockedConnections) Stop() {} diff --git a/cmd/syncthing/mocked_model_test.go b/cmd/syncthing/mocked_model_test.go index 6cb18ce03..c01d59d95 100644 --- a/cmd/syncthing/mocked_model_test.go +++ b/cmd/syncthing/mocked_model_test.go @@ -7,8 +7,10 @@ package main import ( + "net" "time" + "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/connections" "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/model" @@ -150,3 +152,38 @@ func (m *mockedModel) WatchError(folder string) error { func (m *mockedModel) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated { return nil } + +func (m *mockedModel) Serve() {} +func (m *mockedModel) Stop() {} +func (m *mockedModel) Index(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) {} +func (m *mockedModel) IndexUpdate(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo) { +} + +func (m *mockedModel) Request(deviceID protocol.DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (protocol.RequestResponse, error) { + return nil, nil +} + +func (m *mockedModel) ClusterConfig(deviceID protocol.DeviceID, config protocol.ClusterConfig) {} + +func (m *mockedModel) Closed(conn protocol.Connection, err error) {} + +func (m *mockedModel) DownloadProgress(deviceID protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) { +} + +func (m *mockedModel) AddConnection(conn connections.Connection, hello protocol.HelloResult) {} + +func (m *mockedModel) OnHello(protocol.DeviceID, net.Addr, protocol.HelloResult) error { + return nil +} + +func (m *mockedModel) GetHello(protocol.DeviceID) protocol.HelloIntf { + return nil +} + +func (m *mockedModel) AddFolder(cfg config.FolderConfiguration) {} + +func (m *mockedModel) RestartFolder(from, to config.FolderConfiguration) {} + +func (m *mockedModel) StartFolder(folder string) {} + +func (m *mockedModel) StartDeadlockDetector(timeout time.Duration) {} diff --git a/cmd/syncthing/summaryservice.go b/cmd/syncthing/summaryservice.go index a693721d5..ec12fd790 100644 --- a/cmd/syncthing/summaryservice.go +++ b/cmd/syncthing/summaryservice.go @@ -9,7 +9,9 @@ package main import ( "time" + "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/events" + "github.com/syncthing/syncthing/lib/model" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/sync" "github.com/thejerf/suture" @@ -20,8 +22,8 @@ import ( type folderSummaryService struct { *suture.Supervisor - cfg configIntf - model modelIntf + cfg config.Wrapper + model model.Model stop chan struct{} immediate chan string @@ -34,7 +36,7 @@ type folderSummaryService struct { lastEventReqMut sync.Mutex } -func newFolderSummaryService(cfg configIntf, m modelIntf) *folderSummaryService { +func newFolderSummaryService(cfg config.Wrapper, m model.Model) *folderSummaryService { service := &folderSummaryService{ Supervisor: suture.New("folderSummaryService", suture.Spec{ PassThroughPanics: true, diff --git a/cmd/syncthing/usage_report.go b/cmd/syncthing/usage_report.go index 2677b21f8..7b33a01f0 100644 --- a/cmd/syncthing/usage_report.go +++ b/cmd/syncthing/usage_report.go @@ -37,7 +37,7 @@ const usageReportVersion = 3 // reportData returns the data to be sent in a usage report. It's used in // various places, so not part of the usageReportingManager object. -func reportData(cfg configIntf, m modelIntf, connectionsService connectionsIntf, version int, preview bool) map[string]interface{} { +func reportData(cfg config.Wrapper, m model.Model, connectionsService connections.Service, version int, preview bool) map[string]interface{} { opts := cfg.Options() res := make(map[string]interface{}) res["urVersion"] = version @@ -323,16 +323,16 @@ func reportData(cfg configIntf, m modelIntf, connectionsService connectionsIntf, } type usageReportingService struct { - cfg *config.Wrapper - model *model.Model - connectionsService *connections.Service + cfg config.Wrapper + model model.Model + connectionsService connections.Service forceRun chan struct{} stop chan struct{} stopped chan struct{} stopMut sync.RWMutex } -func newUsageReportingService(cfg *config.Wrapper, model *model.Model, connectionsService *connections.Service) *usageReportingService { +func newUsageReportingService(cfg config.Wrapper, model model.Model, connectionsService connections.Service) *usageReportingService { svc := &usageReportingService{ cfg: cfg, model: model, diff --git a/lib/config/config_test.go b/lib/config/config_test.go index e845c28ca..ee6142d8d 100644 --- a/lib/config/config_test.go +++ b/lib/config/config_test.go @@ -93,7 +93,7 @@ func TestDeviceConfig(t *testing.T) { t.Fatal("Unexpected file") } - cfg := wr.cfg + cfg := wr.(*wrapper).cfg expectedFolders := []FolderConfiguration{ { @@ -515,7 +515,7 @@ func TestNewSaveLoad(t *testing.T) { cfg := Wrap(path, intCfg) // To make the equality pass later - cfg.cfg.XMLName.Local = "configuration" + cfg.(*wrapper).cfg.XMLName.Local = "configuration" if exists(path) { t.Error(path, "exists") @@ -827,7 +827,7 @@ func TestIgnoredFolders(t *testing.T) { // 2 for folder2, 1 for folder1, as non-existing device and device the folder is shared with is removed. expectedIgnoredFolders := 3 - for _, dev := range wrapper.cfg.Devices { + for _, dev := range wrapper.Devices() { expectedIgnoredFolders -= len(dev.IgnoredFolders) } if expectedIgnoredFolders != 0 { diff --git a/lib/config/wrapper.go b/lib/config/wrapper.go index 1dda3dac8..17b24555c 100644 --- a/lib/config/wrapper.go +++ b/lib/config/wrapper.go @@ -52,10 +52,48 @@ type noopWaiter struct{} func (noopWaiter) Wait() {} -// A wrapper around a Configuration that manages loads, saves and published +// A Wrapper around a Configuration that manages loads, saves and published // notifications of changes to registered Handlers +type Wrapper interface { + MyName() string + ConfigPath() string -type Wrapper struct { + RawCopy() Configuration + Replace(cfg Configuration) (Waiter, error) + RequiresRestart() bool + Save() error + + GUI() GUIConfiguration + SetGUI(gui GUIConfiguration) (Waiter, error) + LDAP() LDAPConfiguration + + Options() OptionsConfiguration + SetOptions(opts OptionsConfiguration) (Waiter, error) + + Folder(id string) (FolderConfiguration, bool) + Folders() map[string]FolderConfiguration + FolderList() []FolderConfiguration + SetFolder(fld FolderConfiguration) (Waiter, error) + + Device(id protocol.DeviceID) (DeviceConfiguration, bool) + Devices() map[protocol.DeviceID]DeviceConfiguration + RemoveDevice(id protocol.DeviceID) (Waiter, error) + SetDevice(DeviceConfiguration) (Waiter, error) + SetDevices([]DeviceConfiguration) (Waiter, error) + + AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) + AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) + IgnoredDevice(id protocol.DeviceID) bool + IgnoredFolder(device protocol.DeviceID, folder string) bool + + ListenAddresses() []string + GlobalDiscoveryServers() []string + + Subscribe(c Committer) + Unsubscribe(c Committer) +} + +type wrapper struct { cfg Configuration path string @@ -69,8 +107,8 @@ type Wrapper struct { // Wrap wraps an existing Configuration structure and ties it to a file on // disk. -func Wrap(path string, cfg Configuration) *Wrapper { - w := &Wrapper{ +func Wrap(path string, cfg Configuration) Wrapper { + w := &wrapper{ cfg: cfg, path: path, mut: sync.NewMutex(), @@ -80,7 +118,7 @@ func Wrap(path string, cfg Configuration) *Wrapper { // Load loads an existing file on disk and returns a new configuration // wrapper. -func Load(path string, myID protocol.DeviceID) (*Wrapper, error) { +func Load(path string, myID protocol.DeviceID) (Wrapper, error) { fd, err := os.Open(path) if err != nil { return nil, err @@ -95,13 +133,13 @@ func Load(path string, myID protocol.DeviceID) (*Wrapper, error) { return Wrap(path, cfg), nil } -func (w *Wrapper) ConfigPath() string { +func (w *wrapper) ConfigPath() string { return w.path } // Subscribe registers the given handler to be called on any future // configuration changes. -func (w *Wrapper) Subscribe(c Committer) { +func (w *wrapper) Subscribe(c Committer) { w.mut.Lock() w.subs = append(w.subs, c) w.mut.Unlock() @@ -109,7 +147,7 @@ func (w *Wrapper) Subscribe(c Committer) { // Unsubscribe de-registers the given handler from any future calls to // configuration changes -func (w *Wrapper) Unsubscribe(c Committer) { +func (w *wrapper) Unsubscribe(c Committer) { w.mut.Lock() for i := range w.subs { if w.subs[i] == c { @@ -123,20 +161,20 @@ func (w *Wrapper) Unsubscribe(c Committer) { } // RawCopy returns a copy of the currently wrapped Configuration object. -func (w *Wrapper) RawCopy() Configuration { +func (w *wrapper) RawCopy() Configuration { w.mut.Lock() defer w.mut.Unlock() return w.cfg.Copy() } // Replace swaps the current configuration object for the given one. -func (w *Wrapper) Replace(cfg Configuration) (Waiter, error) { +func (w *wrapper) Replace(cfg Configuration) (Waiter, error) { w.mut.Lock() defer w.mut.Unlock() return w.replaceLocked(cfg.Copy()) } -func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) { +func (w *wrapper) replaceLocked(to Configuration) (Waiter, error) { from := w.cfg if err := to.clean(); err != nil { @@ -158,7 +196,7 @@ func (w *Wrapper) replaceLocked(to Configuration) (Waiter, error) { return w.notifyListeners(from.Copy(), to.Copy()), nil } -func (w *Wrapper) notifyListeners(from, to Configuration) Waiter { +func (w *wrapper) notifyListeners(from, to Configuration) Waiter { wg := sync.NewWaitGroup() wg.Add(len(w.subs)) for _, sub := range w.subs { @@ -170,7 +208,7 @@ func (w *Wrapper) notifyListeners(from, to Configuration) Waiter { return wg } -func (w *Wrapper) notifyListener(sub Committer, from, to Configuration) { +func (w *wrapper) notifyListener(sub Committer, from, to Configuration) { l.Debugln(sub, "committing configuration") if !sub.CommitConfiguration(from, to) { l.Debugln(sub, "requires restart") @@ -179,7 +217,7 @@ func (w *Wrapper) notifyListener(sub Committer, from, to Configuration) { } // Devices returns a map of devices. -func (w *Wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration { +func (w *wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration { w.mut.Lock() defer w.mut.Unlock() if w.deviceMap == nil { @@ -193,7 +231,7 @@ func (w *Wrapper) Devices() map[protocol.DeviceID]DeviceConfiguration { // SetDevices adds new devices to the configuration, or overwrites existing // devices with the same ID. -func (w *Wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) { +func (w *wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) { w.mut.Lock() defer w.mut.Unlock() @@ -218,12 +256,12 @@ func (w *Wrapper) SetDevices(devs []DeviceConfiguration) (Waiter, error) { // SetDevice adds a new device to the configuration, or overwrites an existing // device with the same ID. -func (w *Wrapper) SetDevice(dev DeviceConfiguration) (Waiter, error) { +func (w *wrapper) SetDevice(dev DeviceConfiguration) (Waiter, error) { return w.SetDevices([]DeviceConfiguration{dev}) } // RemoveDevice removes the device from the configuration -func (w *Wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) { +func (w *wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) { w.mut.Lock() defer w.mut.Unlock() @@ -240,7 +278,7 @@ func (w *Wrapper) RemoveDevice(id protocol.DeviceID) (Waiter, error) { // Folders returns a map of folders. Folder structures should not be changed, // other than for the purpose of updating via SetFolder(). -func (w *Wrapper) Folders() map[string]FolderConfiguration { +func (w *wrapper) Folders() map[string]FolderConfiguration { w.mut.Lock() defer w.mut.Unlock() if w.folderMap == nil { @@ -253,7 +291,7 @@ func (w *Wrapper) Folders() map[string]FolderConfiguration { } // FolderList returns a slice of folders. -func (w *Wrapper) FolderList() []FolderConfiguration { +func (w *wrapper) FolderList() []FolderConfiguration { w.mut.Lock() defer w.mut.Unlock() return w.cfg.Copy().Folders @@ -261,7 +299,7 @@ func (w *Wrapper) FolderList() []FolderConfiguration { // SetFolder adds a new folder to the configuration, or overwrites an existing // folder with the same ID. -func (w *Wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) { +func (w *wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) { w.mut.Lock() defer w.mut.Unlock() @@ -280,14 +318,14 @@ func (w *Wrapper) SetFolder(fld FolderConfiguration) (Waiter, error) { } // Options returns the current options configuration object. -func (w *Wrapper) Options() OptionsConfiguration { +func (w *wrapper) Options() OptionsConfiguration { w.mut.Lock() defer w.mut.Unlock() return w.cfg.Options.Copy() } // SetOptions replaces the current options configuration object. -func (w *Wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) { +func (w *wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) { w.mut.Lock() defer w.mut.Unlock() newCfg := w.cfg.Copy() @@ -295,21 +333,21 @@ func (w *Wrapper) SetOptions(opts OptionsConfiguration) (Waiter, error) { return w.replaceLocked(newCfg) } -func (w *Wrapper) LDAP() LDAPConfiguration { +func (w *wrapper) LDAP() LDAPConfiguration { w.mut.Lock() defer w.mut.Unlock() return w.cfg.LDAP.Copy() } // GUI returns the current GUI configuration object. -func (w *Wrapper) GUI() GUIConfiguration { +func (w *wrapper) GUI() GUIConfiguration { w.mut.Lock() defer w.mut.Unlock() return w.cfg.GUI.Copy() } // SetGUI replaces the current GUI configuration object. -func (w *Wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) { +func (w *wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) { w.mut.Lock() defer w.mut.Unlock() newCfg := w.cfg.Copy() @@ -319,7 +357,7 @@ func (w *Wrapper) SetGUI(gui GUIConfiguration) (Waiter, error) { // IgnoredDevice returns whether or not connection attempts from the given // device should be silently ignored. -func (w *Wrapper) IgnoredDevice(id protocol.DeviceID) bool { +func (w *wrapper) IgnoredDevice(id protocol.DeviceID) bool { w.mut.Lock() defer w.mut.Unlock() for _, device := range w.cfg.IgnoredDevices { @@ -332,7 +370,7 @@ func (w *Wrapper) IgnoredDevice(id protocol.DeviceID) bool { // IgnoredFolder returns whether or not share attempts for the given // folder should be silently ignored. -func (w *Wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool { +func (w *wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool { dev, ok := w.Device(device) if !ok { return false @@ -341,7 +379,7 @@ func (w *Wrapper) IgnoredFolder(device protocol.DeviceID, folder string) bool { } // Device returns the configuration for the given device and an "ok" bool. -func (w *Wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) { +func (w *wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) { w.mut.Lock() defer w.mut.Unlock() for _, device := range w.cfg.Devices { @@ -353,7 +391,7 @@ func (w *Wrapper) Device(id protocol.DeviceID) (DeviceConfiguration, bool) { } // Folder returns the configuration for the given folder and an "ok" bool. -func (w *Wrapper) Folder(id string) (FolderConfiguration, bool) { +func (w *wrapper) Folder(id string) (FolderConfiguration, bool) { w.mut.Lock() defer w.mut.Unlock() for _, folder := range w.cfg.Folders { @@ -365,7 +403,7 @@ func (w *Wrapper) Folder(id string) (FolderConfiguration, bool) { } // 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() @@ -390,7 +428,7 @@ func (w *Wrapper) Save() error { return nil } -func (w *Wrapper) GlobalDiscoveryServers() []string { +func (w *wrapper) GlobalDiscoveryServers() []string { var servers []string for _, srv := range w.Options().GlobalAnnServers { switch srv { @@ -407,7 +445,7 @@ func (w *Wrapper) GlobalDiscoveryServers() []string { return util.UniqueStrings(servers) } -func (w *Wrapper) ListenAddresses() []string { +func (w *wrapper) ListenAddresses() []string { var addresses []string for _, addr := range w.Options().ListenAddresses { switch addr { @@ -420,15 +458,15 @@ func (w *Wrapper) ListenAddresses() []string { return util.UniqueStrings(addresses) } -func (w *Wrapper) RequiresRestart() bool { +func (w *wrapper) RequiresRestart() bool { return atomic.LoadUint32(&w.requiresRestart) != 0 } -func (w *Wrapper) setRequiresRestart() { +func (w *wrapper) setRequiresRestart() { atomic.StoreUint32(&w.requiresRestart, 1) } -func (w *Wrapper) MyName() string { +func (w *wrapper) MyName() string { w.mut.Lock() myID := w.cfg.MyID w.mut.Unlock() @@ -436,7 +474,7 @@ func (w *Wrapper) MyName() string { return cfg.Name } -func (w *Wrapper) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) { +func (w *wrapper) AddOrUpdatePendingDevice(device protocol.DeviceID, name, address string) { defer w.Save() w.mut.Lock() @@ -459,7 +497,7 @@ func (w *Wrapper) AddOrUpdatePendingDevice(device protocol.DeviceID, name, addre }) } -func (w *Wrapper) AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) { +func (w *wrapper) AddOrUpdatePendingFolder(id, label string, device protocol.DeviceID) { defer w.Save() w.mut.Lock() diff --git a/lib/connections/lan_test.go b/lib/connections/lan_test.go index 68f985bcd..a9f388253 100644 --- a/lib/connections/lan_test.go +++ b/lib/connections/lan_test.go @@ -36,7 +36,7 @@ func TestIsLANHost(t *testing.T) { AlwaysLocalNets: []string{"10.20.30.0/24"}, }, }) - s := &Service{cfg: cfg} + s := &service{cfg: cfg} for _, tc := range cases { res := s.isLANHost(tc.addr) diff --git a/lib/connections/limiter.go b/lib/connections/limiter.go index 8dc17d5f9..b2ff22f56 100644 --- a/lib/connections/limiter.go +++ b/lib/connections/limiter.go @@ -36,7 +36,7 @@ type waiter interface { const limiterBurstSize = 4 * 128 << 10 -func newLimiter(cfg *config.Wrapper) *limiter { +func newLimiter(cfg config.Wrapper) *limiter { l := &limiter{ write: rate.NewLimiter(rate.Inf, limiterBurstSize), read: rate.NewLimiter(rate.Inf, limiterBurstSize), diff --git a/lib/connections/limiter_test.go b/lib/connections/limiter_test.go index c8a4f654a..22319f1f3 100644 --- a/lib/connections/limiter_test.go +++ b/lib/connections/limiter_test.go @@ -24,7 +24,7 @@ func init() { device4, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2") } -func initConfig() *config.Wrapper { +func initConfig() config.Wrapper { cfg := config.Wrap("/dev/null", config.New(device1)) dev1Conf = config.NewDeviceConfiguration(device1, "device1") dev2Conf = config.NewDeviceConfiguration(device2, "device2") diff --git a/lib/connections/relay_dial.go b/lib/connections/relay_dial.go index 5d08e337a..ab236e289 100644 --- a/lib/connections/relay_dial.go +++ b/lib/connections/relay_dial.go @@ -22,7 +22,7 @@ func init() { } type relayDialer struct { - cfg *config.Wrapper + cfg config.Wrapper tlsCfg *tls.Config } @@ -70,7 +70,7 @@ func (d *relayDialer) RedialFrequency() time.Duration { type relayDialerFactory struct{} -func (relayDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer { +func (relayDialerFactory) New(cfg config.Wrapper, tlsCfg *tls.Config) genericDialer { return &relayDialer{ cfg: cfg, tlsCfg: tlsCfg, diff --git a/lib/connections/relay_listen.go b/lib/connections/relay_listen.go index 889ce3ad8..4d0387a13 100644 --- a/lib/connections/relay_listen.go +++ b/lib/connections/relay_listen.go @@ -29,7 +29,7 @@ type relayListener struct { onAddressesChangedNotifier uri *url.URL - cfg *config.Wrapper + cfg config.Wrapper tlsCfg *tls.Config conns chan internalConn factory listenerFactory @@ -180,7 +180,7 @@ func (t *relayListener) NATType() string { type relayListenerFactory struct{} -func (f *relayListenerFactory) New(uri *url.URL, cfg *config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener { +func (f *relayListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener { return &relayListener{ uri: uri, cfg: cfg, diff --git a/lib/connections/service.go b/lib/connections/service.go index 90874596d..7f158cf8a 100644 --- a/lib/connections/service.go +++ b/lib/connections/service.go @@ -77,9 +77,15 @@ var tlsCipherSuiteNames = map[uint16]string{ // Service listens and dials all configured unconnected devices, via supported // dialers. Successful connections are handed to the model. -type Service struct { +type Service interface { + suture.Service + Status() map[string]interface{} + NATType() string +} + +type service struct { *suture.Supervisor - cfg *config.Wrapper + cfg config.Wrapper myID protocol.DeviceID model Model tlsCfg *tls.Config @@ -97,10 +103,10 @@ type Service struct { listenerSupervisor *suture.Supervisor } -func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, - bepProtocolName string, tlsDefaultCommonName string) *Service { +func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, + bepProtocolName string, tlsDefaultCommonName string) *service { - service := &Service{ + service := &service{ Supervisor: suture.New("connections.Service", suture.Spec{ Log: func(line string) { l.Infoln(line) @@ -156,7 +162,7 @@ func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg * return service } -func (s *Service) handle() { +func (s *service) handle() { next: for c := range s.conns { cs := c.ConnectionState() @@ -282,7 +288,7 @@ next: } } -func (s *Service) connect() { +func (s *service) connect() { nextDial := make(map[string]time.Time) // Used as delay for the first few connection attempts, increases @@ -433,7 +439,7 @@ func (s *Service) connect() { } } -func (s *Service) isLANHost(host string) bool { +func (s *service) isLANHost(host string) bool { // Probably we are called with an ip:port combo which we can resolve as // a TCP address. if addr, err := net.ResolveTCPAddr("tcp", host); err == nil { @@ -447,7 +453,7 @@ func (s *Service) isLANHost(host string) bool { return false } -func (s *Service) isLAN(addr net.Addr) bool { +func (s *service) isLAN(addr net.Addr) bool { var ip net.IP switch addr := addr.(type) { @@ -488,7 +494,7 @@ func (s *Service) isLAN(addr net.Addr) bool { return false } -func (s *Service) createListener(factory listenerFactory, uri *url.URL) bool { +func (s *service) createListener(factory listenerFactory, uri *url.URL) bool { // must be called with listenerMut held l.Debugln("Starting listener", uri) @@ -500,7 +506,7 @@ func (s *Service) createListener(factory listenerFactory, uri *url.URL) bool { return true } -func (s *Service) logListenAddressesChangedEvent(l genericListener) { +func (s *service) logListenAddressesChangedEvent(l genericListener) { events.Default.Log(events.ListenAddressesChanged, map[string]interface{}{ "address": l.URI(), "lan": l.LANAddresses(), @@ -508,11 +514,11 @@ func (s *Service) logListenAddressesChangedEvent(l genericListener) { }) } -func (s *Service) VerifyConfiguration(from, to config.Configuration) error { +func (s *service) VerifyConfiguration(from, to config.Configuration) error { return nil } -func (s *Service) CommitConfiguration(from, to config.Configuration) bool { +func (s *service) CommitConfiguration(from, to config.Configuration) bool { newDevices := make(map[protocol.DeviceID]bool, len(to.Devices)) for _, dev := range to.Devices { newDevices[dev.DeviceID] = true @@ -589,7 +595,7 @@ func (s *Service) CommitConfiguration(from, to config.Configuration) bool { return true } -func (s *Service) AllAddresses() []string { +func (s *service) AllAddresses() []string { s.listenersMut.RLock() var addrs []string for _, listener := range s.listeners { @@ -604,7 +610,7 @@ func (s *Service) AllAddresses() []string { return util.UniqueStrings(addrs) } -func (s *Service) ExternalAddresses() []string { +func (s *service) ExternalAddresses() []string { s.listenersMut.RLock() var addrs []string for _, listener := range s.listeners { @@ -616,7 +622,7 @@ func (s *Service) ExternalAddresses() []string { return util.UniqueStrings(addrs) } -func (s *Service) Status() map[string]interface{} { +func (s *service) Status() map[string]interface{} { s.listenersMut.RLock() result := make(map[string]interface{}) for addr, listener := range s.listeners { @@ -636,7 +642,7 @@ func (s *Service) Status() map[string]interface{} { return result } -func (s *Service) NATType() string { +func (s *service) NATType() string { s.listenersMut.RLock() defer s.listenersMut.RUnlock() for _, listener := range s.listeners { diff --git a/lib/connections/structs.go b/lib/connections/structs.go index b6ed39829..4358c4014 100644 --- a/lib/connections/structs.go +++ b/lib/connections/structs.go @@ -122,7 +122,7 @@ func (c internalConn) String() string { } type dialerFactory interface { - New(*config.Wrapper, *tls.Config) genericDialer + New(config.Wrapper, *tls.Config) genericDialer Priority() int AlwaysWAN() bool Valid(config.Configuration) error @@ -135,7 +135,7 @@ type genericDialer interface { } type listenerFactory interface { - New(*url.URL, *config.Wrapper, *tls.Config, chan internalConn, *nat.Service) genericListener + New(*url.URL, config.Wrapper, *tls.Config, chan internalConn, *nat.Service) genericListener Valid(config.Configuration) error } diff --git a/lib/connections/tcp_dial.go b/lib/connections/tcp_dial.go index 750db415c..618e772bf 100644 --- a/lib/connections/tcp_dial.go +++ b/lib/connections/tcp_dial.go @@ -24,7 +24,7 @@ func init() { } type tcpDialer struct { - cfg *config.Wrapper + cfg config.Wrapper tlsCfg *tls.Config } @@ -62,7 +62,7 @@ func (d *tcpDialer) RedialFrequency() time.Duration { type tcpDialerFactory struct{} -func (tcpDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer { +func (tcpDialerFactory) New(cfg config.Wrapper, tlsCfg *tls.Config) genericDialer { return &tcpDialer{ cfg: cfg, tlsCfg: tlsCfg, diff --git a/lib/connections/tcp_listen.go b/lib/connections/tcp_listen.go index 5bd6661a2..65573f0b5 100644 --- a/lib/connections/tcp_listen.go +++ b/lib/connections/tcp_listen.go @@ -29,7 +29,7 @@ type tcpListener struct { onAddressesChangedNotifier uri *url.URL - cfg *config.Wrapper + cfg config.Wrapper tlsCfg *tls.Config stop chan struct{} conns chan internalConn @@ -195,7 +195,7 @@ func (t *tcpListener) NATType() string { type tcpListenerFactory struct{} -func (f *tcpListenerFactory) New(uri *url.URL, cfg *config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener { +func (f *tcpListenerFactory) New(uri *url.URL, cfg config.Wrapper, tlsCfg *tls.Config, conns chan internalConn, natService *nat.Service) genericListener { return &tcpListener{ uri: fixupPort(uri, config.DefaultTCPPort), cfg: cfg, diff --git a/lib/model/folder.go b/lib/model/folder.go index 73ec362d5..12b8ee1a2 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -39,7 +39,7 @@ type folder struct { config.FolderConfiguration localFlags uint32 - model *Model + model *model shortID protocol.ShortID ctx context.Context cancel context.CancelFunc @@ -73,7 +73,7 @@ type puller interface { pull() bool // true when successfull and should not be retried } -func newFolder(model *Model, cfg config.FolderConfiguration) folder { +func newFolder(model *model, cfg config.FolderConfiguration) folder { ctx, cancel := context.WithCancel(context.Background()) return folder{ diff --git a/lib/model/folder_recvonly.go b/lib/model/folder_recvonly.go index 4f7dcc22d..57d5a4d73 100644 --- a/lib/model/folder_recvonly.go +++ b/lib/model/folder_recvonly.go @@ -56,7 +56,7 @@ type receiveOnlyFolder struct { *sendReceiveFolder } -func newReceiveOnlyFolder(model *Model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service { +func newReceiveOnlyFolder(model *model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service { sr := newSendReceiveFolder(model, cfg, ver, fs).(*sendReceiveFolder) sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files return &receiveOnlyFolder{sr} diff --git a/lib/model/folder_recvonly_test.go b/lib/model/folder_recvonly_test.go index 3fc2189c0..62708cdbc 100644 --- a/lib/model/folder_recvonly_test.go +++ b/lib/model/folder_recvonly_test.go @@ -336,7 +336,7 @@ func setupKnownFiles(t *testing.T, data []byte) []protocol.FileInfo { return knownFiles } -func setupROFolder() *Model { +func setupROFolder() *model { fcfg := config.NewFolderConfiguration(myID, "ro", "receive only test", fs.FilesystemTypeBasic, "_recvonly") fcfg.Type = config.FolderTypeReceiveOnly fcfg.Devices = []config.FolderDeviceConfiguration{{DeviceID: device1}} @@ -349,7 +349,7 @@ func setupROFolder() *Model { wrp := createTmpWrapper(cfg) db := db.OpenMemory() - m := NewModel(wrp, myID, "syncthing", "dev", db, nil) + m := newModel(wrp, myID, "syncthing", "dev", db, nil) m.ServeBackground() m.AddFolder(fcfg) diff --git a/lib/model/folder_sendonly.go b/lib/model/folder_sendonly.go index bd4a7ac7e..3deaa2125 100644 --- a/lib/model/folder_sendonly.go +++ b/lib/model/folder_sendonly.go @@ -22,7 +22,7 @@ type sendOnlyFolder struct { folder } -func newSendOnlyFolder(model *Model, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem) service { +func newSendOnlyFolder(model *model, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem) service { f := &sendOnlyFolder{ folder: newFolder(model, cfg), } diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index da221a692..a051da985 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -104,7 +104,7 @@ type sendReceiveFolder struct { pullErrorsMut sync.Mutex } -func newSendReceiveFolder(model *Model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service { +func newSendReceiveFolder(model *model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service { f := &sendReceiveFolder{ folder: newFolder(model, cfg), fs: fs, diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index ffbc47e3f..6ac65aa4d 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -75,9 +75,9 @@ func setUpFile(filename string, blockNumbers []int) protocol.FileInfo { } } -func setupSendReceiveFolder(files ...protocol.FileInfo) (*Model, *sendReceiveFolder, string) { +func setupSendReceiveFolder(files ...protocol.FileInfo) (*model, *sendReceiveFolder, string) { w := createTmpWrapper(defaultCfg) - model := NewModel(w, myID, "syncthing", "dev", db.OpenMemory(), nil) + model := newModel(w, myID, "syncthing", "dev", db.OpenMemory(), nil) fcfg, tmpDir := testFolderConfigTmp() model.AddFolder(fcfg) diff --git a/lib/model/model.go b/lib/model/model.go index 1b4b9e842..c009b37c2 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -76,10 +76,60 @@ type Availability struct { FromTemporary bool `json:"fromTemporary"` } -type Model struct { +type Model interface { + suture.Service + + connections.Model + + AddFolder(cfg config.FolderConfiguration) + RestartFolder(from, to config.FolderConfiguration) + StartFolder(folder string) + ResetFolder(folder string) + DelayScan(folder string, next time.Duration) + ScanFolder(folder string) error + ScanFolders() map[string]error + ScanFolderSubdirs(folder string, subs []string) error + State(folder string) (string, time.Time, error) + FolderErrors(folder string) ([]FileError, error) + WatchError(folder string) error + Override(folder string) + Revert(folder string) + BringToFront(folder, file string) + GetIgnores(folder string) ([]string, []string, error) + SetIgnores(folder string, content []string) error + + GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error) + RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error) + + LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated + NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated) + RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error) + CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) + CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool) + Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []Availability + + GlobalSize(folder string) db.Counts + LocalSize(folder string) db.Counts + NeedSize(folder string) db.Counts + ReceiveOnlyChangedSize(folder string) db.Counts + + CurrentSequence(folder string) (int64, bool) + RemoteSequence(folder string) (int64, bool) + + Completion(device protocol.DeviceID, folder string) FolderCompletion + ConnectionStats() map[string]interface{} + DeviceStatistics() map[string]stats.DeviceStatistics + FolderStatistics() map[string]stats.FolderStatistics + UsageReportingStats(version int, preview bool) map[string]interface{} + + StartDeadlockDetector(timeout time.Duration) + GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} +} + +type model struct { *suture.Supervisor - cfg *config.Wrapper + cfg config.Wrapper db *db.Lowlevel finder *db.BlockFinder progressEmitter *ProgressEmitter @@ -112,7 +162,7 @@ type Model struct { foldersRunning int32 // for testing only } -type folderFactory func(*Model, config.FolderConfiguration, versioner.Versioner, fs.Filesystem) service +type folderFactory func(*model, config.FolderConfiguration, versioner.Versioner, fs.Filesystem) service var ( folderFactories = make(map[config.FolderType]folderFactory) @@ -134,8 +184,8 @@ var ( // NewModel creates and starts a new model. The model starts in read-only mode, // where it sends index information to connected peers and responds to requests // for file data without altering the local folder in any way. -func NewModel(cfg *config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *Model { - m := &Model{ +func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) Model { + m := &model{ Supervisor: suture.New("model", suture.Spec{ Log: func(line string) { l.Debugln(line) @@ -180,7 +230,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, clientName, clientVersi // StartDeadlockDetector starts a deadlock detector on the models locks which // causes panics in case the locks cannot be acquired in the given timeout // period. -func (m *Model) StartDeadlockDetector(timeout time.Duration) { +func (m *model) StartDeadlockDetector(timeout time.Duration) { l.Infof("Starting deadlock detector with %v timeout", timeout) detector := newDeadlockDetector(timeout) detector.Watch("fmut", m.fmut) @@ -188,7 +238,7 @@ func (m *Model) StartDeadlockDetector(timeout time.Duration) { } // StartFolder constructs the folder service and starts it. -func (m *Model) StartFolder(folder string) { +func (m *model) StartFolder(folder string) { m.fmut.Lock() m.pmut.Lock() folderType := m.startFolderLocked(folder) @@ -199,7 +249,7 @@ func (m *Model) StartFolder(folder string) { l.Infof("Ready to synchronize %s (%s)", folderCfg.Description(), folderType) } -func (m *Model) startFolderLocked(folder string) config.FolderType { +func (m *model) startFolderLocked(folder string) config.FolderType { if err := m.checkFolderRunningLocked(folder); err == errFolderMissing { panic("cannot start nonexistent folder " + folder) } else if err == nil { @@ -274,7 +324,7 @@ func (m *Model) startFolderLocked(folder string) config.FolderType { return cfg.Type } -func (m *Model) warnAboutOverwritingProtectedFiles(folder string) { +func (m *model) warnAboutOverwritingProtectedFiles(folder string) { if m.folderCfgs[folder].Type == config.FolderTypeSendOnly { return } @@ -308,7 +358,7 @@ func (m *Model) warnAboutOverwritingProtectedFiles(folder string) { } } -func (m *Model) AddFolder(cfg config.FolderConfiguration) { +func (m *model) AddFolder(cfg config.FolderConfiguration) { if len(cfg.ID) == 0 { panic("cannot add empty folder id") } @@ -322,7 +372,7 @@ func (m *Model) AddFolder(cfg config.FolderConfiguration) { m.fmut.Unlock() } -func (m *Model) addFolderLocked(cfg config.FolderConfiguration) { +func (m *model) addFolderLocked(cfg config.FolderConfiguration) { m.folderCfgs[cfg.ID] = cfg folderFs := cfg.Filesystem() m.folderFiles[cfg.ID] = db.NewFileSet(cfg.ID, folderFs, m.db) @@ -334,7 +384,7 @@ func (m *Model) addFolderLocked(cfg config.FolderConfiguration) { m.folderIgnores[cfg.ID] = ignores } -func (m *Model) RemoveFolder(cfg config.FolderConfiguration) { +func (m *model) RemoveFolder(cfg config.FolderConfiguration) { m.fmut.Lock() m.pmut.Lock() // Delete syncthing specific files @@ -348,7 +398,7 @@ func (m *Model) RemoveFolder(cfg config.FolderConfiguration) { m.fmut.Unlock() } -func (m *Model) tearDownFolderLocked(cfg config.FolderConfiguration, err error) { +func (m *model) tearDownFolderLocked(cfg config.FolderConfiguration, err error) { // Close connections to affected devices // Must happen before stopping the folder service to abort ongoing // transmissions and thus allow timely service termination. @@ -376,7 +426,7 @@ func (m *Model) tearDownFolderLocked(cfg config.FolderConfiguration, err error) delete(m.folderStatRefs, cfg.ID) } -func (m *Model) RestartFolder(from, to config.FolderConfiguration) { +func (m *model) RestartFolder(from, to config.FolderConfiguration) { if len(to.ID) == 0 { panic("bug: cannot restart empty folder ID") } @@ -421,7 +471,7 @@ func (m *Model) RestartFolder(from, to config.FolderConfiguration) { l.Infof("%v folder %v (%v)", infoMsg, to.Description(), to.Type) } -func (m *Model) UsageReportingStats(version int, preview bool) map[string]interface{} { +func (m *model) UsageReportingStats(version int, preview bool) map[string]interface{} { stats := make(map[string]interface{}) if version >= 3 { // Block stats @@ -540,7 +590,7 @@ func (info ConnectionInfo) MarshalJSON() ([]byte, error) { } // ConnectionStats returns a map with connection statistics for each device. -func (m *Model) ConnectionStats() map[string]interface{} { +func (m *model) ConnectionStats() map[string]interface{} { m.fmut.RLock() m.pmut.RLock() @@ -587,7 +637,7 @@ func (m *Model) ConnectionStats() map[string]interface{} { } // DeviceStatistics returns statistics about each device -func (m *Model) DeviceStatistics() map[string]stats.DeviceStatistics { +func (m *model) DeviceStatistics() map[string]stats.DeviceStatistics { res := make(map[string]stats.DeviceStatistics) for id := range m.cfg.Devices() { res[id.String()] = m.deviceStatRef(id).GetStatistics() @@ -596,7 +646,7 @@ func (m *Model) DeviceStatistics() map[string]stats.DeviceStatistics { } // FolderStatistics returns statistics about each folder -func (m *Model) FolderStatistics() map[string]stats.FolderStatistics { +func (m *model) FolderStatistics() map[string]stats.FolderStatistics { res := make(map[string]stats.FolderStatistics) for id := range m.cfg.Folders() { res[id] = m.folderStatRef(id).GetStatistics() @@ -614,7 +664,7 @@ type FolderCompletion struct { // Completion returns the completion status, in percent, for the given device // and folder. -func (m *Model) Completion(device protocol.DeviceID, folder string) FolderCompletion { +func (m *model) Completion(device protocol.DeviceID, folder string) FolderCompletion { m.fmut.RLock() rf, ok := m.folderFiles[folder] m.fmut.RUnlock() @@ -696,7 +746,7 @@ func addSizeOfFile(s *db.Counts, f db.FileIntf) { // GlobalSize returns the number of files, deleted files and total bytes for all // files in the global model. -func (m *Model) GlobalSize(folder string) db.Counts { +func (m *model) GlobalSize(folder string) db.Counts { m.fmut.RLock() defer m.fmut.RUnlock() if rf, ok := m.folderFiles[folder]; ok { @@ -707,7 +757,7 @@ func (m *Model) GlobalSize(folder string) db.Counts { // LocalSize returns the number of files, deleted files and total bytes for all // files in the local folder. -func (m *Model) LocalSize(folder string) db.Counts { +func (m *model) LocalSize(folder string) db.Counts { m.fmut.RLock() defer m.fmut.RUnlock() if rf, ok := m.folderFiles[folder]; ok { @@ -719,7 +769,7 @@ func (m *Model) LocalSize(folder string) db.Counts { // ReceiveOnlyChangedSize returns the number of files, deleted files and // total bytes for all files that have changed locally in a receieve only // folder. -func (m *Model) ReceiveOnlyChangedSize(folder string) db.Counts { +func (m *model) ReceiveOnlyChangedSize(folder string) db.Counts { m.fmut.RLock() defer m.fmut.RUnlock() if rf, ok := m.folderFiles[folder]; ok { @@ -729,7 +779,7 @@ func (m *Model) ReceiveOnlyChangedSize(folder string) db.Counts { } // NeedSize returns the number and total size of currently needed files. -func (m *Model) NeedSize(folder string) db.Counts { +func (m *model) NeedSize(folder string) db.Counts { m.fmut.RLock() defer m.fmut.RUnlock() @@ -753,7 +803,7 @@ func (m *Model) NeedSize(folder string) db.Counts { // NeedFolderFiles returns paginated list of currently needed files in // progress, queued, and to be queued on next puller iteration, as well as the // total number of files currently needed. -func (m *Model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated) { +func (m *model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated) { m.fmut.RLock() defer m.fmut.RUnlock() @@ -820,7 +870,7 @@ func (m *Model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfo // LocalChangedFiles returns a paginated list of currently needed files in // progress, queued, and to be queued on next puller iteration, as well as the // total number of files currently needed. -func (m *Model) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated { +func (m *model) LocalChangedFiles(folder string, page, perpage int) []db.FileInfoTruncated { m.fmut.RLock() defer m.fmut.RUnlock() @@ -861,7 +911,7 @@ func (m *Model) LocalChangedFiles(folder string, page, perpage int) []db.FileInf // RemoteNeedFolderFiles returns paginated list of currently needed files in // progress, queued, and to be queued on next puller iteration, as well as the // total number of files currently needed. -func (m *Model) RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error) { +func (m *model) RemoteNeedFolderFiles(device protocol.DeviceID, folder string, page, perpage int) ([]db.FileInfoTruncated, error) { m.fmut.RLock() m.pmut.RLock() if err := m.checkDeviceFolderConnectedLocked(device, folder); err != nil { @@ -891,17 +941,17 @@ func (m *Model) RemoteNeedFolderFiles(device protocol.DeviceID, folder string, p // Index is called when a new device is connected and we receive their full index. // Implements the protocol.Model interface. -func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) { +func (m *model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) { m.handleIndex(deviceID, folder, fs, false) } // IndexUpdate is called for incremental updates to connected devices' indexes. // Implements the protocol.Model interface. -func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) { +func (m *model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) { m.handleIndex(deviceID, folder, fs, true) } -func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, update bool) { +func (m *model) handleIndex(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, update bool) { op := "Index" if update { op += " update" @@ -956,7 +1006,7 @@ func (m *Model) handleIndex(deviceID protocol.DeviceID, folder string, fs []prot }) } -func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) { +func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) { // Check the peer device's announced folders against our own. Emits events // for folders that we don't expect (unknown or not shared). // Also, collect a list of folders we do share, and if he's interested in @@ -1136,7 +1186,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon } // handleIntroductions handles adding devices/shares that are shared by an introducer device -func (m *Model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig) (folderDeviceSet, bool) { +func (m *model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig) (folderDeviceSet, bool) { // This device is an introducer. Go through the announced lists of folders // and devices and add what we are missing, remove what we have extra that // has been introducer by the introducer. @@ -1192,7 +1242,7 @@ func (m *Model) handleIntroductions(introducerCfg config.DeviceConfiguration, cm } // handleDeintroductions handles removals of devices/shares that are removed by an introducer device -func (m *Model) handleDeintroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig, foldersDevices folderDeviceSet) bool { +func (m *model) handleDeintroductions(introducerCfg config.DeviceConfiguration, cm protocol.ClusterConfig, foldersDevices folderDeviceSet) bool { changed := false devicesNotIntroduced := make(map[protocol.DeviceID]struct{}) @@ -1249,7 +1299,7 @@ func (m *Model) handleDeintroductions(introducerCfg config.DeviceConfiguration, // handleAutoAccepts handles adding and sharing folders for devices that have // AutoAcceptFolders set to true. -func (m *Model) handleAutoAccepts(deviceCfg config.DeviceConfiguration, folder protocol.Folder) bool { +func (m *model) handleAutoAccepts(deviceCfg config.DeviceConfiguration, folder protocol.Folder) bool { if cfg, ok := m.cfg.Folder(folder.ID); !ok { defaultPath := m.cfg.Options().DefaultFolderPath defaultPathFs := fs.NewFilesystem(fs.FilesystemTypeBasic, defaultPath) @@ -1295,7 +1345,7 @@ func (m *Model) handleAutoAccepts(deviceCfg config.DeviceConfiguration, folder p } } -func (m *Model) introduceDevice(device protocol.Device, introducerCfg config.DeviceConfiguration) { +func (m *model) introduceDevice(device protocol.Device, introducerCfg config.DeviceConfiguration) { addresses := []string{"dynamic"} for _, addr := range device.Addresses { if addr != "dynamic" { @@ -1324,7 +1374,7 @@ func (m *Model) introduceDevice(device protocol.Device, introducerCfg config.Dev } // Closed is called when a connection has been closed -func (m *Model) Closed(conn protocol.Connection, err error) { +func (m *model) Closed(conn protocol.Connection, err error) { device := conn.ID() m.pmut.Lock() @@ -1350,14 +1400,14 @@ func (m *Model) Closed(conn protocol.Connection, err error) { } // close will close the underlying connection for a given device -func (m *Model) close(device protocol.DeviceID, err error) { +func (m *model) close(device protocol.DeviceID, err error) { m.pmut.Lock() m.closeLocked(device, err) m.pmut.Unlock() } // closeLocked will close the underlying connection for a given device -func (m *Model) closeLocked(device protocol.DeviceID, err error) { +func (m *model) closeLocked(device protocol.DeviceID, err error) { conn, ok := m.conn[device] if !ok { // There is no connection to close @@ -1398,7 +1448,7 @@ func (r *requestResponse) Wait() { // Request returns the specified data segment by reading it from local disk. // Implements the protocol.Model interface. -func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) { +func (m *model) Request(deviceID protocol.DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) { if size < 0 || offset < 0 { return nil, protocol.ErrInvalid } @@ -1519,7 +1569,7 @@ func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, size in return res, nil } -func (m *Model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem, folder, name string, blockIndex int, hash []byte) { +func (m *model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem, folder, name string, blockIndex int, hash []byte) { cf, ok := m.CurrentFolderFile(folder, name) if !ok { l.Debugf("%v recheckFile: %s: %q / %q: no current file", m, deviceID, folder, name) @@ -1560,7 +1610,7 @@ func (m *Model) recheckFile(deviceID protocol.DeviceID, folderFs fs.Filesystem, } } -func (m *Model) CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) { +func (m *model) CurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) { m.fmut.RLock() fs, ok := m.folderFiles[folder] m.fmut.RUnlock() @@ -1570,7 +1620,7 @@ func (m *Model) CurrentFolderFile(folder string, file string) (protocol.FileInfo return fs.Get(protocol.LocalDeviceID, file) } -func (m *Model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool) { +func (m *model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo, bool) { m.fmut.RLock() fs, ok := m.folderFiles[folder] m.fmut.RUnlock() @@ -1581,7 +1631,7 @@ func (m *Model) CurrentGlobalFile(folder string, file string) (protocol.FileInfo } type cFiler struct { - m *Model + m Model r string } @@ -1591,7 +1641,7 @@ func (cf cFiler) CurrentFile(file string) (protocol.FileInfo, bool) { } // Connection returns the current connection for device, and a boolean whether a connection was found. -func (m *Model) Connection(deviceID protocol.DeviceID) (connections.Connection, bool) { +func (m *model) Connection(deviceID protocol.DeviceID) (connections.Connection, bool) { m.pmut.RLock() cn, ok := m.conn[deviceID] m.pmut.RUnlock() @@ -1601,7 +1651,7 @@ func (m *Model) Connection(deviceID protocol.DeviceID) (connections.Connection, return cn, ok } -func (m *Model) GetIgnores(folder string) ([]string, []string, error) { +func (m *model) GetIgnores(folder string) ([]string, []string, error) { m.fmut.RLock() defer m.fmut.RUnlock() @@ -1630,7 +1680,7 @@ func (m *Model) GetIgnores(folder string) ([]string, []string, error) { return ignores.Lines(), ignores.Patterns(), nil } -func (m *Model) SetIgnores(folder string, content []string) error { +func (m *model) SetIgnores(folder string, content []string) error { cfg, ok := m.cfg.Folders()[folder] if !ok { return fmt.Errorf("folder %s does not exist", cfg.Description()) @@ -1664,7 +1714,7 @@ func (m *Model) SetIgnores(folder string, content []string) error { // OnHello is called when an device connects to us. // This allows us to extract some information from the Hello message // and add it to a list of known devices ahead of any checks. -func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.HelloResult) error { +func (m *model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.HelloResult) error { if m.cfg.IgnoredDevice(remoteID) { return errDeviceIgnored } @@ -1694,7 +1744,7 @@ func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protoco } // GetHello is called when we are about to connect to some remote device. -func (m *Model) GetHello(id protocol.DeviceID) protocol.HelloIntf { +func (m *model) GetHello(id protocol.DeviceID) protocol.HelloIntf { name := "" if _, ok := m.cfg.Device(id); ok { name = m.cfg.MyName() @@ -1709,7 +1759,7 @@ func (m *Model) GetHello(id protocol.DeviceID) protocol.HelloIntf { // AddConnection adds a new peer connection to the model. An initial index will // be sent to the connected peer, thereafter index updates whenever the local // folder changes. -func (m *Model) AddConnection(conn connections.Connection, hello protocol.HelloResult) { +func (m *model) AddConnection(conn connections.Connection, hello protocol.HelloResult) { deviceID := conn.ID() device, ok := m.cfg.Device(deviceID) if !ok { @@ -1778,7 +1828,7 @@ func (m *Model) AddConnection(conn connections.Connection, hello protocol.HelloR m.deviceWasSeen(deviceID) } -func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) { +func (m *model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) { m.fmut.RLock() cfg, ok := m.folderCfgs[folder] m.fmut.RUnlock() @@ -1799,7 +1849,7 @@ func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, update }) } -func (m *Model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatisticsReference { +func (m *model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatisticsReference { m.fmut.Lock() defer m.fmut.Unlock() @@ -1812,11 +1862,11 @@ func (m *Model) deviceStatRef(deviceID protocol.DeviceID) *stats.DeviceStatistic return sr } -func (m *Model) deviceWasSeen(deviceID protocol.DeviceID) { +func (m *model) deviceWasSeen(deviceID protocol.DeviceID) { m.deviceStatRef(deviceID).WasSeen() } -func (m *Model) folderStatRef(folder string) *stats.FolderStatisticsReference { +func (m *model) folderStatRef(folder string) *stats.FolderStatisticsReference { m.fmut.Lock() defer m.fmut.Unlock() @@ -1828,7 +1878,7 @@ func (m *Model) folderStatRef(folder string) *stats.FolderStatisticsReference { return sr } -func (m *Model) receivedFile(folder string, file protocol.FileInfo) { +func (m *model) receivedFile(folder string, file protocol.FileInfo) { m.folderStatRef(folder).ReceivedFile(file.Name, file.IsDeleted()) } @@ -1949,7 +1999,7 @@ func sendIndexTo(prevSequence int64, conn protocol.Connection, folder string, fs return f.Sequence, err } -func (m *Model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo) { +func (m *model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo) { m.updateLocals(folder, fs) m.fmut.RLock() @@ -1959,7 +2009,7 @@ func (m *Model) updateLocalsFromScanning(folder string, fs []protocol.FileInfo) m.diskChangeDetected(folderCfg, fs, events.LocalChangeDetected) } -func (m *Model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) { +func (m *model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) { m.updateLocals(folder, fs) m.fmut.RLock() @@ -1969,7 +2019,7 @@ func (m *Model) updateLocalsFromPulling(folder string, fs []protocol.FileInfo) { m.diskChangeDetected(folderCfg, fs, events.RemoteChangeDetected) } -func (m *Model) updateLocals(folder string, fs []protocol.FileInfo) { +func (m *model) updateLocals(folder string, fs []protocol.FileInfo) { m.fmut.RLock() files := m.folderFiles[folder] m.fmut.RUnlock() @@ -1992,7 +2042,7 @@ func (m *Model) updateLocals(folder string, fs []protocol.FileInfo) { }) } -func (m *Model) diskChangeDetected(folderCfg config.FolderConfiguration, files []protocol.FileInfo, typeOfEvent events.EventType) { +func (m *model) diskChangeDetected(folderCfg config.FolderConfiguration, files []protocol.FileInfo, typeOfEvent events.EventType) { for _, file := range files { if file.IsInvalid() { continue @@ -2035,7 +2085,7 @@ func (m *Model) diskChangeDetected(folderCfg config.FolderConfiguration, files [ } } -func (m *Model) requestGlobal(deviceID protocol.DeviceID, folder, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) { +func (m *model) requestGlobal(deviceID protocol.DeviceID, folder, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) { m.pmut.RLock() nc, ok := m.conn[deviceID] m.pmut.RUnlock() @@ -2049,7 +2099,7 @@ func (m *Model) requestGlobal(deviceID protocol.DeviceID, folder, name string, o return nc.Request(folder, name, offset, size, hash, weakHash, fromTemporary) } -func (m *Model) ScanFolders() map[string]error { +func (m *model) ScanFolders() map[string]error { m.fmut.RLock() folders := make([]string, 0, len(m.folderCfgs)) for folder := range m.folderCfgs { @@ -2087,11 +2137,11 @@ func (m *Model) ScanFolders() map[string]error { return errors } -func (m *Model) ScanFolder(folder string) error { +func (m *model) ScanFolder(folder string) error { return m.ScanFolderSubdirs(folder, nil) } -func (m *Model) ScanFolderSubdirs(folder string, subs []string) error { +func (m *model) ScanFolderSubdirs(folder string, subs []string) error { m.fmut.RLock() if err := m.checkFolderRunningLocked(folder); err != nil { m.fmut.RUnlock() @@ -2103,7 +2153,7 @@ func (m *Model) ScanFolderSubdirs(folder string, subs []string) error { return runner.Scan(subs) } -func (m *Model) DelayScan(folder string, next time.Duration) { +func (m *model) DelayScan(folder string, next time.Duration) { m.fmut.Lock() runner, ok := m.folderRunners[folder] m.fmut.Unlock() @@ -2115,7 +2165,7 @@ func (m *Model) DelayScan(folder string, next time.Duration) { // numHashers returns the number of hasher routines to use for a given folder, // taking into account configuration and available CPU cores. -func (m *Model) numHashers(folder string) int { +func (m *model) numHashers(folder string) int { m.fmut.Lock() folderCfg := m.folderCfgs[folder] numFolders := len(m.folderCfgs) @@ -2144,7 +2194,7 @@ func (m *Model) numHashers(folder string) int { // generateClusterConfig returns a ClusterConfigMessage that is correct for // the given peer device -func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfig { +func (m *model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfig { var message protocol.ClusterConfig m.fmut.RLock() @@ -2201,7 +2251,7 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster return message } -func (m *Model) State(folder string) (string, time.Time, error) { +func (m *model) State(folder string) (string, time.Time, error) { m.fmut.RLock() runner, ok := m.folderRunners[folder] m.fmut.RUnlock() @@ -2215,7 +2265,7 @@ func (m *Model) State(folder string) (string, time.Time, error) { return state.String(), changed, err } -func (m *Model) FolderErrors(folder string) ([]FileError, error) { +func (m *model) FolderErrors(folder string) ([]FileError, error) { m.fmut.RLock() defer m.fmut.RUnlock() if err := m.checkFolderRunningLocked(folder); err != nil { @@ -2224,7 +2274,7 @@ func (m *Model) FolderErrors(folder string) ([]FileError, error) { return m.folderRunners[folder].Errors(), nil } -func (m *Model) WatchError(folder string) error { +func (m *model) WatchError(folder string) error { m.fmut.RLock() defer m.fmut.RUnlock() if err := m.checkFolderRunningLocked(folder); err != nil { @@ -2233,7 +2283,7 @@ func (m *Model) WatchError(folder string) error { return m.folderRunners[folder].WatchError() } -func (m *Model) Override(folder string) { +func (m *model) Override(folder string) { // Grab the runner and the file set. m.fmut.RLock() @@ -2251,7 +2301,7 @@ func (m *Model) Override(folder string) { }) } -func (m *Model) Revert(folder string) { +func (m *model) Revert(folder string) { // Grab the runner and the file set. m.fmut.RLock() @@ -2272,7 +2322,7 @@ func (m *Model) Revert(folder string) { // CurrentSequence returns the change version for the given folder. // This is guaranteed to increment if the contents of the local folder has // changed. -func (m *Model) CurrentSequence(folder string) (int64, bool) { +func (m *model) CurrentSequence(folder string) (int64, bool) { m.fmut.RLock() fs, ok := m.folderFiles[folder] m.fmut.RUnlock() @@ -2288,7 +2338,7 @@ func (m *Model) CurrentSequence(folder string) (int64, bool) { // RemoteSequence returns the change version for the given folder, as // sent by remote peers. This is guaranteed to increment if the contents of // the remote or global folder has changed. -func (m *Model) RemoteSequence(folder string) (int64, bool) { +func (m *model) RemoteSequence(folder string) (int64, bool) { m.fmut.RLock() defer m.fmut.RUnlock() @@ -2308,7 +2358,7 @@ func (m *Model) RemoteSequence(folder string) (int64, bool) { return ver, true } -func (m *Model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} { +func (m *model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} { m.fmut.RLock() files, ok := m.folderFiles[folder] m.fmut.RUnlock() @@ -2372,7 +2422,7 @@ func (m *Model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly return output } -func (m *Model) GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error) { +func (m *model) GetFolderVersions(folder string) (map[string][]versioner.FileVersion, error) { fcfg, ok := m.cfg.Folder(folder) if !ok { return nil, errFolderMissing @@ -2432,7 +2482,7 @@ func (m *Model) GetFolderVersions(folder string) (map[string][]versioner.FileVer return files, nil } -func (m *Model) RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error) { +func (m *model) RestoreFolderVersions(folder string, versions map[string]time.Time) (map[string]string, error) { fcfg, ok := m.cfg.Folder(folder) if !ok { return nil, errFolderMissing @@ -2503,7 +2553,7 @@ func (m *Model) RestoreFolderVersions(folder string, versions map[string]time.Ti return errors, nil } -func (m *Model) Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []Availability { +func (m *model) Availability(folder string, file protocol.FileInfo, block protocol.BlockInfo) []Availability { // The slightly unusual locking sequence here is because we need to hold // pmut for the duration (as the value returned from foldersFiles can // get heavily modified on Close()), but also must acquire fmut before @@ -2544,7 +2594,7 @@ next: } // BringToFront bumps the given files priority in the job queue. -func (m *Model) BringToFront(folder, file string) { +func (m *model) BringToFront(folder, file string) { m.pmut.RLock() defer m.pmut.RUnlock() @@ -2554,20 +2604,20 @@ func (m *Model) BringToFront(folder, file string) { } } -func (m *Model) ResetFolder(folder string) { +func (m *model) ResetFolder(folder string) { l.Infof("Cleaning data for folder %q", folder) db.DropFolder(m.db, folder) } -func (m *Model) String() string { +func (m *model) String() string { return fmt.Sprintf("model@%p", m) } -func (m *Model) VerifyConfiguration(from, to config.Configuration) error { +func (m *model) VerifyConfiguration(from, to config.Configuration) error { return nil } -func (m *Model) CommitConfiguration(from, to config.Configuration) bool { +func (m *model) CommitConfiguration(from, to config.Configuration) bool { // TODO: This should not use reflect, and should take more care to try to handle stuff without restart. // Go through the folder configs and figure out if we need to restart or not. @@ -2661,7 +2711,7 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool { // checkFolderRunningLocked returns nil if the folder is up and running and a // descriptive error if not. // Need to hold (read) lock on m.fmut when calling this. -func (m *Model) checkFolderRunningLocked(folder string) error { +func (m *model) checkFolderRunningLocked(folder string) error { _, ok := m.folderRunners[folder] if ok { return nil @@ -2679,7 +2729,7 @@ func (m *Model) checkFolderRunningLocked(folder string) error { // checkFolderDeviceStatusLocked first checks the folder and then whether the // given device is connected and shares this folder. // Need to hold (read) lock on both m.fmut and m.pmut when calling this. -func (m *Model) checkDeviceFolderConnectedLocked(device protocol.DeviceID, folder string) error { +func (m *model) checkDeviceFolderConnectedLocked(device protocol.DeviceID, folder string) error { if err := m.checkFolderRunningLocked(folder); err != nil { return err } diff --git a/lib/model/model_test.go b/lib/model/model_test.go index 91cd84d1c..f3fbfc175 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -38,7 +38,7 @@ import ( ) var myID, device1, device2 protocol.DeviceID -var defaultCfgWrapper *config.Wrapper +var defaultCfgWrapper config.Wrapper var defaultFolderConfig config.FolderConfiguration var defaultFs fs.Filesystem var defaultCfg config.Configuration @@ -161,7 +161,7 @@ func prepareTmpFile(to fs.Filesystem) (string, error) { return tmpName, nil } -func createTmpWrapper(cfg config.Configuration) *config.Wrapper { +func createTmpWrapper(cfg config.Configuration) config.Wrapper { tmpFile, err := ioutil.TempFile(tmpLocation, "syncthing-testConfig-") if err != nil { panic(err) @@ -171,7 +171,11 @@ func createTmpWrapper(cfg config.Configuration) *config.Wrapper { return wrapper } -func newState(cfg config.Configuration) (*config.Wrapper, *Model) { +func newModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string) *model { + return NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles).(*model) +} + +func newState(cfg config.Configuration) (config.Wrapper, *model) { wcfg := createTmpWrapper(cfg) m := setupModel(wcfg) @@ -183,9 +187,9 @@ func newState(cfg config.Configuration) (*config.Wrapper, *Model) { return wcfg, m } -func setupModel(w *config.Wrapper) *Model { +func setupModel(w config.Wrapper) *model { db := db.OpenMemory() - m := NewModel(w, myID, "syncthing", "dev", db, nil) + m := newModel(w, myID, "syncthing", "dev", db, nil) m.ServeBackground() for id, cfg := range w.Folders() { if !cfg.Paused { @@ -322,7 +326,7 @@ type fakeConnection struct { files []protocol.FileInfo fileData map[string][]byte folder string - model *Model + model *model indexFn func(string, []protocol.FileInfo) requestFn func(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) mut sync.Mutex @@ -563,7 +567,7 @@ func TestDeviceRename(t *testing.T) { cfg := config.Wrap("testdata/tmpconfig.xml", rawCfg) db := db.OpenMemory() - m := NewModel(cfg, myID, "syncthing", "dev", db, nil) + m := newModel(cfg, myID, "syncthing", "dev", db, nil) if cfg.Devices()[device1].Name != "" { t.Errorf("Device already has a name") @@ -662,7 +666,7 @@ func TestClusterConfig(t *testing.T) { wrapper := createTmpWrapper(cfg) defer os.Remove(wrapper.ConfigPath()) - m := NewModel(wrapper, myID, "syncthing", "dev", db, nil) + m := newModel(wrapper, myID, "syncthing", "dev", db, nil) m.AddFolder(cfg.Folders[0]) m.AddFolder(cfg.Folders[1]) m.ServeBackground() @@ -1644,7 +1648,7 @@ func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) { } } -func changeIgnores(t *testing.T, m *Model, expected []string) { +func changeIgnores(t *testing.T, m *model, expected []string) { arrEqual := func(a, b []string) bool { if len(a) != len(b) { return false @@ -1796,7 +1800,7 @@ func TestROScanRecovery(t *testing.T) { testOs.RemoveAll(fcfg.Path) - m := NewModel(cfg, myID, "syncthing", "dev", ldb, nil) + m := newModel(cfg, myID, "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) m.StartFolder("default") m.ServeBackground() @@ -1883,7 +1887,7 @@ func TestRWScanRecovery(t *testing.T) { testOs.RemoveAll(fcfg.Path) - m := NewModel(cfg, myID, "syncthing", "dev", ldb, nil) + m := newModel(cfg, myID, "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) m.StartFolder("default") m.ServeBackground() @@ -1941,7 +1945,7 @@ func TestRWScanRecovery(t *testing.T) { func TestGlobalDirectoryTree(t *testing.T) { db := db.OpenMemory() - m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil) + m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() defer m.Stop() @@ -2193,7 +2197,7 @@ func TestGlobalDirectoryTree(t *testing.T) { func TestGlobalDirectorySelfFixing(t *testing.T) { db := db.OpenMemory() - m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil) + m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() @@ -2368,7 +2372,7 @@ func BenchmarkTree_100_10(b *testing.B) { func benchmarkTree(b *testing.B, n1, n2 int) { db := db.OpenMemory() - m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil) + m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() @@ -2438,7 +2442,7 @@ func TestIssue4357(t *testing.T) { // Create a separate wrapper not to pollute other tests. wrapper := createTmpWrapper(config.Configuration{}) defer os.Remove(wrapper.ConfigPath()) - m := NewModel(wrapper, myID, "syncthing", "dev", db, nil) + m := newModel(wrapper, myID, "syncthing", "dev", db, nil) m.ServeBackground() defer m.Stop() @@ -2568,7 +2572,7 @@ func TestIndexesForUnknownDevicesDropped(t *testing.T) { t.Error("expected two devices") } - m := NewModel(defaultCfgWrapper, myID, "syncthing", "dev", dbi, nil) + m := newModel(defaultCfgWrapper, myID, "syncthing", "dev", dbi, nil) m.AddFolder(defaultFolderConfig) m.StartFolder("default") @@ -3055,7 +3059,7 @@ func TestCustomMarkerName(t *testing.T) { testOs.RemoveAll(fcfg.Path) defer testOs.RemoveAll(fcfg.Path) - m := NewModel(cfg, myID, "syncthing", "dev", ldb, nil) + m := newModel(cfg, myID, "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) m.StartFolder("default") m.ServeBackground() @@ -3466,7 +3470,7 @@ func TestIssue4094(t *testing.T) { // Create a separate wrapper not to pollute other tests. wrapper := createTmpWrapper(config.Configuration{}) defer os.Remove(wrapper.ConfigPath()) - m := NewModel(wrapper, myID, "syncthing", "dev", db, nil) + m := newModel(wrapper, myID, "syncthing", "dev", db, nil) m.ServeBackground() defer m.Stop() @@ -3505,7 +3509,7 @@ func TestIssue4903(t *testing.T) { // Create a separate wrapper not to pollute other tests. wrapper := createTmpWrapper(config.Configuration{}) defer os.Remove(wrapper.ConfigPath()) - m := NewModel(wrapper, myID, "syncthing", "dev", db, nil) + m := newModel(wrapper, myID, "syncthing", "dev", db, nil) m.ServeBackground() defer m.Stop() @@ -3575,7 +3579,7 @@ func TestParentOfUnignored(t *testing.T) { } } -func addFakeConn(m *Model, dev protocol.DeviceID) *fakeConnection { +func addFakeConn(m *model, dev protocol.DeviceID) *fakeConnection { fc := &fakeConnection{id: dev, model: m} m.AddConnection(fc, protocol.HelloResult{}) diff --git a/lib/model/progressemitter.go b/lib/model/progressemitter.go index 55f3467a9..d2b77d97e 100644 --- a/lib/model/progressemitter.go +++ b/lib/model/progressemitter.go @@ -31,7 +31,7 @@ type ProgressEmitter struct { // NewProgressEmitter creates a new progress emitter which emits // DownloadProgress events every interval. -func NewProgressEmitter(cfg *config.Wrapper) *ProgressEmitter { +func NewProgressEmitter(cfg config.Wrapper) *ProgressEmitter { t := &ProgressEmitter{ stop: make(chan struct{}), registry: make(map[string]*sharedPullerState), diff --git a/lib/model/requests_test.go b/lib/model/requests_test.go index 04cc9ac4e..308bc0a00 100644 --- a/lib/model/requests_test.go +++ b/lib/model/requests_test.go @@ -684,7 +684,7 @@ func TestRequestSymlinkWindows(t *testing.T) { } } -func tmpDefaultWrapper() (*config.Wrapper, string) { +func tmpDefaultWrapper() (config.Wrapper, string) { w := createTmpWrapper(defaultCfgWrapper.RawCopy()) fcfg, tmpDir := testFolderConfigTmp() w.SetFolder(fcfg) @@ -703,13 +703,13 @@ func testFolderConfig(path string) config.FolderConfiguration { return cfg } -func setupModelWithConnection() (*Model, *fakeConnection, string, *config.Wrapper) { +func setupModelWithConnection() (*model, *fakeConnection, string, config.Wrapper) { w, tmpDir := tmpDefaultWrapper() m, fc := setupModelWithConnectionFromWrapper(w) return m, fc, tmpDir, w } -func setupModelWithConnectionFromWrapper(w *config.Wrapper) (*Model, *fakeConnection) { +func setupModelWithConnectionFromWrapper(w config.Wrapper) (*model, *fakeConnection) { m := setupModel(w) fc := addFakeConn(m, device1) diff --git a/lib/nat/service.go b/lib/nat/service.go index a0a480a90..5beeea603 100644 --- a/lib/nat/service.go +++ b/lib/nat/service.go @@ -23,7 +23,7 @@ import ( // setup/renewal of a port mapping. type Service struct { id protocol.DeviceID - cfg *config.Wrapper + cfg config.Wrapper stop chan struct{} mappings []*Mapping @@ -31,7 +31,7 @@ type Service struct { mut sync.RWMutex } -func NewService(id protocol.DeviceID, cfg *config.Wrapper) *Service { +func NewService(id protocol.DeviceID, cfg config.Wrapper) *Service { return &Service{ id: id, cfg: cfg, diff --git a/lib/watchaggregator/aggregator.go b/lib/watchaggregator/aggregator.go index b57c8c20f..9175625d6 100644 --- a/lib/watchaggregator/aggregator.go +++ b/lib/watchaggregator/aggregator.go @@ -125,14 +125,14 @@ func newAggregator(folderCfg config.FolderConfiguration, ctx context.Context) *a return a } -func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg *config.Wrapper, ctx context.Context) { +func Aggregate(in <-chan fs.Event, out chan<- []string, folderCfg config.FolderConfiguration, cfg config.Wrapper, ctx context.Context) { a := newAggregator(folderCfg, ctx) // Necessary for unit tests where the backend is mocked go a.mainLoop(in, out, cfg) } -func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg *config.Wrapper) { +func (a *aggregator) mainLoop(in <-chan fs.Event, out chan<- []string, cfg config.Wrapper) { a.notifyTimer = time.NewTimer(a.notifyDelay) defer a.notifyTimer.Stop()