Skip to main content

btrfs_cli/quota/
rescan.rs

1use crate::{Format, Runnable};
2use anyhow::{Context, Result};
3use clap::Parser;
4use nix::errno::Errno;
5use std::{fs::File, os::unix::io::AsFd, path::PathBuf};
6
7/// Trash all qgroup numbers and scan the metadata again
8#[derive(Parser, Debug)]
9pub struct QuotaRescanCommand {
10    /// Show status of a running rescan operation
11    #[clap(short = 's', long, conflicts_with = "wait")]
12    pub status: bool,
13
14    /// Start rescan and wait for it to finish
15    #[clap(short = 'w', long)]
16    pub wait: bool,
17
18    /// Wait for rescan to finish without starting it
19    #[clap(short = 'W', long)]
20    pub wait_norescan: bool,
21
22    /// Path to a mounted btrfs filesystem
23    pub path: PathBuf,
24}
25
26impl Runnable for QuotaRescanCommand {
27    fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
28        let file = File::open(&self.path).with_context(|| {
29            format!("failed to open '{}'", self.path.display())
30        })?;
31        let fd = file.as_fd();
32
33        if self.status {
34            let st = btrfs_uapi::quota::quota_rescan_status(fd).with_context(
35                || {
36                    format!(
37                        "failed to get quota rescan status on '{}'",
38                        self.path.display()
39                    )
40                },
41            )?;
42
43            if st.running {
44                println!(
45                    "rescan operation running (current key {})",
46                    st.progress
47                );
48            } else {
49                println!("no rescan operation in progress");
50            }
51
52            return Ok(());
53        }
54
55        if self.wait_norescan {
56            // Just wait — do not start a new rescan.
57            btrfs_uapi::quota::quota_rescan_wait(fd).with_context(|| {
58                format!(
59                    "failed to wait for quota rescan on '{}'",
60                    self.path.display()
61                )
62            })?;
63            return Ok(());
64        }
65
66        // Start the rescan.  If one is already running and the caller asked to
67        // wait, treat EINPROGRESS as a non-error and proceed to the wait step.
68        match btrfs_uapi::quota::quota_rescan(fd) {
69            Ok(()) => {
70                println!("quota rescan started");
71            }
72            Err(Errno::EINPROGRESS) if self.wait => {
73                // Already running; we'll still wait below.
74            }
75            Err(e) => {
76                return Err(e).with_context(|| {
77                    format!(
78                        "failed to start quota rescan on '{}'",
79                        self.path.display()
80                    )
81                });
82            }
83        }
84
85        if self.wait {
86            btrfs_uapi::quota::quota_rescan_wait(fd).with_context(|| {
87                format!(
88                    "failed to wait for quota rescan on '{}'",
89                    self.path.display()
90                )
91            })?;
92        }
93
94        Ok(())
95    }
96}