|
@@ -91,6 +91,7 @@ type rwFolder struct {
|
|
allowSparse bool
|
|
allowSparse bool
|
|
checkFreeSpace bool
|
|
checkFreeSpace bool
|
|
ignoreDelete bool
|
|
ignoreDelete bool
|
|
|
|
+ fsync bool
|
|
|
|
|
|
copiers int
|
|
copiers int
|
|
pullers int
|
|
pullers int
|
|
@@ -126,6 +127,7 @@ func newRWFolder(model *Model, cfg config.FolderConfiguration, ver versioner.Ver
|
|
allowSparse: !cfg.DisableSparseFiles,
|
|
allowSparse: !cfg.DisableSparseFiles,
|
|
checkFreeSpace: cfg.MinDiskFreePct != 0,
|
|
checkFreeSpace: cfg.MinDiskFreePct != 0,
|
|
ignoreDelete: cfg.IgnoreDelete,
|
|
ignoreDelete: cfg.IgnoreDelete,
|
|
|
|
+ fsync: cfg.Fsync,
|
|
|
|
|
|
queue: newJobQueue(),
|
|
queue: newJobQueue(),
|
|
pullTimer: time.NewTimer(time.Second),
|
|
pullTimer: time.NewTimer(time.Second),
|
|
@@ -1372,12 +1374,50 @@ func (f *rwFolder) dbUpdaterRoutine() {
|
|
tick := time.NewTicker(maxBatchTime)
|
|
tick := time.NewTicker(maxBatchTime)
|
|
defer tick.Stop()
|
|
defer tick.Stop()
|
|
|
|
|
|
|
|
+ var changedFiles []string
|
|
|
|
+ var changedDirs []string
|
|
|
|
+ if f.fsync {
|
|
|
|
+ changedFiles = make([]string, 0, maxBatchSize)
|
|
|
|
+ changedDirs = make([]string, 0, maxBatchSize)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ syncFilesOnce := func(files []string, syncFn func(string) error) {
|
|
|
|
+ sort.Strings(files)
|
|
|
|
+ var lastFile string
|
|
|
|
+ for _, file := range files {
|
|
|
|
+ if lastFile == file {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ lastFile = file
|
|
|
|
+ if err := syncFn(file); err != nil {
|
|
|
|
+ l.Infof("fsync %q failed: %v", file, err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
handleBatch := func() {
|
|
handleBatch := func() {
|
|
found := false
|
|
found := false
|
|
var lastFile protocol.FileInfo
|
|
var lastFile protocol.FileInfo
|
|
|
|
|
|
for _, job := range batch {
|
|
for _, job := range batch {
|
|
files = append(files, job.file)
|
|
files = append(files, job.file)
|
|
|
|
+ if f.fsync {
|
|
|
|
+ // collect changed files and dirs
|
|
|
|
+ switch job.jobType {
|
|
|
|
+ case dbUpdateHandleFile, dbUpdateShortcutFile:
|
|
|
|
+ // fsyncing symlinks is only supported by MacOS
|
|
|
|
+ if !job.file.IsSymlink() {
|
|
|
|
+ changedFiles = append(changedFiles,
|
|
|
|
+ filepath.Join(f.dir, job.file.Name))
|
|
|
|
+ }
|
|
|
|
+ case dbUpdateHandleDir:
|
|
|
|
+ changedDirs = append(changedDirs, filepath.Join(f.dir, job.file.Name))
|
|
|
|
+ }
|
|
|
|
+ if job.jobType != dbUpdateShortcutFile {
|
|
|
|
+ changedDirs = append(changedDirs,
|
|
|
|
+ filepath.Dir(filepath.Join(f.dir, job.file.Name)))
|
|
|
|
+ }
|
|
|
|
+ }
|
|
if job.file.IsInvalid() || (job.file.IsDirectory() && !job.file.IsSymlink()) {
|
|
if job.file.IsInvalid() || (job.file.IsDirectory() && !job.file.IsSymlink()) {
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
@@ -1390,6 +1430,14 @@ func (f *rwFolder) dbUpdaterRoutine() {
|
|
lastFile = job.file
|
|
lastFile = job.file
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if f.fsync {
|
|
|
|
+ // sync files and dirs to disk
|
|
|
|
+ syncFilesOnce(changedFiles, osutil.SyncFile)
|
|
|
|
+ changedFiles = changedFiles[:0]
|
|
|
|
+ syncFilesOnce(changedDirs, osutil.SyncDir)
|
|
|
|
+ changedDirs = changedDirs[:0]
|
|
|
|
+ }
|
|
|
|
+
|
|
// All updates to file/folder objects that originated remotely
|
|
// All updates to file/folder objects that originated remotely
|
|
// (across the network) use this call to updateLocals
|
|
// (across the network) use this call to updateLocals
|
|
f.model.updateLocalsFromPulling(f.folderID, files)
|
|
f.model.updateLocalsFromPulling(f.folderID, files)
|