diff --git a/lib/config/folderconfiguration.go b/lib/config/folderconfiguration.go index 0e79b9ddd..f7c054899 100644 --- a/lib/config/folderconfiguration.go +++ b/lib/config/folderconfiguration.go @@ -58,6 +58,7 @@ type FolderConfiguration struct { CopyOwnershipFromParent bool `xml:"copyOwnershipFromParent" json:"copyOwnershipFromParent"` RawModTimeWindowS int `xml:"modTimeWindowS" json:"modTimeWindowS"` MaxConcurrentWrites int `xml:"maxConcurrentWrites" json:"maxConcurrentWrites" default:"2"` + DisableFsync bool `xml:"disableFsync" json:"disableFsync"` cachedFilesystem fs.Filesystem cachedModTimeWindow time.Duration diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index c1d11856f..bef74c2d3 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -1082,7 +1082,7 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot "action": "update", }) - s := newSharedPullerState(file, f.fs, f.folderID, tempName, blocks, reused, f.IgnorePerms || file.NoPermissions, hasCurFile, curFile, !f.DisableSparseFiles) + s := newSharedPullerState(file, f.fs, f.folderID, tempName, blocks, reused, f.IgnorePerms || file.NoPermissions, hasCurFile, curFile, !f.DisableSparseFiles, !f.DisableFsync) l.Debugf("%v need file %s; copy %d, reused %v", f, file.Name, len(blocks), len(reused)) @@ -1581,15 +1581,17 @@ func (f *sendReceiveFolder) dbUpdaterRoutine(dbUpdateChan <-chan dbUpdateJob) { // sync directories for dir := range changedDirs { delete(changedDirs, dir) - fd, err := f.fs.Open(dir) - if err != nil { - l.Debugf("fsync %q failed: %v", dir, err) - continue + if !f.FolderConfiguration.DisableFsync { + fd, err := f.fs.Open(dir) + if err != nil { + l.Debugf("fsync %q failed: %v", dir, err) + continue + } + if err := fd.Sync(); err != nil { + l.Debugf("fsync %q failed: %v", dir, err) + } + fd.Close() } - if err := fd.Sync(); err != nil { - l.Debugf("fsync %q failed: %v", dir, err) - } - fd.Close() } // All updates to file/folder objects that originated remotely diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index 969669eea..176e6cd2d 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -1031,7 +1031,7 @@ func TestPullCtxCancel(t *testing.T) { emptyState := func() pullBlockState { return pullBlockState{ - sharedPullerState: newSharedPullerState(protocol.FileInfo{}, nil, f.folderID, "", nil, nil, false, false, protocol.FileInfo{}, false), + sharedPullerState: newSharedPullerState(protocol.FileInfo{}, nil, f.folderID, "", nil, nil, false, false, protocol.FileInfo{}, false, false), block: protocol.BlockInfo{}, } } diff --git a/lib/model/sharedpullerstate.go b/lib/model/sharedpullerstate.go index 37aa625be..6ab67a967 100644 --- a/lib/model/sharedpullerstate.go +++ b/lib/model/sharedpullerstate.go @@ -32,6 +32,7 @@ type sharedPullerState struct { curFile protocol.FileInfo // The file as it exists now in our database sparse bool created time.Time + fsync bool // Mutable, must be locked for access err error // The first error we hit @@ -49,7 +50,7 @@ type sharedPullerState struct { mut sync.RWMutex // Protects the above } -func newSharedPullerState(file protocol.FileInfo, fs fs.Filesystem, folderID, tempName string, blocks []protocol.BlockInfo, reused []int32, ignorePerms, hasCurFile bool, curFile protocol.FileInfo, sparse bool) *sharedPullerState { +func newSharedPullerState(file protocol.FileInfo, fs fs.Filesystem, folderID, tempName string, blocks []protocol.BlockInfo, reused []int32, ignorePerms, hasCurFile bool, curFile protocol.FileInfo, sparse bool, fsync bool) *sharedPullerState { return &sharedPullerState{ file: file, fs: fs, @@ -67,6 +68,7 @@ func newSharedPullerState(file protocol.FileInfo, fs fs.Filesystem, folderID, te curFile: curFile, mut: sync.NewRWMutex(), sparse: sparse, + fsync: fsync, created: time.Now(), } } @@ -101,13 +103,15 @@ func (w *lockedWriterAt) WriteAt(p []byte, off int64) (n int, err error) { // SyncClose ensures that no more writes are happening before going ahead and // syncing and closing the fd, thus needs to acquire a write-lock. -func (w *lockedWriterAt) SyncClose() error { +func (w *lockedWriterAt) SyncClose(fsync bool) error { w.mut.Lock() defer w.mut.Unlock() - if err := w.fd.Sync(); err != nil { - // Sync() is nice if it works but not worth failing the - // operation over if it fails. - l.Debugf("fsync failed: %v", err) + if fsync { + if err := w.fd.Sync(); err != nil { + // Sync() is nice if it works but not worth failing the + // operation over if it fails. + l.Debugf("fsync failed: %v", err) + } } return w.fd.Close() } @@ -303,7 +307,7 @@ func (s *sharedPullerState) finalClose() (bool, error) { } if s.writer != nil { - if err := s.writer.SyncClose(); err != nil && s.err == nil { + if err := s.writer.SyncClose(s.fsync); err != nil && s.err == nil { // This is our error as we weren't errored before. s.err = err }