use std::collections::{btree_map, BTreeMap};
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io;
use std::mem::MaybeUninit;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::atomic::{AtomicU64, AtomicU8, Ordering};
use std::sync::Mutex;
use super::inode_store::InodeId;
use super::MAX_HOST_INO;
use crate::abi::fuse_abi as fuse;
use crate::api::EMPTY_CSTR;
const VIRTUAL_INODE_FLAG: u64 = 1 << 55;
#[derive(Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, Debug)]
struct DevMntIDPair(libc::dev_t, u64);
pub struct UniqueInodeGenerator {
dev_mntid_map: Mutex<BTreeMap<DevMntIDPair, u8>>,
next_unique_id: AtomicU8,
next_virtual_inode: AtomicU64,
}
impl UniqueInodeGenerator {
pub fn new() -> Self {
UniqueInodeGenerator {
dev_mntid_map: Mutex::new(Default::default()),
next_unique_id: AtomicU8::new(1),
next_virtual_inode: AtomicU64::new(fuse::ROOT_ID + 1),
}
}
pub fn get_unique_inode(&self, id: &InodeId) -> io::Result<libc::ino64_t> {
let unique_id = {
let id: DevMntIDPair = DevMntIDPair(id.dev, id.mnt);
let mut id_map_guard = self.dev_mntid_map.lock().unwrap();
match id_map_guard.entry(id) {
btree_map::Entry::Occupied(v) => *v.get(),
btree_map::Entry::Vacant(v) => {
if self.next_unique_id.load(Ordering::Relaxed) == u8::MAX {
return Err(io::Error::new(
io::ErrorKind::Other,
"the number of combinations of dev and mntid exceeds 255",
));
}
let next_id = self.next_unique_id.fetch_add(1, Ordering::Relaxed);
v.insert(next_id);
next_id
}
}
};
let inode = if id.ino <= MAX_HOST_INO {
id.ino
} else {
if self.next_virtual_inode.load(Ordering::Relaxed) > MAX_HOST_INO {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("the virtual inode excess {}", MAX_HOST_INO),
));
}
self.next_virtual_inode.fetch_add(1, Ordering::Relaxed) | VIRTUAL_INODE_FLAG
};
Ok((unique_id as u64) << 47 | inode)
}
#[cfg(test)]
fn decode_unique_inode(&self, inode: libc::ino64_t) -> io::Result<InodeId> {
if inode > crate::api::VFS_MAX_INO {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("the inode {} excess {}", inode, crate::api::VFS_MAX_INO),
));
}
let dev_mntid = (inode >> 47) as u8;
if dev_mntid == u8::MAX {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("invalid dev and mntid {} excess 255", dev_mntid),
));
}
let mut dev: libc::dev_t = 0;
let mut mnt: u64 = 0;
let mut found = false;
let id_map_guard = self.dev_mntid_map.lock().unwrap();
for (k, v) in id_map_guard.iter() {
if *v == dev_mntid {
found = true;
dev = k.0;
mnt = k.1;
break;
}
}
if !found {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"invalid dev and mntid {},there is no record in memory ",
dev_mntid
),
));
}
Ok(InodeId {
ino: inode & MAX_HOST_INO,
dev,
mnt,
})
}
}
pub fn openat(
dir_fd: &impl AsRawFd,
path: &CStr,
flags: libc::c_int,
mode: u32,
) -> io::Result<File> {
let fd = if flags & libc::O_CREAT == libc::O_CREAT {
unsafe { libc::openat(dir_fd.as_raw_fd(), path.as_ptr(), flags, mode) }
} else {
unsafe { libc::openat(dir_fd.as_raw_fd(), path.as_ptr(), flags) }
};
if fd >= 0 {
Ok(unsafe { File::from_raw_fd(fd) })
} else {
Err(io::Error::last_os_error())
}
}
pub fn reopen_fd_through_proc(
fd: &impl AsRawFd,
flags: libc::c_int,
proc_self_fd: &impl AsRawFd,
) -> io::Result<File> {
let name = CString::new(format!("{}", fd.as_raw_fd()).as_str())?;
openat(
proc_self_fd,
&name,
flags & !libc::O_NOFOLLOW & !libc::O_CREAT,
0,
)
}
pub fn stat_fd(dir: &impl AsRawFd, path: Option<&CStr>) -> io::Result<libc::stat64> {
let pathname =
path.unwrap_or_else(|| unsafe { CStr::from_bytes_with_nul_unchecked(EMPTY_CSTR) });
let mut st = MaybeUninit::<libc::stat64>::zeroed();
let res = unsafe {
libc::fstatat64(
dir.as_raw_fd(),
pathname.as_ptr(),
st.as_mut_ptr(),
libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
)
};
if res >= 0 {
Ok(unsafe { st.assume_init() })
} else {
Err(io::Error::last_os_error())
}
}
pub fn is_safe_inode(mode: u32) -> bool {
matches!(mode & libc::S_IFMT, libc::S_IFREG | libc::S_IFDIR)
}
pub fn is_dir(mode: u32) -> bool {
(mode & libc::S_IFMT) == libc::S_IFDIR
}
pub fn ebadf() -> io::Error {
io::Error::from_raw_os_error(libc::EBADF)
}
pub fn einval() -> io::Error {
io::Error::from_raw_os_error(libc::EINVAL)
}
pub fn enosys() -> io::Error {
io::Error::from_raw_os_error(libc::ENOSYS)
}
pub fn eperm() -> io::Error {
io::Error::from_raw_os_error(libc::EPERM)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_safe_inode() {
let mode = libc::S_IFREG;
assert!(is_safe_inode(mode));
let mode = libc::S_IFDIR;
assert!(is_safe_inode(mode));
let mode = libc::S_IFBLK;
assert!(!is_safe_inode(mode));
let mode = libc::S_IFCHR;
assert!(!is_safe_inode(mode));
let mode = libc::S_IFIFO;
assert!(!is_safe_inode(mode));
let mode = libc::S_IFLNK;
assert!(!is_safe_inode(mode));
let mode = libc::S_IFSOCK;
assert!(!is_safe_inode(mode));
}
#[test]
fn test_is_dir() {
let mode = libc::S_IFREG;
assert!(!is_dir(mode));
let mode = libc::S_IFDIR;
assert!(is_dir(mode));
}
#[test]
fn test_generate_unique_inode() {
{
let generator = UniqueInodeGenerator::new();
let inode_alt_key = InodeId {
ino: 1,
dev: 0,
mnt: 0,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x00800000000001);
let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
assert_eq!(expect_inode_alt_key, inode_alt_key);
let inode_alt_key = InodeId {
ino: 1,
dev: 0,
mnt: 1,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x01000000000001);
let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
assert_eq!(expect_inode_alt_key, inode_alt_key);
let inode_alt_key = InodeId {
ino: 2,
dev: 0,
mnt: 1,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x01000000000002);
let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
assert_eq!(expect_inode_alt_key, inode_alt_key);
let inode_alt_key = InodeId {
ino: MAX_HOST_INO,
dev: 0,
mnt: 1,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x017fffffffffff);
let expect_inode_alt_key = generator.decode_unique_inode(unique_inode).unwrap();
assert_eq!(expect_inode_alt_key, inode_alt_key);
}
{
let generator = UniqueInodeGenerator::new();
let inode_alt_key = InodeId {
ino: MAX_HOST_INO + 1,
dev: u64::MAX,
mnt: u64::MAX,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x80800000000002);
let inode_alt_key = InodeId {
ino: MAX_HOST_INO + 2,
dev: u64::MAX,
mnt: u64::MAX,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x80800000000003);
let inode_alt_key = InodeId {
ino: MAX_HOST_INO + 3,
dev: u64::MAX,
mnt: 0,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x81000000000004);
let inode_alt_key = InodeId {
ino: u64::MAX,
dev: u64::MAX,
mnt: u64::MAX,
};
let unique_inode = generator.get_unique_inode(&inode_alt_key).unwrap();
assert_eq!(unique_inode, 0x80800000000005);
}
}
#[test]
fn test_stat_fd() {
let topdir = env!("CARGO_MANIFEST_DIR");
let dir = File::open(topdir).unwrap();
let filename = CString::new("build.rs").unwrap();
let st1 = stat_fd(&dir, None).unwrap();
let st2 = stat_fd(&dir, Some(&filename)).unwrap();
assert_eq!(st1.st_dev, st2.st_dev);
assert_ne!(st1.st_ino, st2.st_ino);
}
}