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}