use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Debug, Display};
use std::iter::FusedIterator;
use std::path::PathBuf;
use std::time::SystemTime;
use bitflags::bitflags;
#[cfg(all(feature = "fs", target_family = "unix"))]
use nix::unistd;
use crate::sql::FileDiscriminant;
use crate::util::system_time_from_nanos;
pub const ACCESS_ACL_XATTR_NAME: &str = "system.posix_acl_access";
pub const DEFAULT_ACL_XATTR_NAME: &str = "system.posix_acl_default";
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Device {
major: u64,
minor: u64,
}
impl Device {
pub const fn new(major: u64, minor: u64) -> Self {
Self { major, minor }
}
pub const fn major(&self) -> u64 {
self.major
}
pub const fn minor(&self) -> u64 {
self.minor
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FileKind {
Regular,
Dir,
Symlink {
target: PathBuf,
},
Block {
dev: Device,
},
Char {
dev: Device,
},
Pipe,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Uid {
uid: u32,
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.uid)
}
}
impl Uid {
pub const ROOT: Self = Self { uid: 0 };
#[cfg(all(feature = "fs", target_family = "unix"))]
pub fn current() -> Self {
Self {
uid: unistd::getuid().as_raw(),
}
}
pub const fn from_raw(uid: u32) -> Self {
Self { uid }
}
pub const fn as_raw(self) -> u32 {
self.uid
}
}
impl From<u32> for Uid {
fn from(uid: u32) -> Self {
Self::from_raw(uid)
}
}
impl From<Uid> for u32 {
fn from(uid: Uid) -> Self {
uid.as_raw()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Gid {
gid: u32,
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Display for Gid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.gid)
}
}
impl Gid {
pub const ROOT: Self = Self { gid: 0 };
#[cfg(all(feature = "fs", target_family = "unix"))]
pub fn current() -> Self {
Self {
gid: unistd::getgid().as_raw(),
}
}
pub const fn from_raw(gid: u32) -> Self {
Self { gid }
}
pub const fn as_raw(self) -> u32 {
self.gid
}
}
impl From<u32> for Gid {
fn from(gid: u32) -> Self {
Self::from_raw(gid)
}
}
impl From<Gid> for u32 {
fn from(gid: Gid) -> Self {
gid.as_raw()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Owner {
pub user: Uid,
pub group: Gid,
}
impl Owner {
pub const ROOT: Self = Self {
user: Uid::ROOT,
group: Gid::ROOT,
};
#[cfg(all(feature = "fs", target_family = "unix"))]
pub fn current() -> Self {
Self {
user: Uid::current(),
group: Gid::current(),
}
}
}
bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct FileMode: u32 {
const OWNER_R = 0o400;
const OWNER_W = 0o200;
const OWNER_X = 0o100;
const OWNER_RWX = 0o700;
const GROUP_R = 0o040;
const GROUP_W = 0o020;
const GROUP_X = 0o010;
const GROUP_RWX = 0o070;
const OTHER_R = 0o004;
const OTHER_W = 0o002;
const OTHER_X = 0o001;
const OTHER_RWX = 0o007;
const SUID = 0o4000;
const SGID = 0o2000;
const STICKY = 0o1000;
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Debug for FileMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut output = String::with_capacity(9);
if self.contains(FileMode::OWNER_R) {
output.push('r');
} else {
output.push('-');
}
if self.contains(FileMode::OWNER_W) {
output.push('w');
} else {
output.push('-');
}
if self.contains(FileMode::OWNER_X) {
output.push('x');
} else {
output.push('-');
}
if self.contains(FileMode::GROUP_R) {
output.push('r');
} else {
output.push('-');
}
if self.contains(FileMode::GROUP_W) {
output.push('w');
} else {
output.push('-');
}
if self.contains(FileMode::GROUP_X) {
output.push('x');
} else {
output.push('-');
}
if self.contains(FileMode::OTHER_R) {
output.push('r');
} else {
output.push('-');
}
if self.contains(FileMode::OTHER_W) {
output.push('w');
} else {
output.push('-');
}
if self.contains(FileMode::OTHER_X) {
output.push('x');
} else {
output.push('-');
}
write!(f, "{}", output)
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AclMode: u16 {
const R = 0o4;
const W = 0o2;
const X = 0o1;
const RWX = Self::R.bits() | Self::W.bits() | Self::X.bits();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AclQualifier {
User(Uid),
Group(Gid),
OwningUser,
OwningGroup,
Other,
Mask,
}
#[derive(Debug)]
pub struct AclIter<'a> {
inner: std::collections::hash_map::Iter<'a, AclQualifier, AclMode>,
}
impl<'a> Iterator for AclIter<'a> {
type Item = (&'a AclQualifier, &'a AclMode);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl<'a> FusedIterator for AclIter<'a> {}
impl<'a> ExactSizeIterator for AclIter<'a> {
fn len(&self) -> usize {
self.inner.len()
}
}
#[derive(Debug)]
pub struct AclIntoIter {
inner: std::collections::hash_map::IntoIter<AclQualifier, AclMode>,
}
impl Iterator for AclIntoIter {
type Item = (AclQualifier, AclMode);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl FusedIterator for AclIntoIter {}
impl ExactSizeIterator for AclIntoIter {
fn len(&self) -> usize {
self.inner.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Acl {
acl: HashMap<AclQualifier, AclMode>,
}
impl Acl {
pub fn new() -> Self {
Self {
acl: HashMap::new(),
}
}
pub fn get(&self, qualifier: AclQualifier) -> Option<AclMode> {
self.acl.get(&qualifier).copied()
}
pub fn get_mut(&mut self, qualifier: AclQualifier) -> Option<&mut AclMode> {
self.acl.get_mut(&qualifier)
}
pub fn set(&mut self, qualifier: AclQualifier, mode: AclMode) {
self.acl.insert(qualifier, mode);
}
pub fn remove(&mut self, qualifier: AclQualifier) {
self.acl.remove(&qualifier);
}
pub fn clear(&mut self) {
self.acl.clear();
}
pub fn iter(&self) -> AclIter<'_> {
AclIter {
inner: self.acl.iter(),
}
}
}
impl FromIterator<(AclQualifier, AclMode)> for Acl {
fn from_iter<T: IntoIterator<Item = (AclQualifier, AclMode)>>(iter: T) -> Self {
Self {
acl: HashMap::from_iter(iter),
}
}
}
impl IntoIterator for Acl {
type Item = (AclQualifier, AclMode);
type IntoIter = AclIntoIter;
fn into_iter(self) -> Self::IntoIter {
AclIntoIter {
inner: self.acl.into_iter(),
}
}
}
impl<'a> IntoIterator for &'a Acl {
type Item = (&'a AclQualifier, &'a AclMode);
type IntoIter = AclIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug)]
pub struct XattrsIter<'a> {
inner: std::collections::hash_map::Iter<'a, OsString, Vec<u8>>,
}
impl<'a> Iterator for XattrsIter<'a> {
type Item = (&'a OsStr, &'a [u8]);
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|(k, v)| (k.as_os_str(), v.as_slice()))
}
}
impl<'a> FusedIterator for XattrsIter<'a> {}
impl<'a> ExactSizeIterator for XattrsIter<'a> {
fn len(&self) -> usize {
self.inner.len()
}
}
#[derive(Debug)]
pub struct XattrsIntoIter {
inner: std::collections::hash_map::IntoIter<OsString, Vec<u8>>,
}
impl Iterator for XattrsIntoIter {
type Item = (OsString, Vec<u8>);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl FusedIterator for XattrsIntoIter {}
impl ExactSizeIterator for XattrsIntoIter {
fn len(&self) -> usize {
self.inner.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Xattrs {
xattrs: HashMap<OsString, Vec<u8>>,
}
impl Xattrs {
pub fn new() -> Self {
Self {
xattrs: HashMap::new(),
}
}
pub fn get<S: AsRef<OsStr> + ?Sized>(&self, name: &S) -> Option<&[u8]> {
self.xattrs.get(name.as_ref()).map(|v| v.as_slice())
}
pub fn get_mut<S: AsRef<OsStr> + ?Sized>(&mut self, name: &S) -> Option<&mut Vec<u8>> {
self.xattrs.get_mut(name.as_ref())
}
pub fn set(&mut self, name: OsString, value: Vec<u8>) {
self.xattrs.insert(name, value);
}
pub fn remove<S: AsRef<OsStr> + ?Sized>(&mut self, name: &S) {
self.xattrs.remove(name.as_ref());
}
pub fn clear(&mut self) {
self.xattrs.clear();
}
pub fn iter(&self) -> XattrsIter<'_> {
XattrsIter {
inner: self.xattrs.iter(),
}
}
}
impl FromIterator<(OsString, Vec<u8>)> for Xattrs {
fn from_iter<T: IntoIterator<Item = (OsString, Vec<u8>)>>(iter: T) -> Self {
Self {
xattrs: HashMap::from_iter(iter),
}
}
}
impl IntoIterator for Xattrs {
type Item = (OsString, Vec<u8>);
type IntoIter = XattrsIntoIter;
fn into_iter(self) -> Self::IntoIter {
XattrsIntoIter {
inner: self.xattrs.into_iter(),
}
}
}
impl<'a> IntoIterator for &'a Xattrs {
type Item = (&'a OsStr, &'a [u8]);
type IntoIter = XattrsIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RawMetadata {
pub discriminant: FileDiscriminant,
pub mode: FileMode,
pub uid: Uid,
pub gid: Gid,
pub atime: i128,
pub mtime: i128,
pub ctime: i128,
pub btime: Option<i128>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileMetadata {
mode: FileMode,
uid: Uid,
gid: Gid,
atime: SystemTime,
mtime: SystemTime,
ctime: SystemTime,
btime: Option<SystemTime>,
}
impl FileMetadata {
pub(crate) fn for_new_file(
discriminant: FileDiscriminant,
umask: FileMode,
owner: Owner,
) -> Self {
let now = SystemTime::now();
let default_mode = match discriminant {
FileDiscriminant::Dir => {
FileMode::OWNER_RWX | FileMode::GROUP_RWX | FileMode::OTHER_RWX
}
_ => {
FileMode::OWNER_R
| FileMode::OWNER_W
| FileMode::GROUP_R
| FileMode::GROUP_W
| FileMode::OTHER_R
| FileMode::OTHER_W
}
};
Self {
mode: default_mode & !umask,
uid: owner.user,
gid: owner.group,
atime: now,
mtime: now,
ctime: now,
btime: Some(now),
}
}
pub(crate) fn from_raw(raw: RawMetadata) -> Self {
Self {
mode: raw.mode,
uid: raw.uid,
gid: raw.gid,
atime: system_time_from_nanos(raw.atime),
mtime: system_time_from_nanos(raw.mtime),
ctime: system_time_from_nanos(raw.ctime),
btime: raw.btime.map(system_time_from_nanos),
}
}
pub fn mode(&self) -> FileMode {
self.mode
}
pub fn user(&self) -> Uid {
self.uid
}
pub fn group(&self) -> Gid {
self.gid
}
pub fn accessed(&self) -> SystemTime {
self.atime
}
pub fn modified(&self) -> SystemTime {
self.mtime
}
pub fn changed(&self) -> SystemTime {
self.ctime
}
pub fn created(&self) -> Option<SystemTime> {
self.btime
}
}