btrfs_cli/balance/
start.rs1use super::{filters::parse_filters, open_path};
2use crate::{Format, Runnable};
3use anyhow::{Context, Result};
4use btrfs_uapi::balance::{BalanceFlags, balance};
5use clap::Parser;
6use nix::errno::Errno;
7use std::{os::unix::io::AsFd, path::PathBuf, thread, time::Duration};
8
9type Filters = String;
10
11#[derive(Parser, Debug)]
21#[allow(clippy::struct_excessive_bools)]
22pub struct BalanceStartCommand {
23 #[clap(long, short)]
25 pub data_filters: Option<Filters>,
26 #[clap(long, short)]
28 pub metadata_filters: Option<Filters>,
29 #[clap(long, short, requires = "force")]
31 pub system_filters: Option<Filters>,
32
33 #[clap(long, short)]
35 pub force: bool,
36
37 #[clap(long)]
39 pub full_balance: bool,
40
41 #[clap(long, short, alias = "bg")]
43 pub background: bool,
44
45 #[clap(long)]
47 pub enqueue: bool,
48
49 pub path: PathBuf,
50}
51
52impl Runnable for BalanceStartCommand {
53 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
54 if self.background {
56 anyhow::bail!("--background is not yet implemented");
57 }
58
59 let has_filters = self.data_filters.is_some()
60 || self.metadata_filters.is_some()
61 || self.system_filters.is_some();
62
63 let mut flags = BalanceFlags::empty();
64
65 let data_args = match &self.data_filters {
66 Some(f) => {
67 flags |= BalanceFlags::DATA;
68 Some(parse_filters(f).context("invalid data filter")?)
69 }
70 None => None,
71 };
72
73 let meta_args = match &self.metadata_filters {
76 Some(f) => {
77 flags |= BalanceFlags::METADATA | BalanceFlags::SYSTEM;
78 Some(parse_filters(f).context("invalid metadata filter")?)
79 }
80 None => None,
81 };
82
83 let sys_args = match &self.system_filters {
86 Some(f) => {
87 flags |= BalanceFlags::SYSTEM;
88 Some(parse_filters(f).context("invalid system filter")?)
89 }
90 None => meta_args.clone(),
91 };
92
93 if !has_filters {
94 flags |= BalanceFlags::DATA
96 | BalanceFlags::METADATA
97 | BalanceFlags::SYSTEM;
98 }
99
100 if self.force {
101 flags |= BalanceFlags::FORCE;
102 }
103
104 if !has_filters && !self.full_balance {
107 eprintln!("WARNING:\n");
108 eprintln!(
109 "\tFull balance without filters requested. This operation is very"
110 );
111 eprintln!(
112 "\tintense and takes potentially very long. It is recommended to"
113 );
114 eprintln!(
115 "\tuse the balance filters to narrow down the scope of balance."
116 );
117 eprintln!(
118 "\tUse 'btrfs balance start --full-balance' to skip this warning."
119 );
120 eprintln!(
121 "\tThe operation will start in 10 seconds. Use Ctrl-C to stop it."
122 );
123 thread::sleep(Duration::from_secs(10));
124 eprintln!("\nStarting balance without any filters.");
125 }
126
127 let file = open_path(&self.path)?;
128
129 match balance(file.as_fd(), flags, data_args, meta_args, sys_args) {
130 Ok(progress) => {
131 println!(
132 "Done, had to relocate {} out of {} chunks",
133 progress.completed, progress.considered
134 );
135 Ok(())
136 }
137 Err(Errno::ECANCELED) => {
138 eprintln!("Balance was paused or cancelled by user.");
142 Ok(())
143 }
144 Err(e) => Err(e).with_context(|| {
145 format!("error during balancing '{}'", self.path.display())
146 }),
147 }
148 }
149}