From ec3eaf0b8776500a9762555eac754b6c893420fd Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Wed, 14 Oct 2020 13:40:12 +0200 Subject: [PATCH] syscallcompat: don't retry Close() After Close() returns, the fd is dead, even if we received EINTR. Don't retry, we could shoot down an unrelated fd that received the same fd number. --- internal/fusefrontend/file.go | 14 +++----------- internal/syscallcompat/eintr.go | 27 +++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index 0367705..98e5802 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -391,9 +391,9 @@ func (f *File) Release(ctx context.Context) syscall.Errno { } f.released = true openfiletable.Unregister(f.qIno) - f.fd.Close() + err := f.fd.Close() f.fdLock.Unlock() - return 0 + return fs.ToErrno(err) } // Flush - FUSE call @@ -401,15 +401,7 @@ func (f *File) Flush(ctx context.Context) syscall.Errno { f.fdLock.RLock() defer f.fdLock.RUnlock() - // Since Flush() may be called for each dup'd fd, we don't - // want to really close the file, we just want to flush. This - // is achieved by closing a dup'd fd. - newFd, err := syscall.Dup(f.intFd()) - - if err != nil { - return fs.ToErrno(err) - } - err = syscallcompat.Close(newFd) + err := syscallcompat.Flush(f.intFd()) return fs.ToErrno(err) } diff --git a/internal/syscallcompat/eintr.go b/internal/syscallcompat/eintr.go index 122af9b..f0a82f5 100644 --- a/internal/syscallcompat/eintr.go +++ b/internal/syscallcompat/eintr.go @@ -11,6 +11,9 @@ import ( // // This is needed because CIFS throws lots of EINTR errors: // https://github.com/rfjakob/gocryptfs/issues/483 +// +// Don't use retryEINTR() with syscall.Close()! +// See https://code.google.com/p/chromium/issues/detail?id=269623 . func retryEINTR(op func() error) error { for { err := op() @@ -40,9 +43,25 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { return fd, err } -// Close wraps syscall.Close. +// Flush is a helper for the FUSE command FLUSH. // Retries on EINTR. -func Close(fd int) (err error) { - err = retryEINTR(func() error { return syscall.Close(fd) }) - return err +func Flush(fd int) error { + for { + // Flushing is achieved by closing a dup'd fd. + newFd, err := syscall.Dup(fd) + if err == syscall.EINTR { + continue + } + if err != nil { + return err + } + err = syscall.Close(newFd) + // Even if we get EINTR here, newFd is dead - see + // https://code.google.com/p/chromium/issues/detail?id=269623 . + // We have to make a new one with Dup(), so continue at the top. + if err == syscall.EINTR { + continue + } + return err + } }