diff options
Diffstat (limited to 'third_party/rust/nix/src/sys/quota.rs')
-rw-r--r-- | third_party/rust/nix/src/sys/quota.rs | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/quota.rs b/third_party/rust/nix/src/sys/quota.rs new file mode 100644 index 0000000000..b3c44ca705 --- /dev/null +++ b/third_party/rust/nix/src/sys/quota.rs @@ -0,0 +1,338 @@ +//! Set and configure disk quotas for users, groups, or projects. +//! +//! # Examples +//! +//! Enabling and setting a quota: +//! +//! ```rust,no_run +//! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags}; +//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user").unwrap(); +//! let mut dqblk: Dqblk = Default::default(); +//! dqblk.set_blocks_hard_limit(10000); +//! dqblk.set_blocks_soft_limit(8000); +//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS).unwrap(); +//! ``` +use crate::errno::Errno; +use crate::{NixPath, Result}; +use libc::{self, c_char, c_int}; +use std::default::Default; +use std::{mem, ptr}; + +struct QuotaCmd(QuotaSubCmd, QuotaType); + +impl QuotaCmd { + #[allow(unused_unsafe)] + fn as_int(&self) -> c_int { + unsafe { libc::QCMD(self.0 as i32, self.1 as i32) } + } +} + +// linux quota version >= 2 +libc_enum! { + #[repr(i32)] + enum QuotaSubCmd { + Q_SYNC, + Q_QUOTAON, + Q_QUOTAOFF, + Q_GETQUOTA, + Q_SETQUOTA, + } +} + +libc_enum! { + /// The scope of the quota. + #[repr(i32)] + #[non_exhaustive] + pub enum QuotaType { + /// Specify a user quota + USRQUOTA, + /// Specify a group quota + GRPQUOTA, + } +} + +libc_enum! { + /// The type of quota format to use. + #[repr(i32)] + #[non_exhaustive] + pub enum QuotaFmt { + /// Use the original quota format. + QFMT_VFS_OLD, + /// Use the standard VFS v0 quota format. + /// + /// Handles 32-bit UIDs/GIDs and quota limits up to 2<sup>32</sup> bytes/2<sup>32</sup> inodes. + QFMT_VFS_V0, + /// Use the VFS v1 quota format. + /// + /// Handles 32-bit UIDs/GIDs and quota limits of 2<sup>64</sup> bytes/2<sup>64</sup> inodes. + QFMT_VFS_V1, + } +} + +libc_bitflags!( + /// Indicates the quota fields that are valid to read from. + #[derive(Default)] + pub struct QuotaValidFlags: u32 { + /// The block hard & soft limit fields. + QIF_BLIMITS; + /// The current space field. + QIF_SPACE; + /// The inode hard & soft limit fields. + QIF_ILIMITS; + /// The current inodes field. + QIF_INODES; + /// The disk use time limit field. + QIF_BTIME; + /// The file quote time limit field. + QIF_ITIME; + /// All block & inode limits. + QIF_LIMITS; + /// The space & inodes usage fields. + QIF_USAGE; + /// The time limit fields. + QIF_TIMES; + /// All fields. + QIF_ALL; + } +); + +/// Wrapper type for `if_dqblk` +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Dqblk(libc::dqblk); + +impl Default for Dqblk { + fn default() -> Dqblk { + Dqblk(libc::dqblk { + dqb_bhardlimit: 0, + dqb_bsoftlimit: 0, + dqb_curspace: 0, + dqb_ihardlimit: 0, + dqb_isoftlimit: 0, + dqb_curinodes: 0, + dqb_btime: 0, + dqb_itime: 0, + dqb_valid: 0, + }) + } +} + +impl Dqblk { + /// The absolute limit on disk quota blocks allocated. + pub fn blocks_hard_limit(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) { + Some(self.0.dqb_bhardlimit) + } else { + None + } + } + + /// Set the absolute limit on disk quota blocks allocated. + pub fn set_blocks_hard_limit(&mut self, limit: u64) { + self.0.dqb_bhardlimit = limit; + } + + /// Preferred limit on disk quota blocks + pub fn blocks_soft_limit(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) { + Some(self.0.dqb_bsoftlimit) + } else { + None + } + } + + /// Set the preferred limit on disk quota blocks allocated. + pub fn set_blocks_soft_limit(&mut self, limit: u64) { + self.0.dqb_bsoftlimit = limit; + } + + /// Current occupied space (bytes). + pub fn occupied_space(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_SPACE) { + Some(self.0.dqb_curspace) + } else { + None + } + } + + /// Maximum number of allocated inodes. + pub fn inodes_hard_limit(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) { + Some(self.0.dqb_ihardlimit) + } else { + None + } + } + + /// Set the maximum number of allocated inodes. + pub fn set_inodes_hard_limit(&mut self, limit: u64) { + self.0.dqb_ihardlimit = limit; + } + + /// Preferred inode limit + pub fn inodes_soft_limit(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) { + Some(self.0.dqb_isoftlimit) + } else { + None + } + } + + /// Set the preferred limit of allocated inodes. + pub fn set_inodes_soft_limit(&mut self, limit: u64) { + self.0.dqb_isoftlimit = limit; + } + + /// Current number of allocated inodes. + pub fn allocated_inodes(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_INODES) { + Some(self.0.dqb_curinodes) + } else { + None + } + } + + /// Time limit for excessive disk use. + pub fn block_time_limit(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_BTIME) { + Some(self.0.dqb_btime) + } else { + None + } + } + + /// Set the time limit for excessive disk use. + pub fn set_block_time_limit(&mut self, limit: u64) { + self.0.dqb_btime = limit; + } + + /// Time limit for excessive files. + pub fn inode_time_limit(&self) -> Option<u64> { + let valid_fields = + QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_ITIME) { + Some(self.0.dqb_itime) + } else { + None + } + } + + /// Set the time limit for excessive files. + pub fn set_inode_time_limit(&mut self, limit: u64) { + self.0.dqb_itime = limit; + } +} + +fn quotactl<P: ?Sized + NixPath>( + cmd: QuotaCmd, + special: Option<&P>, + id: c_int, + addr: *mut c_char, +) -> Result<()> { + unsafe { + Errno::clear(); + let res = match special { + Some(dev) => dev.with_nix_path(|path| { + libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr) + }), + None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)), + }?; + + Errno::result(res).map(drop) + } +} + +/// Turn on disk quotas for a block device. +pub fn quotactl_on<P: ?Sized + NixPath>( + which: QuotaType, + special: &P, + format: QuotaFmt, + quota_file: &P, +) -> Result<()> { + quota_file.with_nix_path(|path| { + let mut path_copy = path.to_bytes_with_nul().to_owned(); + let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char; + quotactl( + QuotaCmd(QuotaSubCmd::Q_QUOTAON, which), + Some(special), + format as c_int, + p, + ) + })? +} + +/// Disable disk quotas for a block device. +pub fn quotactl_off<P: ?Sized + NixPath>( + which: QuotaType, + special: &P, +) -> Result<()> { + quotactl( + QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), + Some(special), + 0, + ptr::null_mut(), + ) +} + +/// Update the on-disk copy of quota usages for a filesystem. +/// +/// If `special` is `None`, then all file systems with active quotas are sync'd. +pub fn quotactl_sync<P: ?Sized + NixPath>( + which: QuotaType, + special: Option<&P>, +) -> Result<()> { + quotactl( + QuotaCmd(QuotaSubCmd::Q_SYNC, which), + special, + 0, + ptr::null_mut(), + ) +} + +/// Get disk quota limits and current usage for the given user/group id. +pub fn quotactl_get<P: ?Sized + NixPath>( + which: QuotaType, + special: &P, + id: c_int, +) -> Result<Dqblk> { + let mut dqblk = mem::MaybeUninit::uninit(); + quotactl( + QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), + Some(special), + id, + dqblk.as_mut_ptr() as *mut c_char, + )?; + Ok(unsafe { Dqblk(dqblk.assume_init()) }) +} + +/// Configure quota values for the specified fields for a given user/group id. +pub fn quotactl_set<P: ?Sized + NixPath>( + which: QuotaType, + special: &P, + id: c_int, + dqblk: &Dqblk, + fields: QuotaValidFlags, +) -> Result<()> { + let mut dqblk_copy = *dqblk; + dqblk_copy.0.dqb_valid = fields.bits(); + quotactl( + QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which), + Some(special), + id, + &mut dqblk_copy as *mut _ as *mut c_char, + ) +} |