22 changed files with 1413 additions and 26 deletions
-
6test/fuse_integration/POSIX_COMPLIANCE.md
-
271test/fuse_integration/POSIX_IMPLEMENTATION_ROADMAP.md
-
2test/fuse_integration/atime_nonlinux.go
-
12test/fuse_integration/atime_windows.go
-
39test/fuse_integration/directio_darwin.go
-
21test/fuse_integration/directio_linux.go
-
29test/fuse_integration/directio_unsupported.go
-
73test/fuse_integration/fallocate_darwin.go
-
38test/fuse_integration/fallocate_linux.go
-
30test/fuse_integration/fallocate_unsupported.go
-
85test/fuse_integration/mmap_unix.go
-
47test/fuse_integration/mmap_unsupported.go
-
2test/fuse_integration/posix_compliance_test.go
-
251test/fuse_integration/posix_extended_test.go
-
43test/fuse_integration/sendfile_darwin.go
-
33test/fuse_integration/sendfile_linux.go
-
19test/fuse_integration/sendfile_unsupported.go
-
126test/fuse_integration/vectored_io_unix.go
-
41test/fuse_integration/vectored_io_unsupported.go
-
125test/fuse_integration/xattr_darwin.go
-
115test/fuse_integration/xattr_linux.go
-
31test/fuse_integration/xattr_unsupported.go
@ -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/) |
||||
@ -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() |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
) |
||||
@ -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 |
||||
|
) |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue