Browse Source

add more posix tests

improve-fuse-mount2
chrislu 2 months ago
parent
commit
d7dc9a47d5
  1. 6
      test/fuse_integration/POSIX_COMPLIANCE.md
  2. 271
      test/fuse_integration/POSIX_IMPLEMENTATION_ROADMAP.md
  3. 2
      test/fuse_integration/atime_nonlinux.go
  4. 12
      test/fuse_integration/atime_windows.go
  5. 39
      test/fuse_integration/directio_darwin.go
  6. 21
      test/fuse_integration/directio_linux.go
  7. 29
      test/fuse_integration/directio_unsupported.go
  8. 73
      test/fuse_integration/fallocate_darwin.go
  9. 38
      test/fuse_integration/fallocate_linux.go
  10. 30
      test/fuse_integration/fallocate_unsupported.go
  11. 85
      test/fuse_integration/mmap_unix.go
  12. 47
      test/fuse_integration/mmap_unsupported.go
  13. 2
      test/fuse_integration/posix_compliance_test.go
  14. 251
      test/fuse_integration/posix_extended_test.go
  15. 43
      test/fuse_integration/sendfile_darwin.go
  16. 33
      test/fuse_integration/sendfile_linux.go
  17. 19
      test/fuse_integration/sendfile_unsupported.go
  18. 126
      test/fuse_integration/vectored_io_unix.go
  19. 41
      test/fuse_integration/vectored_io_unsupported.go
  20. 125
      test/fuse_integration/xattr_darwin.go
  21. 115
      test/fuse_integration/xattr_linux.go
  22. 31
      test/fuse_integration/xattr_unsupported.go

6
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

271
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/)

2
test/fuse_integration/atime_nonlinux.go

@ -1,4 +1,4 @@
//go:build !linux
//go:build !linux && !windows
package fuse

12
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()
}

39
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
}

21
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
}

29
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
}

73
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
}

38
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
}

30
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
}

85
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
)

47
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
)

2
test/fuse_integration/posix_compliance_test.go

@ -1,3 +1,5 @@
//go:build !windows
package fuse
import (

251
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)

43
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
}

33
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
}

19
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
}

126
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
}

41
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
}

125
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
}

115
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
}

31
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
}
Loading…
Cancel
Save