use std::time::{Duration, SystemTime};
use fuser::FileAttr as FuseFileAttr;
pub use fuser::RequestId;
use fuser::{FileType, Request, TimeOrNow, INodeNo, BsdFileFlags};
use super::LockType;
pub use std::io::SeekFrom;
pub fn seek_from_raw(whence: Option<i32>, offset: i64) -> SeekFrom {
match whence {
Some(libc::SEEK_SET) => SeekFrom::Start(
offset
.try_into()
.expect("Invalid negative seek offset for file start"),
),
Some(libc::SEEK_CUR) => SeekFrom::Current(offset),
Some(libc::SEEK_END) => SeekFrom::End(offset),
Some(_) => panic!("Invalid seek code"),
None => SeekFrom::Start(
offset
.try_into()
.expect("Invalid negative seek offset for file start"),
),
}
}
#[derive(Debug, Clone, Copy)]
pub enum DeviceType {
RegularFile,
Directory,
CharacterDevice { major: u32, minor: u32 },
BlockDevice { major: u32, minor: u32 },
NamedPipe,
Socket,
Symlink,
Unknown,
}
impl DeviceType {
pub fn from_file_type_and_rdev(file_type: FileType, rdev: libc::dev_t) -> Self {
let major = libc::major(rdev) as u32;
let minor = libc::minor(rdev) as u32;
match file_type {
FileType::RegularFile => DeviceType::RegularFile,
FileType::Directory => DeviceType::Directory,
FileType::CharDevice => DeviceType::CharacterDevice { major, minor },
FileType::BlockDevice => DeviceType::BlockDevice { major, minor },
FileType::NamedPipe => DeviceType::NamedPipe,
FileType::Socket => DeviceType::Socket,
FileType::Symlink => DeviceType::Symlink,
}
}
pub fn to_rdev(&self) -> libc::dev_t {
match self {
DeviceType::RegularFile => 0,
DeviceType::Directory => 0,
DeviceType::CharacterDevice { major, minor } => libc::makedev(*major as _, *minor as _),
DeviceType::BlockDevice { major, minor } => libc::makedev(*major as _, *minor as _),
DeviceType::NamedPipe => 0,
DeviceType::Socket => 0,
DeviceType::Symlink => 0,
DeviceType::Unknown => 0, }
}
}
#[cfg(test)]
mod device_type_tests {
use super::*;
#[test]
fn device_numbers_round_trip() {
let rdev = libc::makedev(0x12, 0x3456);
let character = DeviceType::from_file_type_and_rdev(FileType::CharDevice, rdev);
assert!(matches!(character, DeviceType::CharacterDevice { major: 0x12, minor: 0x3456 }));
assert_eq!(character.to_rdev(), rdev);
let block = DeviceType::from_file_type_and_rdev(FileType::BlockDevice, rdev);
assert!(matches!(block, DeviceType::BlockDevice { major: 0x12, minor: 0x3456 }));
assert_eq!(block.to_rdev(), rdev);
}
#[test]
fn non_device_uses_file_kind_and_has_no_rdev() {
let kind = DeviceType::from_file_type_and_rdev(FileType::NamedPipe, 0);
assert!(matches!(kind, DeviceType::NamedPipe));
assert_eq!(kind.to_rdev(), 0);
}
}
#[derive(Debug, Clone)]
pub struct StatFs {
pub total_blocks: u64,
pub free_blocks: u64,
pub available_blocks: u64,
pub total_files: u64,
pub free_files: u64,
pub block_size: u32,
pub max_filename_length: u32,
pub fragment_size: u32,
}
impl StatFs {
pub fn default() -> Self {
StatFs {
total_blocks: u64::MAX,
free_blocks: u64::MAX,
available_blocks: u64::MAX,
total_files: u64::MAX / 2,
free_files: u64::MAX / 2,
block_size: 4096,
max_filename_length: 255,
fragment_size: 4096,
}
}
}
#[derive(Debug, Clone)]
pub struct RequestInfo {
pub id: RequestId,
pub uid: u32,
pub gid: u32,
pub pid: u32,
}
impl From<&Request> for RequestInfo {
fn from(req: &Request) -> Self {
Self {
id: req.unique(),
uid: req.uid(),
gid: req.gid(),
pid: req.pid(),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileAttribute {
pub size: u64,
pub blocks: u64,
pub atime: SystemTime,
pub mtime: SystemTime,
pub ctime: SystemTime,
pub crtime: SystemTime,
pub kind: FileType,
pub perm: u16,
pub nlink: u32,
pub uid: u32,
pub gid: u32,
pub rdev: u32,
pub blksize: u32,
pub flags: u32,
pub ttl: Option<Duration>,
pub generation: Option<u64>,
}
impl FileAttribute {
pub(crate) fn to_fuse(&self, ino: INodeNo) -> (FuseFileAttr, Option<Duration>, Option<u64>) {
(
FuseFileAttr {
ino,
size: self.size,
blocks: self.blocks,
atime: self.atime,
mtime: self.mtime,
ctime: self.ctime,
crtime: self.crtime,
kind: self.kind,
perm: self.perm,
nlink: self.nlink,
uid: self.uid,
gid: self.gid,
rdev: self.rdev,
blksize: self.blksize,
flags: self.flags,
},
self.ttl,
self.generation,
)
}
}
#[derive(Debug)]
pub struct SetAttrRequest {
pub mode: Option<u32>,
pub uid: Option<u32>,
pub gid: Option<u32>,
pub size: Option<u64>,
pub atime: Option<TimeOrNow>,
pub mtime: Option<TimeOrNow>,
pub ctime: Option<SystemTime>,
pub crtime: Option<SystemTime>,
pub chgtime: Option<SystemTime>,
pub bkuptime: Option<SystemTime>,
pub flags: Option<BsdFileFlags>,
}
impl Default for SetAttrRequest {
fn default() -> Self {
Self::new()
}
}
impl SetAttrRequest {
pub fn new() -> Self {
Self {
mode: None,
uid: None,
gid: None,
size: None,
atime: None,
mtime: None,
ctime: None,
crtime: None,
chgtime: None,
bkuptime: None,
flags: None,
}
}
pub fn mode(mut self, mode: u32) -> Self {
self.mode = Some(mode);
self
}
pub fn uid(mut self, uid: u32) -> Self {
self.uid = Some(uid);
self
}
pub fn gid(mut self, gid: u32) -> Self {
self.gid = Some(gid);
self
}
pub fn size(mut self, size: u64) -> Self {
self.size = Some(size);
self
}
pub fn atime(mut self, atime: TimeOrNow) -> Self {
self.atime = Some(atime);
self
}
pub fn mtime(mut self, mtime: TimeOrNow) -> Self {
self.mtime = Some(mtime);
self
}
pub fn ctime(mut self, ctime: SystemTime) -> Self {
self.ctime = Some(ctime);
self
}
pub fn crtime(mut self, crtime: SystemTime) -> Self {
self.crtime = Some(crtime);
self
}
pub fn chgtime(mut self, chgtime: SystemTime) -> Self {
self.chgtime = Some(chgtime);
self
}
pub fn bkuptime(mut self, bkuptime: SystemTime) -> Self {
self.bkuptime = Some(bkuptime);
self
}
pub fn flags(mut self, flags: BsdFileFlags) -> Self {
self.flags = Some(flags);
self
}
}
#[derive(Debug)]
pub struct LockInfo {
pub start: u64,
pub end: u64,
pub lock_type: LockType,
pub pid: u32,
}