use std::time::SystemTime;
use fuser::{FileAttr, INodeNo};
use nix::sys::stat;
use crate::{Acl, AclMode, AclQualifier, File, FileDiscriminant, FileKind, FileMode};
fn user_perm(mode: u32) -> u16 {
((mode & 0o700) >> 6) as u16
}
fn group_perm(mode: u32) -> u16 {
((mode & 0o070) >> 3) as u16
}
fn other_perm(mode: u32) -> u16 {
(mode & 0o007) as u16
}
fn constrain_acl(acl: &mut Acl, mode: FileMode) {
if let Some(acl_mode) = acl.get_mut(AclQualifier::OwningUser) {
*acl_mode = AclMode::from_bits(acl_mode.bits() & user_perm(mode.bits())).unwrap();
}
if let Some(acl_mode) = match acl.get_mut(AclQualifier::Mask) {
Some(acl_mode) => Some(acl_mode),
None => acl.get_mut(AclQualifier::OwningGroup),
} {
*acl_mode = AclMode::from_bits(acl_mode.bits() & group_perm(mode.bits())).unwrap();
}
if let Some(acl_mode) = acl.get_mut(AclQualifier::Other) {
*acl_mode = AclMode::from_bits(acl_mode.bits() & other_perm(mode.bits())).unwrap();
}
}
fn constrain_mode(acl: &Acl, mode: FileMode) -> FileMode {
let mut acl_mode = 0u16;
let user_mode = acl
.get(AclQualifier::OwningUser)
.unwrap_or(AclMode::RWX)
.bits();
acl_mode |= user_mode << 6;
let group_mode = acl.get(AclQualifier::OwningGroup);
let mask_or_group_mode = acl
.get(AclQualifier::Mask)
.or(group_mode)
.unwrap_or(AclMode::RWX)
.bits();
acl_mode |= mask_or_group_mode << 3;
let other_mode = acl.get(AclQualifier::Other).unwrap_or(AclMode::RWX).bits();
acl_mode |= other_mode;
FileMode::from_bits_truncate(acl_mode as u32 & mode.bits())
}
const NON_SPECIAL_RDEV: u32 = 0;
const BLOCK_SIZE: u32 = 512;
#[derive(Debug)]
pub struct FileAttrWithoutInode {
attrs: FileAttr,
}
impl FileAttrWithoutInode {
pub fn with_inode(mut self, inode: INodeNo) -> FileAttr {
self.attrs.ino = inode;
self.attrs
}
}
impl<'conn, 'fs> File<'conn, 'fs> {
pub(super) fn attrs(&mut self) -> crate::Result<FileAttrWithoutInode> {
let metadata = self.metadata()?;
let link_count = self.link_count()?;
let size = match self.kind() {
FileKind::Regular => self.len()?,
FileKind::Dir => 0,
FileKind::Symlink { target } => target.as_os_str().len() as u64,
_ => 0,
};
let acl = self.access_acl()?;
let mode = match acl.get(AclQualifier::Mask) {
None => metadata.mode().bits(),
Some(mask_mode) => (metadata.mode().bits() & 0o707) | ((mask_mode.bits() as u32) << 3),
};
Ok(FileAttrWithoutInode {
attrs: FileAttr {
ino: INodeNo(0),
size,
blocks: size / u64::from(BLOCK_SIZE),
atime: metadata.accessed(),
mtime: metadata.modified(),
ctime: metadata.changed(),
crtime: metadata.created().unwrap_or_else(SystemTime::now),
kind: self.kind().discriminant().to_fuse_file_type(),
perm: mode as u16,
nlink: link_count,
uid: metadata.user().into(),
gid: metadata.group().into(),
rdev: match self.kind() {
FileKind::Block { dev } => stat::makedev(dev.major(), dev.minor()) as u32,
FileKind::Char { dev } => stat::makedev(dev.major(), dev.minor()) as u32,
_ => NON_SPECIAL_RDEV,
},
blksize: BLOCK_SIZE,
flags: 0,
},
})
}
pub(super) fn constrain_permissions(
&mut self,
parent_default_acl: &Acl,
mode: Option<FileMode>,
) -> crate::Result<()> {
let mut access_acl = parent_default_acl.clone();
if matches!(self.kind().discriminant(), FileDiscriminant::Dir) {
self.set_default_acl(parent_default_acl)?;
}
if let Some(mode) = mode {
constrain_acl(&mut access_acl, mode);
self.set_mode(constrain_mode(&access_acl, mode))?;
};
self.set_access_acl(&access_acl)?;
Ok(())
}
}