btrfs_cli/qgroup/
limit.rs1use crate::{
2 Format, Runnable,
3 util::{open_path, parse_qgroupid, parse_size_with_suffix},
4};
5use anyhow::{Context, Result};
6use btrfs_uapi::quota::QgroupLimitFlags;
7use clap::Parser;
8use nix::errno::Errno;
9use std::{os::unix::io::AsFd, path::PathBuf};
10
11#[derive(Debug, Clone)]
13pub enum QgroupLimitSize {
14 Bytes(u64),
15 None,
16}
17
18impl std::str::FromStr for QgroupLimitSize {
19 type Err = anyhow::Error;
20
21 fn from_str(s: &str) -> Result<Self> {
22 if s.eq_ignore_ascii_case("none") {
23 Ok(Self::None)
24 } else {
25 parse_size_with_suffix(s).map(Self::Bytes)
26 }
27 }
28}
29
30#[derive(Parser, Debug)]
32pub struct QgroupLimitCommand {
33 pub size: QgroupLimitSize,
35
36 #[clap(value_name = "QGROUPID_OR_PATH")]
38 pub target: String,
39
40 pub path: Option<PathBuf>,
42
43 #[clap(short = 'c', long)]
45 pub compress: bool,
46
47 #[clap(short = 'e', long)]
49 pub exclusive: bool,
50}
51
52impl Runnable for QgroupLimitCommand {
53 fn run(&self, _format: Format, _dry_run: bool) -> Result<()> {
54 let (fs_path, qgroupid) = match &self.path {
55 None => {
56 let path = PathBuf::from(&self.target);
58 (path, 0u64)
59 }
60 Some(path) => {
61 let qgroupid = parse_qgroupid(&self.target)?;
63 (path.clone(), qgroupid)
64 }
65 };
66
67 let size = match self.size {
68 QgroupLimitSize::Bytes(n) => n,
69 QgroupLimitSize::None => u64::MAX,
70 };
71
72 let mut flags = QgroupLimitFlags::empty();
73 let mut max_rfer = u64::MAX;
74 let mut max_excl = u64::MAX;
75
76 if self.compress {
77 flags |= QgroupLimitFlags::RFER_CMPR | QgroupLimitFlags::EXCL_CMPR;
78 }
79
80 if self.exclusive {
81 flags |= QgroupLimitFlags::MAX_EXCL;
82 max_excl = size;
83 } else {
84 flags |= QgroupLimitFlags::MAX_RFER;
85 max_rfer = size;
86 }
87
88 let file = open_path(&fs_path)?;
89
90 match btrfs_uapi::quota::qgroup_limit(
91 file.as_fd(),
92 qgroupid,
93 flags,
94 max_rfer,
95 max_excl,
96 ) {
97 Ok(()) => Ok(()),
98 Err(Errno::ENOTCONN) => {
99 anyhow::bail!("quota not enabled on '{}'", fs_path.display())
100 }
101 Err(e) => Err(e).with_context(|| {
102 format!("failed to set qgroup limit on '{}'", fs_path.display())
103 }),
104 }
105 }
106}