Skip to content

Commit 1db4d18

Browse files
committed
Migrate quota module to libc FFI types
1 parent 28c5b4a commit 1db4d18

File tree

2 files changed

+240
-79
lines changed

2 files changed

+240
-79
lines changed

src/sys/mod.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,7 @@ pub mod ptrace;
5454

5555
pub mod select;
5656

57-
#[cfg(all(target_os = "linux",
58-
any(target_arch = "x86",
59-
target_arch = "x86_64",
60-
target_arch = "arm")),
61-
)]
57+
#[cfg(target_os = "linux")]
6258
pub mod quota;
6359

6460

src/sys/quota.rs

+239-74
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,235 @@
1-
use {Errno, Result, NixPath};
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::*;
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, QIF_BLIMITS);
14+
//! ```
15+
use std::default::Default;
16+
use std::{mem, ptr};
217
use libc::{self, c_int, c_char};
18+
use {Errno, Result, NixPath};
319

4-
#[cfg(all(target_os = "linux",
5-
any(target_arch = "x86",
6-
target_arch = "x86_64",
7-
target_arch = "arm")),
8-
)]
9-
pub mod quota {
10-
use libc::{self, c_int};
20+
struct QuotaCmd(QuotaSubCmd, QuotaType);
1121

12-
pub struct QuotaCmd(pub QuotaSubCmd, pub QuotaType);
13-
pub type QuotaSubCmd = c_int;
22+
impl QuotaCmd {
23+
fn as_int(&self) -> c_int {
24+
unsafe { libc::QCMD(self.0 as i32, self.1 as i32) }
25+
}
26+
}
27+
28+
// linux quota version >= 2
29+
libc_enum!{
30+
#[repr(i32)]
31+
enum QuotaSubCmd {
32+
Q_SYNC,
33+
Q_QUOTAON,
34+
Q_QUOTAOFF,
35+
Q_GETFMT,
36+
Q_GETINFO,
37+
Q_SETINFO,
38+
Q_GETQUOTA,
39+
Q_SETQUOTA,
40+
}
41+
}
42+
43+
libc_enum!{
44+
/// The scope of the quota.
45+
#[repr(i32)]
46+
pub enum QuotaType {
47+
/// Specify a user quota
48+
USRQUOTA,
49+
/// Specify a group quota
50+
GRPQUOTA,
51+
}
52+
}
53+
54+
libc_enum!{
55+
/// The type of quota format to use.
56+
#[repr(i32)]
57+
pub enum QuotaFmt {
58+
/// Use the original quota format.
59+
QFMT_VFS_OLD,
60+
/// Use the standard VFS v0 quota format.
61+
///
62+
/// Handles 32-bit UIDs/GIDs and quota limits up to 2^42 bytes/2^32 inodes.
63+
QFMT_VFS_V0,
64+
/// Use the VFS v1 quota format.
65+
///
66+
/// Handles 32-bit UIDs/GIDs and quota limits of 2^64 bytes/2^64 inodes.
67+
QFMT_VFS_V1,
68+
}
69+
}
1470

15-
impl QuotaCmd {
16-
pub fn as_int(&self) -> c_int {
17-
((self.0 << 8) | (self.1 & 0x00ff)) as c_int
71+
libc_bitflags!(
72+
/// Indicates the quota fields that are valid to read from.
73+
#[derive(Default)]
74+
pub struct QuotaValidFlags: u32 {
75+
/// The block hard & soft limit fields.
76+
QIF_BLIMITS;
77+
/// The current space field.
78+
QIF_SPACE;
79+
/// The inode hard & soft limit fields.
80+
QIF_ILIMITS;
81+
/// The current inodes field.
82+
QIF_INODES;
83+
/// The disk use time limit field.
84+
QIF_BTIME;
85+
/// The file quote time limit field.
86+
QIF_ITIME;
87+
/// All block & inode limits.
88+
QIF_LIMITS;
89+
/// The space & inodes usage fields.
90+
QIF_USAGE;
91+
/// The time limit fields.
92+
QIF_TIMES;
93+
/// All fields.
94+
QIF_ALL;
95+
}
96+
);
97+
98+
/// Wrapper type for `if_dqblk`
99+
// FIXME: Change to repr(transparent)
100+
#[repr(C)]
101+
#[derive(Clone, Copy)]
102+
pub struct Dqblk(libc::dqblk);
103+
104+
impl 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+
120+
impl Dqblk {
121+
/// The absolute limit on disk quota blocks allocated.
122+
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(QIF_BLIMITS) {
125+
Some(self.0.dqb_bhardlimit)
126+
} else {
127+
None
18128
}
19129
}
20130

21-
// linux quota version >= 2
22-
pub const Q_SYNC: QuotaSubCmd = 0x800001;
23-
pub const Q_QUOTAON: QuotaSubCmd = 0x800002;
24-
pub const Q_QUOTAOFF: QuotaSubCmd = 0x800003;
25-
pub const Q_GETFMT: QuotaSubCmd = 0x800004;
26-
pub const Q_GETINFO: QuotaSubCmd = 0x800005;
27-
pub const Q_SETINFO: QuotaSubCmd = 0x800006;
28-
pub const Q_GETQUOTA: QuotaSubCmd = 0x800007;
29-
pub const Q_SETQUOTA: QuotaSubCmd = 0x800008;
30-
31-
pub type QuotaType = c_int;
32-
33-
pub const USRQUOTA: QuotaType = 0;
34-
pub const GRPQUOTA: QuotaType = 1;
35-
36-
pub type QuotaFmt = c_int;
37-
38-
pub const QFMT_VFS_OLD: QuotaFmt = 1;
39-
pub const QFMT_VFS_V0: QuotaFmt = 2;
40-
pub const QFMT_VFS_V1: QuotaFmt = 4;
41-
42-
libc_bitflags!(
43-
#[derive(Default)]
44-
pub struct QuotaValidFlags: u32 {
45-
QIF_BLIMITS;
46-
QIF_SPACE;
47-
QIF_ILIMITS;
48-
QIF_INODES;
49-
QIF_BTIME;
50-
QIF_ITIME;
51-
QIF_LIMITS;
52-
QIF_USAGE;
53-
QIF_TIMES;
54-
QIF_ALL;
131+
/// Set the absolute limit on disk quota blocks allocated.
132+
pub fn set_blocks_hard_limit(&mut self, limit: u64) {
133+
self.0.dqb_bhardlimit = limit;
134+
}
135+
136+
/// Preferred limit on disk quota blocks
137+
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(QIF_BLIMITS) {
140+
Some(self.0.dqb_bsoftlimit)
141+
} else {
142+
None
55143
}
56-
);
57-
58-
#[repr(C)]
59-
#[derive(Default,Debug,Copy,Clone)]
60-
pub struct Dqblk {
61-
pub bhardlimit: u64,
62-
pub bsoftlimit: u64,
63-
pub curspace: u64,
64-
pub ihardlimit: u64,
65-
pub isoftlimit: u64,
66-
pub curinodes: u64,
67-
pub btime: u64,
68-
pub itime: u64,
69-
pub valid: QuotaValidFlags,
70144
}
71-
}
72145

73-
use std::ptr;
146+
/// Set the preferred limit on disk quota blocks allocated.
147+
pub fn set_blocks_soft_limit(&mut self, limit: u64) {
148+
self.0.dqb_bsoftlimit = limit;
149+
}
150+
151+
/// Current occupied space (bytes).
152+
pub fn occupied_space(&self) -> Option<u64> {
153+
let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
154+
if valid_fields.contains(QIF_SPACE) {
155+
Some(self.0.dqb_curspace)
156+
} else {
157+
None
158+
}
159+
}
160+
161+
/// Maximum number of allocated inodes.
162+
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(QIF_ILIMITS) {
165+
Some(self.0.dqb_ihardlimit)
166+
} else {
167+
None
168+
}
169+
}
170+
171+
/// Set the maximum number of allocated inodes.
172+
pub fn set_inodes_hard_limit(&mut self, limit: u64) {
173+
self.0.dqb_ihardlimit = limit;
174+
}
175+
176+
/// Preferred inode limit
177+
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(QIF_ILIMITS) {
180+
Some(self.0.dqb_isoftlimit)
181+
} else {
182+
None
183+
}
184+
}
185+
186+
/// Set the preferred limit of allocated inodes.
187+
pub fn set_inodes_soft_limit(&mut self, limit: u64) {
188+
self.0.dqb_isoftlimit = limit;
189+
}
190+
191+
/// Current number of allocated inodes.
192+
pub fn allocated_inodes(&self) -> Option<u64> {
193+
let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
194+
if valid_fields.contains(QIF_INODES) {
195+
Some(self.0.dqb_curinodes)
196+
} else {
197+
None
198+
}
199+
}
200+
201+
/// Time limit for excessive disk use.
202+
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(QIF_BTIME) {
205+
Some(self.0.dqb_btime)
206+
} else {
207+
None
208+
}
209+
}
210+
211+
/// Set the time limit for excessive disk use.
212+
pub fn set_block_time_limit(&mut self, limit: u64) {
213+
self.0.dqb_btime = limit;
214+
}
215+
216+
/// Time limit for excessive files.
217+
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(QIF_ITIME) {
220+
Some(self.0.dqb_itime)
221+
} else {
222+
None
223+
}
224+
}
225+
226+
/// Set the time limit for excessive files.
227+
pub fn set_inode_time_limit(&mut self, limit: u64) {
228+
self.0.dqb_itime = limit;
229+
}
230+
}
74231

75-
fn quotactl<P: ?Sized + NixPath>(cmd: quota::QuotaCmd, special: Option<&P>, id: c_int, addr: *mut c_char) -> Result<()> {
232+
fn quotactl<P: ?Sized + NixPath>(cmd: QuotaCmd, special: Option<&P>, id: c_int, addr: *mut c_char) -> Result<()> {
76233
unsafe {
77234
Errno::clear();
78235
let res = try!(
@@ -86,27 +243,35 @@ fn quotactl<P: ?Sized + NixPath>(cmd: quota::QuotaCmd, special: Option<&P>, id:
86243
}
87244
}
88245

89-
pub fn quotactl_on<P: ?Sized + NixPath>(which: quota::QuotaType, special: &P, format: quota::QuotaFmt, quota_file: &P) -> Result<()> {
246+
/// Turn on disk quotas for a block device.
247+
pub fn quotactl_on<P: ?Sized + NixPath>(which: QuotaType, special: &P, format: QuotaFmt, quota_file: &P) -> Result<()> {
90248
try!(quota_file.with_nix_path(|path| {
91249
let mut path_copy = path.to_bytes_with_nul().to_owned();
92250
let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char;
93-
quotactl(quota::QuotaCmd(quota::Q_QUOTAON, which), Some(special), format as c_int, p)
251+
quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAON, which), Some(special), format as c_int, p)
94252
}))
95253
}
96254

97-
pub fn quotactl_off<P: ?Sized + NixPath>(which: quota::QuotaType, special: &P) -> Result<()> {
98-
quotactl(quota::QuotaCmd(quota::Q_QUOTAOFF, which), Some(special), 0, ptr::null_mut())
255+
/// Disable disk quotas for a block device.
256+
pub fn quotactl_off<P: ?Sized + NixPath>(which: QuotaType, special: &P) -> Result<()> {
257+
quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), Some(special), 0, ptr::null_mut())
99258
}
100259

101-
pub fn quotactl_sync<P: ?Sized + NixPath>(which: quota::QuotaType, special: Option<&P>) -> Result<()> {
102-
quotactl(quota::QuotaCmd(quota::Q_SYNC, which), special, 0, ptr::null_mut())
260+
/// Update the on-disk copy of quota usages for a filesystem.
261+
pub fn quotactl_sync<P: ?Sized + NixPath>(which: QuotaType, special: Option<&P>) -> Result<()> {
262+
quotactl(QuotaCmd(QuotaSubCmd::Q_SYNC, which), special, 0, ptr::null_mut())
103263
}
104264

105-
pub fn quotactl_get<P: ?Sized + NixPath>(which: quota::QuotaType, special: &P, id: c_int, dqblk: &mut quota::Dqblk) -> Result<()> {
106-
quotactl(quota::QuotaCmd(quota::Q_GETQUOTA, which), Some(special), id, dqblk as *mut _ as *mut c_char)
265+
/// Get disk quota limits and current usage for the given user/group id.
266+
pub fn quotactl_get<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int) -> Result<Dqblk> {
267+
let mut dqblk = unsafe { mem::uninitialized() };
268+
quotactl(QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), Some(special), id, &mut dqblk as *mut _ as *mut c_char)?;
269+
dqblk
107270
}
108271

109-
pub fn quotactl_set<P: ?Sized + NixPath>(which: quota::QuotaType, special: &P, id: c_int, dqblk: &quota::Dqblk) -> Result<()> {
272+
/// Configure quota values for the specified fields for a given user/group id.
273+
pub fn quotactl_set<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int, dqblk: &Dqblk, fields: QuotaValidFlags) -> Result<()> {
110274
let mut dqblk_copy = *dqblk;
111-
quotactl(quota::QuotaCmd(quota::Q_SETQUOTA, which), Some(special), id, &mut dqblk_copy as *mut _ as *mut c_char)
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)
112277
}

0 commit comments

Comments
 (0)