13da5c369Sopenharmony_ci//! Set and configure disk quotas for users, groups, or projects. 23da5c369Sopenharmony_ci//! 33da5c369Sopenharmony_ci//! # Examples 43da5c369Sopenharmony_ci//! 53da5c369Sopenharmony_ci//! Enabling and setting a quota: 63da5c369Sopenharmony_ci//! 73da5c369Sopenharmony_ci//! ```rust,no_run 83da5c369Sopenharmony_ci//! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags}; 93da5c369Sopenharmony_ci//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user").unwrap(); 103da5c369Sopenharmony_ci//! let mut dqblk: Dqblk = Default::default(); 113da5c369Sopenharmony_ci//! dqblk.set_blocks_hard_limit(10000); 123da5c369Sopenharmony_ci//! dqblk.set_blocks_soft_limit(8000); 133da5c369Sopenharmony_ci//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS).unwrap(); 143da5c369Sopenharmony_ci//! ``` 153da5c369Sopenharmony_ciuse crate::errno::Errno; 163da5c369Sopenharmony_ciuse crate::{NixPath, Result}; 173da5c369Sopenharmony_ciuse libc::{self, c_char, c_int}; 183da5c369Sopenharmony_ciuse std::default::Default; 193da5c369Sopenharmony_ciuse std::{mem, ptr}; 203da5c369Sopenharmony_ci 213da5c369Sopenharmony_cistruct QuotaCmd(QuotaSubCmd, QuotaType); 223da5c369Sopenharmony_ci 233da5c369Sopenharmony_ciimpl QuotaCmd { 243da5c369Sopenharmony_ci #[allow(unused_unsafe)] 253da5c369Sopenharmony_ci fn as_int(&self) -> c_int { 263da5c369Sopenharmony_ci unsafe { libc::QCMD(self.0 as i32, self.1 as i32) } 273da5c369Sopenharmony_ci } 283da5c369Sopenharmony_ci} 293da5c369Sopenharmony_ci 303da5c369Sopenharmony_ci// linux quota version >= 2 313da5c369Sopenharmony_cilibc_enum! { 323da5c369Sopenharmony_ci #[repr(i32)] 333da5c369Sopenharmony_ci enum QuotaSubCmd { 343da5c369Sopenharmony_ci Q_SYNC, 353da5c369Sopenharmony_ci Q_QUOTAON, 363da5c369Sopenharmony_ci Q_QUOTAOFF, 373da5c369Sopenharmony_ci Q_GETQUOTA, 383da5c369Sopenharmony_ci Q_SETQUOTA, 393da5c369Sopenharmony_ci } 403da5c369Sopenharmony_ci} 413da5c369Sopenharmony_ci 423da5c369Sopenharmony_cilibc_enum! { 433da5c369Sopenharmony_ci /// The scope of the quota. 443da5c369Sopenharmony_ci #[repr(i32)] 453da5c369Sopenharmony_ci #[non_exhaustive] 463da5c369Sopenharmony_ci pub enum QuotaType { 473da5c369Sopenharmony_ci /// Specify a user quota 483da5c369Sopenharmony_ci USRQUOTA, 493da5c369Sopenharmony_ci /// Specify a group quota 503da5c369Sopenharmony_ci GRPQUOTA, 513da5c369Sopenharmony_ci } 523da5c369Sopenharmony_ci} 533da5c369Sopenharmony_ci 543da5c369Sopenharmony_cilibc_enum! { 553da5c369Sopenharmony_ci /// The type of quota format to use. 563da5c369Sopenharmony_ci #[repr(i32)] 573da5c369Sopenharmony_ci #[non_exhaustive] 583da5c369Sopenharmony_ci pub enum QuotaFmt { 593da5c369Sopenharmony_ci /// Use the original quota format. 603da5c369Sopenharmony_ci QFMT_VFS_OLD, 613da5c369Sopenharmony_ci /// Use the standard VFS v0 quota format. 623da5c369Sopenharmony_ci /// 633da5c369Sopenharmony_ci /// Handles 32-bit UIDs/GIDs and quota limits up to 2<sup>32</sup> bytes/2<sup>32</sup> inodes. 643da5c369Sopenharmony_ci QFMT_VFS_V0, 653da5c369Sopenharmony_ci /// Use the VFS v1 quota format. 663da5c369Sopenharmony_ci /// 673da5c369Sopenharmony_ci /// Handles 32-bit UIDs/GIDs and quota limits of 2<sup>64</sup> bytes/2<sup>64</sup> inodes. 683da5c369Sopenharmony_ci QFMT_VFS_V1, 693da5c369Sopenharmony_ci } 703da5c369Sopenharmony_ci} 713da5c369Sopenharmony_ci 723da5c369Sopenharmony_cilibc_bitflags!( 733da5c369Sopenharmony_ci /// Indicates the quota fields that are valid to read from. 743da5c369Sopenharmony_ci #[derive(Default)] 753da5c369Sopenharmony_ci pub struct QuotaValidFlags: u32 { 763da5c369Sopenharmony_ci /// The block hard & soft limit fields. 773da5c369Sopenharmony_ci QIF_BLIMITS; 783da5c369Sopenharmony_ci /// The current space field. 793da5c369Sopenharmony_ci QIF_SPACE; 803da5c369Sopenharmony_ci /// The inode hard & soft limit fields. 813da5c369Sopenharmony_ci QIF_ILIMITS; 823da5c369Sopenharmony_ci /// The current inodes field. 833da5c369Sopenharmony_ci QIF_INODES; 843da5c369Sopenharmony_ci /// The disk use time limit field. 853da5c369Sopenharmony_ci QIF_BTIME; 863da5c369Sopenharmony_ci /// The file quote time limit field. 873da5c369Sopenharmony_ci QIF_ITIME; 883da5c369Sopenharmony_ci /// All block & inode limits. 893da5c369Sopenharmony_ci QIF_LIMITS; 903da5c369Sopenharmony_ci /// The space & inodes usage fields. 913da5c369Sopenharmony_ci QIF_USAGE; 923da5c369Sopenharmony_ci /// The time limit fields. 933da5c369Sopenharmony_ci QIF_TIMES; 943da5c369Sopenharmony_ci /// All fields. 953da5c369Sopenharmony_ci QIF_ALL; 963da5c369Sopenharmony_ci } 973da5c369Sopenharmony_ci); 983da5c369Sopenharmony_ci 993da5c369Sopenharmony_ci/// Wrapper type for `if_dqblk` 1003da5c369Sopenharmony_ci#[repr(transparent)] 1013da5c369Sopenharmony_ci#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 1023da5c369Sopenharmony_cipub struct Dqblk(libc::dqblk); 1033da5c369Sopenharmony_ci 1043da5c369Sopenharmony_ciimpl Default for Dqblk { 1053da5c369Sopenharmony_ci fn default() -> Dqblk { 1063da5c369Sopenharmony_ci Dqblk(libc::dqblk { 1073da5c369Sopenharmony_ci dqb_bhardlimit: 0, 1083da5c369Sopenharmony_ci dqb_bsoftlimit: 0, 1093da5c369Sopenharmony_ci dqb_curspace: 0, 1103da5c369Sopenharmony_ci dqb_ihardlimit: 0, 1113da5c369Sopenharmony_ci dqb_isoftlimit: 0, 1123da5c369Sopenharmony_ci dqb_curinodes: 0, 1133da5c369Sopenharmony_ci dqb_btime: 0, 1143da5c369Sopenharmony_ci dqb_itime: 0, 1153da5c369Sopenharmony_ci dqb_valid: 0, 1163da5c369Sopenharmony_ci }) 1173da5c369Sopenharmony_ci } 1183da5c369Sopenharmony_ci} 1193da5c369Sopenharmony_ci 1203da5c369Sopenharmony_ciimpl Dqblk { 1213da5c369Sopenharmony_ci /// The absolute limit on disk quota blocks allocated. 1223da5c369Sopenharmony_ci pub fn blocks_hard_limit(&self) -> Option<u64> { 1233da5c369Sopenharmony_ci let valid_fields = 1243da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 1253da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) { 1263da5c369Sopenharmony_ci Some(self.0.dqb_bhardlimit) 1273da5c369Sopenharmony_ci } else { 1283da5c369Sopenharmony_ci None 1293da5c369Sopenharmony_ci } 1303da5c369Sopenharmony_ci } 1313da5c369Sopenharmony_ci 1323da5c369Sopenharmony_ci /// Set the absolute limit on disk quota blocks allocated. 1333da5c369Sopenharmony_ci pub fn set_blocks_hard_limit(&mut self, limit: u64) { 1343da5c369Sopenharmony_ci self.0.dqb_bhardlimit = limit; 1353da5c369Sopenharmony_ci } 1363da5c369Sopenharmony_ci 1373da5c369Sopenharmony_ci /// Preferred limit on disk quota blocks 1383da5c369Sopenharmony_ci pub fn blocks_soft_limit(&self) -> Option<u64> { 1393da5c369Sopenharmony_ci let valid_fields = 1403da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 1413da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) { 1423da5c369Sopenharmony_ci Some(self.0.dqb_bsoftlimit) 1433da5c369Sopenharmony_ci } else { 1443da5c369Sopenharmony_ci None 1453da5c369Sopenharmony_ci } 1463da5c369Sopenharmony_ci } 1473da5c369Sopenharmony_ci 1483da5c369Sopenharmony_ci /// Set the preferred limit on disk quota blocks allocated. 1493da5c369Sopenharmony_ci pub fn set_blocks_soft_limit(&mut self, limit: u64) { 1503da5c369Sopenharmony_ci self.0.dqb_bsoftlimit = limit; 1513da5c369Sopenharmony_ci } 1523da5c369Sopenharmony_ci 1533da5c369Sopenharmony_ci /// Current occupied space (bytes). 1543da5c369Sopenharmony_ci pub fn occupied_space(&self) -> Option<u64> { 1553da5c369Sopenharmony_ci let valid_fields = 1563da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 1573da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_SPACE) { 1583da5c369Sopenharmony_ci Some(self.0.dqb_curspace) 1593da5c369Sopenharmony_ci } else { 1603da5c369Sopenharmony_ci None 1613da5c369Sopenharmony_ci } 1623da5c369Sopenharmony_ci } 1633da5c369Sopenharmony_ci 1643da5c369Sopenharmony_ci /// Maximum number of allocated inodes. 1653da5c369Sopenharmony_ci pub fn inodes_hard_limit(&self) -> Option<u64> { 1663da5c369Sopenharmony_ci let valid_fields = 1673da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 1683da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) { 1693da5c369Sopenharmony_ci Some(self.0.dqb_ihardlimit) 1703da5c369Sopenharmony_ci } else { 1713da5c369Sopenharmony_ci None 1723da5c369Sopenharmony_ci } 1733da5c369Sopenharmony_ci } 1743da5c369Sopenharmony_ci 1753da5c369Sopenharmony_ci /// Set the maximum number of allocated inodes. 1763da5c369Sopenharmony_ci pub fn set_inodes_hard_limit(&mut self, limit: u64) { 1773da5c369Sopenharmony_ci self.0.dqb_ihardlimit = limit; 1783da5c369Sopenharmony_ci } 1793da5c369Sopenharmony_ci 1803da5c369Sopenharmony_ci /// Preferred inode limit 1813da5c369Sopenharmony_ci pub fn inodes_soft_limit(&self) -> Option<u64> { 1823da5c369Sopenharmony_ci let valid_fields = 1833da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 1843da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) { 1853da5c369Sopenharmony_ci Some(self.0.dqb_isoftlimit) 1863da5c369Sopenharmony_ci } else { 1873da5c369Sopenharmony_ci None 1883da5c369Sopenharmony_ci } 1893da5c369Sopenharmony_ci } 1903da5c369Sopenharmony_ci 1913da5c369Sopenharmony_ci /// Set the preferred limit of allocated inodes. 1923da5c369Sopenharmony_ci pub fn set_inodes_soft_limit(&mut self, limit: u64) { 1933da5c369Sopenharmony_ci self.0.dqb_isoftlimit = limit; 1943da5c369Sopenharmony_ci } 1953da5c369Sopenharmony_ci 1963da5c369Sopenharmony_ci /// Current number of allocated inodes. 1973da5c369Sopenharmony_ci pub fn allocated_inodes(&self) -> Option<u64> { 1983da5c369Sopenharmony_ci let valid_fields = 1993da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 2003da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_INODES) { 2013da5c369Sopenharmony_ci Some(self.0.dqb_curinodes) 2023da5c369Sopenharmony_ci } else { 2033da5c369Sopenharmony_ci None 2043da5c369Sopenharmony_ci } 2053da5c369Sopenharmony_ci } 2063da5c369Sopenharmony_ci 2073da5c369Sopenharmony_ci /// Time limit for excessive disk use. 2083da5c369Sopenharmony_ci pub fn block_time_limit(&self) -> Option<u64> { 2093da5c369Sopenharmony_ci let valid_fields = 2103da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 2113da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_BTIME) { 2123da5c369Sopenharmony_ci Some(self.0.dqb_btime) 2133da5c369Sopenharmony_ci } else { 2143da5c369Sopenharmony_ci None 2153da5c369Sopenharmony_ci } 2163da5c369Sopenharmony_ci } 2173da5c369Sopenharmony_ci 2183da5c369Sopenharmony_ci /// Set the time limit for excessive disk use. 2193da5c369Sopenharmony_ci pub fn set_block_time_limit(&mut self, limit: u64) { 2203da5c369Sopenharmony_ci self.0.dqb_btime = limit; 2213da5c369Sopenharmony_ci } 2223da5c369Sopenharmony_ci 2233da5c369Sopenharmony_ci /// Time limit for excessive files. 2243da5c369Sopenharmony_ci pub fn inode_time_limit(&self) -> Option<u64> { 2253da5c369Sopenharmony_ci let valid_fields = 2263da5c369Sopenharmony_ci QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); 2273da5c369Sopenharmony_ci if valid_fields.contains(QuotaValidFlags::QIF_ITIME) { 2283da5c369Sopenharmony_ci Some(self.0.dqb_itime) 2293da5c369Sopenharmony_ci } else { 2303da5c369Sopenharmony_ci None 2313da5c369Sopenharmony_ci } 2323da5c369Sopenharmony_ci } 2333da5c369Sopenharmony_ci 2343da5c369Sopenharmony_ci /// Set the time limit for excessive files. 2353da5c369Sopenharmony_ci pub fn set_inode_time_limit(&mut self, limit: u64) { 2363da5c369Sopenharmony_ci self.0.dqb_itime = limit; 2373da5c369Sopenharmony_ci } 2383da5c369Sopenharmony_ci} 2393da5c369Sopenharmony_ci 2403da5c369Sopenharmony_cifn quotactl<P: ?Sized + NixPath>( 2413da5c369Sopenharmony_ci cmd: QuotaCmd, 2423da5c369Sopenharmony_ci special: Option<&P>, 2433da5c369Sopenharmony_ci id: c_int, 2443da5c369Sopenharmony_ci addr: *mut c_char, 2453da5c369Sopenharmony_ci) -> Result<()> { 2463da5c369Sopenharmony_ci unsafe { 2473da5c369Sopenharmony_ci Errno::clear(); 2483da5c369Sopenharmony_ci let res = match special { 2493da5c369Sopenharmony_ci Some(dev) => dev.with_nix_path(|path| { 2503da5c369Sopenharmony_ci libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr) 2513da5c369Sopenharmony_ci }), 2523da5c369Sopenharmony_ci None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)), 2533da5c369Sopenharmony_ci }?; 2543da5c369Sopenharmony_ci 2553da5c369Sopenharmony_ci Errno::result(res).map(drop) 2563da5c369Sopenharmony_ci } 2573da5c369Sopenharmony_ci} 2583da5c369Sopenharmony_ci 2593da5c369Sopenharmony_ci/// Turn on disk quotas for a block device. 2603da5c369Sopenharmony_cipub fn quotactl_on<P: ?Sized + NixPath>( 2613da5c369Sopenharmony_ci which: QuotaType, 2623da5c369Sopenharmony_ci special: &P, 2633da5c369Sopenharmony_ci format: QuotaFmt, 2643da5c369Sopenharmony_ci quota_file: &P, 2653da5c369Sopenharmony_ci) -> Result<()> { 2663da5c369Sopenharmony_ci quota_file.with_nix_path(|path| { 2673da5c369Sopenharmony_ci let mut path_copy = path.to_bytes_with_nul().to_owned(); 2683da5c369Sopenharmony_ci let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char; 2693da5c369Sopenharmony_ci quotactl( 2703da5c369Sopenharmony_ci QuotaCmd(QuotaSubCmd::Q_QUOTAON, which), 2713da5c369Sopenharmony_ci Some(special), 2723da5c369Sopenharmony_ci format as c_int, 2733da5c369Sopenharmony_ci p, 2743da5c369Sopenharmony_ci ) 2753da5c369Sopenharmony_ci })? 2763da5c369Sopenharmony_ci} 2773da5c369Sopenharmony_ci 2783da5c369Sopenharmony_ci/// Disable disk quotas for a block device. 2793da5c369Sopenharmony_cipub fn quotactl_off<P: ?Sized + NixPath>( 2803da5c369Sopenharmony_ci which: QuotaType, 2813da5c369Sopenharmony_ci special: &P, 2823da5c369Sopenharmony_ci) -> Result<()> { 2833da5c369Sopenharmony_ci quotactl( 2843da5c369Sopenharmony_ci QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), 2853da5c369Sopenharmony_ci Some(special), 2863da5c369Sopenharmony_ci 0, 2873da5c369Sopenharmony_ci ptr::null_mut(), 2883da5c369Sopenharmony_ci ) 2893da5c369Sopenharmony_ci} 2903da5c369Sopenharmony_ci 2913da5c369Sopenharmony_ci/// Update the on-disk copy of quota usages for a filesystem. 2923da5c369Sopenharmony_ci/// 2933da5c369Sopenharmony_ci/// If `special` is `None`, then all file systems with active quotas are sync'd. 2943da5c369Sopenharmony_cipub fn quotactl_sync<P: ?Sized + NixPath>( 2953da5c369Sopenharmony_ci which: QuotaType, 2963da5c369Sopenharmony_ci special: Option<&P>, 2973da5c369Sopenharmony_ci) -> Result<()> { 2983da5c369Sopenharmony_ci quotactl( 2993da5c369Sopenharmony_ci QuotaCmd(QuotaSubCmd::Q_SYNC, which), 3003da5c369Sopenharmony_ci special, 3013da5c369Sopenharmony_ci 0, 3023da5c369Sopenharmony_ci ptr::null_mut(), 3033da5c369Sopenharmony_ci ) 3043da5c369Sopenharmony_ci} 3053da5c369Sopenharmony_ci 3063da5c369Sopenharmony_ci/// Get disk quota limits and current usage for the given user/group id. 3073da5c369Sopenharmony_cipub fn quotactl_get<P: ?Sized + NixPath>( 3083da5c369Sopenharmony_ci which: QuotaType, 3093da5c369Sopenharmony_ci special: &P, 3103da5c369Sopenharmony_ci id: c_int, 3113da5c369Sopenharmony_ci) -> Result<Dqblk> { 3123da5c369Sopenharmony_ci let mut dqblk = mem::MaybeUninit::uninit(); 3133da5c369Sopenharmony_ci quotactl( 3143da5c369Sopenharmony_ci QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), 3153da5c369Sopenharmony_ci Some(special), 3163da5c369Sopenharmony_ci id, 3173da5c369Sopenharmony_ci dqblk.as_mut_ptr() as *mut c_char, 3183da5c369Sopenharmony_ci )?; 3193da5c369Sopenharmony_ci Ok(unsafe { Dqblk(dqblk.assume_init()) }) 3203da5c369Sopenharmony_ci} 3213da5c369Sopenharmony_ci 3223da5c369Sopenharmony_ci/// Configure quota values for the specified fields for a given user/group id. 3233da5c369Sopenharmony_cipub fn quotactl_set<P: ?Sized + NixPath>( 3243da5c369Sopenharmony_ci which: QuotaType, 3253da5c369Sopenharmony_ci special: &P, 3263da5c369Sopenharmony_ci id: c_int, 3273da5c369Sopenharmony_ci dqblk: &Dqblk, 3283da5c369Sopenharmony_ci fields: QuotaValidFlags, 3293da5c369Sopenharmony_ci) -> Result<()> { 3303da5c369Sopenharmony_ci let mut dqblk_copy = *dqblk; 3313da5c369Sopenharmony_ci dqblk_copy.0.dqb_valid = fields.bits(); 3323da5c369Sopenharmony_ci quotactl( 3333da5c369Sopenharmony_ci QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which), 3343da5c369Sopenharmony_ci Some(special), 3353da5c369Sopenharmony_ci id, 3363da5c369Sopenharmony_ci &mut dqblk_copy as *mut _ as *mut c_char, 3373da5c369Sopenharmony_ci ) 3383da5c369Sopenharmony_ci} 339