Skip to main content

btrfs_cli/quota/
rescan.rs

1use crate::{RunContext, Runnable, util::open_path};
2use anyhow::{Context, Result};
3use clap::Parser;
4use nix::errno::Errno;
5use std::{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, _ctx: &RunContext) -> Result<()> {
28        let file = open_path(&self.path)?;
29        let fd = file.as_fd();
30
31        if self.status {
32            let st = btrfs_uapi::quota::quota_rescan_status(fd).with_context(
33                || {
34                    format!(
35                        "failed to get quota rescan status on '{}'",
36                        self.path.display()
37                    )
38                },
39            )?;
40
41            if st.running {
42                println!(
43                    "rescan operation running (current key {})",
44                    st.progress
45                );
46            } else {
47                println!("no rescan operation in progress");
48            }
49
50            return Ok(());
51        }
52
53        if self.wait_norescan {
54            // Just wait — do not start a new rescan.
55            btrfs_uapi::quota::quota_rescan_wait(fd).with_context(|| {
56                format!(
57                    "failed to wait for quota rescan on '{}'",
58                    self.path.display()
59                )
60            })?;
61            return Ok(());
62        }
63
64        // Start the rescan.  If one is already running and the caller asked to
65        // wait, treat EINPROGRESS as a non-error and proceed to the wait step.
66        match btrfs_uapi::quota::quota_rescan(fd) {
67            Ok(()) => {
68                println!("quota rescan started");
69            }
70            Err(Errno::EINPROGRESS) if self.wait => {
71                // Already running; we'll still wait below.
72            }
73            Err(e) => {
74                return Err(e).with_context(|| {
75                    format!(
76                        "failed to start quota rescan on '{}'",
77                        self.path.display()
78                    )
79                });
80            }
81        }
82
83        if self.wait {
84            btrfs_uapi::quota::quota_rescan_wait(fd).with_context(|| {
85                format!(
86                    "failed to wait for quota rescan on '{}'",
87                    self.path.display()
88                )
89            })?;
90        }
91
92        Ok(())
93    }
94}