Skip to main content

btrfs_uapi/
quota.rs

1//! # Filesystem quota: enabling, disabling, and rescanning quota accounting
2//!
3//! Quota accounting tracks disk usage per subvolume via qgroups.  It must be
4//! explicitly enabled before any qgroup limits or usage data are available.
5//! Once enabled, usage numbers are maintained incrementally by the kernel; a
6//! rescan rebuilds them from scratch if they become inconsistent.
7//!
8//! Quota status (whether quotas are on, which mode, inconsistency flag) is
9//! read from sysfs via [`crate::sysfs::SysfsBtrfs::quota_status`].
10
11use crate::raw::{
12    BTRFS_QUOTA_CTL_DISABLE, BTRFS_QUOTA_CTL_ENABLE, BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA,
13    btrfs_ioc_quota_ctl, btrfs_ioc_quota_rescan, btrfs_ioc_quota_rescan_status,
14    btrfs_ioc_quota_rescan_wait, btrfs_ioctl_quota_ctl_args, btrfs_ioctl_quota_rescan_args,
15};
16use std::{
17    mem,
18    os::{fd::AsRawFd, unix::io::BorrowedFd},
19};
20
21/// Enable quota accounting on the filesystem referred to by `fd`.
22///
23/// When `simple` is `true`, uses `BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA`, which
24/// accounts for extent ownership by lifetime rather than backref walks. This is
25/// faster but less precise than full qgroup accounting.
26pub fn quota_enable(fd: BorrowedFd, simple: bool) -> nix::Result<()> {
27    let cmd = if simple {
28        BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA as u64
29    } else {
30        BTRFS_QUOTA_CTL_ENABLE as u64
31    };
32    let mut args: btrfs_ioctl_quota_ctl_args = unsafe { mem::zeroed() };
33    args.cmd = cmd;
34    unsafe { btrfs_ioc_quota_ctl(fd.as_raw_fd(), &mut args) }?;
35    Ok(())
36}
37
38/// Disable quota accounting on the filesystem referred to by `fd`.
39pub fn quota_disable(fd: BorrowedFd) -> nix::Result<()> {
40    let mut args: btrfs_ioctl_quota_ctl_args = unsafe { mem::zeroed() };
41    args.cmd = BTRFS_QUOTA_CTL_DISABLE as u64;
42    unsafe { btrfs_ioc_quota_ctl(fd.as_raw_fd(), &mut args) }?;
43    Ok(())
44}
45
46/// Start a quota rescan on the filesystem referred to by `fd`.
47///
48/// Returns immediately after kicking off the background scan. Use
49/// [`quota_rescan_wait`] to block until it finishes. If a rescan is already
50/// in progress the kernel returns `EINPROGRESS`; callers that are about to
51/// wait anyway can treat that as a non-error.
52pub fn quota_rescan(fd: BorrowedFd) -> nix::Result<()> {
53    let args: btrfs_ioctl_quota_rescan_args = unsafe { mem::zeroed() };
54    unsafe { btrfs_ioc_quota_rescan(fd.as_raw_fd(), &args) }?;
55    Ok(())
56}
57
58/// Block until the quota rescan currently running on the filesystem referred
59/// to by `fd` completes. Returns immediately if no rescan is in progress.
60pub fn quota_rescan_wait(fd: BorrowedFd) -> nix::Result<()> {
61    unsafe { btrfs_ioc_quota_rescan_wait(fd.as_raw_fd()) }?;
62    Ok(())
63}
64
65/// Status of an in-progress (or absent) quota rescan.
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct QuotaRescanStatus {
68    /// Whether a rescan is currently running.
69    pub running: bool,
70    /// Object ID of the most recently scanned tree item. Only meaningful
71    /// when `running` is `true`.
72    pub progress: u64,
73}
74
75/// Query the status of the quota rescan on the filesystem referred to by `fd`.
76pub fn quota_rescan_status(fd: BorrowedFd) -> nix::Result<QuotaRescanStatus> {
77    let mut args: btrfs_ioctl_quota_rescan_args = unsafe { mem::zeroed() };
78    unsafe { btrfs_ioc_quota_rescan_status(fd.as_raw_fd(), &mut args) }?;
79    Ok(QuotaRescanStatus {
80        running: args.flags != 0,
81        progress: args.progress,
82    })
83}