aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrafjaf2025-07-20 18:24:34 +0200
committerJakob Unterwurzacher2025-08-07 23:06:03 +0200
commitf59e552e755f35c0278eaebd27a0dacb10914551 (patch)
treefb7305584a724c17630c92dcec2ea8ba7df9706c
parent2ebd0d754b8ee46e6c65e90e1d1e13491b03b7b5 (diff)
darwin: syscallcompat: add RenameatxNp plus flags
Fix macos file saving problem by implementing RENAME_EXCHANGE flag. With test. The dummy value for RENAME_WHITEOUT collides with the new flags. Move it wayyy up. https://github.com/rfjakob/gocryptfs/issues/914
-rw-r--r--.gitignore3
-rw-r--r--internal/syscallcompat/rename_exchange_test.go58
-rw-r--r--internal/syscallcompat/sys_darwin.go19
3 files changed, 74 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index 2e2743f..73b7357 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,6 @@ gocryptfs.1
# Source tarball version. Should never be committed to git.
/VERSION
+
+# Ignore macos specific files
+.DS_Store \ No newline at end of file
diff --git a/internal/syscallcompat/rename_exchange_test.go b/internal/syscallcompat/rename_exchange_test.go
new file mode 100644
index 0000000..97a95f8
--- /dev/null
+++ b/internal/syscallcompat/rename_exchange_test.go
@@ -0,0 +1,58 @@
+package syscallcompat
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "golang.org/x/sys/unix"
+)
+
+func TestRenameExchange(t *testing.T) {
+ // Create a temporary directory for testing
+ tmpDir, err := os.MkdirTemp("", "renameat2_test")
+ if err != nil {
+ t.Fatalf("Failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ // Test basic exchange functionality
+ file1 := filepath.Join(tmpDir, "file1.txt")
+ file2 := filepath.Join(tmpDir, "file2.txt")
+
+ content1 := []byte("content of file 1")
+ content2 := []byte("content of file 2")
+
+ if err := os.WriteFile(file1, content1, 0644); err != nil {
+ t.Fatalf("Failed to create file1: %v", err)
+ }
+
+ if err := os.WriteFile(file2, content2, 0644); err != nil {
+ t.Fatalf("Failed to create file2: %v", err)
+ }
+
+ // Test RENAME_EXCHANGE - this is the core functionality for issue #914
+ err = Renameat2(unix.AT_FDCWD, file1, unix.AT_FDCWD, file2, RENAME_EXCHANGE)
+ if err != nil {
+ t.Fatalf("RENAME_EXCHANGE failed: %v", err)
+ }
+
+ // Verify that the files have been swapped
+ newContent1, err := os.ReadFile(file1)
+ if err != nil {
+ t.Fatalf("Failed to read file1 after exchange: %v", err)
+ }
+
+ newContent2, err := os.ReadFile(file2)
+ if err != nil {
+ t.Fatalf("Failed to read file2 after exchange: %v", err)
+ }
+
+ if string(newContent1) != string(content2) {
+ t.Errorf("file1 content after exchange. Expected: %s, Got: %s", content2, newContent1)
+ }
+
+ if string(newContent2) != string(content1) {
+ t.Errorf("file2 content after exchange. Expected: %s, Got: %s", content1, newContent2)
+ }
+}
diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go
index cf2f3f0..0ebdd3b 100644
--- a/internal/syscallcompat/sys_darwin.go
+++ b/internal/syscallcompat/sys_darwin.go
@@ -20,11 +20,13 @@ const (
// O_PATH is only defined on Linux
O_PATH = 0
+ // Same meaning, different name
+ RENAME_NOREPLACE = unix.RENAME_EXCL
+ RENAME_EXCHANGE = unix.RENAME_SWAP
+
// Only exists on Linux. Define here to fix build failure, even though
- // we will never see the flags.
- RENAME_NOREPLACE = 1
- RENAME_EXCHANGE = 2
- RENAME_WHITEOUT = 4
+ // we will never see this flag.
+ RENAME_WHITEOUT = 1 << 30
)
// Unfortunately fsetattrlist does not have a syscall wrapper yet.
@@ -152,7 +154,12 @@ func GetdentsSpecial(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.Dir
return emulateGetdents(fd)
}
-// Renameat2 does not exist on Darwin, so we call Renameat and ignore the flags.
+// Renameat2 does not exist on Darwin, but RenameatxNp does.
func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error) {
- return unix.Renameat(olddirfd, oldpath, newdirfd, newpath)
+ // If no flags are set, use tried and true renameat
+ if flags == 0 {
+ return unix.Renameat(olddirfd, oldpath, newdirfd, newpath)
+ }
+ // Let RenameatxNp handle everything else
+ return unix.RenameatxNp(olddirfd, oldpath, newdirfd, newpath, uint32(flags))
}