Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

os: File.Sync() returns ENOTSUP on network mounts that support fsync #64215

Closed
iamcalledrob opened this issue Nov 16, 2023 · 5 comments
Closed
Assignees
Labels
NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Milestone

Comments

@iamcalledrob
Copy link

What version of Go are you using (go version)?

$ go version
go version go1.21.0 darwin/arm64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/rob/Library/Caches/go-build'
GOENV='/Users/rob/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/rob/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/rob/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.21.0/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.21.0/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.21.0'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/9x/tzryjjm93zvcvhmn517n9t3h0000gn/T/go-build3328922198=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

  1. Mount a SMB share on macOS that supports fsync (strict sync = yes, the default since Samba 4.7.0)
  2. Open a file on that share
  3. Call file.Sync()

What did you expect to see?

No error. The fsync instruction is passed down to the network share, which takes action (or not, depending on its configuration)

What did you see instead?

Error: ENOTSUP

It appears that a change was implemented in go1.12 for #26650 which switched File.Sync() to call fcntl(fd, F_FULLFSYNC) rather than fsync(fd). This was due to fsync not being implemented as expected on darwin.

On local volumes, this change appears to work correctly. However, F_FULLFSYNC appears to be unsupported on SMB volumes--and likely elsewhere too.

This means that the change as a result of #26650 leads to ENOTSUP being returned where a Sync would have succeeded before.

(I've verified via samba logging that calling unix.Fsync() on the fd results in the correct fsync instruction being sent to the server, so darwin's wonky fsync() behaviour seems to only apply to local volumes)

I'd bet that most Go developers are not testing file operations in SMB mounts, so are shipping software that would have worked in 1.11, but in 1.12+ will fail.

I've had to resort to a workaround:

func sync(f *os.File) error {
  err := f.Sync()

  // Ensure fsync is called if Sync is not supported. There are scenarios (SMB mounts, ...?) where fsync will succeed but Sync will fail due
  if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && errors.Is(err, syscall.ENOTSUP) {
    err = unix.Fsync(int(file.Fd()))
  }

  return err
}
@mauri870 mauri870 added OS-Darwin NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Nov 17, 2023
@mauri870
Copy link
Member

Thanks. It seems plausible to default to fsync if we get ENOTSUP in those platforms.

/cc @golang/darwin

@seankhliao seankhliao changed the title os/File: Sync() returns ENOTSUP on network mounts that support fsync os: File.Sync() returns ENOTSUP on network mounts that support fsync Nov 17, 2023
@gopherbot
Copy link

Change https://go.dev/cl/543535 mentions this issue: internal/poll: change Fsync to fallback to syscall.FSync on darwin

@mauri870
Copy link
Member

I found this post mentioning the issue:

https://macosx.com/threads/smb-samba-for-time-machine-backup.324958/

Although I don't have a SMB server to test this against, falling back to syscall.Fsync on ENOTSUP seems to be a fairly safe fix for the issue.

@mauri870 mauri870 self-assigned this Nov 18, 2023
@mauri870 mauri870 added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Nov 18, 2023
@iamcalledrob
Copy link
Author

For testing purposes, spinning up Samba configured with:

vfs objects = full_audit
full_audit:prefix = %u|%I|%m|%S
full_audit:success = fsync fsync_recv
full_audit:failure = fsync fsync_recv
full_audit:priority = INFO

will show appropriate logging to verify that fsyncs are occurring as expected

@mauri870
Copy link
Member

mauri870 commented Dec 7, 2023

This is a bug in internal/poll.Fsync (os.File.Sync() as well) on darwin that only affect SMB mounts. There is no way around it AFAIK and it has been around circa 2018, after CL 130676 was merged.

Given that we entered a freeze, I want to check if the proposed fix at CL 543535 is worth submitting for 1.22 and if this bug deserves backports for 1.20 and 1.21.

cc @golang/release

@dmitshur dmitshur added this to the Go1.23 milestone Mar 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants