1use std::default::Default;
16use std::{mem, ptr};
17use libc::{self, c_int, c_char};
18use crate::{Result, NixPath};
19use crate::errno::Errno;
20
21struct QuotaCmd(QuotaSubCmd, QuotaType);
22
23impl QuotaCmd {
24 #[allow(unused_unsafe)]
25 fn as_int(&self) -> c_int {
26 unsafe { libc::QCMD(self.0 as i32, self.1 as i32) }
27 }
28}
29
30libc_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
42libc_enum!{
43 #[repr(i32)]
45 #[non_exhaustive]
46 pub enum QuotaType {
47 USRQUOTA,
49 GRPQUOTA,
51 }
52}
53
54libc_enum!{
55 #[repr(i32)]
57 #[non_exhaustive]
58 pub enum QuotaFmt {
59 QFMT_VFS_OLD,
61 QFMT_VFS_V0,
65 QFMT_VFS_V1,
69 }
70}
71
72libc_bitflags!(
73 #[derive(Default)]
75 pub struct QuotaValidFlags: u32 {
76 QIF_BLIMITS;
78 QIF_SPACE;
80 QIF_ILIMITS;
82 QIF_INODES;
84 QIF_BTIME;
86 QIF_ITIME;
88 QIF_LIMITS;
90 QIF_USAGE;
92 QIF_TIMES;
94 QIF_ALL;
96 }
97);
98
99#[repr(transparent)]
101#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
102pub struct Dqblk(libc::dqblk);
103
104impl Default for Dqblk {
105 fn default() -> Dqblk {
106 Dqblk(libc::dqblk {
107 dqb_bhardlimit: 0,
108 dqb_bsoftlimit: 0,
109 dqb_curspace: 0,
110 dqb_ihardlimit: 0,
111 dqb_isoftlimit: 0,
112 dqb_curinodes: 0,
113 dqb_btime: 0,
114 dqb_itime: 0,
115 dqb_valid: 0,
116 })
117 }
118}
119
120impl Dqblk {
121 pub fn blocks_hard_limit(&self) -> Option<u64> {
123 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
124 if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
125 Some(self.0.dqb_bhardlimit)
126 } else {
127 None
128 }
129 }
130
131 pub fn set_blocks_hard_limit(&mut self, limit: u64) {
133 self.0.dqb_bhardlimit = limit;
134 }
135
136 pub fn blocks_soft_limit(&self) -> Option<u64> {
138 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
139 if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
140 Some(self.0.dqb_bsoftlimit)
141 } else {
142 None
143 }
144 }
145
146 pub fn set_blocks_soft_limit(&mut self, limit: u64) {
148 self.0.dqb_bsoftlimit = limit;
149 }
150
151 pub fn occupied_space(&self) -> Option<u64> {
153 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
154 if valid_fields.contains(QuotaValidFlags::QIF_SPACE) {
155 Some(self.0.dqb_curspace)
156 } else {
157 None
158 }
159 }
160
161 pub fn inodes_hard_limit(&self) -> Option<u64> {
163 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
164 if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
165 Some(self.0.dqb_ihardlimit)
166 } else {
167 None
168 }
169 }
170
171 pub fn set_inodes_hard_limit(&mut self, limit: u64) {
173 self.0.dqb_ihardlimit = limit;
174 }
175
176 pub fn inodes_soft_limit(&self) -> Option<u64> {
178 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
179 if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
180 Some(self.0.dqb_isoftlimit)
181 } else {
182 None
183 }
184 }
185
186 pub fn set_inodes_soft_limit(&mut self, limit: u64) {
188 self.0.dqb_isoftlimit = limit;
189 }
190
191 pub fn allocated_inodes(&self) -> Option<u64> {
193 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
194 if valid_fields.contains(QuotaValidFlags::QIF_INODES) {
195 Some(self.0.dqb_curinodes)
196 } else {
197 None
198 }
199 }
200
201 pub fn block_time_limit(&self) -> Option<u64> {
203 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
204 if valid_fields.contains(QuotaValidFlags::QIF_BTIME) {
205 Some(self.0.dqb_btime)
206 } else {
207 None
208 }
209 }
210
211 pub fn set_block_time_limit(&mut self, limit: u64) {
213 self.0.dqb_btime = limit;
214 }
215
216 pub fn inode_time_limit(&self) -> Option<u64> {
218 let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
219 if valid_fields.contains(QuotaValidFlags::QIF_ITIME) {
220 Some(self.0.dqb_itime)
221 } else {
222 None
223 }
224 }
225
226 pub fn set_inode_time_limit(&mut self, limit: u64) {
228 self.0.dqb_itime = limit;
229 }
230}
231
232fn quotactl<P: ?Sized + NixPath>(cmd: QuotaCmd, special: Option<&P>, id: c_int, addr: *mut c_char) -> Result<()> {
233 unsafe {
234 Errno::clear();
235 let res = match special {
236 Some(dev) => dev.with_nix_path(|path| libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)),
237 None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)),
238 }?;
239
240 Errno::result(res).map(drop)
241 }
242}
243
244pub fn quotactl_on<P: ?Sized + NixPath>(which: QuotaType, special: &P, format: QuotaFmt, quota_file: &P) -> Result<()> {
246 quota_file.with_nix_path(|path| {
247 let mut path_copy = path.to_bytes_with_nul().to_owned();
248 let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char;
249 quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAON, which), Some(special), format as c_int, p)
250 })?
251}
252
253pub fn quotactl_off<P: ?Sized + NixPath>(which: QuotaType, special: &P) -> Result<()> {
255 quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), Some(special), 0, ptr::null_mut())
256}
257
258pub fn quotactl_sync<P: ?Sized + NixPath>(which: QuotaType, special: Option<&P>) -> Result<()> {
262 quotactl(QuotaCmd(QuotaSubCmd::Q_SYNC, which), special, 0, ptr::null_mut())
263}
264
265pub fn quotactl_get<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int) -> Result<Dqblk> {
267 let mut dqblk = mem::MaybeUninit::uninit();
268 quotactl(QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), Some(special), id, dqblk.as_mut_ptr() as *mut c_char)?;
269 Ok(unsafe{ Dqblk(dqblk.assume_init())})
270}
271
272pub fn quotactl_set<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int, dqblk: &Dqblk, fields: QuotaValidFlags) -> Result<()> {
274 let mut dqblk_copy = *dqblk;
275 dqblk_copy.0.dqb_valid = fields.bits();
276 quotactl(QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which), Some(special), id, &mut dqblk_copy as *mut _ as *mut c_char)
277}