2015-10-27 10:37:03 +00: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,
2017-02-09 06:52:18 +00:00
// You can obtain one at https://mozilla.org/MPL/2.0/.
2015-10-27 10:37:03 +00:00
package config
import (
2017-10-24 07:58:55 +00:00
"errors"
2016-11-17 15:12:41 +00:00
"fmt"
2015-10-27 10:37:03 +00:00
"runtime"
2017-08-19 14:36:56 +00:00
"github.com/syncthing/syncthing/lib/fs"
2015-10-27 10:37:03 +00:00
"github.com/syncthing/syncthing/lib/protocol"
)
2017-10-24 07:58:55 +00:00
var (
errPathMissing = errors . New ( "folder path missing" )
errMarkerMissing = errors . New ( "folder marker missing" )
)
2017-11-05 12:18:05 +00:00
const DefaultMarkerName = ".stfolder"
2015-10-27 10:37:03 +00:00
type FolderConfiguration struct {
ID string ` xml:"id,attr" json:"id" `
2016-03-11 09:48:46 +00:00
Label string ` xml:"label,attr" json:"label" `
2017-08-19 14:36:56 +00:00
FilesystemType fs . FilesystemType ` xml:"filesystemType" json:"filesystemType" `
Path string ` xml:"path,attr" json:"path" `
2016-05-04 11:26:36 +00:00
Type FolderType ` xml:"type,attr" json:"type" `
2015-10-27 10:37:03 +00:00
Devices [ ] FolderDeviceConfiguration ` xml:"device" json:"devices" `
RescanIntervalS int ` xml:"rescanIntervalS,attr" json:"rescanIntervalS" `
2017-10-20 14:52:55 +00:00
FSWatcherEnabled bool ` xml:"fsWatcherEnabled,attr" json:"fsWatcherEnabled" `
FSWatcherDelayS int ` xml:"fsWatcherDelayS,attr" json:"fsWatcherDelayS" `
2015-10-27 10:37:03 +00:00
IgnorePerms bool ` xml:"ignorePerms,attr" json:"ignorePerms" `
AutoNormalize bool ` xml:"autoNormalize,attr" json:"autoNormalize" `
2017-04-12 09:01:19 +00:00
MinDiskFree Size ` xml:"minDiskFree" json:"minDiskFree" `
2015-10-27 10:37:03 +00:00
Versioning VersioningConfiguration ` xml:"versioning" json:"versioning" `
Copiers int ` xml:"copiers" json:"copiers" ` // This defines how many files are handled concurrently.
Pullers int ` xml:"pullers" json:"pullers" ` // Defines how many blocks are fetched at the same time, possibly between separate copier routines.
Hashers int ` xml:"hashers" json:"hashers" ` // Less than one sets the value to the number of cores. These are CPU bound due to hashing.
Order PullOrder ` xml:"order" json:"order" `
IgnoreDelete bool ` xml:"ignoreDelete" json:"ignoreDelete" `
ScanProgressIntervalS int ` xml:"scanProgressIntervalS" json:"scanProgressIntervalS" ` // Set to a negative value to disable. Value of 0 will get replaced with value of 2 (default value)
PullerPauseS int ` xml:"pullerPauseS" json:"pullerPauseS" `
MaxConflicts int ` xml:"maxConflicts" json:"maxConflicts" `
2015-11-21 15:30:53 +00:00
DisableSparseFiles bool ` xml:"disableSparseFiles" json:"disableSparseFiles" `
2016-04-15 10:59:41 +00:00
DisableTempIndexes bool ` xml:"disableTempIndexes" json:"disableTempIndexes" `
2016-12-21 18:41:25 +00:00
Paused bool ` xml:"paused" json:"paused" `
2017-01-04 21:04:13 +00:00
WeakHashThresholdPct int ` xml:"weakHashThresholdPct" json:"weakHashThresholdPct" ` // Use weak hash if more than X percent of the file has changed. Set to -1 to always use weak hash.
2017-11-05 12:18:05 +00:00
MarkerName string ` xml:"markerName" json:"markerName" `
2015-10-27 10:37:03 +00:00
2017-08-19 14:36:56 +00:00
cachedFilesystem fs . Filesystem
2016-05-04 10:47:33 +00:00
2017-04-12 09:01:19 +00:00
DeprecatedReadOnly bool ` xml:"ro,attr,omitempty" json:"-" `
2017-04-27 05:46:19 +00:00
DeprecatedMinDiskFreePct float64 ` xml:"minDiskFreePct,omitempty" json:"-" `
2015-10-27 10:37:03 +00:00
}
type FolderDeviceConfiguration struct {
2016-11-07 16:40:48 +00:00
DeviceID protocol . DeviceID ` xml:"id,attr" json:"deviceID" `
IntroducedBy protocol . DeviceID ` xml:"introducedBy,attr" json:"introducedBy" `
2015-10-27 10:37:03 +00:00
}
2017-08-19 14:36:56 +00:00
func NewFolderConfiguration ( id string , fsType fs . FilesystemType , path string ) FolderConfiguration {
2015-11-07 08:47:31 +00:00
f := FolderConfiguration {
2017-08-19 14:36:56 +00:00
ID : id ,
FilesystemType : fsType ,
Path : path ,
2015-11-07 08:47:31 +00:00
}
f . prepare ( )
return f
}
2015-10-27 10:37:03 +00:00
func ( f FolderConfiguration ) Copy ( ) FolderConfiguration {
c := f
c . Devices = make ( [ ] FolderDeviceConfiguration , len ( f . Devices ) )
copy ( c . Devices , f . Devices )
2015-10-27 10:53:42 +00:00
c . Versioning = f . Versioning . Copy ( )
2015-10-27 10:37:03 +00:00
return c
}
2017-08-19 14:36:56 +00:00
func ( f FolderConfiguration ) Filesystem ( ) fs . Filesystem {
2015-10-27 10:37:03 +00:00
// This is intentionally not a pointer method, because things like
2017-08-19 14:36:56 +00:00
// cfg.Folders["default"].Filesystem() should be valid.
if f . cachedFilesystem == nil && f . Path != "" {
l . Infoln ( "bug: uncached filesystem call (should only happen in tests)" )
return fs . NewFilesystem ( f . FilesystemType , f . Path )
2015-10-27 10:37:03 +00:00
}
2017-08-19 14:36:56 +00:00
return f . cachedFilesystem
2015-10-27 10:37:03 +00:00
}
func ( f * FolderConfiguration ) CreateMarker ( ) error {
2017-10-24 07:58:55 +00:00
if err := f . CheckPath ( ) ; err != errMarkerMissing {
return err
2015-10-27 10:37:03 +00:00
}
2017-11-05 12:18:05 +00:00
if f . MarkerName != DefaultMarkerName {
// Folder uses a non-default marker so we shouldn't mess with it.
// Pretend we created it and let the subsequent health checks sort
// out the actual situation.
return nil
}
2015-10-27 10:37:03 +00:00
2017-10-24 07:58:55 +00:00
permBits := fs . FileMode ( 0777 )
if runtime . GOOS == "windows" {
// Windows has no umask so we must chose a safer set of bits to
// begin with.
permBits = 0700
}
fs := f . Filesystem ( )
2017-11-05 12:18:05 +00:00
err := fs . Mkdir ( DefaultMarkerName , permBits )
2017-10-24 07:58:55 +00:00
if err != nil {
return err
}
if dir , err := fs . Open ( "." ) ; err != nil {
l . Debugln ( "folder marker: open . failed:" , err )
} else if err := dir . Sync ( ) ; err != nil {
l . Debugln ( "folder marker: fsync . failed:" , err )
}
2017-11-05 12:18:05 +00:00
fs . Hide ( DefaultMarkerName )
2017-10-24 07:58:55 +00:00
2015-10-27 10:37:03 +00:00
return nil
}
2017-10-24 07:58:55 +00:00
// CheckPath returns nil if the folder root exists and contains the marker file
func ( f * FolderConfiguration ) CheckPath ( ) error {
fi , err := f . Filesystem ( ) . Stat ( "." )
if err != nil || ! fi . IsDir ( ) {
return errPathMissing
}
2017-11-05 12:18:05 +00:00
_ , err = f . Filesystem ( ) . Stat ( f . MarkerName )
2017-10-24 07:58:55 +00:00
if err != nil {
return errMarkerMissing
}
return nil
2015-10-27 10:37:03 +00:00
}
2017-04-23 23:50:56 +00:00
func ( f * FolderConfiguration ) CreateRoot ( ) ( err error ) {
// Directory permission bits. Will be filtered down to something
// sane by umask on Unixes.
2017-08-19 14:36:56 +00:00
permBits := fs . FileMode ( 0777 )
2017-04-23 23:50:56 +00:00
if runtime . GOOS == "windows" {
// Windows has no umask so we must chose a safer set of bits to
// begin with.
permBits = 0700
}
2017-08-19 14:36:56 +00:00
filesystem := f . Filesystem ( )
if _ , err = filesystem . Stat ( "." ) ; fs . IsNotExist ( err ) {
if err = filesystem . MkdirAll ( "." , permBits ) ; err != nil {
l . Warnf ( "Creating directory for %v: %v" , f . Description ( ) , err )
2017-04-23 23:50:56 +00:00
}
}
return err
}
2016-11-17 15:12:41 +00:00
func ( f FolderConfiguration ) Description ( ) string {
2016-12-19 09:12:06 +00:00
if f . Label == "" {
return f . ID
}
2016-11-17 15:12:41 +00:00
return fmt . Sprintf ( "%q (%s)" , f . Label , f . ID )
}
2015-10-27 10:37:03 +00:00
func ( f * FolderConfiguration ) DeviceIDs ( ) [ ] protocol . DeviceID {
deviceIDs := make ( [ ] protocol . DeviceID , len ( f . Devices ) )
for i , n := range f . Devices {
deviceIDs [ i ] = n . DeviceID
}
return deviceIDs
}
2015-11-05 08:01:47 +00:00
func ( f * FolderConfiguration ) prepare ( ) {
2017-08-19 14:36:56 +00:00
if f . Path != "" {
f . cachedFilesystem = fs . NewFilesystem ( f . FilesystemType , f . Path )
2015-11-05 08:01:47 +00:00
}
if f . RescanIntervalS > MaxRescanIntervalS {
f . RescanIntervalS = MaxRescanIntervalS
} else if f . RescanIntervalS < 0 {
f . RescanIntervalS = 0
}
2016-05-09 11:30:19 +00:00
2017-10-20 14:52:55 +00:00
if f . FSWatcherDelayS <= 0 {
f . FSWatcherEnabled = false
f . FSWatcherDelayS = 10
}
2016-05-09 11:30:19 +00:00
if f . Versioning . Params == nil {
f . Versioning . Params = make ( map [ string ] string )
}
2017-01-04 21:04:13 +00:00
if f . WeakHashThresholdPct == 0 {
f . WeakHashThresholdPct = 25
}
2017-11-05 12:18:05 +00:00
if f . MarkerName == "" {
f . MarkerName = DefaultMarkerName
}
2015-11-05 08:01:47 +00:00
}
2015-10-27 10:37:03 +00:00
type FolderDeviceConfigurationList [ ] FolderDeviceConfiguration
func ( l FolderDeviceConfigurationList ) Less ( a , b int ) bool {
return l [ a ] . DeviceID . Compare ( l [ b ] . DeviceID ) == - 1
}
func ( l FolderDeviceConfigurationList ) Swap ( a , b int ) {
l [ a ] , l [ b ] = l [ b ] , l [ a ]
}
func ( l FolderDeviceConfigurationList ) Len ( ) int {
return len ( l )
}
2017-10-24 07:58:55 +00:00
func ( f * FolderConfiguration ) CheckFreeSpace ( ) ( err error ) {
return checkFreeSpace ( f . MinDiskFree , f . Filesystem ( ) )
}