btrfs_uapi/
send_receive.rs1use crate::{
14 raw::{
15 self, btrfs_ioc_clone_range, btrfs_ioc_encoded_write, btrfs_ioc_send,
16 btrfs_ioc_set_received_subvol, btrfs_ioctl_clone_range_args,
17 btrfs_ioctl_encoded_io_args, btrfs_ioctl_received_subvol_args,
18 btrfs_ioctl_send_args,
19 },
20 tree_search::{SearchKey, tree_search},
21};
22use bitflags::bitflags;
23use nix::libc::c_int;
24use std::os::fd::{AsRawFd, BorrowedFd, RawFd};
25use uuid::Uuid;
26
27bitflags! {
28 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
30 pub struct SendFlags: u64 {
31 const NO_FILE_DATA = raw::BTRFS_SEND_FLAG_NO_FILE_DATA as u64;
33 const OMIT_STREAM_HEADER = raw::BTRFS_SEND_FLAG_OMIT_STREAM_HEADER as u64;
35 const OMIT_END_CMD = raw::BTRFS_SEND_FLAG_OMIT_END_CMD as u64;
37 const VERSION = raw::BTRFS_SEND_FLAG_VERSION as u64;
39 const COMPRESSED = raw::BTRFS_SEND_FLAG_COMPRESSED as u64;
41 }
42}
43
44pub fn send(
54 subvol_fd: BorrowedFd<'_>,
55 send_fd: RawFd,
56 parent_root: u64,
57 clone_sources: &mut [u64],
58 flags: SendFlags,
59 version: u32,
60) -> nix::Result<()> {
61 let mut args: btrfs_ioctl_send_args = unsafe { std::mem::zeroed() };
62 args.send_fd = send_fd as i64;
63 args.parent_root = parent_root;
64 args.clone_sources_count = clone_sources.len() as u64;
65 args.clone_sources = if clone_sources.is_empty() {
66 std::ptr::null_mut()
67 } else {
68 clone_sources.as_mut_ptr()
69 };
70 args.flags = flags.bits();
71 args.version = version;
72
73 unsafe {
76 btrfs_ioc_send(subvol_fd.as_raw_fd() as c_int, &args)?;
77 }
78
79 Ok(())
80}
81
82#[derive(Debug, Clone)]
84pub struct SubvolumeSearchResult {
85 pub root_id: u64,
87}
88
89pub fn received_subvol_set(
95 fd: BorrowedFd<'_>,
96 uuid: &Uuid,
97 stransid: u64,
98) -> nix::Result<u64> {
99 let mut args: btrfs_ioctl_received_subvol_args =
100 unsafe { std::mem::zeroed() };
101
102 let uuid_bytes = uuid.as_bytes();
103 for (i, &b) in uuid_bytes.iter().enumerate() {
105 args.uuid[i] = b as std::os::raw::c_char;
106 }
107 args.stransid = stransid;
108
109 unsafe {
111 btrfs_ioc_set_received_subvol(fd.as_raw_fd() as c_int, &mut args)?;
112 }
113
114 Ok(args.rtransid)
115}
116
117pub fn clone_range(
122 dest_fd: BorrowedFd<'_>,
123 src_fd: BorrowedFd<'_>,
124 src_offset: u64,
125 src_length: u64,
126 dest_offset: u64,
127) -> nix::Result<()> {
128 let args = btrfs_ioctl_clone_range_args {
129 src_fd: src_fd.as_raw_fd() as i64,
130 src_offset,
131 src_length,
132 dest_offset,
133 };
134
135 unsafe {
137 btrfs_ioc_clone_range(dest_fd.as_raw_fd() as c_int, &args)?;
138 }
139
140 Ok(())
141}
142
143#[allow(clippy::too_many_arguments)]
151pub fn encoded_write(
152 fd: BorrowedFd<'_>,
153 data: &[u8],
154 offset: u64,
155 unencoded_file_len: u64,
156 unencoded_len: u64,
157 unencoded_offset: u64,
158 compression: u32,
159 encryption: u32,
160) -> nix::Result<()> {
161 let iov = nix::libc::iovec {
162 iov_base: data.as_ptr() as *mut _,
163 iov_len: data.len(),
164 };
165
166 let mut args: btrfs_ioctl_encoded_io_args = unsafe { std::mem::zeroed() };
167 args.iov = &iov as *const _ as *mut _;
168 args.iovcnt = 1;
169 args.offset = offset as i64;
170 args.len = unencoded_file_len;
171 args.unencoded_len = unencoded_len;
172 args.unencoded_offset = unencoded_offset;
173 args.compression = compression;
174 args.encryption = encryption;
175
176 unsafe {
180 btrfs_ioc_encoded_write(fd.as_raw_fd() as c_int, &args)?;
181 }
182
183 Ok(())
184}
185
186pub fn subvolume_search_by_uuid(
191 fd: BorrowedFd<'_>,
192 uuid: &Uuid,
193) -> nix::Result<u64> {
194 search_uuid_tree(fd, uuid, raw::BTRFS_UUID_KEY_SUBVOL as u32)
195}
196
197pub fn subvolume_search_by_received_uuid(
202 fd: BorrowedFd<'_>,
203 uuid: &Uuid,
204) -> nix::Result<u64> {
205 search_uuid_tree(fd, uuid, raw::BTRFS_UUID_KEY_RECEIVED_SUBVOL as u32)
206}
207
208fn search_uuid_tree(
215 fd: BorrowedFd<'_>,
216 uuid: &Uuid,
217 item_type: u32,
218) -> nix::Result<u64> {
219 let bytes = uuid.as_bytes();
220 let objectid = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
221 let offset = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
222
223 let mut key =
224 SearchKey::for_type(raw::BTRFS_UUID_TREE_OBJECTID as u64, item_type);
225 key.min_objectid = objectid;
226 key.max_objectid = objectid;
227 key.min_offset = offset;
228 key.max_offset = offset;
229
230 let mut result: Option<u64> = None;
231
232 tree_search(fd, key, |_hdr, data| {
233 if data.len() >= 8 {
234 result = Some(u64::from_le_bytes(data[0..8].try_into().unwrap()));
235 }
236 Ok(())
237 })?;
238
239 result.ok_or(nix::errno::Errno::ENOENT)
240}