1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ResizeAmount {
17 Cancel,
19 Max,
21 Set(u64),
23 Add(u64),
25 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#[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 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#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
82 fn resize_amount_cancel() {
83 assert_eq!(ResizeAmount::Cancel.to_string(), "cancel");
84 }
85
86 #[test]
87 fn resize_amount_max() {
88 assert_eq!(ResizeAmount::Max.to_string(), "max");
89 }
90
91 #[test]
92 fn resize_amount_set() {
93 assert_eq!(ResizeAmount::Set(1073741824).to_string(), "1073741824");
94 }
95
96 #[test]
97 fn resize_amount_add() {
98 assert_eq!(ResizeAmount::Add(512000000).to_string(), "+512000000");
99 }
100
101 #[test]
102 fn resize_amount_sub() {
103 assert_eq!(ResizeAmount::Sub(256000000).to_string(), "-256000000");
104 }
105
106 #[test]
109 fn resize_args_no_devid() {
110 let args = ResizeArgs::new(ResizeAmount::Max);
111 assert!(args.devid.is_none());
112 assert_eq!(args.format_name(), "max");
113 }
114
115 #[test]
116 fn resize_args_with_devid() {
117 let args = ResizeArgs::new(ResizeAmount::Add(1024)).with_devid(2);
118 assert_eq!(args.devid, Some(2));
119 assert_eq!(args.format_name(), "2:+1024");
120 }
121
122 #[test]
123 fn resize_args_set_with_devid() {
124 let args = ResizeArgs::new(ResizeAmount::Set(999)).with_devid(1);
125 assert_eq!(args.format_name(), "1:999");
126 }
127}
128
129pub fn resize(fd: BorrowedFd, args: ResizeArgs) -> nix::Result<()> {
134 let name = args.format_name();
135 let name_bytes = name.as_bytes();
136
137 if name_bytes.len() >= 4088 {
142 return Err(nix::errno::Errno::EINVAL);
143 }
144
145 let mut raw: btrfs_ioctl_vol_args = unsafe { mem::zeroed() };
146 for (i, &b) in name_bytes.iter().enumerate() {
147 raw.name[i] = b as c_char;
148 }
149
150 unsafe { btrfs_ioc_resize(fd.as_raw_fd(), &mut raw) }?;
151 Ok(())
152}