Skip to content

Add FUSE remap_file_range support for FICLONE/FICLONERANGE#26

Closed
ejc3 wants to merge 1 commit intomainfrom
fuse-remap-support
Closed

Add FUSE remap_file_range support for FICLONE/FICLONERANGE#26
ejc3 wants to merge 1 commit intomainfrom
fuse-remap-support

Conversation

@ejc3
Copy link
Copy Markdown
Owner

@ejc3 ejc3 commented Dec 26, 2025

Summary

Enables cp --reflink=always and FICLONE/FICLONERANGE ioctls through FUSE by adding remap_file_range support to the full stack.

Problem: FICLONE fails with EOPNOTSUPP through FUSE because there's no kernel opcode for it.

Solution:

  • Kernel patch adds FUSE_REMAP_FILE_RANGE opcode (54)
  • fuse-pipe protocol extension for RemapFileRange
  • Passthrough implementation using copy_file_range (which handles FICLONE on btrfs)
  • Integration tests that skip gracefully on unpatched kernels

Full Stack Implementation

Layer Component Changes
Kernel kernel/build.sh Inlines FUSE_REMAP_FILE_RANGE patch (opcode 54, struct, handler)
fuser ejc3/fuser:remap-file-range Opcode constant + remap_file_range trait method
fuse-backend-rs ejc3/fuse-backend-rs:remap-file-range PassthroughFs impl using copy_file_range
libfuse ejc3/libfuse:remap-file-range passthrough_ll.c impl for C-based testing
fuse-pipe src/protocol/, src/client/, src/server/ Wire protocol + handler
Tests tests/test_remap_file_range.rs VM end-to-end test
Tests fuse-pipe/tests/test_remap_file_range.rs Host-side FUSE tests
Tests Containerfile.libfuse-remap libfuse container test

Key Fixes During Development

  1. Opcode 54 (not 53) - opcode 53 is reserved for FUSE_SYNCFS
  2. len=0 semantics - FICLONE passes len=0 meaning "to end of file", fixed underflow in writeback range calculation
  3. Return value - must return bytes cloned via fuse_write_out, not just success/fail
  4. Exit code handling - cp --reflink=always returns 1 (not errno) on failure

Test Behavior

Tests skip gracefully without patched kernel - won't break CI:

Test Skip Condition
test_ficlone_cp_reflink_in_vm No btrfs, or exit code 1/38/95
test_ficlone_whole_file ENOSYS or EOPNOTSUPP from kernel
test_ficlonerange_partial ENOSYS or EOPNOTSUPP from kernel

To run with patched kernel:

REMAP_KERNEL=/mnt/fcvm-btrfs/kernels/vmlinux-6.12.10-*.bin make test-root FILTER=remap

Verified Working

remap_file_range called ino_in=2 fh_in=2 offset_in=0 ino_out=3 fh_out=3 len=0 remap_flags=0
remap_file_range response response=Written { size: 0 }
[ctr:stdout] FICLONE test passed
✓ Verified: files share physical extents (true reflink)
[REMAP-VM] ✓ ficlone (6.9s)

Test plan

  • cargo build --release passes
  • make test-root passes (tests skip on standard kernel)
  • REMAP_KERNEL=... make test-root FILTER=remap passes with patched kernel
  • libfuse container test passes: FICLONE + FICLONERANGE (offset=0 and offset=512KB)
  • filefrag confirms shared extents (true reflinks on btrfs)

@ejc3 ejc3 closed this Dec 26, 2025
ejc3 pushed a commit that referenced this pull request Feb 7, 2026
…tion

- Remove std::env::set_var for writeback cache propagation (#26): pass
  no_writeback_cache flag through mount_vsock_with_options API instead.
  set_var is unsound in multi-threaded Rust programs.
- Bound exec line reader to 1MB (#27): prevents OOM from malicious or
  malformed exec requests sent over vsock.
- Replace bash -c shell injection with direct Command args (#28): TAP
  device verification now uses ip link show directly instead of through
  a shell.
ejc3 pushed a commit that referenced this pull request Feb 7, 2026
…tion

- Remove std::env::set_var for writeback cache propagation (#26): pass
  no_writeback_cache flag through mount_vsock_with_options API instead.
  set_var is unsound in multi-threaded Rust programs.
- Bound exec line reader to 1MB (#27): prevents OOM from malicious or
  malformed exec requests sent over vsock.
- Replace bash -c shell injection with direct Command args (#28): TAP
  device verification now uses ip link show directly instead of through
  a shell.
ejc3 pushed a commit that referenced this pull request Feb 7, 2026
…tion

- Remove std::env::set_var for writeback cache propagation (#26): pass
  no_writeback_cache flag through mount_vsock_with_options API instead.
  set_var is unsound in multi-threaded Rust programs.
- Bound exec line reader to 1MB (#27): prevents OOM from malicious or
  malformed exec requests sent over vsock.
- Replace bash -c shell injection with direct Command args (#28): TAP
  device verification now uses ip link show directly instead of through
  a shell.
ejc3 added a commit that referenced this pull request Mar 2, 2026
…tion

- Remove std::env::set_var for writeback cache propagation (#26): pass
  no_writeback_cache flag through mount_vsock_with_options API instead.
  set_var is unsound in multi-threaded Rust programs.
- Bound exec line reader to 1MB (#27): prevents OOM from malicious or
  malformed exec requests sent over vsock.
- Replace bash -c shell injection with direct Command args (#28): TAP
  device verification now uses ip link show directly instead of through
  a shell.
ejc3 added a commit that referenced this pull request Mar 2, 2026
…tion

- Remove std::env::set_var for writeback cache propagation (#26): pass
  no_writeback_cache flag through mount_vsock_with_options API instead.
  set_var is unsound in multi-threaded Rust programs.
- Bound exec line reader to 1MB (#27): prevents OOM from malicious or
  malformed exec requests sent over vsock.
- Replace bash -c shell injection with direct Command args (#28): TAP
  device verification now uses ip link show directly instead of through
  a shell.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant