btrfs_cli/qgroup/
assign.rs1use crate::{
2 Format, Runnable,
3 util::{open_path, parse_qgroupid},
4};
5use anyhow::{Context, Result, bail};
6use btrfs_uapi::quota::qgroupid_level;
7use clap::Parser;
8use nix::errno::Errno;
9use std::{os::unix::io::AsFd, path::PathBuf};
10
11#[derive(Parser, Debug)]
13pub struct QgroupAssignCommand {
14 pub src: String,
16 pub dst: String,
18 pub path: PathBuf,
20 #[clap(long, conflicts_with = "no_rescan")]
22 pub rescan: bool,
23 #[clap(long)]
25 pub no_rescan: bool,
26}
27
28impl Runnable for QgroupAssignCommand {
29 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
30 let src = parse_qgroupid(&self.src)?;
31 let dst = parse_qgroupid(&self.dst)?;
32
33 if qgroupid_level(src) >= qgroupid_level(dst) {
34 bail!(
35 "source qgroup '{}' must be at a lower level than destination qgroup '{}'",
36 self.src,
37 self.dst
38 );
39 }
40
41 let file = open_path(&self.path)?;
42 let fd = file.as_fd();
43
44 let needs_rescan = match btrfs_uapi::quota::qgroup_assign(fd, src, dst)
45 {
46 Ok(needs_rescan) => needs_rescan,
47 Err(Errno::ENOTCONN) => {
48 bail!("quota not enabled on '{}'", self.path.display())
49 }
50 Err(e) => {
51 return Err(e).with_context(|| {
52 format!(
53 "failed to assign qgroup '{}' to '{}' on '{}'",
54 self.src,
55 self.dst,
56 self.path.display()
57 )
58 });
59 }
60 };
61
62 let do_rescan = !self.no_rescan;
64
65 if needs_rescan {
66 if do_rescan {
67 btrfs_uapi::quota::quota_rescan(fd).with_context(|| {
68 format!(
69 "failed to schedule quota rescan on '{}'",
70 self.path.display()
71 )
72 })?;
73 println!("Quota data changed, rescan scheduled");
74 } else {
75 eprintln!(
76 "WARNING: quotas may be inconsistent, rescan needed on '{}'",
77 self.path.display()
78 );
79 }
80 }
81
82 Ok(())
83 }
84}