| //! 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"); |
| //! 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); |
| //! ``` |
| use std::default::Default; |
| use std::{mem, ptr}; |
| use libc::{self, c_int, c_char}; |
| use crate::{Result, NixPath}; |
| use crate::errno::Errno; |
| |
| 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)] |
| pub enum QuotaType { |
| /// Specify a user quota |
| USRQUOTA, |
| /// Specify a group quota |
| GRPQUOTA, |
| } |
| } |
| |
| libc_enum!{ |
| /// The type of quota format to use. |
| #[repr(i32)] |
| 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) |
| } |