use crate::file::VirtioFsFile;
use crate::util;
use fuse::protocol::*;
use lx::LxStr;
use lx::LxString;
use lxutil::LxCreateOptions;
use lxutil::LxVolume;
use lxutil::PathBufExt;
use parking_lot::RwLock;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
pub struct VirtioFsInode {
volume: Arc<LxVolume>,
path: RwLock<PathBuf>,
lookup_count: AtomicU64,
inode_nr: lx::ino_t,
}
impl VirtioFsInode {
pub fn new(volume: Arc<LxVolume>, path: PathBuf) -> lx::Result<(Self, lx::Stat)> {
let stat = volume.lstat(&path)?;
let inode = Self::with_attr(volume, path, &stat);
Ok((inode, stat))
}
pub fn with_attr(volume: Arc<LxVolume>, path: PathBuf, stat: &lx::Stat) -> Self {
Self {
volume,
path: RwLock::new(path),
lookup_count: AtomicU64::new(1),
inode_nr: stat.inode_nr,
}
}
pub fn inode_nr(&self) -> lx::ino_t {
self.inode_nr
}
pub fn lookup(&self, new_path: PathBuf) {
self.lookup_count.fetch_add(1, Ordering::AcqRel);
let mut path = self.path.write();
*path = new_path;
}
pub fn forget(&self, node_id: u64, lookup_count: u64) -> u64 {
let mut old_count = self.lookup_count.load(Ordering::Acquire);
loop {
let new_count = if lookup_count > old_count {
tracing::warn!(node_id, "Too many forgets for inode");
0
} else {
old_count - lookup_count
};
match self.lookup_count.compare_exchange_weak(
old_count,
new_count,
Ordering::AcqRel,
Ordering::Relaxed,
) {
Ok(_) => break new_count,
Err(value) => old_count = value,
}
}
}
pub fn lookup_child(&self, name: &LxStr) -> lx::Result<(VirtioFsInode, fuse_attr)> {
let path = self.child_path(name)?;
let (inode, stat) = VirtioFsInode::new(Arc::clone(&self.volume), path)?;
let attr = util::stat_to_fuse_attr(&stat);
Ok((inode, attr))
}
pub fn get_attr(&self) -> lx::Result<fuse_attr> {
let stat = self.volume.lstat(&*self.get_path())?;
Ok(util::stat_to_fuse_attr(&stat))
}
pub fn set_attr(&self, arg: &fuse_setattr_in, request_uid: lx::uid_t) -> lx::Result<fuse_attr> {
let attr = util::fuse_set_attr_to_lxutil(arg, request_uid);
let stat = self.volume.set_attr_stat(&*self.get_path(), attr)?;
Ok(util::stat_to_fuse_attr(&stat))
}
pub fn open(self: Arc<VirtioFsInode>, flags: u32) -> lx::Result<VirtioFsFile> {
let flags = (flags as i32) | lx::O_NOFOLLOW;
let file = self.volume.open(&*self.get_path(), flags, None)?;
Ok(VirtioFsFile::new(file, self))
}
pub fn create(
&self,
name: &LxStr,
flags: u32,
mode: u32,
uid: u32,
gid: u32,
) -> lx::Result<(VirtioFsInode, fuse_attr, lxutil::LxFile)> {
let path = self.child_path(name)?;
let options = LxCreateOptions::new(mode, uid, gid);
let flags = (flags as i32) | lx::O_CREAT | lx::O_NOFOLLOW;
let file = self.volume.open(&path, flags, Some(options))?;
let stat = file.fstat()?;
let inode = Self::with_attr(Arc::clone(&self.volume), path, &stat);
let attr = util::stat_to_fuse_attr(&stat);
Ok((inode, attr, file))
}
pub fn mkdir(
&self,
name: &LxStr,
mode: u32,
uid: u32,
gid: u32,
) -> lx::Result<(VirtioFsInode, fuse_attr)> {
let path = self.child_path(name)?;
let stat = self
.volume
.mkdir_stat(&path, LxCreateOptions::new(mode, uid, gid))?;
let inode = Self::with_attr(Arc::clone(&self.volume), path, &stat);
let attr = util::stat_to_fuse_attr(&stat);
Ok((inode, attr))
}
pub fn mknod(
&self,
name: &LxStr,
mode: u32,
uid: u32,
gid: u32,
device_id: u32,
) -> lx::Result<(VirtioFsInode, fuse_attr)> {
let path = self.child_path(name)?;
let stat = self.volume.mknod_stat(
&path,
LxCreateOptions::new(mode, uid, gid),
device_id as usize,
)?;
let inode = Self::with_attr(Arc::clone(&self.volume), path, &stat);
let attr = util::stat_to_fuse_attr(&stat);
Ok((inode, attr))
}
pub fn symlink(
&self,
name: &LxStr,
target: &LxStr,
uid: u32,
gid: u32,
) -> lx::Result<(VirtioFsInode, fuse_attr)> {
let path = self.child_path(name)?;
let stat = self.volume.symlink_stat(
&path,
target,
LxCreateOptions::new(lx::S_IFLNK | 0o777, uid, gid),
)?;
let inode = Self::with_attr(Arc::clone(&self.volume), path, &stat);
let attr = util::stat_to_fuse_attr(&stat);
Ok((inode, attr))
}
pub fn link(&self, name: &LxStr, target: &VirtioFsInode) -> lx::Result<fuse_attr> {
let path = self.child_path(name)?;
let stat = self.volume.link_stat(&*target.get_path(), path)?;
Ok(util::stat_to_fuse_attr(&stat))
}
pub fn read_link(&self) -> lx::Result<LxString> {
self.volume.read_link(&*self.get_path())
}
pub fn unlink(&self, name: &LxStr, flags: i32) -> lx::Result<()> {
let path = self.child_path(name)?;
self.volume.unlink(path, flags)
}
pub fn rename(
&self,
name: &LxStr,
new_dir: &VirtioFsInode,
new_name: &LxStr,
flags: u32,
) -> lx::Result<()> {
let path = self.child_path(name)?;
let new_path = new_dir.child_path(new_name)?;
self.volume.rename(path, new_path, flags)
}
pub fn stat_fs(&self) -> lx::Result<fuse_kstatfs> {
let stat_fs = self.volume.stat_fs(&*self.get_path())?;
Ok(fuse_kstatfs::new(
stat_fs.block_count,
stat_fs.free_block_count,
stat_fs.available_block_count,
stat_fs.file_count,
stat_fs.available_file_count,
stat_fs.block_size as u32,
stat_fs.maximum_file_name_length as u32,
stat_fs.file_record_size as u32,
))
}
pub fn get_xattr(&self, name: &LxStr, value: Option<&mut [u8]>) -> lx::Result<usize> {
self.volume.get_xattr(&*self.get_path(), name, value)
}
pub fn set_xattr(&self, name: &LxStr, value: &[u8], flags: u32) -> lx::Result<()> {
self.volume
.set_xattr(&*self.get_path(), name, value, flags as i32)
}
pub fn list_xattr(&self, list: Option<&mut [u8]>) -> lx::Result<usize> {
self.volume.list_xattr(&*self.get_path(), list)
}
pub fn remove_xattr(&self, name: &LxStr) -> lx::Result<()> {
self.volume.remove_xattr(&*self.get_path(), name)
}
pub fn clone_path(&self) -> PathBuf {
self.get_path().clone()
}
fn child_path(&self, name: &LxStr) -> lx::Result<PathBuf> {
let mut path = self.clone_path();
path.push_lx(name)?;
Ok(path)
}
fn get_path(&self) -> parking_lot::RwLockReadGuard<'_, PathBuf> {
self.path.read()
}
}