diff options
Diffstat (limited to 'internal/syscallcompat')
| -rw-r--r-- | internal/syscallcompat/rename_exchange_test.go | 58 | ||||
| -rw-r--r-- | internal/syscallcompat/sys_darwin.go | 19 | 
2 files changed, 71 insertions, 6 deletions
| 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))  } | 
