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