From 0ca302f12aa8be391d6b8e7081b5c75fbec2e872 Mon Sep 17 00:00:00 2001
From: Jakob Unterwurzacher
Date: Thu, 29 Jul 2021 20:39:50 +0200
Subject: fusefrontend: implement fsync on directories

Fixes https://github.com/rfjakob/gocryptfs/issues/587
---
 internal/fusefrontend/file.go |  5 ++++-
 internal/fusefrontend/node.go | 19 +++++++++++++++++++
 tests/defaults/main_test.go   | 28 ++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go
index cbf78e9..4866e65 100644
--- a/internal/fusefrontend/file.go
+++ b/internal/fusefrontend/file.go
@@ -427,7 +427,10 @@ func (f *File) Flush(ctx context.Context) syscall.Errno {
 	return fs.ToErrno(err)
 }
 
-// Fsync FUSE call
+// Fsync: handles FUSE opcode FSYNC
+//
+// Unfortunately, as Node.Fsync is also defined and takes precedence,
+// File.Fsync is never called at the moment.
 func (f *File) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
 	f.fdLock.RLock()
 	defer f.fdLock.RUnlock()
diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go
index dd446a3..5d3c178 100644
--- a/internal/fusefrontend/node.go
+++ b/internal/fusefrontend/node.go
@@ -447,3 +447,22 @@ func (n *Node) Rename(ctx context.Context, name string, newParent fs.InodeEmbedd
 	}
 	return 0
 }
+
+// Fsync: handles FUSE opcodes FSYNC & FDIRSYNC
+//
+// Note: f is always set to nil by go-fuse
+func (n *Node) Fsync(ctx context.Context, f fs.FileHandle, flags uint32) syscall.Errno {
+	dirfd, cName, errno := n.prepareAtSyscallMyself()
+	if errno != 0 {
+		return errno
+	}
+	defer syscall.Close(dirfd)
+
+	fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
+	if err != nil {
+		return fs.ToErrno(err)
+	}
+	defer syscall.Close(fd)
+
+	return fs.ToErrno(syscall.Fsync(fd))
+}
diff --git a/tests/defaults/main_test.go b/tests/defaults/main_test.go
index b356f41..2513860 100644
--- a/tests/defaults/main_test.go
+++ b/tests/defaults/main_test.go
@@ -11,6 +11,7 @@ import (
 	"runtime"
 	"strings"
 	"sync"
+	"syscall"
 	"testing"
 
 	"github.com/rfjakob/gocryptfs/tests/test_helpers"
@@ -394,3 +395,30 @@ func TestMaxlen(t *testing.T) {
 		t.Errorf("wrong output: %s", string(out))
 	}
 }
+
+func TestFsync(t *testing.T) {
+	fileName := test_helpers.DefaultPlainDir + "/" + t.Name() + ".file"
+	fileFD, err := syscall.Creat(fileName, 0600)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer syscall.Close(fileFD)
+	dirName := test_helpers.DefaultPlainDir + "/" + t.Name() + ".dir"
+	if err := os.Mkdir(dirName, 0700); err != nil {
+		t.Fatal(err)
+	}
+	dirFD, err := syscall.Open(dirName, syscall.O_RDONLY, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer syscall.Close(dirFD)
+
+	err = syscall.Fsync(dirFD)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = syscall.Fsync(fileFD)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
-- 
cgit v1.2.3