btrfs_uapi/resize.rs
1//! # Device resizing: growing or shrinking a device within a mounted filesystem
2//!
3//! Resizing adjusts how much of a block device's capacity btrfs uses, without
4//! unmounting. A device can be grown up to its physical size, shrunk to the
5//! minimum space currently occupied, or set to an explicit byte count.
6
7use crate::raw::{btrfs_ioc_resize, btrfs_ioctl_vol_args};
8use nix::libc::c_char;
9use std::{
10 mem,
11 os::{fd::AsRawFd, unix::io::BorrowedFd},
12};
13
14/// The target size for a resize operation.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ResizeAmount {
17 /// Cancel an in-progress resize.
18 Cancel,
19 /// Grow the device to its maximum available size.
20 Max,
21 /// Set the device to exactly this many bytes.
22 Set(u64),
23 /// Add this many bytes to the current device size.
24 Add(u64),
25 /// Subtract this many bytes from the current device size.
26 Sub(u64),
27}
28
29impl ResizeAmount {
30 fn to_string(&self) -> String {
31 match self {
32 Self::Cancel => "cancel".to_owned(),
33 Self::Max => "max".to_owned(),
34 Self::Set(n) => n.to_string(),
35 Self::Add(n) => format!("+{n}"),
36 Self::Sub(n) => format!("-{n}"),
37 }
38 }
39}
40
41/// Arguments for a resize operation.
42///
43/// `devid` selects which device within the filesystem to resize. When `None`,
44/// the kernel defaults to device ID 1 (the first device).
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct ResizeArgs {
47 pub devid: Option<u64>,
48 pub amount: ResizeAmount,
49}
50
51impl ResizeArgs {
52 pub fn new(amount: ResizeAmount) -> Self {
53 Self {
54 devid: None,
55 amount,
56 }
57 }
58
59 pub fn with_devid(mut self, devid: u64) -> Self {
60 self.devid = Some(devid);
61 self
62 }
63
64 /// Format into the string that `BTRFS_IOC_RESIZE` expects in
65 /// `btrfs_ioctl_vol_args.name`: `[<devid>:]<amount>`.
66 fn format_name(&self) -> String {
67 let amount = self.amount.to_string();
68 match self.devid {
69 Some(devid) => format!("{devid}:{amount}"),
70 None => amount,
71 }
72 }
73}
74
75/// Resize a device within the btrfs filesystem referred to by `fd`.
76///
77/// `fd` must be an open file descriptor to a directory on the mounted
78/// filesystem. Use [`ResizeArgs`] to specify the target device and amount.
79pub fn resize(fd: BorrowedFd, args: ResizeArgs) -> nix::Result<()> {
80 let name = args.format_name();
81 let name_bytes = name.as_bytes();
82
83 // BTRFS_PATH_NAME_MAX is 4087; the name field is [c_char; 4088].
84 // A well-formed resize string (devid + colon + u64 digits) is at most
85 // ~23 characters, so this can only fail if the caller constructs a
86 // pathological devid.
87 if name_bytes.len() >= 4088 {
88 return Err(nix::errno::Errno::EINVAL);
89 }
90
91 let mut raw: btrfs_ioctl_vol_args = unsafe { mem::zeroed() };
92 for (i, &b) in name_bytes.iter().enumerate() {
93 raw.name[i] = b as c_char;
94 }
95
96 unsafe { btrfs_ioc_resize(fd.as_raw_fd(), &mut raw) }?;
97 Ok(())
98}