Skip to main content

btrfs_uapi/
send.rs

1//! # Send stream: generating an incremental or full send stream from a subvolume
2//!
3//! The kernel generates a binary stream representing the contents of a read-only
4//! subvolume (or the delta between a parent and child snapshot). The stream is
5//! written to a pipe; the caller reads from the other end and writes it to a
6//! file or stdout for later consumption by `btrfs receive`.
7
8use crate::raw::{self, btrfs_ioc_send, btrfs_ioctl_send_args};
9use bitflags::bitflags;
10use nix::libc::c_int;
11use std::os::fd::{AsRawFd, BorrowedFd, RawFd};
12
13bitflags! {
14    /// Flags for the send ioctl.
15    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
16    pub struct SendFlags: u64 {
17        /// Do not include file data in the stream (metadata only).
18        const NO_FILE_DATA = raw::BTRFS_SEND_FLAG_NO_FILE_DATA as u64;
19        /// Omit the stream header (for multi-subvolume sends).
20        const OMIT_STREAM_HEADER = raw::BTRFS_SEND_FLAG_OMIT_STREAM_HEADER as u64;
21        /// Omit the end-cmd marker (for multi-subvolume sends).
22        const OMIT_END_CMD = raw::BTRFS_SEND_FLAG_OMIT_END_CMD as u64;
23        /// Request a specific protocol version (set the version field).
24        const VERSION = raw::BTRFS_SEND_FLAG_VERSION as u64;
25        /// Send compressed data directly without decompressing.
26        const COMPRESSED = raw::BTRFS_SEND_FLAG_COMPRESSED as u64;
27    }
28}
29
30/// Invoke `BTRFS_IOC_SEND` on the given subvolume.
31///
32/// The kernel writes the send stream to `send_fd` (the write end of a pipe).
33/// The caller is responsible for reading from the read end of the pipe,
34/// typically in a separate thread.
35///
36/// `clone_sources` is a list of root IDs that the kernel may reference for
37/// clone operations in the stream. `parent_root` is the root ID of the parent
38/// snapshot for incremental sends, or `0` for a full send.
39pub fn send(
40    subvol_fd: BorrowedFd<'_>,
41    send_fd: RawFd,
42    parent_root: u64,
43    clone_sources: &mut [u64],
44    flags: SendFlags,
45    version: u32,
46) -> nix::Result<()> {
47    let mut args: btrfs_ioctl_send_args = unsafe { std::mem::zeroed() };
48    args.send_fd = send_fd as i64;
49    args.parent_root = parent_root;
50    args.clone_sources_count = clone_sources.len() as u64;
51    args.clone_sources = if clone_sources.is_empty() {
52        std::ptr::null_mut()
53    } else {
54        clone_sources.as_mut_ptr()
55    };
56    args.flags = flags.bits();
57    args.version = version;
58
59    // SAFETY: args is fully initialized, clone_sources points to valid memory
60    // that outlives the ioctl call, and subvol_fd is a valid borrowed fd.
61    unsafe {
62        btrfs_ioc_send(subvol_fd.as_raw_fd() as c_int, &args)?;
63    }
64
65    Ok(())
66}