Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ Versioning](https://semver.org/spec/v2.0.0.html) once it reaches

## Unreleased

### Reflink copies actually reflink

`chain::reflink_copy` passed `0x40209409` as `FICLONE`; the real number is
`_IOW(0x94, 9, int)` = `0x40049409`. The kernel answered the wrong value with
`ENOTTY`, which the helper treats as "filesystem has no reflink", so every
chain-assembly base copy and the bake's rootfs baseline clone streamed a full
copy even on btrfs, XFS, and ZFS 2.2+. Found by @jrimmer in #321.

### Upgrade note: legacy sandbox rows block startup

The controller now persists a boot identity (start time + boot id) for every
Expand Down
20 changes: 17 additions & 3 deletions crates/forkd-vmm/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,6 @@ fn copy_base_memory(src: &Path, dst: &Path) -> Result<u64> {
// EINVAL/EXDEV/EOPNOTSUPP signal "this FS doesn't support
// reflink for this pair," in which case we fall back. ENOTSUP
// sometimes appears too.
// ioctl number: _IO(0x94, 9). 0x94 is the BTRFS_IOCTL_MAGIC also
// used by ficlone (overlayfs, btrfs, xfs, ext4-reflink).
const FICLONE: libc::c_ulong = 0x4020_9409;
// SAFETY: both fds are valid open file descriptors; FICLONE
// takes the source fd as its argument.
let rc = unsafe { libc::ioctl(dst_f.as_raw_fd(), FICLONE, src_f.as_raw_fd()) };
Expand Down Expand Up @@ -341,8 +338,25 @@ fn fallback_stream_copy(
Ok(n)
}

/// `FICLONE` is `_IOW(0x94, 9, int)` = `0x4004_9409`. The kernel matches
/// the whole number, size field included, so any other value — the old
/// `0x4020_9409` encoded a 32-byte payload — is answered with ENOTTY,
/// which the fallback below reads as "no reflink here" and silently
/// streams a full copy even on btrfs/XFS/ZFS.
#[cfg(target_os = "linux")]
const FICLONE: libc::c_ulong = 0x4004_9409;

#[cfg(test)]
mod tests {
/// `_IOW(type, nr, size)` = write dir (1 << 30) | size << 16 | type << 8 | nr.
#[cfg(target_os = "linux")]
#[test]
fn ficlone_is_iow_0x94_9_int() {
let int_size = std::mem::size_of::<libc::c_int>() as libc::c_ulong;
let iow: libc::c_ulong = (1 << 30) | (int_size << 16) | (0x94 << 8) | 9;
assert_eq!(super::FICLONE, iow);
}

use super::*;
use crate::VolumeSpec;
use std::collections::HashMap;
Expand Down