aboutsummaryrefslogtreecommitdiff
path: root/internal/syscallcompat
diff options
context:
space:
mode:
Diffstat (limited to 'internal/syscallcompat')
-rw-r--r--internal/syscallcompat/asuser_linux.go4
-rw-r--r--internal/syscallcompat/getdents_test.go5
-rw-r--r--internal/syscallcompat/main_test.go3
-rw-r--r--internal/syscallcompat/rename_exchange_test.go58
-rw-r--r--internal/syscallcompat/sys_darwin.go19
-rw-r--r--internal/syscallcompat/sys_linux.go12
6 files changed, 86 insertions, 15 deletions
diff --git a/internal/syscallcompat/asuser_linux.go b/internal/syscallcompat/asuser_linux.go
index 804a898..39e3ff2 100644
--- a/internal/syscallcompat/asuser_linux.go
+++ b/internal/syscallcompat/asuser_linux.go
@@ -2,7 +2,7 @@ package syscallcompat
import (
"fmt"
- "io/ioutil"
+ "os"
"runtime"
"strconv"
"strings"
@@ -55,7 +55,7 @@ func asUser(f func() (int, error), context *fuse.Context) (int, error) {
func getSupplementaryGroups(pid uint32) (gids []int) {
procPath := fmt.Sprintf("/proc/%d/task/%d/status", pid, pid)
- blob, err := ioutil.ReadFile(procPath)
+ blob, err := os.ReadFile(procPath)
if err != nil {
return nil
}
diff --git a/internal/syscallcompat/getdents_test.go b/internal/syscallcompat/getdents_test.go
index eb670d6..c9e6a99 100644
--- a/internal/syscallcompat/getdents_test.go
+++ b/internal/syscallcompat/getdents_test.go
@@ -4,7 +4,6 @@
package syscallcompat
import (
- "io/ioutil"
"os"
"runtime"
"strings"
@@ -49,13 +48,13 @@ func testGetdents(t *testing.T) {
getdentsUnderTest = emulateGetdents
}
// Fill a directory with filenames of length 1 ... 255
- testDir, err := ioutil.TempDir(tmpDir, "TestGetdents")
+ testDir, err := os.MkdirTemp(tmpDir, "TestGetdents")
if err != nil {
t.Fatal(err)
}
for i := 1; i <= unix.NAME_MAX; i++ {
n := strings.Repeat("x", i)
- err = ioutil.WriteFile(testDir+"/"+n, nil, 0600)
+ err = os.WriteFile(testDir+"/"+n, nil, 0600)
if err != nil {
t.Fatal(err)
}
diff --git a/internal/syscallcompat/main_test.go b/internal/syscallcompat/main_test.go
index ddf6bc4..7183f5a 100644
--- a/internal/syscallcompat/main_test.go
+++ b/internal/syscallcompat/main_test.go
@@ -2,7 +2,6 @@ package syscallcompat
import (
"fmt"
- "io/ioutil"
"os"
"testing"
)
@@ -23,7 +22,7 @@ func TestMain(m *testing.M) {
fmt.Println(err)
os.Exit(1)
}
- tmpDir, err = ioutil.TempDir(parent, "syscallcompat")
+ tmpDir, err = os.MkdirTemp(parent, "syscallcompat")
if err != nil {
fmt.Println(err)
os.Exit(1)
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))
}
diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go
index 5a4a4ab..19d2c56 100644
--- a/internal/syscallcompat/sys_linux.go
+++ b/internal/syscallcompat/sys_linux.go
@@ -124,8 +124,16 @@ func LsetxattrUser(path string, attr string, data []byte, flags int, context *fu
func timesToTimespec(a *time.Time, m *time.Time) []unix.Timespec {
ts := make([]unix.Timespec, 2)
- ts[0] = unix.Timespec(fuse.UtimeToTimespec(a))
- ts[1] = unix.Timespec(fuse.UtimeToTimespec(m))
+ if a == nil {
+ ts[0] = unix.Timespec{Nsec: unix.UTIME_OMIT}
+ } else {
+ ts[0], _ = unix.TimeToTimespec(*a)
+ }
+ if m == nil {
+ ts[1] = unix.Timespec{Nsec: unix.UTIME_OMIT}
+ } else {
+ ts[1], _ = unix.TimeToTimespec(*m)
+ }
return ts
}