aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/fuse_integration/POSIX_COMPLIANCE.md6
-rw-r--r--test/fuse_integration/POSIX_IMPLEMENTATION_ROADMAP.md271
-rw-r--r--test/fuse_integration/atime_nonlinux.go2
-rw-r--r--test/fuse_integration/atime_windows.go12
-rw-r--r--test/fuse_integration/directio_darwin.go39
-rw-r--r--test/fuse_integration/directio_linux.go21
-rw-r--r--test/fuse_integration/directio_unsupported.go29
-rw-r--r--test/fuse_integration/fallocate_darwin.go73
-rw-r--r--test/fuse_integration/fallocate_linux.go38
-rw-r--r--test/fuse_integration/fallocate_unsupported.go30
-rw-r--r--test/fuse_integration/mmap_unix.go85
-rw-r--r--test/fuse_integration/mmap_unsupported.go47
-rw-r--r--test/fuse_integration/posix_compliance_test.go2
-rw-r--r--test/fuse_integration/posix_extended_test.go251
-rw-r--r--test/fuse_integration/sendfile_darwin.go43
-rw-r--r--test/fuse_integration/sendfile_linux.go33
-rw-r--r--test/fuse_integration/sendfile_unsupported.go19
-rw-r--r--test/fuse_integration/vectored_io_unix.go126
-rw-r--r--test/fuse_integration/vectored_io_unsupported.go41
-rw-r--r--test/fuse_integration/xattr_darwin.go125
-rw-r--r--test/fuse_integration/xattr_linux.go115
-rw-r--r--test/fuse_integration/xattr_unsupported.go31
22 files changed, 1413 insertions, 26 deletions
diff --git a/test/fuse_integration/POSIX_COMPLIANCE.md b/test/fuse_integration/POSIX_COMPLIANCE.md
index b166870e7..d7586aa23 100644
--- a/test/fuse_integration/POSIX_COMPLIANCE.md
+++ b/test/fuse_integration/POSIX_COMPLIANCE.md
@@ -8,12 +8,14 @@ This comprehensive test suite provides full POSIX compliance testing for Seaweed
### ✅ **Comprehensive Test Coverage**
- **Basic POSIX Operations**: File/directory create, read, write, delete, rename
-- **Advanced Features**: Extended attributes, file locking, memory mapping
-- **I/O Operations**: Synchronous/asynchronous I/O, direct I/O, vectored I/O
+- **Advanced Features**: Extended attributes, file locking, memory mapping *(see roadmap)*
+- **I/O Operations**: Synchronous/asynchronous I/O, direct I/O, vectored I/O *(see roadmap)*
- **Error Handling**: Comprehensive error condition testing
- **Concurrent Operations**: Multi-threaded stress testing
- **External Integration**: pjdfstest, nfstest, FIO integration
+> **📋 Implementation Status**: Some advanced features are currently skipped pending platform-specific implementations. See [`POSIX_IMPLEMENTATION_ROADMAP.md`](./POSIX_IMPLEMENTATION_ROADMAP.md) for a detailed implementation plan and current status.
+
### 📊 **Performance Analysis**
- **Benchmarking**: Built-in performance benchmarks
- **Profiling**: CPU and memory profiling support
diff --git a/test/fuse_integration/POSIX_IMPLEMENTATION_ROADMAP.md b/test/fuse_integration/POSIX_IMPLEMENTATION_ROADMAP.md
new file mode 100644
index 000000000..59d6430e8
--- /dev/null
+++ b/test/fuse_integration/POSIX_IMPLEMENTATION_ROADMAP.md
@@ -0,0 +1,271 @@
+# POSIX Compliance Implementation Roadmap
+
+This document tracks the implementation status of POSIX features in the SeaweedFS FUSE mount compliance test suite and provides a roadmap for completing comprehensive POSIX compliance testing.
+
+## Overview
+
+The POSIX compliance test suite currently has several tests that are skipped due to requiring platform-specific implementations. This roadmap outlines the steps needed to implement these tests and achieve comprehensive POSIX compliance validation.
+
+## Current Status
+
+### ✅ Implemented Features
+- Basic file operations (create, read, write, delete)
+- Directory operations (mkdir, rmdir, rename)
+- File permissions and ownership
+- Symbolic and hard links
+- Basic I/O operations (seek, append, positioned I/O)
+- File descriptors and atomic operations
+- Concurrent access patterns
+- Error handling compliance
+- Timestamp operations
+
+### 🚧 Partially Implemented
+- Cross-platform compatibility (basic implementation with platform-specific access time handling)
+
+### ❌ Missing Implementations
+
+#### 1. Extended Attributes (xattr)
+**Priority: High**
+**Platforms: Linux, macOS, FreeBSD**
+
+**Current Status:** All xattr tests are skipped
+- `TestExtendedAttributes/SetExtendedAttribute`
+- `TestExtendedAttributes/ListExtendedAttributes`
+- `TestExtendedAttributes/RemoveExtendedAttribute`
+
+**Implementation Plan:**
+```go
+// Linux implementation
+//go:build linux
+func setXattr(path, name string, value []byte) error {
+ return syscall.Setxattr(path, name, value, 0)
+}
+
+// macOS implementation
+//go:build darwin
+func setXattr(path, name string, value []byte) error {
+ return syscall.Setxattr(path, name, value, 0, 0)
+}
+```
+
+**Required Files:**
+- `xattr_linux.go` - Linux syscall implementations
+- `xattr_darwin.go` - macOS syscall implementations
+- `xattr_freebsd.go` - FreeBSD syscall implementations
+- `xattr_unsupported.go` - Fallback for unsupported platforms
+
+#### 2. Memory Mapping (mmap)
+**Priority: High**
+**Platforms: All POSIX systems**
+
+**Current Status:** All mmap tests are skipped
+- `TestMemoryMapping/MemoryMappedRead`
+- `TestMemoryMapping/MemoryMappedWrite`
+
+**Implementation Plan:**
+```go
+func testMmap(t *testing.T, filePath string) {
+ fd, err := syscall.Open(filePath, syscall.O_RDWR, 0)
+ require.NoError(t, err)
+ defer syscall.Close(fd)
+
+ // Platform-specific mmap implementation
+ data, err := syscall.Mmap(fd, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
+ require.NoError(t, err)
+ defer syscall.Munmap(data)
+}
+```
+
+**Required Files:**
+- `mmap_unix.go` - Unix-like systems implementation
+- `mmap_windows.go` - Windows implementation (if needed)
+
+#### 3. Vectored I/O (readv/writev)
+**Priority: Medium**
+**Platforms: All POSIX systems**
+
+**Current Status:** Vectored I/O test is skipped
+- `TestAdvancedIO/VectoredIO`
+
+**Implementation Plan:**
+```go
+func testVectoredIO(t *testing.T, filePath string) {
+ // Use syscall.Syscall for readv/writev
+ // Linux: SYS_READV, SYS_WRITEV
+ // Other platforms: platform-specific syscall numbers
+}
+```
+
+#### 4. Direct I/O
+**Priority: Medium**
+**Platforms: Linux, some Unix variants**
+
+**Current Status:** Direct I/O test is skipped
+- `TestDirectIO/DirectIO`
+
+**Implementation Plan:**
+```go
+//go:build linux
+func testDirectIO(t *testing.T, filePath string) {
+ fd, err := syscall.Open(filePath, syscall.O_RDWR|syscall.O_DIRECT, 0)
+ // Test with aligned buffers
+}
+```
+
+#### 5. File Preallocation (fallocate)
+**Priority: Medium**
+**Platforms: Linux, some Unix variants**
+
+**Current Status:** fallocate test is skipped
+- `TestFilePreallocation/Fallocate`
+
+**Implementation Plan:**
+```go
+//go:build linux
+func testFallocate(t *testing.T, filePath string) {
+ fd, err := syscall.Open(filePath, syscall.O_RDWR, 0)
+ err = syscall.Syscall6(syscall.SYS_FALLOCATE, uintptr(fd), 0, 0, uintptr(size), 0, 0)
+}
+```
+
+#### 6. Zero-Copy Transfer (sendfile)
+**Priority: Low**
+**Platforms: Linux, FreeBSD, some Unix variants**
+
+**Current Status:** sendfile test is skipped
+- `TestZeroCopyTransfer/Sendfile`
+
+**Implementation Plan:**
+```go
+//go:build linux
+func testSendfile(t *testing.T, srcPath, dstPath string) {
+ // Use syscall.Syscall for sendfile
+ // Platform-specific implementations
+}
+```
+
+#### 7. File Sealing (Linux-specific)
+**Priority: Low**
+**Platforms: Linux only**
+
+**Current Status:** File sealing test is skipped
+- `TestFileSealing`
+
+**Implementation Plan:**
+```go
+//go:build linux
+func testFileSealing(t *testing.T) {
+ // Use fcntl with F_ADD_SEALS, F_GET_SEALS
+ // Test various seal types: F_SEAL_WRITE, F_SEAL_SHRINK, etc.
+}
+```
+
+## Implementation Strategy
+
+### Phase 1: Core Features (High Priority)
+1. **Extended Attributes** - Essential for many applications
+2. **Memory Mapping** - Critical for performance-sensitive applications
+
+### Phase 2: Advanced I/O (Medium Priority)
+3. **Vectored I/O** - Important for efficient bulk operations
+4. **Direct I/O** - Needed for database and high-performance applications
+5. **File Preallocation** - Important for preventing fragmentation
+
+### Phase 3: Specialized Features (Low Priority)
+6. **Zero-Copy Transfer** - Performance optimization feature
+7. **File Sealing** - Security feature, Linux-specific
+
+## Platform Support Matrix
+
+| Feature | Linux | macOS | FreeBSD | Windows | Notes |
+|---------|-------|-------|---------|---------|-------|
+| Extended Attributes | ✅ | ✅ | ✅ | ❌ | Different syscall signatures |
+| Memory Mapping | ✅ | ✅ | ✅ | ✅ | Standard POSIX |
+| Vectored I/O | ✅ | ✅ | ✅ | ❌ | readv/writev syscalls |
+| Direct I/O | ✅ | ❌ | ✅ | ❌ | O_DIRECT flag |
+| fallocate | ✅ | ❌ | ❌ | ❌ | Linux-specific |
+| sendfile | ✅ | ❌ | ✅ | ❌ | Platform-specific |
+| File Sealing | ✅ | ❌ | ❌ | ❌ | Linux-only |
+
+## Development Guidelines
+
+### 1. Platform-Specific Implementation Pattern
+```go
+// feature_linux.go
+//go:build linux
+package fuse
+func platformSpecificFunction() { /* Linux implementation */ }
+
+// feature_darwin.go
+//go:build darwin
+package fuse
+func platformSpecificFunction() { /* macOS implementation */ }
+
+// feature_unsupported.go
+//go:build !linux && !darwin
+package fuse
+func platformSpecificFunction() { /* Skip or error */ }
+```
+
+### 2. Test Structure Pattern
+```go
+func (s *POSIXExtendedTestSuite) TestFeature(t *testing.T) {
+ if !isFeatureSupported() {
+ t.Skip("Feature not supported on this platform")
+ return
+ }
+
+ t.Run("FeatureTest", func(t *testing.T) {
+ // Actual test implementation
+ })
+}
+```
+
+### 3. Error Handling
+- Use platform-specific error checking
+- Provide meaningful error messages
+- Gracefully handle unsupported features
+
+## Testing Strategy
+
+### 1. Continuous Integration
+- Run tests on multiple platforms (Linux, macOS)
+- Use build tags to enable/disable platform-specific tests
+- Ensure graceful degradation on unsupported platforms
+
+### 2. Feature Detection
+- Implement runtime feature detection where possible
+- Skip tests gracefully when features are unavailable
+- Log clear messages about skipped functionality
+
+### 3. Documentation
+- Document platform-specific behavior
+- Provide examples for each implemented feature
+- Maintain compatibility matrices
+
+## Contributing
+
+When implementing these features:
+
+1. **Start with high-priority items** (Extended Attributes, Memory Mapping)
+2. **Follow the platform-specific pattern** outlined above
+3. **Add comprehensive tests** for each feature
+4. **Update this roadmap** as features are implemented
+5. **Document any platform-specific quirks** or limitations
+
+## Future Enhancements
+
+Beyond the current roadmap, consider:
+- **File locking** (flock, fcntl locks)
+- **Asynchronous I/O** (aio_read, aio_write)
+- **File change notifications** (inotify, kqueue)
+- **POSIX ACLs** (Access Control Lists)
+- **Sparse file operations**
+- **File hole punching**
+
+## References
+
+- [POSIX.1-2017 Standard](https://pubs.opengroup.org/onlinepubs/9699919799/)
+- [Linux Programmer's Manual](https://man7.org/linux/man-pages/)
+- [FreeBSD System Calls](https://www.freebsd.org/cgi/man.cgi)
+- [macOS System Calls](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/)
diff --git a/test/fuse_integration/atime_nonlinux.go b/test/fuse_integration/atime_nonlinux.go
index 6bafa4cd4..c3b7cda16 100644
--- a/test/fuse_integration/atime_nonlinux.go
+++ b/test/fuse_integration/atime_nonlinux.go
@@ -1,4 +1,4 @@
-//go:build !linux
+//go:build !linux && !windows
package fuse
diff --git a/test/fuse_integration/atime_windows.go b/test/fuse_integration/atime_windows.go
new file mode 100644
index 000000000..4b8a7d9c6
--- /dev/null
+++ b/test/fuse_integration/atime_windows.go
@@ -0,0 +1,12 @@
+//go:build windows
+
+package fuse
+
+import (
+ "syscall"
+)
+
+// getAtimeNano returns the access time in nanoseconds for Windows systems
+func getAtimeNano(stat *syscall.Win32FileAttributeData) int64 {
+ return stat.LastAccessTime.Nanoseconds()
+}
diff --git a/test/fuse_integration/directio_darwin.go b/test/fuse_integration/directio_darwin.go
new file mode 100644
index 000000000..67ccaf9a0
--- /dev/null
+++ b/test/fuse_integration/directio_darwin.go
@@ -0,0 +1,39 @@
+//go:build darwin
+
+package fuse
+
+import (
+ "syscall"
+)
+
+// Direct I/O support for macOS
+
+const (
+ // macOS doesn't have O_DIRECT, but we can use fcntl with F_NOCACHE
+ F_NOCACHE = 48
+)
+
+func openDirectIO(path string, flags int, mode uint32) (int, error) {
+ // Open file normally first
+ fd, err := syscall.Open(path, flags, mode)
+ if err != nil {
+ return -1, err
+ }
+
+ // Set F_NOCACHE to bypass buffer cache (similar to O_DIRECT)
+ _, _, errno := syscall.Syscall(syscall.SYS_FCNTL,
+ uintptr(fd),
+ F_NOCACHE,
+ 1) // enable
+
+ if errno != 0 {
+ syscall.Close(fd)
+ return -1, errno
+ }
+
+ return fd, nil
+}
+
+func isDirectIOSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/directio_linux.go b/test/fuse_integration/directio_linux.go
new file mode 100644
index 000000000..862146f9d
--- /dev/null
+++ b/test/fuse_integration/directio_linux.go
@@ -0,0 +1,21 @@
+//go:build linux
+
+package fuse
+
+import (
+ "syscall"
+)
+
+// Direct I/O support for Linux
+
+const (
+ O_DIRECT = 0x4000 // Direct I/O flag for Linux
+)
+
+func openDirectIO(path string, flags int, mode uint32) (int, error) {
+ return syscall.Open(path, flags|O_DIRECT, mode)
+}
+
+func isDirectIOSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/directio_unsupported.go b/test/fuse_integration/directio_unsupported.go
new file mode 100644
index 000000000..dc1168b1c
--- /dev/null
+++ b/test/fuse_integration/directio_unsupported.go
@@ -0,0 +1,29 @@
+//go:build !linux && !darwin
+
+package fuse
+
+import (
+ "errors"
+ "syscall"
+)
+
+// Direct I/O support for unsupported platforms
+
+var ErrDirectIONotSupported = errors.New("direct I/O not supported on this platform")
+
+const (
+ O_DIRECT = 0x4000 // Dummy flag for compatibility
+)
+
+func openDirectIO(path string, flags int, mode uint32) (int, error) {
+ // Fall back to regular open
+ fd, err := syscall.Open(path, flags, mode)
+ if err != nil {
+ return -1, err
+ }
+ return int(fd), nil
+}
+
+func isDirectIOSupported() bool {
+ return false
+}
diff --git a/test/fuse_integration/fallocate_darwin.go b/test/fuse_integration/fallocate_darwin.go
new file mode 100644
index 000000000..94f5de7b9
--- /dev/null
+++ b/test/fuse_integration/fallocate_darwin.go
@@ -0,0 +1,73 @@
+//go:build darwin
+
+package fuse
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// File allocation support for macOS using fcntl
+
+const (
+ // macOS doesn't have fallocate, but we can use fcntl with F_PREALLOCATE
+ F_ALLOCATECONTIG = 0x02
+ F_ALLOCATEALL = 0x04
+
+ // Dummy flags for compatibility
+ FALLOC_FL_KEEP_SIZE = 0x01
+ FALLOC_FL_PUNCH_HOLE = 0x02
+ FALLOC_FL_NO_HIDE_STALE = 0x04
+ FALLOC_FL_COLLAPSE_RANGE = 0x08
+ FALLOC_FL_ZERO_RANGE = 0x10
+ FALLOC_FL_INSERT_RANGE = 0x20
+ FALLOC_FL_UNSHARE_RANGE = 0x40
+)
+
+// fstore_t structure for F_PREALLOCATE
+type fstore struct {
+ flags uint32
+ posmode int16
+ offset int64
+ length int64
+ bytesalloc int64
+}
+
+func fallocateFile(fd int, mode int, offset int64, length int64) error {
+ // On macOS, we use fcntl with F_PREALLOCATE
+ store := fstore{
+ flags: F_ALLOCATECONTIG,
+ posmode: syscall.F_PEOFPOSMODE, // Allocate from EOF
+ offset: 0,
+ length: length,
+ }
+
+ _, _, errno := syscall.Syscall(syscall.SYS_FCNTL,
+ uintptr(fd),
+ syscall.F_PREALLOCATE,
+ uintptr(unsafe.Pointer(&store)))
+
+ if errno != 0 {
+ // If contiguous allocation fails, try non-contiguous
+ store.flags = F_ALLOCATEALL
+ _, _, errno = syscall.Syscall(syscall.SYS_FCNTL,
+ uintptr(fd),
+ syscall.F_PREALLOCATE,
+ uintptr(unsafe.Pointer(&store)))
+
+ if errno != 0 {
+ return errno
+ }
+ }
+
+ // Set file size if not keeping size
+ if mode&FALLOC_FL_KEEP_SIZE == 0 {
+ return syscall.Ftruncate(fd, offset+length)
+ }
+
+ return nil
+}
+
+func isFallocateSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/fallocate_linux.go b/test/fuse_integration/fallocate_linux.go
new file mode 100644
index 000000000..7b1feaa4e
--- /dev/null
+++ b/test/fuse_integration/fallocate_linux.go
@@ -0,0 +1,38 @@
+//go:build linux
+
+package fuse
+
+import (
+ "syscall"
+)
+
+// File allocation support for Linux
+
+const (
+ // Fallocate flags
+ FALLOC_FL_KEEP_SIZE = 0x01
+ FALLOC_FL_PUNCH_HOLE = 0x02
+ FALLOC_FL_NO_HIDE_STALE = 0x04
+ FALLOC_FL_COLLAPSE_RANGE = 0x08
+ FALLOC_FL_ZERO_RANGE = 0x10
+ FALLOC_FL_INSERT_RANGE = 0x20
+ FALLOC_FL_UNSHARE_RANGE = 0x40
+)
+
+func fallocateFile(fd int, mode int, offset int64, length int64) error {
+ _, _, errno := syscall.Syscall6(syscall.SYS_FALLOCATE,
+ uintptr(fd),
+ uintptr(mode),
+ uintptr(offset),
+ uintptr(length),
+ 0, 0)
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func isFallocateSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/fallocate_unsupported.go b/test/fuse_integration/fallocate_unsupported.go
new file mode 100644
index 000000000..8be54785d
--- /dev/null
+++ b/test/fuse_integration/fallocate_unsupported.go
@@ -0,0 +1,30 @@
+//go:build !linux && !darwin
+
+package fuse
+
+import (
+ "errors"
+)
+
+// File allocation support for unsupported platforms
+
+var ErrFallocateNotSupported = errors.New("fallocate not supported on this platform")
+
+const (
+ // Dummy flags for compatibility
+ FALLOC_FL_KEEP_SIZE = 0x01
+ FALLOC_FL_PUNCH_HOLE = 0x02
+ FALLOC_FL_NO_HIDE_STALE = 0x04
+ FALLOC_FL_COLLAPSE_RANGE = 0x08
+ FALLOC_FL_ZERO_RANGE = 0x10
+ FALLOC_FL_INSERT_RANGE = 0x20
+ FALLOC_FL_UNSHARE_RANGE = 0x40
+)
+
+func fallocateFile(fd int, mode int, offset int64, length int64) error {
+ return ErrFallocateNotSupported
+}
+
+func isFallocateSupported() bool {
+ return false
+}
diff --git a/test/fuse_integration/mmap_unix.go b/test/fuse_integration/mmap_unix.go
new file mode 100644
index 000000000..702a1c032
--- /dev/null
+++ b/test/fuse_integration/mmap_unix.go
@@ -0,0 +1,85 @@
+//go:build unix
+
+package fuse
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Memory mapping support for Unix-like systems
+
+func mmapFile(fd int, offset int64, length int, prot int, flags int) ([]byte, error) {
+ addr, _, errno := syscall.Syscall6(syscall.SYS_MMAP,
+ 0, // addr (let kernel choose)
+ uintptr(length),
+ uintptr(prot),
+ uintptr(flags),
+ uintptr(fd),
+ uintptr(offset))
+
+ if errno != 0 {
+ return nil, errno
+ }
+
+ // Convert the address to a byte slice
+ return (*[1 << 30]byte)(unsafe.Pointer(addr))[:length:length], nil
+}
+
+func munmapFile(data []byte) error {
+ if len(data) == 0 {
+ return nil
+ }
+
+ _, _, errno := syscall.Syscall(syscall.SYS_MUNMAP,
+ uintptr(unsafe.Pointer(&data[0])),
+ uintptr(len(data)),
+ 0)
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func msyncFile(data []byte, flags int) error {
+ if len(data) == 0 {
+ return nil
+ }
+
+ _, _, errno := syscall.Syscall(syscall.SYS_MSYNC,
+ uintptr(unsafe.Pointer(&data[0])),
+ uintptr(len(data)),
+ uintptr(flags))
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func isMmapSupported() bool {
+ return true
+}
+
+// Memory protection flags
+const (
+ PROT_READ = syscall.PROT_READ
+ PROT_WRITE = syscall.PROT_WRITE
+ PROT_EXEC = syscall.PROT_EXEC
+ PROT_NONE = syscall.PROT_NONE
+)
+
+// Memory mapping flags
+const (
+ MAP_SHARED = syscall.MAP_SHARED
+ MAP_PRIVATE = syscall.MAP_PRIVATE
+ MAP_ANONYMOUS = syscall.MAP_ANON
+)
+
+// Memory sync flags
+const (
+ MS_ASYNC = syscall.MS_ASYNC
+ MS_SYNC = syscall.MS_SYNC
+ MS_INVALIDATE = syscall.MS_INVALIDATE
+)
diff --git a/test/fuse_integration/mmap_unsupported.go b/test/fuse_integration/mmap_unsupported.go
new file mode 100644
index 000000000..082f9333c
--- /dev/null
+++ b/test/fuse_integration/mmap_unsupported.go
@@ -0,0 +1,47 @@
+//go:build !unix
+
+package fuse
+
+import (
+ "errors"
+)
+
+// Memory mapping support for unsupported platforms
+
+var ErrMmapNotSupported = errors.New("memory mapping not supported on this platform")
+
+func mmapFile(fd int, offset int64, length int, prot int, flags int) ([]byte, error) {
+ return nil, ErrMmapNotSupported
+}
+
+func munmapFile(data []byte) error {
+ return ErrMmapNotSupported
+}
+
+func msyncFile(data []byte, flags int) error {
+ return ErrMmapNotSupported
+}
+
+func isMmapSupported() bool {
+ return false
+}
+
+// Dummy constants for unsupported platforms
+const (
+ PROT_READ = 0x1
+ PROT_WRITE = 0x2
+ PROT_EXEC = 0x4
+ PROT_NONE = 0x0
+)
+
+const (
+ MAP_SHARED = 0x01
+ MAP_PRIVATE = 0x02
+ MAP_ANONYMOUS = 0x20
+)
+
+const (
+ MS_ASYNC = 0x1
+ MS_SYNC = 0x4
+ MS_INVALIDATE = 0x2
+)
diff --git a/test/fuse_integration/posix_compliance_test.go b/test/fuse_integration/posix_compliance_test.go
index b6068c76a..bb3f2a0a1 100644
--- a/test/fuse_integration/posix_compliance_test.go
+++ b/test/fuse_integration/posix_compliance_test.go
@@ -1,3 +1,5 @@
+//go:build !windows
+
package fuse
import (
diff --git a/test/fuse_integration/posix_extended_test.go b/test/fuse_integration/posix_extended_test.go
index 29326f99b..76f3f115d 100644
--- a/test/fuse_integration/posix_extended_test.go
+++ b/test/fuse_integration/posix_extended_test.go
@@ -1,3 +1,5 @@
+//go:build !windows
+
package fuse
import (
@@ -10,7 +12,11 @@ import (
)
// POSIXExtendedTestSuite provides additional POSIX compliance tests
-// covering extended attributes, file locking, and advanced features
+// covering extended attributes, file locking, and advanced features.
+//
+// NOTE: Many tests in this suite are currently skipped due to requiring
+// platform-specific implementations. See POSIX_IMPLEMENTATION_ROADMAP.md
+// for a comprehensive plan to implement these missing features.
type POSIXExtendedTestSuite struct {
framework *FuseTestFramework
t *testing.T
@@ -54,36 +60,99 @@ func (s *POSIXExtendedTestSuite) TestExtendedAttributes(t *testing.T) {
mountPoint := s.framework.GetMountPoint()
t.Run("SetAndGetXattr", func(t *testing.T) {
+ if !isXattrSupported() {
+ t.Skip("Extended attributes not supported on this platform")
+ return
+ }
+
testFile := filepath.Join(mountPoint, "xattr_test.txt")
// Create test file
err := os.WriteFile(testFile, []byte("xattr test"), 0644)
require.NoError(t, err)
- // Extended attributes test - platform dependent
- t.Skip("Extended attributes testing requires platform-specific implementation")
+ // Set extended attribute
+ attrName := "user.test_attr"
+ attrValue := []byte("test_value")
+ err = setXattr(testFile, attrName, attrValue, 0)
+ require.NoError(t, err)
+
+ // Verify attribute was set
+ readValue := make([]byte, 256)
+ size, err := getXattr(testFile, attrName, readValue)
+ require.NoError(t, err)
+ require.Equal(t, len(attrValue), size)
+ require.Equal(t, attrValue, readValue[:size])
})
t.Run("ListXattrs", func(t *testing.T) {
+ if !isXattrSupported() {
+ t.Skip("Extended attributes not supported on this platform")
+ return
+ }
+
testFile := filepath.Join(mountPoint, "xattr_list_test.txt")
// Create test file
err := os.WriteFile(testFile, []byte("list xattr test"), 0644)
require.NoError(t, err)
- // List extended attributes test - platform dependent
- t.Skip("Extended attributes testing requires platform-specific implementation")
+ // Set multiple extended attributes
+ attrs := map[string][]byte{
+ "user.attr1": []byte("value1"),
+ "user.attr2": []byte("value2"),
+ "user.attr3": []byte("value3"),
+ }
+
+ for name, value := range attrs {
+ err = setXattr(testFile, name, value, 0)
+ require.NoError(t, err)
+ }
+
+ // List all attributes
+ listBuf := make([]byte, 1024)
+ size, err := listXattr(testFile, listBuf)
+ require.NoError(t, err)
+ require.Greater(t, size, 0)
+
+ // Parse the null-separated list
+ attrList := string(listBuf[:size])
+ for name := range attrs {
+ require.Contains(t, attrList, name)
+ }
})
t.Run("RemoveXattr", func(t *testing.T) {
+ if !isXattrSupported() {
+ t.Skip("Extended attributes not supported on this platform")
+ return
+ }
+
testFile := filepath.Join(mountPoint, "xattr_remove_test.txt")
// Create test file
err := os.WriteFile(testFile, []byte("remove xattr test"), 0644)
require.NoError(t, err)
- // Remove extended attributes test - platform dependent
- t.Skip("Extended attributes testing requires platform-specific implementation")
+ // Set extended attribute
+ attrName := "user.remove_test"
+ attrValue := []byte("to_be_removed")
+ err = setXattr(testFile, attrName, attrValue, 0)
+ require.NoError(t, err)
+
+ // Verify attribute exists
+ readValue := make([]byte, 256)
+ size, err := getXattr(testFile, attrName, readValue)
+ require.NoError(t, err)
+ require.Equal(t, len(attrValue), size)
+
+ // Remove the attribute
+ err = removeXattr(testFile, attrName)
+ require.NoError(t, err)
+
+ // Verify attribute is gone
+ _, err = getXattr(testFile, attrName, readValue)
+ require.Error(t, err) // Should fail with ENODATA or similar
})
}
@@ -183,6 +252,11 @@ func (s *POSIXExtendedTestSuite) TestAdvancedIO(t *testing.T) {
mountPoint := s.framework.GetMountPoint()
t.Run("ReadWriteV", func(t *testing.T) {
+ if !isVectoredIOSupported() {
+ t.Skip("Vectored I/O not supported on this platform")
+ return
+ }
+
testFile := filepath.Join(mountPoint, "readwritev_test.txt")
// Create file
@@ -190,8 +264,46 @@ func (s *POSIXExtendedTestSuite) TestAdvancedIO(t *testing.T) {
require.NoError(t, err)
defer syscall.Close(fd)
- // Vectored I/O test - requires platform-specific implementation
- t.Skip("Vectored I/O testing requires platform-specific implementation")
+ // Prepare test data in multiple buffers
+ writeBuffers := [][]byte{
+ []byte("Hello "),
+ []byte("vectored "),
+ []byte("I/O "),
+ []byte("world!"),
+ }
+
+ // Write using writev
+ writeIOVs := makeIOVecs(writeBuffers)
+ totalWritten, err := writevFile(fd, writeIOVs)
+ require.NoError(t, err)
+
+ expectedTotal := 0
+ for _, buf := range writeBuffers {
+ expectedTotal += len(buf)
+ }
+ require.Equal(t, expectedTotal, totalWritten)
+
+ // Seek back to beginning
+ _, err = syscall.Seek(fd, 0, 0)
+ require.NoError(t, err)
+
+ // Read using readv into multiple buffers
+ readBuffers := [][]byte{
+ make([]byte, 6), // "Hello "
+ make([]byte, 9), // "vectored "
+ make([]byte, 3), // "I/O "
+ make([]byte, 6), // "world!"
+ }
+
+ readIOVs := makeIOVecs(readBuffers)
+ totalRead, err := readvFile(fd, readIOVs)
+ require.NoError(t, err)
+ require.Equal(t, expectedTotal, totalRead)
+
+ // Verify data matches
+ for i, expected := range writeBuffers {
+ require.Equal(t, expected, readBuffers[i])
+ }
})
}
@@ -281,6 +393,11 @@ func (s *POSIXExtendedTestSuite) TestMemoryMapping(t *testing.T) {
mountPoint := s.framework.GetMountPoint()
t.Run("MmapFile", func(t *testing.T) {
+ if !isMmapSupported() {
+ t.Skip("Memory mapping not supported on this platform")
+ return
+ }
+
testFile := filepath.Join(mountPoint, "mmap_test.txt")
testData := make([]byte, 4096)
for i := range testData {
@@ -291,38 +408,98 @@ func (s *POSIXExtendedTestSuite) TestMemoryMapping(t *testing.T) {
err := os.WriteFile(testFile, testData, 0644)
require.NoError(t, err)
- // Open file
- file, err := os.Open(testFile)
+ // Open file for reading
+ fd, err := syscall.Open(testFile, syscall.O_RDONLY, 0)
require.NoError(t, err)
- defer file.Close()
+ defer syscall.Close(fd)
+
+ // Memory map the file
+ mappedData, err := mmapFile(fd, 0, len(testData), PROT_READ, MAP_SHARED)
+ require.NoError(t, err)
+ defer munmapFile(mappedData)
- // Memory mapping test - requires platform-specific implementation
- t.Skip("Memory mapping testing requires platform-specific implementation")
+ // Verify mapped content matches original
+ require.Equal(t, testData, mappedData)
})
t.Run("MmapWrite", func(t *testing.T) {
+ if !isMmapSupported() {
+ t.Skip("Memory mapping not supported on this platform")
+ return
+ }
+
testFile := filepath.Join(mountPoint, "mmap_write_test.txt")
size := 4096
// Create empty file of specific size
fd, err := syscall.Open(testFile, syscall.O_CREAT|syscall.O_RDWR, 0644)
require.NoError(t, err)
+ defer syscall.Close(fd)
err = syscall.Ftruncate(fd, int64(size))
require.NoError(t, err)
- syscall.Close(fd)
+ // Memory map the file for writing
+ mappedData, err := mmapFile(fd, 0, size, PROT_READ|PROT_WRITE, MAP_SHARED)
+ require.NoError(t, err)
+ defer munmapFile(mappedData)
+
+ // Write test pattern to mapped memory
+ testPattern := []byte("Hello, mmap world!")
+ copy(mappedData, testPattern)
- // Memory mapping write test - requires platform-specific implementation
- t.Skip("Memory mapping testing requires platform-specific implementation")
+ // Sync changes to disk
+ err = msyncFile(mappedData, MS_SYNC)
+ require.NoError(t, err)
+
+ // Verify changes were written by reading file directly
+ readData, err := os.ReadFile(testFile)
+ require.NoError(t, err)
+ require.True(t, len(readData) >= len(testPattern))
+ require.Equal(t, testPattern, readData[:len(testPattern)])
})
}
// TestDirectIO tests direct I/O operations
func (s *POSIXExtendedTestSuite) TestDirectIO(t *testing.T) {
+ mountPoint := s.framework.GetMountPoint()
+
t.Run("DirectIO", func(t *testing.T) {
- // Direct I/O is platform dependent and may not be supported
- t.Skip("Direct I/O testing requires platform-specific implementation")
+ if !isDirectIOSupported() {
+ t.Skip("Direct I/O not supported on this platform")
+ return
+ }
+
+ testFile := filepath.Join(mountPoint, "directio_test.txt")
+
+ // Open file with direct I/O
+ fd, err := openDirectIO(testFile, syscall.O_CREAT|syscall.O_RDWR, 0644)
+ require.NoError(t, err)
+ defer syscall.Close(fd)
+
+ // For direct I/O, data must be aligned to sector boundaries
+ // Use 4KB aligned buffer (common sector size)
+ blockSize := 4096
+ testData := make([]byte, blockSize)
+ for i := range testData {
+ testData[i] = byte(i % 256)
+ }
+
+ // Write data using direct I/O
+ n, err := syscall.Write(fd, testData)
+ require.NoError(t, err)
+ require.Equal(t, len(testData), n)
+
+ // Seek back to beginning
+ _, err = syscall.Seek(fd, 0, 0)
+ require.NoError(t, err)
+
+ // Read data using direct I/O
+ readData := make([]byte, blockSize)
+ n, err = syscall.Read(fd, readData)
+ require.NoError(t, err)
+ require.Equal(t, len(testData), n)
+ require.Equal(t, testData, readData)
})
}
@@ -337,6 +514,11 @@ func (s *POSIXExtendedTestSuite) TestFallocate(t *testing.T) {
mountPoint := s.framework.GetMountPoint()
t.Run("FallocateSpace", func(t *testing.T) {
+ if !isFallocateSupported() {
+ t.Skip("fallocate not supported on this platform")
+ return
+ }
+
testFile := filepath.Join(mountPoint, "fallocate_test.txt")
// Open file
@@ -344,8 +526,22 @@ func (s *POSIXExtendedTestSuite) TestFallocate(t *testing.T) {
require.NoError(t, err)
defer syscall.Close(fd)
- // File preallocation test - requires platform-specific implementation
- t.Skip("fallocate testing requires platform-specific implementation")
+ // Preallocate 1MB of space
+ allocSize := int64(1024 * 1024)
+ err = fallocateFile(fd, 0, 0, allocSize)
+ require.NoError(t, err)
+
+ // Verify file size was extended
+ var stat syscall.Stat_t
+ err = syscall.Fstat(fd, &stat)
+ require.NoError(t, err)
+ require.GreaterOrEqual(t, stat.Size, allocSize)
+
+ // Write some data and verify it works
+ testData := []byte("fallocate test data")
+ n, err := syscall.Write(fd, testData)
+ require.NoError(t, err)
+ require.Equal(t, len(testData), n)
})
}
@@ -376,8 +572,17 @@ func (s *POSIXExtendedTestSuite) TestSendfile(t *testing.T) {
require.NoError(t, err)
defer syscall.Close(dstFd)
- // Sendfile test - requires platform-specific implementation
- t.Skip("sendfile testing requires platform-specific implementation")
+ // Sendfile test
+ if !isSendfileSupported() {
+ t.Skip("sendfile not supported on this platform")
+ return
+ }
+
+ // Use sendfile to copy data
+ var offset int64 = 0
+ transferred, err := sendfileTransfer(dstFd, srcFd, &offset, len(testData))
+ require.NoError(t, err)
+ require.Equal(t, len(testData), transferred)
// Verify copy
copiedData, err := os.ReadFile(targetFile)
diff --git a/test/fuse_integration/sendfile_darwin.go b/test/fuse_integration/sendfile_darwin.go
new file mode 100644
index 000000000..38551bc6e
--- /dev/null
+++ b/test/fuse_integration/sendfile_darwin.go
@@ -0,0 +1,43 @@
+//go:build darwin
+
+package fuse
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Sendfile support for macOS
+
+func sendfileTransfer(outFd int, inFd int, offset *int64, count int) (int, error) {
+ // macOS sendfile has different signature: sendfile(in_fd, out_fd, offset, len, hdtr, flags)
+ var off int64
+ if offset != nil {
+ off = *offset
+ }
+
+ length := int64(count)
+
+ _, _, errno := syscall.Syscall6(syscall.SYS_SENDFILE,
+ uintptr(inFd), // input fd
+ uintptr(outFd), // output fd
+ uintptr(off), // offset
+ uintptr(unsafe.Pointer(&length)), // length (in/out parameter)
+ 0, // hdtr (headers/trailers)
+ 0) // flags
+
+ if errno != 0 {
+ return 0, errno
+ }
+
+ // Update offset if provided
+ if offset != nil {
+ *offset += length
+ }
+
+ return int(length), nil
+}
+
+func isSendfileSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/sendfile_linux.go b/test/fuse_integration/sendfile_linux.go
new file mode 100644
index 000000000..b5a0c0d61
--- /dev/null
+++ b/test/fuse_integration/sendfile_linux.go
@@ -0,0 +1,33 @@
+//go:build linux
+
+package fuse
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Sendfile support for Linux
+
+func sendfileTransfer(outFd int, inFd int, offset *int64, count int) (int, error) {
+ var offsetPtr uintptr
+ if offset != nil {
+ offsetPtr = uintptr(unsafe.Pointer(offset))
+ }
+
+ n, _, errno := syscall.Syscall6(syscall.SYS_SENDFILE,
+ uintptr(outFd),
+ uintptr(inFd),
+ offsetPtr,
+ uintptr(count),
+ 0, 0)
+
+ if errno != 0 {
+ return 0, errno
+ }
+ return int(n), nil
+}
+
+func isSendfileSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/sendfile_unsupported.go b/test/fuse_integration/sendfile_unsupported.go
new file mode 100644
index 000000000..64a7818dd
--- /dev/null
+++ b/test/fuse_integration/sendfile_unsupported.go
@@ -0,0 +1,19 @@
+//go:build !linux && !darwin
+
+package fuse
+
+import (
+ "errors"
+)
+
+// Sendfile support for unsupported platforms
+
+var ErrSendfileNotSupported = errors.New("sendfile not supported on this platform")
+
+func sendfileTransfer(outFd int, inFd int, offset *int64, count int) (int, error) {
+ return 0, ErrSendfileNotSupported
+}
+
+func isSendfileSupported() bool {
+ return false
+}
diff --git a/test/fuse_integration/vectored_io_unix.go b/test/fuse_integration/vectored_io_unix.go
new file mode 100644
index 000000000..59b2b2cc4
--- /dev/null
+++ b/test/fuse_integration/vectored_io_unix.go
@@ -0,0 +1,126 @@
+//go:build unix
+
+package fuse
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Vectored I/O support for Unix-like systems
+
+// IOVec represents an I/O vector for readv/writev operations
+type IOVec struct {
+ Base *byte
+ Len uint64
+}
+
+func readvFile(fd int, iovs []IOVec) (int, error) {
+ if len(iovs) == 0 {
+ return 0, nil
+ }
+
+ n, _, errno := syscall.Syscall(syscall.SYS_READV,
+ uintptr(fd),
+ uintptr(unsafe.Pointer(&iovs[0])),
+ uintptr(len(iovs)))
+
+ if errno != 0 {
+ return 0, errno
+ }
+ return int(n), nil
+}
+
+func writevFile(fd int, iovs []IOVec) (int, error) {
+ if len(iovs) == 0 {
+ return 0, nil
+ }
+
+ n, _, errno := syscall.Syscall(syscall.SYS_WRITEV,
+ uintptr(fd),
+ uintptr(unsafe.Pointer(&iovs[0])),
+ uintptr(len(iovs)))
+
+ if errno != 0 {
+ return 0, errno
+ }
+ return int(n), nil
+}
+
+func preadvFile(fd int, iovs []IOVec, offset int64) (int, error) {
+ if len(iovs) == 0 {
+ return 0, nil
+ }
+
+ // preadv/pwritev may not be available on all Unix systems
+ // Fall back to individual pread calls
+ totalRead := 0
+ currentOffset := offset
+
+ for _, iov := range iovs {
+ if iov.Len == 0 {
+ continue
+ }
+
+ buf := (*[1 << 30]byte)(unsafe.Pointer(iov.Base))[:iov.Len:iov.Len]
+ n, err := syscall.Pread(fd, buf, currentOffset)
+ if err != nil {
+ return totalRead, err
+ }
+ totalRead += n
+ currentOffset += int64(n)
+
+ if n < int(iov.Len) {
+ break // EOF or partial read
+ }
+ }
+
+ return totalRead, nil
+}
+
+func pwritevFile(fd int, iovs []IOVec, offset int64) (int, error) {
+ if len(iovs) == 0 {
+ return 0, nil
+ }
+
+ // preadv/pwritev may not be available on all Unix systems
+ // Fall back to individual pwrite calls
+ totalWritten := 0
+ currentOffset := offset
+
+ for _, iov := range iovs {
+ if iov.Len == 0 {
+ continue
+ }
+
+ buf := (*[1 << 30]byte)(unsafe.Pointer(iov.Base))[:iov.Len:iov.Len]
+ n, err := syscall.Pwrite(fd, buf, currentOffset)
+ if err != nil {
+ return totalWritten, err
+ }
+ totalWritten += n
+ currentOffset += int64(n)
+
+ if n < int(iov.Len) {
+ break // Partial write
+ }
+ }
+
+ return totalWritten, nil
+}
+
+// Helper function to create IOVec from byte slices
+func makeIOVecs(buffers [][]byte) []IOVec {
+ iovs := make([]IOVec, len(buffers))
+ for i, buf := range buffers {
+ if len(buf) > 0 {
+ iovs[i].Base = &buf[0]
+ iovs[i].Len = uint64(len(buf))
+ }
+ }
+ return iovs
+}
+
+func isVectoredIOSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/vectored_io_unsupported.go b/test/fuse_integration/vectored_io_unsupported.go
new file mode 100644
index 000000000..963c6e2d4
--- /dev/null
+++ b/test/fuse_integration/vectored_io_unsupported.go
@@ -0,0 +1,41 @@
+//go:build !unix
+
+package fuse
+
+import (
+ "errors"
+)
+
+// Vectored I/O support for unsupported platforms
+
+var ErrVectoredIONotSupported = errors.New("vectored I/O not supported on this platform")
+
+// IOVec represents an I/O vector for readv/writev operations
+type IOVec struct {
+ Base *byte
+ Len uint64
+}
+
+func readvFile(fd int, iovs []IOVec) (int, error) {
+ return 0, ErrVectoredIONotSupported
+}
+
+func writevFile(fd int, iovs []IOVec) (int, error) {
+ return 0, ErrVectoredIONotSupported
+}
+
+func preadvFile(fd int, iovs []IOVec, offset int64) (int, error) {
+ return 0, ErrVectoredIONotSupported
+}
+
+func pwritevFile(fd int, iovs []IOVec, offset int64) (int, error) {
+ return 0, ErrVectoredIONotSupported
+}
+
+func makeIOVecs(buffers [][]byte) []IOVec {
+ return nil
+}
+
+func isVectoredIOSupported() bool {
+ return false
+}
diff --git a/test/fuse_integration/xattr_darwin.go b/test/fuse_integration/xattr_darwin.go
new file mode 100644
index 000000000..6bd6f2a90
--- /dev/null
+++ b/test/fuse_integration/xattr_darwin.go
@@ -0,0 +1,125 @@
+//go:build darwin
+
+package fuse
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Extended attributes support for macOS
+
+const (
+ // macOS-specific flags
+ XATTR_NOFOLLOW = 0x0001
+ XATTR_CREATE = 0x0002
+ XATTR_REPLACE = 0x0004
+)
+
+func setXattr(path, name string, value []byte, flags int) error {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return err
+ }
+ nameBytes, err := syscall.BytePtrFromString(name)
+ if err != nil {
+ return err
+ }
+
+ var valuePtr unsafe.Pointer
+ if len(value) > 0 {
+ valuePtr = unsafe.Pointer(&value[0])
+ }
+
+ _, _, errno := syscall.Syscall6(syscall.SYS_SETXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(unsafe.Pointer(nameBytes)),
+ uintptr(valuePtr),
+ uintptr(len(value)),
+ uintptr(0), // position (not used for regular files)
+ uintptr(flags))
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func getXattr(path, name string, value []byte) (int, error) {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return 0, err
+ }
+ nameBytes, err := syscall.BytePtrFromString(name)
+ if err != nil {
+ return 0, err
+ }
+
+ var valuePtr unsafe.Pointer
+ if len(value) > 0 {
+ valuePtr = unsafe.Pointer(&value[0])
+ }
+
+ size, _, errno := syscall.Syscall6(syscall.SYS_GETXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(unsafe.Pointer(nameBytes)),
+ uintptr(valuePtr),
+ uintptr(len(value)),
+ uintptr(0), // position
+ uintptr(0)) // options
+
+ if errno != 0 {
+ return 0, errno
+ }
+ return int(size), nil
+}
+
+func listXattr(path string, list []byte) (int, error) {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return 0, err
+ }
+
+ var listPtr unsafe.Pointer
+ if len(list) > 0 {
+ listPtr = unsafe.Pointer(&list[0])
+ }
+
+ size, _, errno := syscall.Syscall6(syscall.SYS_LISTXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(listPtr),
+ uintptr(len(list)),
+ uintptr(0), // options
+ 0, 0)
+
+ if errno != 0 {
+ return 0, errno
+ }
+ return int(size), nil
+}
+
+func removeXattr(path, name string) error {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return err
+ }
+ nameBytes, err := syscall.BytePtrFromString(name)
+ if err != nil {
+ return err
+ }
+
+ _, _, errno := syscall.Syscall6(syscall.SYS_REMOVEXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(unsafe.Pointer(nameBytes)),
+ uintptr(0), // options
+ 0, 0, 0)
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func isXattrSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/xattr_linux.go b/test/fuse_integration/xattr_linux.go
new file mode 100644
index 000000000..82f40d1c7
--- /dev/null
+++ b/test/fuse_integration/xattr_linux.go
@@ -0,0 +1,115 @@
+//go:build linux
+
+package fuse
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// Extended attributes support for Linux
+
+func setXattr(path, name string, value []byte, flags int) error {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return err
+ }
+ nameBytes, err := syscall.BytePtrFromString(name)
+ if err != nil {
+ return err
+ }
+
+ var valuePtr unsafe.Pointer
+ if len(value) > 0 {
+ valuePtr = unsafe.Pointer(&value[0])
+ }
+
+ _, _, errno := syscall.Syscall6(syscall.SYS_SETXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(unsafe.Pointer(nameBytes)),
+ uintptr(valuePtr),
+ uintptr(len(value)),
+ uintptr(flags),
+ 0)
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func getXattr(path, name string, value []byte) (int, error) {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return 0, err
+ }
+ nameBytes, err := syscall.BytePtrFromString(name)
+ if err != nil {
+ return 0, err
+ }
+
+ var valuePtr unsafe.Pointer
+ if len(value) > 0 {
+ valuePtr = unsafe.Pointer(&value[0])
+ }
+
+ size, _, errno := syscall.Syscall6(syscall.SYS_GETXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(unsafe.Pointer(nameBytes)),
+ uintptr(valuePtr),
+ uintptr(len(value)),
+ 0,
+ 0)
+
+ if errno != 0 {
+ return 0, errno
+ }
+ return int(size), nil
+}
+
+func listXattr(path string, list []byte) (int, error) {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return 0, err
+ }
+
+ var listPtr unsafe.Pointer
+ if len(list) > 0 {
+ listPtr = unsafe.Pointer(&list[0])
+ }
+
+ size, _, errno := syscall.Syscall(syscall.SYS_LISTXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(listPtr),
+ uintptr(len(list)))
+
+ if errno != 0 {
+ return 0, errno
+ }
+ return int(size), nil
+}
+
+func removeXattr(path, name string) error {
+ pathBytes, err := syscall.BytePtrFromString(path)
+ if err != nil {
+ return err
+ }
+ nameBytes, err := syscall.BytePtrFromString(name)
+ if err != nil {
+ return err
+ }
+
+ _, _, errno := syscall.Syscall(syscall.SYS_REMOVEXATTR,
+ uintptr(unsafe.Pointer(pathBytes)),
+ uintptr(unsafe.Pointer(nameBytes)),
+ 0)
+
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
+
+func isXattrSupported() bool {
+ return true
+}
diff --git a/test/fuse_integration/xattr_unsupported.go b/test/fuse_integration/xattr_unsupported.go
new file mode 100644
index 000000000..945fc2ff6
--- /dev/null
+++ b/test/fuse_integration/xattr_unsupported.go
@@ -0,0 +1,31 @@
+//go:build !linux && !darwin
+
+package fuse
+
+import (
+ "errors"
+)
+
+// Extended attributes support for unsupported platforms
+
+var ErrXattrNotSupported = errors.New("extended attributes not supported on this platform")
+
+func setXattr(path, name string, value []byte, flags int) error {
+ return ErrXattrNotSupported
+}
+
+func getXattr(path, name string, value []byte) (int, error) {
+ return 0, ErrXattrNotSupported
+}
+
+func listXattr(path string, list []byte) (int, error) {
+ return 0, ErrXattrNotSupported
+}
+
+func removeXattr(path, name string) error {
+ return ErrXattrNotSupported
+}
+
+func isXattrSupported() bool {
+ return false
+}