302 lines
9.7 KiB
Rust
302 lines
9.7 KiB
Rust
use std::ffi::{CString,OsString};
|
|
use std::fs::{self, File, Metadata, OpenOptions};
|
|
use std::io;
|
|
use std::mem;
|
|
use std::os::unix;
|
|
use std::os::unix::ffi::OsStrExt;
|
|
use std::os::unix::fs::{DirBuilderExt,OpenOptionsExt,PermissionsExt};
|
|
use std::os::linux::fs::MetadataExt;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
use libc;
|
|
use crate::devices::virtio_9p::file::{
|
|
P9File, P9_DOTL_RDONLY, P9_DOTL_RDWR, P9_DOTL_WRONLY, translate_p9_flags, Qid
|
|
};
|
|
use crate::devices::virtio_9p::pdu::PduParser;
|
|
use crate::devices::virtio_9p::directory::{Directory, P9DirEntry};
|
|
|
|
|
|
pub enum FsTouch {
|
|
Atime,
|
|
AtimeNow,
|
|
Mtime,
|
|
MtimeNow,
|
|
}
|
|
|
|
pub trait FileSystemOps: Clone+Sync+Send {
|
|
fn read_qid(&self, path: &Path) -> io::Result<Qid>;
|
|
fn write_stat(&self, path: &Path, pp: &mut PduParser) -> io::Result<()>;
|
|
fn open(&self, path: &Path, flags: u32) -> io::Result<P9File>;
|
|
fn create(&self, path: &Path, flags: u32, mode: u32) -> io::Result<P9File>;
|
|
fn write_statfs(&self, path: &Path, pp: &mut PduParser) -> io::Result<()>;
|
|
fn chown(&self, path: &Path, uid: u32, gid: u32) -> io::Result<()>;
|
|
fn set_mode(&self, path: &Path, mode: u32) -> io::Result<()>;
|
|
fn touch(&self, path: &Path, which: FsTouch, tv: (u64, u64)) -> io::Result<()>;
|
|
fn truncate(&self, path: &Path, size: u64) -> io::Result<()>;
|
|
fn readlink(&self, path: &Path) -> io::Result<OsString>;
|
|
fn symlink(&self, target: &Path, linkpath: &Path) -> io::Result<()>;
|
|
fn link(&self, target: &Path, newpath: &Path) -> io::Result<()>;
|
|
fn rename(&self, from: &Path, to: &Path) -> io::Result<()>;
|
|
fn remove_file(&self, path: &Path) -> io::Result<()>;
|
|
fn remove_dir(&self, path: &Path) -> io::Result<()>;
|
|
fn create_dir(&self, path: &Path, mode: u32) -> io::Result<()>;
|
|
fn readdir_populate(&self, path: &Path) -> io::Result<Directory>;
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct FileSystem {
|
|
root: PathBuf,
|
|
readonly: bool,
|
|
euid_root: bool,
|
|
}
|
|
|
|
impl FileSystem {
|
|
pub fn new(root: PathBuf, readonly: bool) -> FileSystem {
|
|
let euid_root = Self::is_euid_root();
|
|
FileSystem { root, readonly, euid_root }
|
|
}
|
|
|
|
pub fn is_euid_root() -> bool {
|
|
unsafe { libc::geteuid() == 0 }
|
|
}
|
|
|
|
pub fn create_with_flags(path: &Path, flags: u32, mode: u32, is_root: bool) -> io::Result<File> {
|
|
let rdwr = flags & libc::O_ACCMODE as u32;
|
|
let flags = translate_p9_flags(flags, is_root) &!libc::O_TRUNC;
|
|
OpenOptions::new()
|
|
.read(rdwr == P9_DOTL_RDONLY || rdwr == P9_DOTL_RDWR)
|
|
.write(rdwr == P9_DOTL_WRONLY || rdwr == P9_DOTL_RDWR)
|
|
.custom_flags(flags)
|
|
.create_new(true)
|
|
.mode(mode)
|
|
.open(path)
|
|
}
|
|
|
|
pub fn open_with_flags(path: &Path, flags: u32, is_root: bool) -> io::Result<File> {
|
|
let rdwr = flags & libc::O_ACCMODE as u32;
|
|
let flags = translate_p9_flags(flags, is_root);
|
|
|
|
OpenOptions::new()
|
|
.read(rdwr == P9_DOTL_RDONLY || rdwr == P9_DOTL_RDWR)
|
|
.write(rdwr == P9_DOTL_WRONLY || rdwr == P9_DOTL_RDWR)
|
|
.custom_flags(flags)
|
|
.open(path)
|
|
}
|
|
|
|
fn new_file(&self, file: File) -> P9File {
|
|
P9File::from_file(file)
|
|
}
|
|
|
|
fn metadata(&self, path: &Path) -> io::Result<Metadata> {
|
|
let path = self.canonicalize(path)?;
|
|
path.symlink_metadata()
|
|
}
|
|
|
|
fn canonicalize_parent(&self, path: &Path) -> io::Result<PathBuf> {
|
|
let parent = path.parent()
|
|
.ok_or(io::Error::from_raw_os_error(libc::ENOENT))?;
|
|
let parent = self.canonicalize(parent)?;
|
|
let filename = path.file_name()
|
|
.ok_or(io::Error::from_raw_os_error(libc::ENOENT))?;
|
|
Ok(parent.join(filename))
|
|
}
|
|
|
|
fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
|
|
let canon = path.canonicalize()?;
|
|
if !canon.starts_with(&self.root) {
|
|
return Err(io::Error::from_raw_os_error(libc::EIO))
|
|
}
|
|
Ok(canon)
|
|
}
|
|
}
|
|
|
|
fn cstr(path: &Path) -> io::Result<CString> {
|
|
Ok(CString::new(path.as_os_str().as_bytes())?)
|
|
}
|
|
|
|
impl FileSystemOps for FileSystem {
|
|
fn read_qid(&self, path: &Path) -> io::Result<Qid> {
|
|
let meta = self.metadata(&path)?;
|
|
let qid = Qid::from_metadata(&meta);
|
|
Ok(qid)
|
|
}
|
|
|
|
fn write_stat(&self, path: &Path, pp: &mut PduParser) -> io::Result<()> {
|
|
let meta = self.metadata(path)?;
|
|
|
|
const P9_STATS_BASIC: u64 = 0x000007ff;
|
|
pp.w64(P9_STATS_BASIC)?;
|
|
|
|
let qid = Qid::from_metadata(&meta);
|
|
qid.write(pp)?;
|
|
|
|
pp.w32(meta.st_mode())?;
|
|
pp.w32(meta.st_uid())?;
|
|
pp.w32(meta.st_gid())?;
|
|
pp.w64(meta.st_nlink())?;
|
|
pp.w64(meta.st_rdev())?;
|
|
pp.w64(meta.st_size())?;
|
|
pp.w64(meta.st_blksize())?;
|
|
pp.w64(meta.st_blocks())?;
|
|
pp.w64(meta.st_atime() as u64)?;
|
|
pp.w64(meta.st_atime_nsec() as u64)?;
|
|
pp.w64(meta.st_mtime() as u64)?;
|
|
pp.w64(meta.st_mtime_nsec() as u64)?;
|
|
pp.w64(meta.st_ctime() as u64)?;
|
|
pp.w64(meta.st_ctime_nsec() as u64)?;
|
|
pp.w64(0)?;
|
|
pp.w64(0)?;
|
|
pp.w64(0)?;
|
|
pp.w64(0)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn open(&self, path: &Path, flags: u32) -> io::Result<P9File> {
|
|
let path = self.canonicalize(path)?;
|
|
let file =FileSystem::open_with_flags(&path, flags, self.euid_root)?;
|
|
Ok(self.new_file(file))
|
|
}
|
|
|
|
fn create(&self, path: &Path, flags: u32, mode: u32) -> io::Result<P9File> {
|
|
let path = self.canonicalize_parent(path)?;
|
|
let file = FileSystem::create_with_flags(&path, flags, mode, self.euid_root)?;
|
|
Ok(self.new_file(file))
|
|
}
|
|
|
|
fn write_statfs(&self, path: &Path, pp: &mut PduParser) -> io::Result<()> {
|
|
let path = self.canonicalize(path)?;
|
|
let path_cstr = cstr(&path)?;
|
|
|
|
let mut statfs: libc::statfs64 = unsafe { mem::zeroed() };
|
|
unsafe {
|
|
let ret = libc::statfs64(path_cstr.as_ptr(), &mut statfs);
|
|
if ret < 0 {
|
|
return Err(io::Error::last_os_error());
|
|
}
|
|
}
|
|
pp.w32(statfs.f_type as u32)?;
|
|
pp.w32(statfs.f_bsize as u32)?;
|
|
pp.w64(statfs.f_blocks)?;
|
|
pp.w64(statfs.f_bfree)?;
|
|
pp.w64(statfs.f_bavail)?;
|
|
pp.w64(statfs.f_files)?;
|
|
pp.w64(statfs.f_ffree)?;
|
|
pp.w64(0)?;
|
|
pp.w32(statfs.f_namelen as u32)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn chown(&self, path: &Path, uid: u32, gid: u32) -> io::Result<()> {
|
|
let path = self.canonicalize(path)?;
|
|
let path_cstr = cstr(&path)?;
|
|
unsafe {
|
|
if libc::chown(path_cstr.as_ptr(), uid, gid) < 0 {
|
|
return Err(io::Error::last_os_error());
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn set_mode(&self, path: &Path, mode: u32) -> io::Result<()> {
|
|
let meta = self.metadata(path)?;
|
|
Ok(meta.permissions().set_mode(mode))
|
|
}
|
|
|
|
fn touch(&self, path: &Path, which: FsTouch, tv: (u64, u64)) -> io::Result<()> {
|
|
let path = self.canonicalize(path)?;
|
|
let path_cstr = cstr(&path)?;
|
|
|
|
let tval = libc::timespec {
|
|
tv_sec: tv.0 as i64,
|
|
tv_nsec: tv.1 as i64,
|
|
};
|
|
let omit = libc::timespec {
|
|
tv_sec: 0,
|
|
tv_nsec: libc::UTIME_OMIT,
|
|
};
|
|
let now = libc::timespec {
|
|
tv_sec: 0,
|
|
tv_nsec: libc::UTIME_NOW,
|
|
};
|
|
|
|
let times = match which {
|
|
FsTouch::Atime => [tval, omit],
|
|
FsTouch::AtimeNow => [ now, omit ],
|
|
FsTouch::Mtime => [omit, tval ],
|
|
FsTouch::MtimeNow => [omit, now],
|
|
};
|
|
unsafe {
|
|
if libc::utimensat(-1, path_cstr.as_ptr(), times.as_ptr(), 0) < 0 {
|
|
return Err(io::Error::last_os_error());
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn truncate(&self, path: &Path, size: u64) -> io::Result<()> {
|
|
let path = self.canonicalize(path)?;
|
|
let path_cstr = cstr(&path)?;
|
|
unsafe {
|
|
if libc::truncate64(path_cstr.as_ptr(), size as i64) < 0 {
|
|
return Err(io::Error::last_os_error());
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn readlink(&self, path: &Path) -> io::Result<OsString> {
|
|
let path = self.canonicalize(path)?;
|
|
fs::read_link(&path).map(|pbuf| pbuf.into_os_string())
|
|
}
|
|
|
|
fn symlink(&self, target: &Path, linkpath: &Path) -> io::Result<()> {
|
|
let linkpath = self.canonicalize(linkpath)?;
|
|
unix::fs::symlink(target, linkpath)
|
|
}
|
|
|
|
fn link(&self, target: &Path, newpath: &Path) -> io::Result<()> {
|
|
let target = self.canonicalize(target)?;
|
|
let newpath= self.canonicalize(newpath)?;
|
|
fs::hard_link(target, newpath)
|
|
}
|
|
|
|
fn rename(&self, from: &Path, to: &Path) -> io::Result<()> {
|
|
let from = self.canonicalize(from)?;
|
|
let to = self.canonicalize(to)?;
|
|
fs::rename(from, to)
|
|
}
|
|
|
|
fn remove_file(&self, path: &Path) -> io::Result<()> {
|
|
let path = self.canonicalize(path)?;
|
|
fs::remove_file(path)
|
|
}
|
|
|
|
fn remove_dir(&self, path: &Path) -> io::Result<()> {
|
|
let path = self.canonicalize(path)?;
|
|
fs::remove_dir(path)
|
|
}
|
|
|
|
fn create_dir(&self, path: &Path, mode: u32) -> io::Result<()> {
|
|
let path = self.canonicalize(path)?;
|
|
fs::DirBuilder::new()
|
|
.recursive(false)
|
|
.mode(mode & 0o755)
|
|
.create(path)
|
|
}
|
|
|
|
fn readdir_populate(&self, path: &Path) -> io::Result<Directory> {
|
|
let mut directory = Directory::new();
|
|
let mut offset = 0;
|
|
for dent in fs::read_dir(path)? {
|
|
let dent = dent?;
|
|
let p9entry = P9DirEntry::from_direntry(dent, offset)?;
|
|
offset = p9entry.offset();
|
|
directory.push_entry(p9entry);
|
|
}
|
|
Ok(directory)
|
|
}
|
|
}
|
|
|
|
|