• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Set and configure disk quotas for users, groups, or projects.
2 //!
3 //! # Examples
4 //!
5 //! Enabling and setting a quota:
6 //!
7 //! ```rust,no_run
8 //! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags};
9 //! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user");
10 //! let mut dqblk: Dqblk = Default::default();
11 //! dqblk.set_blocks_hard_limit(10000);
12 //! dqblk.set_blocks_soft_limit(8000);
13 //! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS);
14 //! ```
15 use std::default::Default;
16 use std::{mem, ptr};
17 use libc::{self, c_int, c_char};
18 use crate::{Result, NixPath};
19 use crate::errno::Errno;
20 
21 struct QuotaCmd(QuotaSubCmd, QuotaType);
22 
23 impl QuotaCmd {
24     #[allow(unused_unsafe)]
as_int(&self) -> c_int25     fn as_int(&self) -> c_int {
26         unsafe { libc::QCMD(self.0 as i32, self.1 as i32) }
27     }
28 }
29 
30 // linux quota version >= 2
31 libc_enum!{
32     #[repr(i32)]
33     enum QuotaSubCmd {
34         Q_SYNC,
35         Q_QUOTAON,
36         Q_QUOTAOFF,
37         Q_GETQUOTA,
38         Q_SETQUOTA,
39     }
40 }
41 
42 libc_enum!{
43     /// The scope of the quota.
44     #[repr(i32)]
45     pub enum QuotaType {
46         /// Specify a user quota
47         USRQUOTA,
48         /// Specify a group quota
49         GRPQUOTA,
50     }
51 }
52 
53 libc_enum!{
54     /// The type of quota format to use.
55     #[repr(i32)]
56     pub enum QuotaFmt {
57         /// Use the original quota format.
58         QFMT_VFS_OLD,
59         /// Use the standard VFS v0 quota format.
60         ///
61         /// Handles 32-bit UIDs/GIDs and quota limits up to 2<sup>32</sup> bytes/2<sup>32</sup> inodes.
62         QFMT_VFS_V0,
63         /// Use the VFS v1 quota format.
64         ///
65         /// Handles 32-bit UIDs/GIDs and quota limits of 2<sup>64</sup> bytes/2<sup>64</sup> inodes.
66         QFMT_VFS_V1,
67     }
68 }
69 
70 libc_bitflags!(
71     /// Indicates the quota fields that are valid to read from.
72     #[derive(Default)]
73     pub struct QuotaValidFlags: u32 {
74         /// The block hard & soft limit fields.
75         QIF_BLIMITS;
76         /// The current space field.
77         QIF_SPACE;
78         /// The inode hard & soft limit fields.
79         QIF_ILIMITS;
80         /// The current inodes field.
81         QIF_INODES;
82         /// The disk use time limit field.
83         QIF_BTIME;
84         /// The file quote time limit field.
85         QIF_ITIME;
86         /// All block & inode limits.
87         QIF_LIMITS;
88         /// The space & inodes usage fields.
89         QIF_USAGE;
90         /// The time limit fields.
91         QIF_TIMES;
92         /// All fields.
93         QIF_ALL;
94     }
95 );
96 
97 /// Wrapper type for `if_dqblk`
98 #[repr(transparent)]
99 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
100 pub struct Dqblk(libc::dqblk);
101 
102 impl Default for Dqblk {
default() -> Dqblk103     fn default() -> Dqblk {
104         Dqblk(libc::dqblk {
105             dqb_bhardlimit: 0,
106             dqb_bsoftlimit: 0,
107             dqb_curspace: 0,
108             dqb_ihardlimit: 0,
109             dqb_isoftlimit: 0,
110             dqb_curinodes: 0,
111             dqb_btime: 0,
112             dqb_itime: 0,
113             dqb_valid: 0,
114         })
115     }
116 }
117 
118 impl Dqblk {
119     /// The absolute limit on disk quota blocks allocated.
blocks_hard_limit(&self) -> Option<u64>120     pub fn blocks_hard_limit(&self) -> Option<u64> {
121         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
122         if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
123             Some(self.0.dqb_bhardlimit)
124         } else {
125             None
126         }
127     }
128 
129     /// Set the absolute limit on disk quota blocks allocated.
set_blocks_hard_limit(&mut self, limit: u64)130     pub fn set_blocks_hard_limit(&mut self, limit: u64) {
131         self.0.dqb_bhardlimit = limit;
132     }
133 
134     /// Preferred limit on disk quota blocks
blocks_soft_limit(&self) -> Option<u64>135     pub fn blocks_soft_limit(&self) -> Option<u64> {
136         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
137         if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
138             Some(self.0.dqb_bsoftlimit)
139         } else {
140             None
141         }
142     }
143 
144     /// Set the preferred limit on disk quota blocks allocated.
set_blocks_soft_limit(&mut self, limit: u64)145     pub fn set_blocks_soft_limit(&mut self, limit: u64) {
146         self.0.dqb_bsoftlimit = limit;
147     }
148 
149     /// Current occupied space (bytes).
occupied_space(&self) -> Option<u64>150     pub fn occupied_space(&self) -> Option<u64> {
151         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
152         if valid_fields.contains(QuotaValidFlags::QIF_SPACE) {
153             Some(self.0.dqb_curspace)
154         } else {
155             None
156         }
157     }
158 
159     /// Maximum number of allocated inodes.
inodes_hard_limit(&self) -> Option<u64>160     pub fn inodes_hard_limit(&self) -> Option<u64> {
161         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
162         if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
163             Some(self.0.dqb_ihardlimit)
164         } else {
165             None
166         }
167     }
168 
169     /// Set the maximum number of allocated inodes.
set_inodes_hard_limit(&mut self, limit: u64)170     pub fn set_inodes_hard_limit(&mut self, limit: u64) {
171         self.0.dqb_ihardlimit = limit;
172     }
173 
174     /// Preferred inode limit
inodes_soft_limit(&self) -> Option<u64>175     pub fn inodes_soft_limit(&self) -> Option<u64> {
176         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
177         if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
178             Some(self.0.dqb_isoftlimit)
179         } else {
180             None
181         }
182     }
183 
184     /// Set the preferred limit of allocated inodes.
set_inodes_soft_limit(&mut self, limit: u64)185     pub fn set_inodes_soft_limit(&mut self, limit: u64) {
186         self.0.dqb_isoftlimit = limit;
187     }
188 
189     /// Current number of allocated inodes.
allocated_inodes(&self) -> Option<u64>190     pub fn allocated_inodes(&self) -> Option<u64> {
191         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
192         if valid_fields.contains(QuotaValidFlags::QIF_INODES) {
193             Some(self.0.dqb_curinodes)
194         } else {
195             None
196         }
197     }
198 
199     /// Time limit for excessive disk use.
block_time_limit(&self) -> Option<u64>200     pub fn block_time_limit(&self) -> Option<u64> {
201         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
202         if valid_fields.contains(QuotaValidFlags::QIF_BTIME) {
203             Some(self.0.dqb_btime)
204         } else {
205             None
206         }
207     }
208 
209     /// Set the time limit for excessive disk use.
set_block_time_limit(&mut self, limit: u64)210     pub fn set_block_time_limit(&mut self, limit: u64) {
211         self.0.dqb_btime = limit;
212     }
213 
214     /// Time limit for excessive files.
inode_time_limit(&self) -> Option<u64>215     pub fn inode_time_limit(&self) -> Option<u64> {
216         let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
217         if valid_fields.contains(QuotaValidFlags::QIF_ITIME) {
218             Some(self.0.dqb_itime)
219         } else {
220             None
221         }
222     }
223 
224     /// Set the time limit for excessive files.
set_inode_time_limit(&mut self, limit: u64)225     pub fn set_inode_time_limit(&mut self, limit: u64) {
226         self.0.dqb_itime = limit;
227     }
228 }
229 
quotactl<P: ?Sized + NixPath>(cmd: QuotaCmd, special: Option<&P>, id: c_int, addr: *mut c_char) -> Result<()>230 fn quotactl<P: ?Sized + NixPath>(cmd: QuotaCmd, special: Option<&P>, id: c_int, addr: *mut c_char) -> Result<()> {
231     unsafe {
232         Errno::clear();
233         let res = match special {
234             Some(dev) => dev.with_nix_path(|path| libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)),
235             None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)),
236         }?;
237 
238         Errno::result(res).map(drop)
239     }
240 }
241 
242 /// Turn on disk quotas for a block device.
quotactl_on<P: ?Sized + NixPath>(which: QuotaType, special: &P, format: QuotaFmt, quota_file: &P) -> Result<()>243 pub fn quotactl_on<P: ?Sized + NixPath>(which: QuotaType, special: &P, format: QuotaFmt, quota_file: &P) -> Result<()> {
244     quota_file.with_nix_path(|path| {
245         let mut path_copy = path.to_bytes_with_nul().to_owned();
246         let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char;
247         quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAON, which), Some(special), format as c_int, p)
248     })?
249 }
250 
251 /// Disable disk quotas for a block device.
quotactl_off<P: ?Sized + NixPath>(which: QuotaType, special: &P) -> Result<()>252 pub fn quotactl_off<P: ?Sized + NixPath>(which: QuotaType, special: &P) -> Result<()> {
253     quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), Some(special), 0, ptr::null_mut())
254 }
255 
256 /// Update the on-disk copy of quota usages for a filesystem.
257 ///
258 /// If `special` is `None`, then all file systems with active quotas are sync'd.
quotactl_sync<P: ?Sized + NixPath>(which: QuotaType, special: Option<&P>) -> Result<()>259 pub fn quotactl_sync<P: ?Sized + NixPath>(which: QuotaType, special: Option<&P>) -> Result<()> {
260     quotactl(QuotaCmd(QuotaSubCmd::Q_SYNC, which), special, 0, ptr::null_mut())
261 }
262 
263 /// Get disk quota limits and current usage for the given user/group id.
quotactl_get<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int) -> Result<Dqblk>264 pub fn quotactl_get<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int) -> Result<Dqblk> {
265     let mut dqblk = mem::MaybeUninit::uninit();
266     quotactl(QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), Some(special), id, dqblk.as_mut_ptr() as *mut c_char)?;
267     Ok(unsafe{ Dqblk(dqblk.assume_init())})
268 }
269 
270 /// Configure quota values for the specified fields for a given user/group id.
quotactl_set<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int, dqblk: &Dqblk, fields: QuotaValidFlags) -> Result<()>271 pub fn quotactl_set<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int, dqblk: &Dqblk, fields: QuotaValidFlags) -> Result<()> {
272     let mut dqblk_copy = *dqblk;
273     dqblk_copy.0.dqb_valid = fields.bits();
274     quotactl(QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which), Some(special), id, &mut dqblk_copy as *mut _ as *mut c_char)
275 }
276