Skip to content

Commit be10ad7

Browse files
committed
internal/poll: use F_FULLFSYNC fcntl for FD.Fsync on OS X
As reported in #26650 and also cautioned on the man page for fsync on OS X, fsync doesn't properly flush content to permanent storage, and might cause corruption of data if the OS crashes or if the drive loses power. Thus it is recommended to use the F_FULLFSYNC fcntl, which flushes all buffered data to permanent storage and is important for applications such as databases that require a strict ordering of writes. Also added a note in syscall_darwin.go that syscall.Fsync is not invoked for os.File.Sync. Fixes #26650. Change-Id: Idecd9adbbdd640b9c5b02e73b60ed254c98b48b6 Reviewed-on: https://go-review.googlesource.com/130676 Run-TryBot: Emmanuel Odeke <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent ccb70bd commit be10ad7

File tree

4 files changed

+42
-9
lines changed

4 files changed

+42
-9
lines changed

src/internal/poll/fd_fsync_darwin.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package poll
6+
7+
import "syscall"
8+
9+
// Fsync invokes SYS_FCNTL with SYS_FULLFSYNC because
10+
// on OS X, SYS_FSYNC doesn't fully flush contents to disk.
11+
// See Issue #26650 as well as the man page for fsync on OS X.
12+
func (fd *FD) Fsync() error {
13+
if err := fd.incref(); err != nil {
14+
return err
15+
}
16+
defer fd.decref()
17+
18+
_, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd.Sysfd), syscall.F_FULLFSYNC, 0)
19+
if e1 != 0 {
20+
return e1
21+
}
22+
return nil
23+
}

src/internal/poll/fd_fsync_posix.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
6+
7+
package poll
8+
9+
import "syscall"
10+
11+
// Fsync wraps syscall.Fsync.
12+
func (fd *FD) Fsync() error {
13+
if err := fd.incref(); err != nil {
14+
return err
15+
}
16+
defer fd.decref()
17+
return syscall.Fsync(fd.Sysfd)
18+
}

src/internal/poll/fd_posix.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,3 @@ func (fd *FD) Ftruncate(size int64) error {
4646
defer fd.decref()
4747
return syscall.Ftruncate(fd.Sysfd, size)
4848
}
49-
50-
// Fsync wraps syscall.Fsync.
51-
func (fd *FD) Fsync() error {
52-
if err := fd.incref(); err != nil {
53-
return err
54-
}
55-
defer fd.decref()
56-
return syscall.Fsync(fd.Sysfd)
57-
}

src/syscall/syscall_darwin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
252252
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
253253
//sys Fstatfs(fd int, stat *Statfs_t) (err error) = SYS_FSTATFS64
254254
//sys Fsync(fd int) (err error)
255+
// Fsync is not called for os.File.Sync(). Please see internal/poll/fd_fsync_darwin.go
255256
//sys Ftruncate(fd int, length int64) (err error)
256257
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) = SYS_GETDIRENTRIES64
257258
//sys Getdtablesize() (size int)

0 commit comments

Comments
 (0)