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