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) }
}
}
libc_enum!{
#[repr(i32)]
enum QuotaSubCmd {
Q_SYNC,
Q_QUOTAON,
Q_QUOTAOFF,
Q_GETQUOTA,
Q_SETQUOTA,
}
}
libc_enum!{
#[repr(i32)]
#[non_exhaustive]
pub enum QuotaType {
USRQUOTA,
GRPQUOTA,
}
}
libc_enum!{
#[repr(i32)]
#[non_exhaustive]
pub enum QuotaFmt {
QFMT_VFS_OLD,
QFMT_VFS_V0,
QFMT_VFS_V1,
}
}
libc_bitflags!(
#[derive(Default)]
pub struct QuotaValidFlags: u32 {
QIF_BLIMITS;
QIF_SPACE;
QIF_ILIMITS;
QIF_INODES;
QIF_BTIME;
QIF_ITIME;
QIF_LIMITS;
QIF_USAGE;
QIF_TIMES;
QIF_ALL;
}
);
#[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 {
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
}
}
pub fn set_blocks_hard_limit(&mut self, limit: u64) {
self.0.dqb_bhardlimit = limit;
}
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
}
}
pub fn set_blocks_soft_limit(&mut self, limit: u64) {
self.0.dqb_bsoftlimit = limit;
}
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
}
}
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
}
}
pub fn set_inodes_hard_limit(&mut self, limit: u64) {
self.0.dqb_ihardlimit = 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
}
}
pub fn set_inodes_soft_limit(&mut self, limit: u64) {
self.0.dqb_isoftlimit = limit;
}
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
}
}
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
}
}
pub fn set_block_time_limit(&mut self, limit: u64) {
self.0.dqb_btime = limit;
}
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
}
}
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)
}
}
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)
})?
}
pub fn quotactl_off<P: ?Sized + NixPath>(which: QuotaType, special: &P) -> Result<()> {
quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), Some(special), 0, ptr::null_mut())
}
pub fn quotactl_sync<P: ?Sized + NixPath>(which: QuotaType, special: Option<&P>) -> Result<()> {
quotactl(QuotaCmd(QuotaSubCmd::Q_SYNC, which), special, 0, ptr::null_mut())
}
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())})
}
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)
}