2019-07-23 23:39:20 +02:00
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package syncthing
import (
"crypto/tls"
"fmt"
"io"
"os"
"github.com/pkg/errors"
"github.com/syncthing/syncthing/lib/config"
2019-11-29 09:11:52 +01:00
"github.com/syncthing/syncthing/lib/db/backend"
2019-08-15 16:29:37 +02:00
"github.com/syncthing/syncthing/lib/events"
2019-07-23 23:39:20 +02:00
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/tlsutil"
)
2021-11-18 22:57:59 +01:00
func EnsureDir ( dir string , mode fs . FileMode ) error {
fs := fs . NewFilesystem ( fs . FilesystemTypeBasic , dir )
err := fs . MkdirAll ( "." , mode )
if err != nil {
return err
}
if fi , err := fs . Stat ( "." ) ; err == nil {
// Apprently the stat may fail even though the mkdirall passed. If it
// does, we'll just assume things are in order and let other things
// fail (like loading or creating the config...).
currentMode := fi . Mode ( ) & 0777
if currentMode != mode {
err := fs . Chmod ( "." , mode )
// This can fail on crappy filesystems, nothing we can do about it.
if err != nil {
l . Warnln ( err )
}
}
}
return nil
}
2019-07-23 23:39:20 +02:00
func LoadOrGenerateCertificate ( certFile , keyFile string ) ( tls . Certificate , error ) {
2021-11-07 23:59:48 +01:00
cert , err := tls . LoadX509KeyPair ( certFile , keyFile )
2019-07-23 23:39:20 +02:00
if err != nil {
2021-11-07 23:59:48 +01:00
return GenerateCertificate ( certFile , keyFile )
2019-07-23 23:39:20 +02:00
}
return cert , nil
}
2021-11-07 23:59:48 +01:00
func GenerateCertificate ( certFile , keyFile string ) ( tls . Certificate , error ) {
l . Infof ( "Generating ECDSA key and certificate for %s..." , tlsDefaultCommonName )
return tlsutil . NewCertificate ( certFile , keyFile , tlsDefaultCommonName , deviceCertLifetimeDays )
}
2019-08-15 16:29:37 +02:00
func DefaultConfig ( path string , myID protocol . DeviceID , evLogger events . Logger , noDefaultFolder bool ) ( config . Wrapper , error ) {
2019-07-23 23:39:20 +02:00
newCfg , err := config . NewWithFreePorts ( myID )
if err != nil {
return nil , err
}
if noDefaultFolder {
l . Infoln ( "We will skip creation of a default folder on first start" )
2020-11-20 14:21:54 +01:00
return config . Wrap ( path , newCfg , myID , evLogger ) , nil
2019-07-23 23:39:20 +02:00
}
2021-02-04 21:10:41 +01:00
fcfg := newCfg . Defaults . Folder . Copy ( )
fcfg . ID = "default"
fcfg . Label = "Default Folder"
fcfg . FilesystemType = fs . FilesystemTypeBasic
fcfg . Path = locations . Get ( locations . DefFolder )
newCfg . Folders = append ( newCfg . Folders , fcfg )
2019-07-23 23:39:20 +02:00
l . Infoln ( "Default folder created and/or linked to new config" )
2020-11-20 14:21:54 +01:00
return config . Wrap ( path , newCfg , myID , evLogger ) , nil
2019-07-23 23:39:20 +02:00
}
// LoadConfigAtStartup loads an existing config. If it doesn't yet exist, it
// creates a default one, without the default folder if noDefaultFolder is ture.
// Otherwise it checks the version, and archives and upgrades the config if
// necessary or returns an error, if the version isn't compatible.
2019-08-15 16:29:37 +02:00
func LoadConfigAtStartup ( path string , cert tls . Certificate , evLogger events . Logger , allowNewerConfig , noDefaultFolder bool ) ( config . Wrapper , error ) {
2019-07-23 23:39:20 +02:00
myID := protocol . NewDeviceID ( cert . Certificate [ 0 ] )
2020-08-25 08:11:14 +02:00
cfg , originalVersion , err := config . Load ( path , myID , evLogger )
2019-07-23 23:39:20 +02:00
if fs . IsNotExist ( err ) {
2019-08-15 16:29:37 +02:00
cfg , err = DefaultConfig ( path , myID , evLogger , noDefaultFolder )
2019-07-23 23:39:20 +02:00
if err != nil {
return nil , errors . Wrap ( err , "failed to generate default config" )
}
err = cfg . Save ( )
if err != nil {
return nil , errors . Wrap ( err , "failed to save default config" )
}
l . Infof ( "Default config saved. Edit %s to taste (with Syncthing stopped) or use the GUI" , cfg . ConfigPath ( ) )
} else if err == io . EOF {
return nil , errors . New ( "failed to load config: unexpected end of file. Truncated or empty configuration?" )
} else if err != nil {
return nil , errors . Wrap ( err , "failed to load config" )
}
2020-08-25 08:11:14 +02:00
if originalVersion != config . CurrentVersion {
if originalVersion == config . CurrentVersion + 1101 {
2019-07-23 23:39:20 +02:00
l . Infof ( "Now, THAT's what we call a config from the future! Don't worry. As long as you hit that wire with the connecting hook at precisely eighty-eight miles per hour the instant the lightning strikes the tower... everything will be fine." )
}
2020-08-25 08:11:14 +02:00
if originalVersion > config . CurrentVersion && ! allowNewerConfig {
2021-11-04 08:42:55 +01:00
return nil , fmt . Errorf ( "config file version (%d) is newer than supported version (%d). If this is expected, use --allow-newer-config to override." , originalVersion , config . CurrentVersion )
2019-07-23 23:39:20 +02:00
}
2020-08-25 08:11:14 +02:00
err = archiveAndSaveConfig ( cfg , originalVersion )
2019-07-23 23:39:20 +02:00
if err != nil {
return nil , errors . Wrap ( err , "config archive" )
}
}
return cfg , nil
}
2020-08-25 08:11:14 +02:00
func archiveAndSaveConfig ( cfg config . Wrapper , originalVersion int ) error {
2019-07-23 23:39:20 +02:00
// Copy the existing config to an archive copy
2020-08-25 08:11:14 +02:00
archivePath := cfg . ConfigPath ( ) + fmt . Sprintf ( ".v%d" , originalVersion )
2019-07-23 23:39:20 +02:00
l . Infoln ( "Archiving a copy of old config file format at:" , archivePath )
if err := copyFile ( cfg . ConfigPath ( ) , archivePath ) ; err != nil {
return err
}
// Do a regular atomic config sve
return cfg . Save ( )
}
func copyFile ( src , dst string ) error {
2021-11-22 08:59:47 +01:00
bs , err := os . ReadFile ( src )
2019-07-23 23:39:20 +02:00
if err != nil {
return err
}
2021-11-22 08:59:47 +01:00
if err := os . WriteFile ( dst , bs , 0600 ) ; err != nil {
2019-07-23 23:39:20 +02:00
// Attempt to clean up
os . Remove ( dst )
return err
}
return nil
}
2019-12-12 16:50:09 +01:00
func OpenDBBackend ( path string , tuning config . Tuning ) ( backend . Backend , error ) {
return backend . Open ( path , backend . Tuning ( tuning ) )
2019-07-23 23:39:20 +02:00
}