Rewrite of 9p filesystem
Old version barely worked, but the new version works very well. Supports two backend types. Exporting a tree from the host filesystem is supported of course, and this is one backend. The second backend is a synthetic filesystem on which directories can be created and then files can be added to the directories. The files added to the filesystem are either backed by a file on the host filesystem or a buffer of bytes in memory.
This commit is contained in:
parent
1a587bb6cc
commit
98a1e9361e
@ -8,6 +8,7 @@ mod virtio_block;
|
||||
|
||||
pub use self::virtio_serial::VirtioSerial;
|
||||
pub use self::virtio_9p::VirtioP9;
|
||||
pub use self::virtio_9p::SyntheticFS;
|
||||
pub use self::virtio_rng::VirtioRandom;
|
||||
pub use self::virtio_wl::VirtioWayland;
|
||||
pub use self::virtio_block::VirtioBlock;
|
||||
|
@ -1,404 +0,0 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
|
||||
use libc;
|
||||
|
||||
use crate::memory::GuestRam;
|
||||
use super::pdu::{PduParser,P9Attr};
|
||||
use super::fid::FidCache;
|
||||
use super::filesystem::{FileSystem,FsTouch,FileSystemOps};
|
||||
|
||||
|
||||
|
||||
const P9_TSTATFS: u8 = 8;
|
||||
const P9_TLOPEN: u8 = 12;
|
||||
const P9_TLCREATE: u8 = 14;
|
||||
const P9_TSYMLINK: u8 = 16;
|
||||
//const P9_TMKNOD: u8 = 18;
|
||||
//const P9_TRENAME: u8 = 20;
|
||||
const P9_TREADLINK: u8 = 22;
|
||||
const P9_TGETATTR: u8 = 24;
|
||||
const P9_TSETATTR: u8 = 26;
|
||||
const P9_TXATTRWALK: u8 = 30;
|
||||
const P9_TXATTRCREATE: u8 = 32;
|
||||
const P9_TREADDIR: u8 = 40;
|
||||
const P9_TFSYNC: u8 = 50;
|
||||
const P9_TLOCK: u8 = 52;
|
||||
const P9_TGETLOCK: u8 = 54;
|
||||
//const P9_TLINK: u8 = 70;
|
||||
//const P9_TMKDIR: u8 = 72;
|
||||
//const P9_TRENAMEAT: u8 = 74;
|
||||
//const P9_TUNLINKAT: u8 = 76;
|
||||
const P9_TVERSION:u8 = 100;
|
||||
const P9_TATTACH :u8 = 104;
|
||||
//const P9_TFLUSH: u8 = 108;
|
||||
const P9_TWALK :u8 = 110;
|
||||
const P9_TREAD: u8 = 116;
|
||||
//const P9_TWRITE: u8 = 118;
|
||||
const P9_TCLUNK: u8 = 120;
|
||||
//const P9_REMOVE: u8 = 122;
|
||||
|
||||
const P9_LOCK_SUCCESS:u32 = 0;
|
||||
const F_UNLCK: u8 = 2;
|
||||
const P9_VERSION_DOTL:&str = "9P2000.L";
|
||||
|
||||
pub struct Commands {
|
||||
filesystem: FileSystem,
|
||||
fids: FidCache,
|
||||
root_dir: PathBuf,
|
||||
_memory: GuestRam,
|
||||
}
|
||||
|
||||
impl Commands {
|
||||
pub fn new(root_dir: PathBuf, init_path: PathBuf, memory: GuestRam) -> Commands {
|
||||
let fsys = FileSystem::new(root_dir.clone(), init_path,true);
|
||||
Commands {
|
||||
filesystem: fsys.clone(),
|
||||
fids: FidCache::new(fsys.clone()),
|
||||
root_dir, _memory: memory,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_io_result(&self, cmd: u8, result: io::Result<()>) {
|
||||
match result {
|
||||
Ok(()) => (),
|
||||
Err(e) => println!("io error in 9p command {} processing: {:?}",cmd, e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(&mut self, pp: &mut PduParser) {
|
||||
match pp.command() {
|
||||
Ok(cmd) => {
|
||||
let res = self.dispatch(cmd, pp);
|
||||
self.handle_io_result(cmd,res);
|
||||
},
|
||||
Err(e) => self.handle_io_result(0,Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch(&mut self, cmd: u8, pp: &mut PduParser) -> io::Result<()> {
|
||||
match cmd {
|
||||
P9_TSTATFS => self.p9_statfs(pp)?,
|
||||
P9_TLOPEN => self.p9_open(pp)?,
|
||||
P9_TLCREATE => self.p9_create(pp)?,
|
||||
P9_TSYMLINK => self.p9_symlink(pp)?,
|
||||
//P9_TMKNOD => self.p9_mknod(pp)?,
|
||||
//P9_TRENAME => self.p9_rename(pp)?,
|
||||
P9_TREADLINK => self.p9_readlink(pp)?,
|
||||
P9_TGETATTR => self.p9_getattr(pp)?,
|
||||
P9_TSETATTR => self.p9_setattr(pp)?,
|
||||
P9_TXATTRWALK => self.p9_unsupported(pp)?,
|
||||
P9_TXATTRCREATE => self.p9_unsupported(pp)?,
|
||||
P9_TREADDIR => self.p9_readdir(pp)?,
|
||||
P9_TFSYNC => self.p9_fsync(pp)?,
|
||||
P9_TLOCK => self.p9_lock(pp)?,
|
||||
P9_TGETLOCK => self.p9_getlock(pp)?,
|
||||
//P9_TLINK => self.p9_link(pp)?,
|
||||
//P9_TMKDIR=> self.p9_mkdir(pp)?,
|
||||
//P9_TRENAMEAT => self.p9_renameat(pp)?,
|
||||
//P9_UNLINKAT => self.p9_unlinkat(pp)?,
|
||||
P9_TVERSION => self.p9_version(pp)?,
|
||||
P9_TATTACH => self.p9_attach(pp)?,
|
||||
//P9_FLUSH => self.p9_flush(pp)?,
|
||||
P9_TWALK => self.p9_walk(pp)?,
|
||||
P9_TREAD => self.p9_read(pp)?,
|
||||
//P9_WRITE => self.p9_write(pp)?,
|
||||
P9_TCLUNK => self.p9_clunk(pp)?,
|
||||
//P9_REMOVE => self.p9_remove(pp)?,
|
||||
n => println!("unhandled 9p command: {}", n),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn p9_unsupported(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
pp.read_done()?;
|
||||
pp.bail_err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
|
||||
|
||||
}
|
||||
|
||||
fn p9_statfs(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
match self.fids.statfs(fid) {
|
||||
Ok(statfs) => {
|
||||
pp.write_statfs(statfs)?;
|
||||
pp.write_done()
|
||||
},
|
||||
Err(err) => pp.bail_err(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn p9_version(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let msize = pp.r32()?;
|
||||
let version = pp.read_string()?;
|
||||
pp.read_done()?;
|
||||
|
||||
pp.w32(msize)?;
|
||||
if version == P9_VERSION_DOTL {
|
||||
pp.write_string(&version)?;
|
||||
} else {
|
||||
pp.write_string("unknown")?;
|
||||
}
|
||||
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_attach(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid_val = pp.r32()?;
|
||||
let _afid = pp.r32()?;
|
||||
let _uname = pp.read_string()?;
|
||||
let _aname = pp.read_string()?;
|
||||
let uid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
|
||||
self.fids.with_fid_mut(fid_val, |fid| {
|
||||
fid.uid = uid;
|
||||
fid.path.push("/");
|
||||
});
|
||||
|
||||
match fs::metadata(&self.root_dir) {
|
||||
Ok(ref meta) => {
|
||||
pp.write_qid(meta)?;
|
||||
pp.write_done()
|
||||
}
|
||||
Err(e) => pp.bail_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn p9_open(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = pp.r32()?;
|
||||
let flags = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
|
||||
if let Err(err) = self.fids.open(fid, flags) {
|
||||
return pp.bail_err(err);
|
||||
}
|
||||
|
||||
let meta = match self.fids.metadata(fid) {
|
||||
Ok(meta) => meta,
|
||||
Err(err) => {
|
||||
return pp.bail_err(err);
|
||||
}
|
||||
};
|
||||
|
||||
pp.write_qid(&meta)?;
|
||||
// XXX iounit goes here
|
||||
pp.w32(0)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_create(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let dfid = pp.r32()?;
|
||||
let name = pp.read_string()?;
|
||||
let flags = pp.r32()?;
|
||||
let mode = pp.r32()?;
|
||||
let gid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
|
||||
match self.fids.create(dfid, name, flags, mode, gid) {
|
||||
Ok(meta) => {
|
||||
pp.write_statl(&meta)?;
|
||||
pp.write_done()?;
|
||||
|
||||
},
|
||||
Err(err) => return pp.bail_err(err),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn p9_symlink(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let _fid = pp.r32()?;
|
||||
let _name = pp.read_string()?;
|
||||
let _old_path = pp.read_string()?;
|
||||
let _gid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
// XXX
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_read(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let id = pp.r32()?;
|
||||
let off = pp.r64()?;
|
||||
let cnt = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
|
||||
// space for size field
|
||||
pp.w32(0)?;
|
||||
|
||||
match self.fids.fid_mut(id).read(off, cnt as usize, pp) {
|
||||
Ok(nread) => {
|
||||
// write nread in space reserved earlier
|
||||
pp.w32_at(0, nread as u32);
|
||||
pp.write_done()?;
|
||||
}
|
||||
Err(err) => {
|
||||
println!("oops error on read: {:?}", err);
|
||||
return pp.bail_err(err)
|
||||
},
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn p9_readdir(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let id = pp.r32()?;
|
||||
let off = pp.r64()?;
|
||||
let cnt = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
|
||||
self.fids.readdir(id,off, cnt as usize, pp)
|
||||
}
|
||||
|
||||
fn p9_clunk(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let id = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
self.fids.clunk(id);
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_readlink(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let id = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
let link = self.fids.readlink(id)?;
|
||||
pp.write_os_string(&link)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_getattr(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let id = pp.r32()?;
|
||||
let _mask = pp.r64()?;
|
||||
pp.read_done()?;
|
||||
|
||||
let meta = match self.fids.metadata(id) {
|
||||
Ok(meta) => meta,
|
||||
Err(e) => return pp.bail_err(e),
|
||||
};
|
||||
|
||||
pp.write_statl(&meta)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn do_setattr(&mut self, fid: u32, attr: P9Attr) -> io::Result<()> {
|
||||
if attr.has_mode() {
|
||||
self.fids.chmod(fid, attr.mode())?
|
||||
}
|
||||
if attr.has_atime() {
|
||||
if attr.has_atime_set() {
|
||||
self.fids.touch(fid, FsTouch::Atime,attr.atime())?
|
||||
} else {
|
||||
self.fids.touch(fid, FsTouch::AtimeNow,(0,0))?
|
||||
}
|
||||
}
|
||||
|
||||
if attr.has_mtime() {
|
||||
if attr.has_mtime_set() {
|
||||
self.fids.touch(fid, FsTouch::Mtime,attr.mtime())?
|
||||
} else {
|
||||
self.fids.touch(fid, FsTouch::MtimeNow,(0,0))?
|
||||
}
|
||||
}
|
||||
|
||||
if attr.has_chown() {
|
||||
let (uid, gid) = attr.chown_ids();
|
||||
self.fids.chown(fid, uid, gid)?;
|
||||
}
|
||||
|
||||
if attr.has_size() {
|
||||
self.fids.truncate(fid, attr.size())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn p9_setattr(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = pp.r32()?;
|
||||
let attr = pp.read_attr()?;
|
||||
pp.read_done()?;
|
||||
|
||||
if let Err(err) = self.do_setattr(fid, attr) {
|
||||
return pp.bail_err(err)
|
||||
}
|
||||
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
// XXX look at walk in qemu
|
||||
fn p9_walk(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid_id = pp.r32()?;
|
||||
let new_fid_id = pp.r32()?;
|
||||
let nwname = pp.r16()?;
|
||||
|
||||
self.fids.dup_fid(fid_id, new_fid_id);
|
||||
|
||||
let mut cur = self.fids.fid(new_fid_id).path.clone();
|
||||
let mut metalist = Vec::new();
|
||||
for _ in 0..nwname {
|
||||
let s = pp.read_string()?;
|
||||
let p = Path::new(&s);
|
||||
if p.components().count() != 1 {
|
||||
println!("uh...");
|
||||
}
|
||||
cur.push(p);
|
||||
match self.filesystem.stat(&cur) {
|
||||
Ok(m) => metalist.push(m),
|
||||
Err(e) => {
|
||||
pp.read_done()?;
|
||||
return pp.bail_err(e)
|
||||
},
|
||||
}
|
||||
}
|
||||
self.fids.with_fid_mut(new_fid_id, |fid| {
|
||||
fid.path = cur;
|
||||
});
|
||||
|
||||
pp.read_done()?;
|
||||
pp.w16(metalist.len() as u16)?;
|
||||
for meta in metalist {
|
||||
pp.write_qid(&meta)?;
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_fsync(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = pp.r32()?;
|
||||
let dsync = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
if let Err(err) = self.fids.fsync(fid, dsync != 0) {
|
||||
return pp.bail_err(err);
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_lock(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let _ = pp.r32()?;
|
||||
let _ = pp.r8()?;
|
||||
let _ = pp.r32()?;
|
||||
let _ = pp.r64()?;
|
||||
let _ = pp.r64()?;
|
||||
let _ = pp.r32()?;
|
||||
let _ = pp.read_string()?;
|
||||
pp.read_done()?;
|
||||
|
||||
pp.w32(P9_LOCK_SUCCESS)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_getlock(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let _fid = pp.r32()?;
|
||||
let _type = pp.r8()?;
|
||||
let glock_start = pp.r64()?;
|
||||
let glock_len = pp.r64()?;
|
||||
let glock_proc_id = pp.r32()?;
|
||||
let glock_client_id = pp.read_string()?;
|
||||
pp.read_done()?;
|
||||
|
||||
pp.w8(F_UNLCK)?;
|
||||
pp.w64(glock_start)?;
|
||||
pp.w64(glock_len)?;
|
||||
pp.w32(glock_proc_id)?;
|
||||
pp.write_string(&glock_client_id)?;
|
||||
pp.write_done()
|
||||
}
|
||||
}
|
95
rust/src/devices/virtio_9p/directory.rs
Normal file
95
rust/src/devices/virtio_9p/directory.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use std::{fs, io};
|
||||
|
||||
use crate::devices::virtio_9p::{
|
||||
pdu::PduParser, file::Qid,
|
||||
};
|
||||
|
||||
pub struct Directory {
|
||||
entries: Vec<P9DirEntry>,
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
|
||||
pub fn new() -> Directory {
|
||||
Directory { entries: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn write_entries(&self, pp: &mut PduParser, offset: u64, size: usize) -> io::Result<()> {
|
||||
let mut remaining = size;
|
||||
|
||||
pp.w32(0)?;
|
||||
for entry in self.entries.iter()
|
||||
.skip_while(|e| e.offset <= offset)
|
||||
{
|
||||
if entry.size() > remaining {
|
||||
break;
|
||||
}
|
||||
entry.write(pp)?;
|
||||
remaining -= entry.size();
|
||||
}
|
||||
pp.w32_at(0, (size - remaining) as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push_entry(&mut self, entry: P9DirEntry) {
|
||||
self.entries.push(entry)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct P9DirEntry{
|
||||
qid: Qid,
|
||||
offset: u64,
|
||||
dtype: u8,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl P9DirEntry {
|
||||
pub fn new(qid: Qid, offset: u64, dtype: u8, name: &str) -> Self {
|
||||
let name = name.to_string();
|
||||
let offset = offset + Self::size_with_name(&name) as u64;
|
||||
|
||||
P9DirEntry { qid, offset, dtype, name }
|
||||
}
|
||||
pub fn from_direntry(entry: fs::DirEntry, offset: u64) -> io::Result<Self> {
|
||||
let meta = entry.metadata()?;
|
||||
let qid = Qid::from_metadata(&meta);
|
||||
let dtype = if meta.is_dir() {
|
||||
libc::DT_DIR
|
||||
} else if meta.is_file() {
|
||||
libc::DT_REG
|
||||
} else {
|
||||
libc::DT_UNKNOWN
|
||||
};
|
||||
let name = match entry.file_name().into_string() {
|
||||
Ok(s) => s,
|
||||
_ => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
|
||||
};
|
||||
// qid + offset + dtype + strlen + name
|
||||
let offset = offset + Self::size_with_name(&name) as u64;
|
||||
Ok(P9DirEntry{
|
||||
qid, offset,
|
||||
dtype, name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> u64 {
|
||||
self.offset
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
Self::size_with_name(&self.name)
|
||||
}
|
||||
|
||||
fn size_with_name(name: &str) -> usize {
|
||||
// qid + offset + dtype + strlen + name
|
||||
13 + 8 + 1 + 2 + name.as_bytes().len()
|
||||
}
|
||||
|
||||
fn write(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
self.qid.write(pp)?;
|
||||
pp.w64(self.offset)?;
|
||||
pp.w8(self.dtype)?;
|
||||
pp.write_string(&self.name)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
use std::fs::Metadata;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::io::{self, Seek,Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ffi::OsString;
|
||||
|
||||
use libc;
|
||||
use super::pdu::PduParser;
|
||||
use super::readdir::DirEntry;
|
||||
use super::filesystem::{FileSystem,FileDescriptor,StatFs,FileSystemOps,FsTouch};
|
||||
|
||||
pub struct FidCache {
|
||||
filesystem: FileSystem,
|
||||
fidmap: HashMap<u32, Fid>,
|
||||
}
|
||||
|
||||
impl FidCache {
|
||||
pub fn new(filesystem: FileSystem) -> FidCache {
|
||||
FidCache {
|
||||
filesystem,
|
||||
fidmap: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_if_absent(&mut self, id: u32) {
|
||||
if !self.fidmap.contains_key(&id) {
|
||||
self.fidmap.insert(id, Fid::new());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fid(&mut self, id: u32) -> &Fid {
|
||||
self.add_if_absent(id);
|
||||
self.fidmap.get(&id).expect("fidmap does not have element")
|
||||
}
|
||||
|
||||
pub fn _fid(&self, id: u32) -> &Fid {
|
||||
self.fidmap.get(&id).expect("fidmap does not have element")
|
||||
}
|
||||
|
||||
pub fn fid_mut(&mut self, id: u32) -> &mut Fid {
|
||||
self.add_if_absent(id);
|
||||
self.fidmap.get_mut(&id).expect("fidmap does not have element")
|
||||
}
|
||||
|
||||
pub fn with_fid_mut<F,U>(&mut self, id: u32, f: F) -> U
|
||||
where F: FnOnce(&mut Fid) -> U {
|
||||
self.add_if_absent(id);
|
||||
f(self.fid_mut(id))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn with_fid<F,U>(&mut self, id: u32, f: F) -> U
|
||||
where F: FnOnce(&Fid) -> U {
|
||||
self.add_if_absent(id);
|
||||
f(self.fid(id))
|
||||
}
|
||||
|
||||
pub fn dup_fid(&mut self, old_id: u32, new_id: u32) {
|
||||
self.fid_mut(new_id).path = self.fid(old_id).path.clone();
|
||||
self.fid_mut(new_id).uid = self.fid(old_id).uid;
|
||||
}
|
||||
|
||||
pub fn clunk(&mut self, id: u32) {
|
||||
match self.fidmap.remove(&id) {
|
||||
Some(ref mut fid) => fid.close(),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(&mut self, id: u32, flags: u32) -> io::Result<()> {
|
||||
let path = self.fid(id).path.clone();
|
||||
let fd = self.filesystem.open(&path, flags)?;
|
||||
self.fid_mut(id).desc = fd;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fid_dir_join(&mut self, id: u32, name: &str) -> io::Result<PathBuf> {
|
||||
let meta = self.metadata(id)?;
|
||||
if !meta.is_dir() {
|
||||
return Err(io::Error::from_raw_os_error(libc::EBADF));
|
||||
}
|
||||
|
||||
let fname = PathBuf::from(name);
|
||||
if fname.is_absolute() || fname.components().count() != 1 {
|
||||
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
let mut path = self.fid(id).path.clone();
|
||||
path.push(fname);
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn create(&mut self, id: u32, name: String, flags: u32, mode: u32, gid: u32) -> io::Result<Metadata> {
|
||||
let path = self.fid_dir_join(id,&name)?;
|
||||
|
||||
self.filesystem.create(&path, flags, mode)?;
|
||||
|
||||
let uid = self.fid(id).uid;
|
||||
self.filesystem.chown(&path, uid, gid)?;
|
||||
|
||||
self.filesystem.stat(&path)
|
||||
}
|
||||
|
||||
pub fn readlink(&mut self, id: u32) -> io::Result<OsString> {
|
||||
let path = self.fid(id).path.clone();
|
||||
self.filesystem.readlink(&path)
|
||||
}
|
||||
|
||||
pub fn metadata(&mut self, id: u32) -> io::Result<Metadata> {
|
||||
let path = self.fid(id).path.clone();
|
||||
self.filesystem.stat(&path)
|
||||
}
|
||||
|
||||
pub fn readdir(&mut self, id: u32, off: u64, len: usize, pp: &mut PduParser) -> io::Result<()> {
|
||||
//let is_dir = self.fid(id).desc.is_dir();
|
||||
if off != 0 {
|
||||
//self.fid_mut(id).desc.borrow_dir().unwrap().seek(off as i64);
|
||||
}
|
||||
self.fid_mut(id).readdir(len, pp)
|
||||
}
|
||||
|
||||
pub fn chmod(&mut self, id: u32, mode: u32) -> io::Result<()> {
|
||||
let path = self.fid(id).path.clone();
|
||||
self.filesystem.chmod(&path, mode)
|
||||
}
|
||||
|
||||
pub fn chown(&mut self, id: u32, uid: u32, gid: u32) -> io::Result<()> {
|
||||
let path = self.fid(id).path.clone();
|
||||
self.filesystem.chown(&path, uid, gid)
|
||||
}
|
||||
|
||||
pub fn touch(&mut self, id: u32, which: FsTouch, tv: (u64,u64)) -> io::Result<()> {
|
||||
let path = self.fid(id).path.clone();
|
||||
self.filesystem.touch(&path, which, tv)
|
||||
}
|
||||
|
||||
pub fn truncate(&mut self, _id: u32, _size: u64) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn statfs(&mut self, fid: u32) -> io::Result<StatFs> {
|
||||
let path = self.fid(fid).path.clone();
|
||||
self.filesystem.statfs(&path)
|
||||
}
|
||||
|
||||
pub fn fsync(&mut self, fid: u32, datasync: bool) -> io::Result<()> {
|
||||
match self.fid(fid).desc {
|
||||
FileDescriptor::File(ref file) => {
|
||||
let fd = file.as_raw_fd();
|
||||
unsafe {
|
||||
let res = if datasync {
|
||||
libc::fdatasync(fd)
|
||||
} else {
|
||||
libc::fsync(fd)
|
||||
};
|
||||
if res < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
},
|
||||
FileDescriptor::Dir(ref dir) => { return dir.fsync(); },
|
||||
FileDescriptor::None => { return Err(io::Error::from_raw_os_error(libc::EBADF))},
|
||||
};
|
||||
Ok(())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Fid {
|
||||
pub uid: u32,
|
||||
pub path: PathBuf,
|
||||
desc: FileDescriptor,
|
||||
}
|
||||
|
||||
impl Fid {
|
||||
fn new() -> Fid {
|
||||
Fid {
|
||||
uid: 0, path: PathBuf::new(), desc: FileDescriptor::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, offset: u64, len: usize, pp: &mut PduParser) -> io::Result<(usize)> {
|
||||
self.desc.borrow_file()?.seek(io::SeekFrom::Start(offset))?;
|
||||
pp.chain.copy_from_reader(self.desc.borrow_file()?, len)
|
||||
}
|
||||
|
||||
fn dirent_len(dent: &DirEntry) -> usize {
|
||||
// qid + offset + type + strlen + str
|
||||
return 13 + 8 + 1 + 2 + dent.name_bytes().len()
|
||||
}
|
||||
|
||||
fn write_dirent(dent: &DirEntry, pp: &mut PduParser) -> io::Result<()> {
|
||||
pp.write_qid_path_only(dent.ino())?;
|
||||
pp.w64(dent.offset())?;
|
||||
pp.w8(dent.file_type())?;
|
||||
pp.w16(dent.name_bytes().len() as u16)?;
|
||||
pp.chain.write(&dent.name_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn readdir(&mut self, len: usize, pp: &mut PduParser) -> io::Result<()> {
|
||||
let mut write_len = 0_usize;
|
||||
pp.w32(0)?;
|
||||
|
||||
|
||||
while let Some(entry) = self.desc.borrow_dir()?.next() {
|
||||
match entry {
|
||||
Ok(ref dent) => {
|
||||
let dlen = Fid::dirent_len(dent);
|
||||
if write_len + dlen > len {
|
||||
self.desc.borrow_dir()?.restore_last_pos();
|
||||
break;
|
||||
}
|
||||
write_len += dlen;
|
||||
Fid::write_dirent(dent, pp)?;
|
||||
}
|
||||
Err(err) => return pp.bail_err(err),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pp.w32_at(0, write_len as u32);
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.desc = FileDescriptor::None;
|
||||
}
|
||||
}
|
||||
|
366
rust/src/devices/virtio_9p/file.rs
Normal file
366
rust/src/devices/virtio_9p/file.rs
Normal file
@ -0,0 +1,366 @@
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::BTreeMap;
|
||||
use std::{io, fmt};
|
||||
use std::path::{Path, PathBuf, Component};
|
||||
use std::fs::{Metadata, File};
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::os::unix::fs::FileExt;
|
||||
|
||||
use crate::devices::virtio_9p::{
|
||||
pdu::PduParser, directory::Directory, filesystem::FileSystemOps,
|
||||
};
|
||||
use std::io::{Cursor, SeekFrom, Seek, Read};
|
||||
use std::sync::{RwLock, Arc};
|
||||
|
||||
|
||||
pub const P9_DOTL_RDONLY: u32 = 0o00000000;
|
||||
pub const P9_DOTL_WRONLY: u32 = 0o00000001;
|
||||
pub const P9_DOTL_RDWR: u32 = 0o00000002;
|
||||
const _P9_DOTL_NOACCESS: u32 = 0o00000003;
|
||||
const P9_DOTL_CREATE: u32 = 0o00000100;
|
||||
const P9_DOTL_EXCL: u32 = 0o00000200;
|
||||
const P9_DOTL_NOCTTY: u32 = 0o00000400;
|
||||
const P9_DOTL_TRUNC: u32 = 0o00001000;
|
||||
const P9_DOTL_APPEND: u32 = 0o00002000;
|
||||
const P9_DOTL_NONBLOCK: u32 = 0o00004000;
|
||||
const P9_DOTL_DSYNC: u32 = 0o00010000;
|
||||
const P9_DOTL_FASYNC: u32 = 0o00020000;
|
||||
const P9_DOTL_DIRECT: u32 = 0o00040000;
|
||||
const P9_DOTL_LARGEFILE: u32 = 0o00100000;
|
||||
const P9_DOTL_DIRECTORY: u32 = 0o00200000;
|
||||
const P9_DOTL_NOFOLLOW: u32 = 0o00400000;
|
||||
const P9_DOTL_NOATIME: u32 = 0o01000000;
|
||||
const _P9_DOTL_CLOEXEC: u32 = 0o02000000;
|
||||
const P9_DOTL_SYNC: u32 = 0o04000000;
|
||||
|
||||
pub const P9_QTFILE: u8 = 0x00;
|
||||
pub const P9_QTSYMLINK: u8 = 0x02;
|
||||
pub const P9_QTDIR: u8 = 0x80;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Buffer<T: AsRef<[u8]>>(Arc<RwLock<Cursor<T>>>);
|
||||
impl <T: AsRef<[u8]>> Buffer <T> {
|
||||
pub fn new(bytes: T) -> Self {
|
||||
Buffer(Arc::new(RwLock::new(Cursor::new(bytes))))
|
||||
}
|
||||
|
||||
pub fn read_at(&self, buffer: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
let mut lock = self.0.write().unwrap();
|
||||
lock.seek(SeekFrom::Start(offset))?;
|
||||
lock.read(buffer)
|
||||
}
|
||||
pub fn write_at(&self, _buffer: &[u8], _offset: u64) -> io::Result<usize> {
|
||||
return Err(io::Error::from_raw_os_error(libc::EPERM))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum FileObject {
|
||||
File(File),
|
||||
BufferFile(Buffer<&'static [u8]>),
|
||||
NotAFile,
|
||||
}
|
||||
|
||||
pub struct P9File {
|
||||
file: FileObject,
|
||||
}
|
||||
|
||||
impl P9File {
|
||||
pub fn new_not_a_file() -> Self {
|
||||
P9File { file: FileObject::NotAFile }
|
||||
}
|
||||
|
||||
pub fn from_file(file: File) -> Self {
|
||||
P9File { file: FileObject::File(file) }
|
||||
}
|
||||
|
||||
pub fn from_buffer(buffer: Buffer<&'static [u8]>) -> Self {
|
||||
P9File { file: FileObject::BufferFile(buffer) }
|
||||
}
|
||||
|
||||
pub fn sync_all(&self) -> io::Result<()> {
|
||||
match self.file {
|
||||
FileObject::File(ref f) => f.sync_all(),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
pub fn sync_data(&self) -> io::Result<()> {
|
||||
match self.file {
|
||||
FileObject::File(ref f) => f.sync_data(),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_at(&self, buffer: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
match self.file {
|
||||
FileObject::File(ref f) => f.read_at(buffer,offset),
|
||||
FileObject::BufferFile(ref f) => f.read_at(buffer, offset),
|
||||
FileObject::NotAFile => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_at(&self, buffer: &[u8], offset: u64) -> io::Result<usize> {
|
||||
match self.file {
|
||||
FileObject::File(ref f) => f.write_at(buffer,offset),
|
||||
FileObject::BufferFile(ref f) => f.write_at(buffer, offset),
|
||||
FileObject::NotAFile => Ok(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy,Clone)]
|
||||
pub struct Qid {
|
||||
qtype: u8,
|
||||
version: u32,
|
||||
path: u64,
|
||||
}
|
||||
|
||||
impl Qid {
|
||||
|
||||
pub fn new(qtype: u8, version: u32, path: u64) -> Qid {
|
||||
Qid { qtype, version, path }
|
||||
}
|
||||
|
||||
pub fn from_metadata(meta: &Metadata) -> Qid {
|
||||
let qtype = if meta.is_dir() {
|
||||
P9_QTDIR
|
||||
} else if meta.is_file() {
|
||||
P9_QTFILE
|
||||
} else if meta.file_type().is_symlink() {
|
||||
P9_QTSYMLINK
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let version = meta.st_mtime() as u32 ^ (meta.st_size() << 8) as u32;
|
||||
let path = meta.st_ino();
|
||||
Qid::new(qtype, version, path)
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.qtype == P9_QTDIR
|
||||
}
|
||||
|
||||
pub fn write(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
pp.w8(self.qtype)?;
|
||||
pp.w32(self.version)?;
|
||||
pp.w64(self.path)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_p9_flags(flags: u32, is_root: bool) -> libc::c_int {
|
||||
let flagmap = &[
|
||||
(P9_DOTL_CREATE, libc::O_CREAT),
|
||||
(P9_DOTL_EXCL, libc::O_EXCL),
|
||||
(P9_DOTL_NOCTTY, libc::O_NOCTTY),
|
||||
(P9_DOTL_TRUNC, libc::O_TRUNC),
|
||||
(P9_DOTL_APPEND, libc::O_APPEND),
|
||||
(P9_DOTL_NONBLOCK, libc::O_NONBLOCK),
|
||||
(P9_DOTL_DSYNC, libc::O_DSYNC),
|
||||
(P9_DOTL_FASYNC, libc::O_ASYNC),
|
||||
(P9_DOTL_DIRECT, libc::O_DIRECT),
|
||||
(P9_DOTL_LARGEFILE, libc::O_LARGEFILE),
|
||||
(P9_DOTL_DIRECTORY, libc::O_DIRECTORY),
|
||||
(P9_DOTL_NOFOLLOW, libc::O_NOFOLLOW),
|
||||
(P9_DOTL_SYNC, libc::O_SYNC),
|
||||
];
|
||||
let mut custom = flagmap.iter()
|
||||
.fold(0, |acc, (a,b)|
|
||||
if flags & *a != 0 { acc | *b } else { acc });
|
||||
|
||||
if is_root && flags & P9_DOTL_NOATIME != 0 {
|
||||
custom |= libc::O_NOATIME;
|
||||
}
|
||||
/* copied from qemu */
|
||||
custom &= !(libc::O_NOCTTY|libc::O_ASYNC|libc::O_CREAT);
|
||||
custom &= !libc::O_DIRECT;
|
||||
custom
|
||||
}
|
||||
|
||||
pub struct Fids<T: FileSystemOps> {
|
||||
ops: T,
|
||||
root: PathBuf,
|
||||
fidmap: BTreeMap<u32, Fid<T>>,
|
||||
}
|
||||
|
||||
impl <T: FileSystemOps> Fids<T> {
|
||||
pub fn new(root: PathBuf, ops: T) -> Self {
|
||||
Fids {
|
||||
ops,
|
||||
root,
|
||||
fidmap: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fid(&self, id: u32) -> io::Result<&Fid<T>> {
|
||||
self.fidmap.get(&id).ok_or(Self::bad_fd_error())
|
||||
}
|
||||
|
||||
pub fn fid_mut(&mut self, id: u32) -> io::Result<&mut Fid<T>> {
|
||||
self.fidmap.get_mut(&id).ok_or(Self::bad_fd_error())
|
||||
}
|
||||
|
||||
pub fn read_fid(&self, pp: &mut PduParser) -> io::Result<&Fid<T>> {
|
||||
let id = pp.r32()?;
|
||||
self.fid(id)
|
||||
}
|
||||
|
||||
pub fn read_new_path(&self, pp: &mut PduParser) -> io::Result<PathBuf> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let name = pp.read_string()?;
|
||||
fid.join_name(&self.root, &name)
|
||||
}
|
||||
|
||||
pub fn path_join_name(&self, qid: Qid, path: &Path, name: &str) -> io::Result<PathBuf> {
|
||||
Fid::<T>::path_join_name(qid, path, &self.root, name)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.fidmap.clear()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, fid: Fid<T>) {
|
||||
self.fidmap.insert(fid.id, fid);
|
||||
}
|
||||
|
||||
pub fn exists(&self, id: u32) -> bool {
|
||||
self.fidmap.contains_key(&id)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: u32) -> io::Result<Fid<T>> {
|
||||
match self.fidmap.remove(&id) {
|
||||
Some(fid) => Ok(fid),
|
||||
None => Err(Self::bad_fd_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create<P: Into<PathBuf>>(&self, id: u32, path: P) -> io::Result<Fid<T>> {
|
||||
Fid::create(self.ops.clone(), id, path)
|
||||
}
|
||||
|
||||
pub fn read_qid(&self, path: &Path) -> io::Result<Qid> {
|
||||
self.ops.read_qid(path)
|
||||
}
|
||||
|
||||
fn bad_fd_error() -> io::Error {
|
||||
io::Error::from_raw_os_error(libc::EBADF)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Fid<T: FileSystemOps> {
|
||||
ops: T,
|
||||
id: u32,
|
||||
path: PathBuf,
|
||||
qid: Qid,
|
||||
file: Option<P9File>,
|
||||
directory: RefCell<Option<Directory>>,
|
||||
}
|
||||
|
||||
impl <T: FileSystemOps> Fid<T> {
|
||||
fn create<P: Into<PathBuf>>(ops: T, id: u32, path: P) -> io::Result<Self> {
|
||||
let path = path.into();
|
||||
let qid = ops.read_qid(&path)?;
|
||||
Ok(Fid {
|
||||
ops, id, path, qid,
|
||||
file: None,
|
||||
directory: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn qid(&self) -> Qid {
|
||||
self.qid
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn write_stat(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
self.ops.write_stat(self.path(), pp)
|
||||
}
|
||||
|
||||
pub fn reload_qid(&mut self) -> io::Result<()> {
|
||||
self.qid = self.ops.read_qid(self.path())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_file(&mut self, file: P9File) {
|
||||
self.file = Some(file)
|
||||
}
|
||||
|
||||
pub fn set_path<P: Into<PathBuf>>(&mut self, path: P) -> io::Result<()> {
|
||||
self.path = path.into();
|
||||
self.reload_qid()
|
||||
}
|
||||
|
||||
pub fn write_qid(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
self.qid.write(pp)
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.qid.is_dir()
|
||||
}
|
||||
|
||||
pub fn file(&self) -> io::Result<&P9File> {
|
||||
match self.file.as_ref() {
|
||||
Some(file) => Ok(file),
|
||||
None => system_error(libc::EBADF),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join_name(&self, root: &Path, name: &str) -> io::Result<PathBuf> {
|
||||
Self::path_join_name(self.qid, self.path(), root, name)
|
||||
}
|
||||
|
||||
fn path_join_name(qid: Qid, path: &Path, root: &Path, name: &str) -> io::Result<PathBuf> {
|
||||
if !qid.is_dir() {
|
||||
return system_error(libc::ENOTDIR);
|
||||
}
|
||||
let p= Path::new(name);
|
||||
|
||||
if p.components().count() > 1 {
|
||||
return system_error(libc::EINVAL);
|
||||
}
|
||||
|
||||
let mut path = path.to_path_buf();
|
||||
match p.components().next() {
|
||||
Some(Component::ParentDir) => {
|
||||
path.pop();
|
||||
if !path.starts_with(root) {
|
||||
return system_error(libc::EINVAL);
|
||||
}
|
||||
}
|
||||
Some(Component::Normal(name)) => path.push(name),
|
||||
None => {},
|
||||
_ => return system_error(libc::EINVAL),
|
||||
};
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn load_directory(&self) -> io::Result<()> {
|
||||
if !self.is_dir() {
|
||||
return system_error(libc::ENOTDIR);
|
||||
}
|
||||
let dir = self.ops.readdir_populate(self.path())?;
|
||||
self.directory.replace(Some(dir));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn directory(&self) -> RefMut<Option<Directory>>{
|
||||
self.directory.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: FileSystemOps> fmt::Display for Fid<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Fid:({}, id={})", self.path().display(), self.id)
|
||||
}
|
||||
}
|
||||
|
||||
fn system_error<T>(errno: libc::c_int) -> io::Result<T> {
|
||||
Err(io::Error::from_raw_os_error(errno))
|
||||
}
|
@ -1,46 +1,21 @@
|
||||
use std::mem;
|
||||
use std::ffi::CString;
|
||||
use std::ffi::OsString;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::ffi::{CString,OsString};
|
||||
use std::fs::{self, File, Metadata, OpenOptions};
|
||||
|
||||
use std::io;
|
||||
use std::path::{PathBuf,Path,Component};
|
||||
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 std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
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};
|
||||
|
||||
use super::readdir::ReadDir;
|
||||
|
||||
const MAX_SYMLINKS: usize = 16;
|
||||
const PATH_MAX: usize = 1024; // it's actually 4096 on linux
|
||||
|
||||
const O_RDONLY: u32 = 0;
|
||||
const O_WRONLY: u32 = 1;
|
||||
const O_RDWR: u32 = 2;
|
||||
const O_ACCMODE: u32 = 0x3;
|
||||
const ALLOWED_FLAGS: u32 = (libc::O_APPEND | libc::O_TRUNC | libc::O_LARGEFILE
|
||||
| libc::O_DIRECTORY | libc::O_DSYNC | libc::O_NOFOLLOW
|
||||
| libc::O_SYNC) as u32;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StatFs {
|
||||
pub f_type: u32,
|
||||
pub f_bsize: u32,
|
||||
pub f_blocks: u64,
|
||||
pub f_bfree: u64,
|
||||
pub f_bavail: u64,
|
||||
pub f_files: u64,
|
||||
pub f_ffree: u64,
|
||||
pub fsid: u64,
|
||||
pub f_namelen: u32,
|
||||
}
|
||||
impl StatFs {
|
||||
fn new() -> StatFs {
|
||||
StatFs { ..Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum FsTouch {
|
||||
Atime,
|
||||
@ -49,195 +24,90 @@ pub enum FsTouch {
|
||||
MtimeNow,
|
||||
}
|
||||
|
||||
pub trait FileSystemOps {
|
||||
fn open(&self, path: &Path, flags: u32) -> io::Result<FileDescriptor>;
|
||||
fn open_dir(&self, path: &Path) -> io::Result<FileDescriptor>;
|
||||
fn create(&self, path: &Path, flags: u32, mode: u32) -> io::Result<FileDescriptor>;
|
||||
fn stat(&self, path: &Path) -> io::Result<Metadata>;
|
||||
fn statfs(&self, path: &Path) -> io::Result<StatFs>;
|
||||
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 chmod(&self, path: &Path, mode: 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 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 {
|
||||
init_path: PathBuf,
|
||||
resolver: PathResolver,
|
||||
root: PathBuf,
|
||||
readonly: bool,
|
||||
}
|
||||
|
||||
pub enum FileDescriptor {
|
||||
None,
|
||||
Dir(ReadDir),
|
||||
File(File),
|
||||
}
|
||||
|
||||
impl FileDescriptor {
|
||||
#[allow(dead_code)]
|
||||
pub fn is_file(&self) -> bool {
|
||||
match *self {
|
||||
FileDescriptor::File(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_dir(&self) -> bool {
|
||||
match *self {
|
||||
FileDescriptor::Dir(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow_file(&mut self) -> io::Result<&mut File> {
|
||||
match *self {
|
||||
FileDescriptor::File(ref mut file_ref) => Ok(file_ref),
|
||||
_ => Err(os_err(libc::EBADF)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow_dir(&mut self) -> io::Result<&mut ReadDir> {
|
||||
match *self {
|
||||
FileDescriptor::Dir(ref mut dir_ref) => Ok(dir_ref),
|
||||
_ => Err(os_err(libc::EBADF)),
|
||||
}
|
||||
}
|
||||
euid_root: bool,
|
||||
}
|
||||
|
||||
impl FileSystem {
|
||||
pub fn new(root: PathBuf, init_path: PathBuf, readonly: bool) -> FileSystem {
|
||||
FileSystem { resolver: PathResolver::new(root), init_path, readonly }
|
||||
pub fn new(root: PathBuf, readonly: bool) -> FileSystem {
|
||||
let euid_root = Self::is_euid_root();
|
||||
FileSystem { root, readonly, euid_root }
|
||||
}
|
||||
|
||||
fn fullpath(&self, path: &Path) -> io::Result<PathBuf> {
|
||||
if path.to_str().unwrap() == "/phinit" {
|
||||
return Ok(self.init_path.clone())
|
||||
}
|
||||
self.resolver.fullpath(path)
|
||||
pub fn is_euid_root() -> bool {
|
||||
unsafe { libc::geteuid() == 0 }
|
||||
}
|
||||
|
||||
|
||||
fn flags_to_open_options(&self, flags: u32) -> io::Result<OpenOptions> {
|
||||
let acc = flags & O_ACCMODE;
|
||||
let mut oo = OpenOptions::new();
|
||||
|
||||
if self.readonly && acc != O_RDONLY {
|
||||
return Err(io::Error::from_raw_os_error(libc::EACCES));
|
||||
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)
|
||||
}
|
||||
|
||||
match acc {
|
||||
O_RDONLY => { oo.read(true).write(false); }
|
||||
O_WRONLY => { oo.read(false).write(true); }
|
||||
O_RDWR => { oo.read(true).write(true); }
|
||||
_ => return Err(os_err(libc::EINVAL))
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
// There should never be a symlink in path but add O_NOFOLLOW anyways
|
||||
let custom = libc::O_NOFOLLOW | (flags & ALLOWED_FLAGS) as i32;
|
||||
oo.custom_flags(custom);
|
||||
Ok(oo)
|
||||
}
|
||||
fn new_file(&self, file: File) -> P9File {
|
||||
P9File::from_file(file)
|
||||
}
|
||||
|
||||
///
|
||||
/// Resolves paths into a canonical path which is always no higher
|
||||
/// than the `root` path.
|
||||
#[derive(Clone)]
|
||||
struct PathResolver {
|
||||
root: PathBuf,
|
||||
fn metadata(&self, path: &Path) -> io::Result<Metadata> {
|
||||
let path = self.canonicalize(path)?;
|
||||
path.symlink_metadata()
|
||||
}
|
||||
|
||||
impl PathResolver {
|
||||
fn new(root: PathBuf) -> PathResolver {
|
||||
// root must be absolute path
|
||||
PathResolver{ root }
|
||||
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))
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Canonicalize `path` so that .. segments in both in
|
||||
/// the path itself and any symlinks in the path do
|
||||
/// not escape. The returned path will not contain any
|
||||
/// symlinks and refers to a path which is a subdirectory
|
||||
/// of `self.root`
|
||||
fn resolve_path(&self, path: &Path) -> io::Result<PathBuf> {
|
||||
let mut buf = PathBuf::from(path);
|
||||
let mut nlinks = 0_usize;
|
||||
while self._resolve(&mut buf)? {
|
||||
nlinks += 1;
|
||||
if nlinks > MAX_SYMLINKS {
|
||||
return Err(io::Error::from_raw_os_error(libc::ELOOP))
|
||||
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))
|
||||
}
|
||||
if buf.as_os_str().len() > PATH_MAX {
|
||||
return Err(io::Error::from_raw_os_error(libc::ENAMETOOLONG))
|
||||
}
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn is_path_symlink(path: &Path) -> bool {
|
||||
match path.symlink_metadata() {
|
||||
Ok(meta) => meta.file_type().is_symlink(),
|
||||
Err(..) => false
|
||||
}
|
||||
}
|
||||
|
||||
fn fullpath(&self, path: &Path) -> io::Result<PathBuf> {
|
||||
let resolved = self.resolve_path(path)?;
|
||||
Ok(self.realpath(&resolved))
|
||||
}
|
||||
|
||||
fn realpath(&self, path: &Path) -> PathBuf {
|
||||
let mut cs = path.components();
|
||||
if path.is_absolute() {
|
||||
cs.next();
|
||||
}
|
||||
self.root.join(cs.as_path())
|
||||
}
|
||||
|
||||
fn resolve_symlink(&self, path: &mut PathBuf) -> io::Result<bool> {
|
||||
let realpath = self.realpath(path);
|
||||
if PathResolver::is_path_symlink(&realpath) {
|
||||
path.pop();
|
||||
path.push(realpath.read_link()?);
|
||||
return Ok(true)
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn resolve_component(&self, c: Component, pathbuf: &mut PathBuf) -> io::Result<bool> {
|
||||
match c {
|
||||
Component::RootDir => pathbuf.push("/"),
|
||||
Component::CurDir | Component::Prefix(..) => (),
|
||||
Component::ParentDir => { pathbuf.pop(); },
|
||||
Component::Normal(name) => {
|
||||
pathbuf.push(name);
|
||||
let link = self.resolve_symlink(pathbuf)?;
|
||||
return Ok(link)
|
||||
}
|
||||
};
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn _resolve(&self, path: &mut PathBuf) -> io::Result<bool> {
|
||||
let copy = (*path).clone();
|
||||
let mut components = copy.components();
|
||||
|
||||
path.push("/");
|
||||
|
||||
while let Some(c) = components.next() {
|
||||
if self.resolve_component(c, path)? {
|
||||
let tmp = path.join(components.as_path());
|
||||
path.push(tmp);
|
||||
return Ok(true)
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
Ok(canon)
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,68 +116,80 @@ fn cstr(path: &Path) -> io::Result<CString> {
|
||||
}
|
||||
|
||||
impl FileSystemOps for FileSystem {
|
||||
fn open(&self, path: &Path, flags: u32) -> io::Result<FileDescriptor> {
|
||||
let fullpath = self.fullpath(path)?;
|
||||
let meta = fullpath.metadata()?;
|
||||
if meta.is_dir() {
|
||||
let read_dir = ReadDir::open(&fullpath)?;
|
||||
return Ok(FileDescriptor::Dir(read_dir))
|
||||
fn read_qid(&self, path: &Path) -> io::Result<Qid> {
|
||||
let meta = self.metadata(&path)?;
|
||||
let qid = Qid::from_metadata(&meta);
|
||||
Ok(qid)
|
||||
}
|
||||
|
||||
let options = self.flags_to_open_options(flags)?;
|
||||
let file = options.open(&fullpath)?;
|
||||
return Ok(FileDescriptor::File(file))
|
||||
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 create(&self, path: &Path, flags: u32, mode: u32) -> io::Result<FileDescriptor> {
|
||||
let fullpath = self.fullpath(path)?;
|
||||
let mut options = self.flags_to_open_options(flags)?;
|
||||
options.create(true);
|
||||
options.mode(mode & 0o777);
|
||||
let file = options.open(&fullpath)?;
|
||||
return Ok(FileDescriptor::File(file))
|
||||
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 open_dir(&self, path: &Path) -> io::Result<FileDescriptor> {
|
||||
let fullpath = self.fullpath(path)?;
|
||||
let read_dir = ReadDir::open(&fullpath)?;
|
||||
return Ok(FileDescriptor::Dir(read_dir))
|
||||
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 stat(&self, path: &Path) -> io::Result<Metadata> {
|
||||
let fullpath = self.fullpath(path)?;
|
||||
let meta = fullpath.metadata()?;
|
||||
Ok(meta)
|
||||
}
|
||||
fn write_statfs(&self, path: &Path, pp: &mut PduParser) -> io::Result<()> {
|
||||
let path = self.canonicalize(path)?;
|
||||
let path_cstr = cstr(&path)?;
|
||||
|
||||
fn statfs(&self, path: &Path) -> io::Result<StatFs> {
|
||||
let fullpath = self.fullpath(path)?;
|
||||
let path_cstr = cstr(&fullpath)?;
|
||||
let mut stat: LibcStatFs;
|
||||
let mut statfs: libc::statfs64 = unsafe { mem::zeroed() };
|
||||
unsafe {
|
||||
stat = mem::zeroed();
|
||||
let ret = statfs(path_cstr.as_ptr(), &mut stat);
|
||||
let ret = libc::statfs64(path_cstr.as_ptr(), &mut statfs);
|
||||
if ret < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
let mut statfs = StatFs::new();
|
||||
statfs.f_type = stat.f_type as u32;
|
||||
statfs.f_bsize = stat.f_bsize as u32;
|
||||
statfs.f_blocks = stat.f_blocks;
|
||||
statfs.f_bfree = stat.f_bfree;
|
||||
statfs.f_bavail = stat.f_bavail;
|
||||
statfs.f_files = stat.f_files;
|
||||
statfs.f_ffree = stat.f_ffree;
|
||||
statfs.f_namelen = stat.f_namelen as u32;
|
||||
statfs.fsid = stat.f_fsid.val[0] as u64 | ((stat.f_fsid.val[1] as u64) << 32);
|
||||
|
||||
Ok(statfs)
|
||||
|
||||
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 fullpath = self.fullpath(path)?;
|
||||
let path_cstr = cstr(&fullpath)?;
|
||||
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());
|
||||
@ -316,21 +198,14 @@ impl FileSystemOps for FileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
fn chmod(&self, path: &Path, mode: u32) -> io::Result<()> {
|
||||
// XXX see std::os::unix::fs::PermissionsExt for a better way
|
||||
let fullpath = self.fullpath(path)?;
|
||||
let path_cstr = cstr(&fullpath)?;
|
||||
unsafe {
|
||||
if libc::chmod(path_cstr.as_ptr(), mode) < 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 fullpath = self.fullpath(path)?;
|
||||
let path_cstr = cstr(&fullpath)?;
|
||||
let path = self.canonicalize(path)?;
|
||||
let path_cstr = cstr(&path)?;
|
||||
|
||||
let tval = libc::timespec {
|
||||
tv_sec: tv.0 as i64,
|
||||
@ -352,8 +227,7 @@ impl FileSystemOps for FileSystem {
|
||||
FsTouch::MtimeNow => [omit, now],
|
||||
};
|
||||
unsafe {
|
||||
// XXX this could be wildly wrong but libc has wrong type
|
||||
if libc::utimensat(-1, path_cstr.as_ptr(), ×.as_ptr() as *const _ as *const libc::timespec, 0) < 0 {
|
||||
if libc::utimensat(-1, path_cstr.as_ptr(), times.as_ptr(), 0) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
@ -361,8 +235,8 @@ impl FileSystemOps for FileSystem {
|
||||
}
|
||||
|
||||
fn truncate(&self, path: &Path, size: u64) -> io::Result<()> {
|
||||
let fullpath = self.fullpath(path)?;
|
||||
let path_cstr = cstr(&fullpath)?;
|
||||
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());
|
||||
@ -371,42 +245,57 @@ impl FileSystemOps for FileSystem {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// XXX
|
||||
fn readlink(&self, path: &Path) -> io::Result<OsString> {
|
||||
let fullpath = self.fullpath(path)?;
|
||||
fs::read_link(&fullpath).map(|pbuf| pbuf.into_os_string())
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LibcStatFs {
|
||||
f_type: u64,
|
||||
f_bsize: u64,
|
||||
f_blocks: u64,
|
||||
f_bfree: u64,
|
||||
f_bavail: u64,
|
||||
|
||||
f_files: u64,
|
||||
f_ffree: u64,
|
||||
f_fsid: FsidT,
|
||||
|
||||
f_namelen: u64,
|
||||
f_frsize: u64,
|
||||
f_spare: [u64; 5],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct FsidT{
|
||||
val: [libc::c_int; 2],
|
||||
}
|
||||
extern {
|
||||
pub fn statfs(path: *const libc::c_char, buf: *mut LibcStatFs) -> libc::c_int;
|
||||
}
|
||||
|
||||
fn os_err(errno: i32) -> io::Error {
|
||||
io::Error::from_raw_os_error(errno)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,33 +1,37 @@
|
||||
use std::sync::{Arc,RwLock};
|
||||
use std::thread;
|
||||
|
||||
use std::path::{Path,PathBuf};
|
||||
use std::path::{PathBuf, Path};
|
||||
|
||||
use crate::memory::{GuestRam, MemoryManager};
|
||||
use crate::virtio::{self,VirtioBus,VirtioDeviceOps, VirtQueue};
|
||||
use crate::vm::Result;
|
||||
|
||||
mod fid;
|
||||
mod pdu;
|
||||
mod commands;
|
||||
mod readdir;
|
||||
mod filesystem;
|
||||
|
||||
use crate::devices::virtio_9p::server::Server;
|
||||
use crate::devices::virtio_9p::filesystem::{FileSystem, FileSystemOps};
|
||||
use self::pdu::PduParser;
|
||||
use self::commands::Commands;
|
||||
|
||||
mod pdu;
|
||||
mod file;
|
||||
mod directory;
|
||||
mod filesystem;
|
||||
mod server;
|
||||
mod synthetic;
|
||||
|
||||
|
||||
const VIRTIO_ID_9P: u16 = 9;
|
||||
const VIRTIO_9P_MOUNT_TAG: u64 = 0x1;
|
||||
|
||||
pub use synthetic::SyntheticFS;
|
||||
|
||||
pub struct VirtioP9 {
|
||||
pub struct VirtioP9<T: FileSystemOps> {
|
||||
filesystem: T,
|
||||
root_dir: PathBuf,
|
||||
init_path: PathBuf,
|
||||
feature_bits: u64,
|
||||
debug: bool,
|
||||
config: Vec<u8>,
|
||||
}
|
||||
|
||||
impl VirtioP9 {
|
||||
impl <T: FileSystemOps+'static> VirtioP9<T> {
|
||||
fn create_config(tag_name: &str) -> Vec<u8> {
|
||||
let tag_len = tag_name.len() as u16;
|
||||
let mut config = Vec::with_capacity(tag_name.len() + 3);
|
||||
@ -38,17 +42,18 @@ impl VirtioP9 {
|
||||
config
|
||||
}
|
||||
|
||||
fn new(tag_name: &str, root_dir: &str, init_path: &Path) -> Arc<RwLock<VirtioP9>> {
|
||||
fn new(filesystem: T, tag_name: &str, root_dir: &str, debug: bool) -> Arc<RwLock<Self>> {
|
||||
Arc::new(RwLock::new(VirtioP9 {
|
||||
filesystem,
|
||||
root_dir: PathBuf::from(root_dir),
|
||||
init_path: init_path.to_path_buf(),
|
||||
feature_bits: 0,
|
||||
config: VirtioP9::create_config(tag_name),
|
||||
debug,
|
||||
config: VirtioP9::<T>::create_config(tag_name),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn create(vbus: &mut VirtioBus, tag_name: &str, root_dir: &str, init_path: &Path) -> Result<()> {
|
||||
vbus.new_virtio_device(VIRTIO_ID_9P, VirtioP9::new(tag_name, root_dir, init_path))
|
||||
pub fn create_with_filesystem(filesystem: T, vbus: &mut VirtioBus, tag_name: &str, root_dir: &str, debug: bool) -> Result<()> {
|
||||
vbus.new_virtio_device(VIRTIO_ID_9P, VirtioP9::new(filesystem, tag_name, root_dir, debug))
|
||||
.set_num_queues(1)
|
||||
.set_features(VIRTIO_9P_MOUNT_TAG)
|
||||
.set_config_size(tag_name.len() + 3)
|
||||
@ -56,7 +61,15 @@ impl VirtioP9 {
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioDeviceOps for VirtioP9 {
|
||||
impl VirtioP9<FileSystem> {
|
||||
|
||||
pub fn create(vbus: &mut VirtioBus, tag_name: &str, root_dir: &str, read_only: bool, debug: bool) -> Result<()> {
|
||||
let filesystem = FileSystem::new(PathBuf::from(root_dir), read_only);
|
||||
Self::create_with_filesystem(filesystem, vbus, tag_name, root_dir, debug)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: FileSystemOps+'static> VirtioDeviceOps for VirtioP9<T> {
|
||||
fn reset(&mut self) {
|
||||
println!("Reset called");
|
||||
}
|
||||
@ -70,22 +83,26 @@ impl VirtioDeviceOps for VirtioP9 {
|
||||
virtio::read_config_buffer(&self.config, offset, size)
|
||||
}
|
||||
|
||||
|
||||
fn start(&mut self, memory: &MemoryManager, mut queues: Vec<VirtQueue>) {
|
||||
let vq = queues.pop().unwrap();
|
||||
let root_dir = self.root_dir.clone();
|
||||
let init_path = self.init_path.clone();
|
||||
let filesystem = self.filesystem.clone();
|
||||
let ram = memory.guest_ram().clone();
|
||||
thread::spawn(|| run_device(ram, vq, root_dir, init_path));
|
||||
let debug = self.debug;
|
||||
thread::spawn(move || run_device(ram, vq, &root_dir, filesystem, debug));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_device(memory: GuestRam, vq: VirtQueue, root_dir: PathBuf, init_path: PathBuf) {
|
||||
let mut commands = Commands::new(root_dir,init_path,memory.clone());
|
||||
fn run_device<T: FileSystemOps>(memory: GuestRam, vq: VirtQueue, root_dir: &Path, filesystem: T, debug: bool) {
|
||||
let mut server = Server::new(&root_dir, filesystem);
|
||||
|
||||
if debug {
|
||||
server.enable_debug();
|
||||
}
|
||||
|
||||
vq.on_each_chain(|mut chain| {
|
||||
let mut pp = PduParser::new(&mut chain, memory.clone());
|
||||
commands.handle(&mut pp);
|
||||
server.handle(&mut pp);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,26 +1,16 @@
|
||||
use std::fs::Metadata;
|
||||
const P9_RLERROR: u8 = 7;
|
||||
use byteorder::{LittleEndian,ReadBytesExt,WriteBytesExt};
|
||||
use std::io::{self,Read,Write};
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use libc;
|
||||
use byteorder::{LittleEndian,ReadBytesExt,WriteBytesExt};
|
||||
|
||||
use crate::devices::virtio_9p::file::Qid;
|
||||
use crate::memory::GuestRam;
|
||||
use crate::virtio::Chain;
|
||||
|
||||
use super::filesystem::StatFs;
|
||||
|
||||
use libc;
|
||||
|
||||
const P9_STATS_BASIC: u64 = 0x000007ff;
|
||||
|
||||
const P9_HEADER_LEN: usize = 7;
|
||||
|
||||
const P9_QTFILE: u8 = 0x00;
|
||||
const P9_QTLINK: u8 = 0x01;
|
||||
const _P9_QTSYMLINK: u8 = 0x02;
|
||||
|
||||
const P9_QTDIR: u8 = 0x80;
|
||||
const P9_RLERROR: u8 = 7;
|
||||
|
||||
pub struct PduParser<'a> {
|
||||
memory: GuestRam,
|
||||
@ -32,7 +22,7 @@ pub struct PduParser<'a> {
|
||||
reply_start_addr: u64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default,Debug)]
|
||||
pub struct P9Attr {
|
||||
valid: u32,
|
||||
mode: u32,
|
||||
@ -46,15 +36,15 @@ pub struct P9Attr {
|
||||
}
|
||||
|
||||
impl P9Attr {
|
||||
const MODE: u32 = (1 << 0);
|
||||
const UID: u32 = (1 << 1);
|
||||
const GID: u32 = (1 << 2);
|
||||
const SIZE: u32 = (1 << 3);
|
||||
const ATIME: u32 = (1 << 4);
|
||||
const MTIME: u32 = (1 << 5);
|
||||
const CTIME: u32 = (1 << 6);
|
||||
const ATIME_SET: u32 = (1 << 7);
|
||||
const MTIME_SET: u32 = (1 << 8);
|
||||
const MODE: u32 = (1 << 0); // 0x01
|
||||
const UID: u32 = (1 << 1); // 0x02
|
||||
const GID: u32 = (1 << 2); // 0x04
|
||||
const SIZE: u32 = (1 << 3); // 0x08
|
||||
const ATIME: u32 = (1 << 4); // 0x10
|
||||
const MTIME: u32 = (1 << 5); // 0x20
|
||||
const CTIME: u32 = (1 << 6); // 0x40
|
||||
const ATIME_SET: u32 = (1 << 7); // 0x80
|
||||
const MTIME_SET: u32 = (1 << 8); // 0x100
|
||||
const MASK: u32 = 127;
|
||||
const NO_UID: u32 = 0xFFFFFFFF;
|
||||
|
||||
@ -75,6 +65,7 @@ impl P9Attr {
|
||||
self.valid & P9Attr::MASK == P9Attr::CTIME||
|
||||
self.is_valid(P9Attr::UID|P9Attr::GID)
|
||||
}
|
||||
|
||||
pub fn has_size(&self) -> bool { self.is_valid(P9Attr::SIZE) }
|
||||
|
||||
pub fn mode(&self) -> u32 {
|
||||
@ -115,7 +106,6 @@ impl P9Attr {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl <'a> PduParser<'a> {
|
||||
pub fn new(chain: &'a mut Chain, memory: GuestRam) -> PduParser<'a> {
|
||||
PduParser{ memory, chain, size: 0, cmd: 0, tag: 0, reply_start_addr: 0 }
|
||||
@ -129,8 +119,9 @@ impl <'a> PduParser<'a> {
|
||||
}
|
||||
|
||||
pub fn read_done(&mut self) -> io::Result<()> {
|
||||
// XXX unwrap
|
||||
self.reply_start_addr = self.chain.current_write_address(8).unwrap();
|
||||
self.reply_start_addr = self.chain.current_write_address(8)
|
||||
.ok_or(io::Error::from_raw_os_error(libc::EIO))?;
|
||||
|
||||
// reserve header
|
||||
self.w32(0)?;
|
||||
self.w8(0)?;
|
||||
@ -138,20 +129,47 @@ impl <'a> PduParser<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn error_code(err: io::Error) -> u32 {
|
||||
if let Some(errno) = err.raw_os_error() {
|
||||
return errno as u32;
|
||||
}
|
||||
let errno = match err.kind() {
|
||||
io::ErrorKind::NotFound => libc::ENOENT,
|
||||
io::ErrorKind::PermissionDenied => libc::EPERM,
|
||||
io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
|
||||
io::ErrorKind::ConnectionReset => libc::ECONNRESET,
|
||||
io::ErrorKind::ConnectionAborted => libc::ECONNABORTED,
|
||||
io::ErrorKind::NotConnected => libc::ENOTCONN,
|
||||
io::ErrorKind::AddrInUse => libc::EADDRINUSE,
|
||||
io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
|
||||
io::ErrorKind::BrokenPipe => libc::EPIPE,
|
||||
io::ErrorKind::AlreadyExists => libc::EEXIST,
|
||||
io::ErrorKind::WouldBlock => libc::EWOULDBLOCK,
|
||||
io::ErrorKind::InvalidInput => libc::EINVAL,
|
||||
io::ErrorKind::InvalidData => libc::EINVAL,
|
||||
io::ErrorKind::TimedOut => libc::ETIMEDOUT,
|
||||
io::ErrorKind::WriteZero => libc::EIO,
|
||||
io::ErrorKind::Interrupted => libc::EINTR,
|
||||
io::ErrorKind::Other => libc::EIO,
|
||||
io::ErrorKind::UnexpectedEof => libc::EIO,
|
||||
_ => libc::EIO,
|
||||
};
|
||||
return errno as u32;
|
||||
}
|
||||
|
||||
pub fn bail_err(&mut self, error: io::Error) -> io::Result<()> {
|
||||
let errno = Self::error_code(error);
|
||||
self.write_err(errno)
|
||||
}
|
||||
|
||||
pub fn write_err(&mut self, errno: u32) -> io::Result<()> {
|
||||
if self.reply_start_addr == 0 {
|
||||
self.read_done()?;
|
||||
}
|
||||
|
||||
let err = match error.raw_os_error() {
|
||||
Some(errno) => errno as u32,
|
||||
None => 0,
|
||||
};
|
||||
|
||||
self.w32(errno)?;
|
||||
self._w32_at(0,P9_HEADER_LEN as u32 + 4);
|
||||
self._w8_at(4, P9_RLERROR);
|
||||
self._w16_at(5, self.tag);
|
||||
self._w32_at(7, err);
|
||||
self.chain.flush_chain();
|
||||
Ok(())
|
||||
}
|
||||
@ -182,7 +200,6 @@ impl <'a> PduParser<'a> {
|
||||
self.memory.write_int::<u32>(self.reply_start_addr + offset as u64, val).unwrap();
|
||||
}
|
||||
|
||||
|
||||
pub fn write_done(&mut self) -> io::Result<()> {
|
||||
self._w32_at(0, self.chain.get_wlen() as u32);
|
||||
let cmd = self.cmd + 1;
|
||||
@ -205,6 +222,16 @@ impl <'a> PduParser<'a> {
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub fn read_string_list(&mut self) -> io::Result<Vec<String>> {
|
||||
let count = self.r16()?;
|
||||
let mut strings = Vec::with_capacity(count as usize);
|
||||
for _ in 0..count {
|
||||
let s = self.read_string()?;
|
||||
strings.push(s);
|
||||
}
|
||||
Ok(strings)
|
||||
}
|
||||
|
||||
pub fn read_attr(&mut self) -> io::Result<P9Attr> {
|
||||
let mut attr = P9Attr::new();
|
||||
attr.parse(self)?;
|
||||
@ -212,8 +239,9 @@ impl <'a> PduParser<'a> {
|
||||
}
|
||||
|
||||
pub fn write_string(&mut self, str: &str) -> io::Result<()> {
|
||||
self.w16(str.len() as u16)?;
|
||||
self.chain.write_all(str.as_bytes())
|
||||
let bytes = str.as_bytes();
|
||||
self.w16(bytes.len() as u16)?;
|
||||
self.chain.write_all(bytes)
|
||||
}
|
||||
|
||||
pub fn write_os_string(&mut self, str: &OsStr) -> io::Result<()> {
|
||||
@ -221,70 +249,11 @@ impl <'a> PduParser<'a> {
|
||||
self.chain.write_all(str.as_bytes())
|
||||
}
|
||||
|
||||
|
||||
fn is_lnk(meta: &Metadata) -> bool {
|
||||
meta.st_mode() & libc::S_IFMT == libc::S_IFLNK
|
||||
pub fn write_qid_list(&mut self, list: &[Qid]) -> io::Result<()> {
|
||||
self.w16(list.len() as u16)?;
|
||||
for qid in list {
|
||||
qid.write(self)?;
|
||||
}
|
||||
|
||||
fn meta_to_qtype(meta: &Metadata) -> u8 {
|
||||
if meta.is_dir() {
|
||||
P9_QTDIR
|
||||
} else if PduParser::is_lnk(meta) {
|
||||
P9_QTLINK
|
||||
} else {
|
||||
P9_QTFILE
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_qid(&mut self, meta: &Metadata) -> io::Result<()> {
|
||||
// type
|
||||
self.w8(PduParser::meta_to_qtype(meta))?;
|
||||
// version
|
||||
self.w32(meta.st_mtime() as u32 ^ (meta.st_size() << 8) as u32)?;
|
||||
// path
|
||||
self.w64(meta.st_ino())
|
||||
}
|
||||
|
||||
pub fn write_qid_path_only(&mut self, ino: u64) -> io::Result<()> {
|
||||
self.w8(0)?;
|
||||
self.w32(0)?;
|
||||
self.w64(ino)
|
||||
}
|
||||
|
||||
pub fn write_statl(&mut self, st: &Metadata) -> io::Result<()> {
|
||||
self.w64(P9_STATS_BASIC)?;
|
||||
self.write_qid(&st)?;
|
||||
self.w32(st.st_mode())?;
|
||||
self.w32(st.st_uid())?;
|
||||
self.w32(st.st_gid())?;
|
||||
self.w64(st.st_nlink())?;
|
||||
self.w64(st.st_rdev())?;
|
||||
self.w64(st.st_size())?;
|
||||
self.w64(st.st_blksize())?;
|
||||
self.w64(st.st_blocks())?;
|
||||
self.w64(st.st_atime() as u64)?;
|
||||
self.w64(st.st_atime_nsec() as u64)?;
|
||||
self.w64(st.st_mtime() as u64)?;
|
||||
self.w64(st.st_mtime_nsec() as u64)?;
|
||||
self.w64(st.st_ctime() as u64)?;
|
||||
self.w64(st.st_ctime_nsec() as u64)?;
|
||||
self.w64(0)?;
|
||||
self.w64(0)?;
|
||||
self.w64(0)?;
|
||||
self.w64(0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_statfs(&mut self, statfs: StatFs) -> io::Result<()> {
|
||||
self.w32(statfs.f_type)?;
|
||||
self.w32(statfs.f_bsize)?;
|
||||
self.w64(statfs.f_blocks)?;
|
||||
self.w64(statfs.f_bfree)?;
|
||||
self.w64(statfs.f_bavail)?;
|
||||
self.w64(statfs.f_files)?;
|
||||
self.w64(statfs.f_ffree)?;
|
||||
self.w64(statfs.fsid)?;
|
||||
self.w32(statfs.f_namelen)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -319,4 +288,3 @@ impl <'a> PduParser<'a> {
|
||||
self.chain.write_u64::<LittleEndian>(val)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,131 +0,0 @@
|
||||
use std::path::Path;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::io;
|
||||
use std::ffi::{OsStr,CStr,CString};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
use libc;
|
||||
|
||||
struct Dir(*mut libc::DIR);
|
||||
|
||||
pub struct ReadDir {
|
||||
dirp: Dir,
|
||||
last_pos: i64,
|
||||
}
|
||||
|
||||
pub struct DirEntry {
|
||||
entry: libc::dirent64,
|
||||
}
|
||||
|
||||
|
||||
fn cstr(path: &Path) -> io::Result<CString> {
|
||||
Ok(CString::new(path.as_os_str().as_bytes())?)
|
||||
}
|
||||
|
||||
impl ReadDir {
|
||||
pub fn open(path: &Path) -> io::Result<ReadDir> {
|
||||
let p = cstr(path)?;
|
||||
unsafe {
|
||||
let ptr = libc::opendir(p.as_ptr());
|
||||
if ptr.is_null() {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(ReadDir{ dirp: Dir(ptr), last_pos: 0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<i64> {
|
||||
unsafe {
|
||||
let loc = libc::telldir(self.dirp.0);
|
||||
if loc == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(loc)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seek(&self, loc: i64) {
|
||||
unsafe { libc::seekdir(self.dirp.0, loc)}
|
||||
}
|
||||
|
||||
pub fn fsync(&self) -> io::Result<()> {
|
||||
unsafe {
|
||||
if libc::fsync(libc::dirfd(self.dirp.0)) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_current_pos(&mut self) {
|
||||
match self.tell() {
|
||||
Ok(loc) => self.last_pos = loc,
|
||||
Err(_) => (),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn restore_last_pos(&mut self) {
|
||||
self.seek(self.last_pos)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Iterator for ReadDir {
|
||||
type Item = io::Result<DirEntry>;
|
||||
|
||||
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
||||
self.save_current_pos();
|
||||
unsafe {
|
||||
let mut ret = DirEntry {
|
||||
entry: mem::zeroed(),
|
||||
};
|
||||
let mut entry_ptr = ptr::null_mut();
|
||||
loop {
|
||||
if libc::readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
|
||||
return Some(Err(io::Error::last_os_error()))
|
||||
}
|
||||
if entry_ptr.is_null() {
|
||||
return None
|
||||
}
|
||||
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
|
||||
return Some(Ok(ret))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Dir {
|
||||
fn drop(&mut self) {
|
||||
let _ = unsafe { libc::closedir(self.0) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl DirEntry {
|
||||
#[allow(dead_code)]
|
||||
pub fn file_name(&self) -> &OsStr {
|
||||
OsStr::from_bytes(self.name_bytes())
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> u64 {
|
||||
self.entry.d_off as u64
|
||||
}
|
||||
|
||||
pub fn file_type(&self) -> u8 {
|
||||
self.entry.d_type
|
||||
}
|
||||
|
||||
pub fn ino(&self) -> u64 {
|
||||
self.entry.d_ino as u64
|
||||
}
|
||||
|
||||
pub fn name_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
684
rust/src/devices/virtio_9p/server.rs
Normal file
684
rust/src/devices/virtio_9p/server.rs
Normal file
@ -0,0 +1,684 @@
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::{io, cmp};
|
||||
|
||||
use crate::devices::virtio_9p::{
|
||||
pdu::{PduParser, P9Attr},
|
||||
filesystem::{FileSystemOps, FsTouch},
|
||||
file::{Fids, Fid, Qid},
|
||||
};
|
||||
|
||||
const P9_TSTATFS: u8 = 8;
|
||||
const P9_TLOPEN: u8 = 12;
|
||||
const P9_TLCREATE: u8 = 14;
|
||||
const P9_TSYMLINK: u8 = 16;
|
||||
const P9_TMKNOD: u8 = 18;
|
||||
const P9_TRENAME: u8 = 20;
|
||||
const P9_TREADLINK: u8 = 22;
|
||||
const P9_TGETATTR: u8 = 24;
|
||||
const P9_TSETATTR: u8 = 26;
|
||||
const P9_TXATTRWALK: u8 = 30;
|
||||
const P9_TXATTRCREATE: u8 = 32;
|
||||
const P9_TREADDIR: u8 = 40;
|
||||
const P9_TFSYNC: u8 = 50;
|
||||
const P9_TLOCK: u8 = 52;
|
||||
const P9_TGETLOCK: u8 = 54;
|
||||
const P9_TLINK: u8 = 70;
|
||||
const P9_TMKDIR: u8 = 72;
|
||||
const P9_TRENAMEAT: u8 = 74;
|
||||
const P9_TUNLINKAT: u8 = 76;
|
||||
const P9_TVERSION:u8 = 100;
|
||||
const P9_TATTACH :u8 = 104;
|
||||
const P9_TFLUSH: u8 = 108;
|
||||
const P9_TWALK :u8 = 110;
|
||||
const P9_TREAD: u8 = 116;
|
||||
const P9_TWRITE: u8 = 118;
|
||||
const P9_TCLUNK: u8 = 120;
|
||||
const P9_REMOVE: u8 = 122;
|
||||
|
||||
pub struct Server<T: FileSystemOps> {
|
||||
root: PathBuf,
|
||||
debug: bool,
|
||||
msize: u32,
|
||||
fids: Fids<T>,
|
||||
filesystem: T,
|
||||
}
|
||||
|
||||
fn system_error<T>(errno: libc::c_int) -> io::Result<T> {
|
||||
Err(io::Error::from_raw_os_error(errno))
|
||||
}
|
||||
|
||||
impl <T: FileSystemOps> Server<T> {
|
||||
|
||||
pub fn new(root: &Path, filesystem: T) -> Self {
|
||||
let root = root.to_owned();
|
||||
let fids = Fids::new(root.clone(), filesystem.clone());
|
||||
Server {
|
||||
root,
|
||||
debug: false,
|
||||
msize: 0,
|
||||
fids,
|
||||
filesystem
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_debug(&mut self) {
|
||||
self.debug = true;
|
||||
}
|
||||
|
||||
fn fid_mut(&mut self, id: u32) -> io::Result<&mut Fid<T>> {
|
||||
self.fids.fid_mut(id)
|
||||
}
|
||||
|
||||
fn read_fid(&self, pp: &mut PduParser) -> io::Result<&Fid<T>> {
|
||||
self.fids.read_fid(pp)
|
||||
}
|
||||
|
||||
/// Reads a directory fid and a string together and constructs a new path by
|
||||
/// joining fid with name
|
||||
fn read_new_path(&self, pp: &mut PduParser) -> io::Result<PathBuf> {
|
||||
self.fids.read_new_path(pp)
|
||||
}
|
||||
|
||||
pub fn handle(&mut self, pp: &mut PduParser) {
|
||||
match pp.command() {
|
||||
Ok(cmd) => {
|
||||
if let Err(err) = self.dispatch(cmd, pp) {
|
||||
if self.debug {
|
||||
notify!("error handling command: {}", err);
|
||||
}
|
||||
let _ = pp.bail_err(err);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error reading p9 command: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch(&mut self, cmd: u8, pp: &mut PduParser) -> io::Result<()> {
|
||||
match cmd {
|
||||
P9_TSTATFS => self.p9_statfs(pp)?,
|
||||
P9_TLOPEN => self.p9_open(pp)?,
|
||||
P9_TLCREATE => self.p9_create(pp)?,
|
||||
P9_TSYMLINK => self.p9_symlink(pp)?,
|
||||
P9_TMKNOD => self.p9_mknod(pp)?,
|
||||
P9_TRENAME => self.p9_rename(pp)?,
|
||||
P9_TREADLINK => self.p9_readlink(pp)?,
|
||||
P9_TGETATTR => self.p9_getattr(pp)?,
|
||||
P9_TSETATTR => self.p9_setattr(pp)?,
|
||||
P9_TXATTRWALK => self.p9_unsupported(pp)?,
|
||||
P9_TXATTRCREATE => self.p9_unsupported(pp)?,
|
||||
P9_TREADDIR => self.p9_readdir(pp)?,
|
||||
P9_TFSYNC => self.p9_fsync(pp)?,
|
||||
P9_TLOCK => self.p9_unsupported(pp)?,
|
||||
P9_TGETLOCK => self.p9_unsupported(pp)?,
|
||||
P9_TUNLINKAT => self.p9_unlinkat(pp)?,
|
||||
P9_TLINK => self.p9_link(pp)?,
|
||||
P9_TMKDIR=> self.p9_mkdir(pp)?,
|
||||
P9_TRENAMEAT => self.p9_renameat(pp)?,
|
||||
P9_TVERSION => self.p9_version(pp)?,
|
||||
P9_TATTACH => self.p9_attach(pp)?,
|
||||
P9_TFLUSH => self.p9_flush(pp)?,
|
||||
P9_TWALK => self.p9_walk(pp)?,
|
||||
P9_TREAD => self.p9_read(pp)?,
|
||||
P9_TWRITE => self.p9_write(pp)?,
|
||||
P9_TCLUNK => self.p9_clunk(pp)?,
|
||||
P9_REMOVE => self.p9_remove(pp)?,
|
||||
n => warn!("unhandled 9p command: {}", n),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn p9_statfs_args(&self, pp: &mut PduParser) -> io::Result<&Fid<T>> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
pp.read_done()?;
|
||||
Ok(fid)
|
||||
}
|
||||
|
||||
fn p9_statfs(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = self.p9_statfs_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_statfs({})", fid)
|
||||
}
|
||||
self.filesystem.write_statfs(fid.path(), pp)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_open_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, u32)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let flags = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((fid, flags))
|
||||
}
|
||||
|
||||
fn p9_open(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid, flags) = self.p9_open_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_open({}, {:08x})", fid, flags)
|
||||
}
|
||||
|
||||
let file = self.filesystem.open(fid.path(), flags)?;
|
||||
|
||||
let id = fid.id();
|
||||
let fid = self.fid_mut(id)?;
|
||||
|
||||
fid.set_file(file);
|
||||
fid.write_qid(pp)?;
|
||||
// iounit
|
||||
pp.w32(0)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_create_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, PathBuf, u32, u32)> {
|
||||
let dfid = self.read_fid(pp)?;
|
||||
let name = pp.read_string()?;
|
||||
let path = dfid.join_name(&self.root, &name)?;
|
||||
let flags = pp.r32()?;
|
||||
let mode = pp.r32()?;
|
||||
let _gid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((dfid, path, flags, mode))
|
||||
}
|
||||
|
||||
fn p9_create(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (dfid, path, flags, mode) = self.p9_create_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_create({:?}, flags={:08x}, mode={:04o})",
|
||||
path, flags, mode)
|
||||
}
|
||||
|
||||
let file = self.filesystem.create(&path, flags, mode)?;
|
||||
|
||||
let id = dfid.id();
|
||||
let dfid = self.fid_mut(id)?;
|
||||
|
||||
dfid.set_path(path)?;
|
||||
dfid.set_file(file);
|
||||
|
||||
dfid.write_qid(pp)?;
|
||||
// iounit
|
||||
pp.w32(0)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_symlink_args(&self, pp: &mut PduParser) -> io::Result<(PathBuf, String)> {
|
||||
let newpath = self.read_new_path(pp)?;
|
||||
let target = pp.read_string()?;
|
||||
let _gid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((newpath, target))
|
||||
}
|
||||
|
||||
fn p9_symlink(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (newpath, target) = self.p9_symlink_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_symlink({:?}, {})", newpath, target)
|
||||
}
|
||||
|
||||
self.filesystem.symlink(&Path::new(&target), &newpath)?;
|
||||
|
||||
self.filesystem.write_stat(&newpath, pp)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_mknod_args(&self, pp: &mut PduParser) -> io::Result<(PathBuf, u32, u32, u32)> {
|
||||
let path = self.read_new_path(pp)?;
|
||||
let mode = pp.r32()?;
|
||||
let major = pp.r32()?;
|
||||
let minor = pp.r32()?;
|
||||
let _gid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((path, mode, major, minor))
|
||||
}
|
||||
|
||||
fn p9_mknod(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (path, mode, major, minor) = self.p9_mknod_args(pp)?;
|
||||
if self.debug {
|
||||
notify!("p9_mknod({:?}, {:04o}, {}:{})", path, mode, major, minor)
|
||||
}
|
||||
system_error(libc::EACCES)
|
||||
}
|
||||
|
||||
fn p9_rename_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, PathBuf)> {
|
||||
let oldfid = self.read_fid(pp)?;
|
||||
let newpath = self.read_new_path(pp)?;
|
||||
pp.read_done()?;
|
||||
Ok((oldfid, newpath))
|
||||
}
|
||||
|
||||
fn p9_rename(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (oldfid, newpath) = self.p9_rename_args(pp)?;
|
||||
if self.debug {
|
||||
format!("p9_rename({}, {:?})", oldfid, newpath);
|
||||
}
|
||||
self.filesystem.rename(oldfid.path(), &newpath)?;
|
||||
let id = oldfid.id();
|
||||
let oldfid = self.fid_mut(id)?;
|
||||
oldfid.set_path(newpath)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_readlink_args(&self, pp: &mut PduParser) -> io::Result<&Fid<T>> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
pp.read_done()?;
|
||||
Ok(fid)
|
||||
}
|
||||
|
||||
fn p9_readlink(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = self.p9_readlink_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_readlink({})", fid);
|
||||
}
|
||||
|
||||
let s = self.filesystem.readlink(fid.path())?;
|
||||
pp.write_os_string(&s)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_getattr_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, u64)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let mask = pp.r64()?;
|
||||
pp.read_done()?;
|
||||
Ok((fid, mask))
|
||||
}
|
||||
|
||||
fn p9_getattr(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid,mask) = self.p9_getattr_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_getattr({}, {})", fid, mask);
|
||||
}
|
||||
|
||||
// XXX mask?
|
||||
fid.write_stat(pp)?;
|
||||
if let Err(err) = fid.write_stat(pp) {
|
||||
notify!("error from write_stat: {}", err);
|
||||
return Err(err);
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_setattr_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, P9Attr)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let attr = pp.read_attr()?;
|
||||
pp.read_done()?;
|
||||
Ok((fid, attr))
|
||||
}
|
||||
|
||||
fn p9_setattr(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid, attr) = self.p9_setattr_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_setattr({}, {:?})", fid, attr);
|
||||
}
|
||||
|
||||
if attr.has_mode() {
|
||||
self.filesystem.set_mode(fid.path(), attr.mode())?;
|
||||
}
|
||||
|
||||
if attr.has_atime() {
|
||||
if attr.has_atime_set() {
|
||||
self.filesystem.touch(fid.path(), FsTouch::Atime, attr.atime())?;
|
||||
} else {
|
||||
self.filesystem.touch(fid.path(), FsTouch::AtimeNow, (0,0))?;
|
||||
}
|
||||
}
|
||||
|
||||
if attr.has_mtime() {
|
||||
if attr.has_mtime_set() {
|
||||
self.filesystem.touch(fid.path(), FsTouch::Mtime, attr.mtime())?;
|
||||
} else {
|
||||
self.filesystem.touch(fid.path(), FsTouch::MtimeNow, (0,0))?;
|
||||
}
|
||||
}
|
||||
|
||||
if attr.has_chown() {
|
||||
let (uid, gid) = attr.chown_ids();
|
||||
self.filesystem.chown(fid.path(), uid, gid)?;
|
||||
}
|
||||
|
||||
if attr.has_size() {
|
||||
self.filesystem.truncate(fid.path(), attr.size())?;
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_readdir_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, u64, u32)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let offset = pp.r64()?;
|
||||
let count = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((fid, offset, count))
|
||||
}
|
||||
|
||||
fn p9_readdir(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid, offset, count) = self.p9_readdir_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_readdir({}, offset={}, count={})", fid, offset, count);
|
||||
}
|
||||
|
||||
if offset == 0 {
|
||||
fid.load_directory()?;
|
||||
}
|
||||
|
||||
let mut dref = fid.directory();
|
||||
let directory = match dref.as_mut() {
|
||||
Some(directory) => directory,
|
||||
None => return system_error(libc::EBADF),
|
||||
};
|
||||
|
||||
let size= cmp::min(self.msize - 4, count) as usize;
|
||||
directory.write_entries(pp, offset, size)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_fsync_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, u32)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let datasync = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((fid, datasync))
|
||||
}
|
||||
|
||||
fn p9_fsync(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid, datasync) = self.p9_fsync_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_fsync({}, {})", fid, datasync);
|
||||
}
|
||||
|
||||
let file = fid.file()?;
|
||||
if datasync == 0 {
|
||||
file.sync_all()?;
|
||||
} else {
|
||||
file.sync_data()?;
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_unlinkat_args(&self, pp: &mut PduParser) -> io::Result<(PathBuf, u32)> {
|
||||
let path = self.read_new_path(pp)?;
|
||||
let flags = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((path, flags))
|
||||
}
|
||||
|
||||
fn p9_unlinkat(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (path, flags) = self.p9_unlinkat_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_unlinkat({:?}, {:08x})", path, flags);
|
||||
}
|
||||
|
||||
if path.is_dir() && (flags & libc::AT_REMOVEDIR as u32) == 0 {
|
||||
return system_error(libc::EISDIR);
|
||||
} else if path.is_dir() {
|
||||
self.filesystem.remove_dir(&path)?;
|
||||
} else {
|
||||
self.filesystem.remove_file(&path)?;
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_link_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, PathBuf)> {
|
||||
let dfid = self.read_fid(pp)?;
|
||||
let fid = self.read_fid(pp)?;
|
||||
let name = pp.read_string()?;
|
||||
pp.read_done()?;
|
||||
let newpath = dfid.join_name(&self.root, &name)?;
|
||||
Ok((fid, newpath))
|
||||
}
|
||||
|
||||
fn p9_link(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid, newpath) = self.p9_link_args(pp)?;
|
||||
self.filesystem.link(fid.path(), &newpath)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_mkdir_args(&self, pp: &mut PduParser) -> io::Result<(PathBuf, u32)> {
|
||||
let newpath = self.read_new_path(pp)?;
|
||||
let mode = pp.r32()?;
|
||||
let _gid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((newpath, mode))
|
||||
}
|
||||
|
||||
fn p9_mkdir(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (newpath, mode) = self.p9_mkdir_args(pp)?;
|
||||
|
||||
self.filesystem.create_dir(&newpath, mode)?;
|
||||
|
||||
let qid = self.filesystem.read_qid(&newpath)?;
|
||||
qid.write(pp)?;
|
||||
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_renameat_args(&self, pp: &mut PduParser) -> io::Result<(PathBuf, PathBuf)> {
|
||||
let oldpath = self.read_new_path(pp)?;
|
||||
let newpath = self.read_new_path(pp)?;
|
||||
pp.read_done()?;
|
||||
Ok((oldpath, newpath))
|
||||
}
|
||||
|
||||
fn p9_renameat(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (oldpath, newpath) = self.p9_renameat_args(pp)?;
|
||||
self.filesystem.rename(&oldpath, &newpath)?;
|
||||
pp.write_done()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn p9_version_args(&self, pp: &mut PduParser) -> io::Result<(u32, String)> {
|
||||
let msize = pp.r32()?;
|
||||
let version = pp.read_string()?;
|
||||
pp.read_done()?;
|
||||
Ok((msize, version))
|
||||
}
|
||||
|
||||
fn p9_version(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (msize, version) = self.p9_version_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_version({}, {})", version, msize);
|
||||
}
|
||||
|
||||
self.msize = msize;
|
||||
self.fids.clear();
|
||||
|
||||
pp.w32(msize)?;
|
||||
if version.as_str() == "9P2000.L" {
|
||||
pp.write_string(&version)?;
|
||||
} else {
|
||||
pp.write_string("unknown")?;
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_attach_args(&self, pp: &mut PduParser) -> io::Result<u32> {
|
||||
let id = pp.r32()?;
|
||||
let _afid = pp.r32()?;
|
||||
let _uname = pp.read_string()?;
|
||||
let _aname = pp.read_string()?;
|
||||
let _uid = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn p9_attach(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let id = self.p9_attach_args(pp)?;
|
||||
|
||||
if self.fids.exists(id) {
|
||||
return system_error(libc::EBADF);
|
||||
}
|
||||
|
||||
let fid = self.fids.create(id, &self.root)?;
|
||||
fid.write_qid(pp)?;
|
||||
self.fids.add(fid);
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_flush(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
pp.read_done()?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_walk_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, u32, Vec<String>)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let newfid_id = pp.r32()?;
|
||||
let names = pp.read_string_list()?;
|
||||
pp.read_done()?;
|
||||
Ok((fid, newfid_id, names))
|
||||
}
|
||||
|
||||
fn p9_walk(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
fn walk_extend<T: FileSystemOps>(fids: &Fids<T>, qid: Qid, path: &Path, name: &str) -> io::Result<(PathBuf, Qid)> {
|
||||
let path = fids.path_join_name(qid, path, name)?;
|
||||
let qid = fids.read_qid(&path)?;
|
||||
Ok((path, qid))
|
||||
}
|
||||
|
||||
let (fid, newfid_id, names) = self.p9_walk_args(pp)?;
|
||||
|
||||
if fid.id() != newfid_id && self.fids.exists(newfid_id) {
|
||||
return system_error(libc::EBADF);
|
||||
}
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_walk({}, newfid={}, names={:?})", fid, newfid_id, names);
|
||||
}
|
||||
|
||||
let mut path = fid.path().to_path_buf();
|
||||
let mut current_qid = fid.qid();
|
||||
|
||||
let mut qid_list = Vec::new();
|
||||
|
||||
for name in names {
|
||||
path = match walk_extend(&self.fids, current_qid, &path, &name) {
|
||||
Ok((path, qid)) => {
|
||||
qid_list.push(qid);
|
||||
current_qid = qid;
|
||||
path
|
||||
},
|
||||
Err(e) => {
|
||||
if qid_list.is_empty() {
|
||||
return Err(e);
|
||||
}
|
||||
pp.write_qid_list(&qid_list)?;
|
||||
pp.write_done()?;
|
||||
return Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let new_fid = self.fids.create(newfid_id, path)?;
|
||||
self.fids.add(new_fid);
|
||||
|
||||
pp.write_qid_list(&qid_list)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_read_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, u64, u32)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let offset = pp.r64()?;
|
||||
let count = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
Ok((fid, offset, count))
|
||||
}
|
||||
|
||||
fn p9_read(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid, offset, count) = self.p9_read_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_read({}, offset={}, count={})", fid, offset, count);
|
||||
}
|
||||
|
||||
let file = fid.file()?;
|
||||
// space for size field
|
||||
pp.w32(0)?;
|
||||
|
||||
let mut nread = 0;
|
||||
|
||||
while nread < count {
|
||||
let current = pp.chain.current_write_slice();
|
||||
if current.len() == 0 {
|
||||
break;
|
||||
}
|
||||
let rlen = cmp::min(current.len(), count as usize);
|
||||
let n = file.read_at(&mut current[..rlen], offset + nread as u64)?;
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
pp.chain.inc_offset(n, true);
|
||||
nread += n as u32;
|
||||
}
|
||||
pp.w32_at(0, nread as u32);
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_write_args(&self, pp: &mut PduParser) -> io::Result<(&Fid<T>, u64, u32)> {
|
||||
let fid = self.read_fid(pp)?;
|
||||
let offset = pp.r64()?;
|
||||
let count = pp.r32()?;
|
||||
Ok((fid, offset, count))
|
||||
}
|
||||
|
||||
fn p9_write(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let (fid, offset, count) = self.p9_write_args(pp)?;
|
||||
|
||||
if self.debug {
|
||||
notify!("p9_write({}, offset={}, count={})", fid, offset, count);
|
||||
}
|
||||
|
||||
let file = fid.file()?;
|
||||
let mut nread = 0;
|
||||
while nread < count {
|
||||
let n = file.write_at(pp.chain.current_read_slice(), offset + nread as u64)?;
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
pp.chain.inc_offset(n, false);
|
||||
nread += n as u32;
|
||||
}
|
||||
pp.read_done()?;
|
||||
pp.w32(nread)?;
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn remove_fid(&mut self, pp: &mut PduParser) -> io::Result<Fid<T>> {
|
||||
let id = pp.r32()?;
|
||||
pp.read_done()?;
|
||||
self.fids.remove(id)
|
||||
}
|
||||
|
||||
fn p9_clunk(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = self.remove_fid(pp)?;
|
||||
if self.debug {
|
||||
notify!("p9_clunk({})", fid);
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_remove(&mut self, pp: &mut PduParser) -> io::Result<()> {
|
||||
let fid = self.remove_fid(pp)?;
|
||||
if self.debug {
|
||||
notify!("p9_remove({})", fid);
|
||||
}
|
||||
if fid.is_dir() {
|
||||
self.filesystem.remove_dir(fid.path())?;
|
||||
} else {
|
||||
self.filesystem.remove_file(fid.path())?;
|
||||
}
|
||||
pp.write_done()
|
||||
}
|
||||
|
||||
fn p9_unsupported(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
pp.read_done()?;
|
||||
system_error(libc::EOPNOTSUPP)
|
||||
}
|
||||
}
|
||||
|
517
rust/src/devices/virtio_9p/synthetic.rs
Normal file
517
rust/src/devices/virtio_9p/synthetic.rs
Normal file
@ -0,0 +1,517 @@
|
||||
use std::collections::{HashSet, BTreeMap};
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::ffi::{OsString, OsStr};
|
||||
use std::io;
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{Path, PathBuf, Component};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::{UNIX_EPOCH, SystemTime};
|
||||
|
||||
use crate::devices::virtio_9p::{
|
||||
directory::{Directory, P9DirEntry},
|
||||
file::{P9File, Qid, P9_QTDIR, P9_QTFILE},
|
||||
filesystem::{FileSystemOps, FsTouch, FileSystem},
|
||||
pdu::PduParser,
|
||||
};
|
||||
use crate::devices::virtio_9p::file::Buffer;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NodeData {
|
||||
name: OsString,
|
||||
qid: Qid,
|
||||
size: u64,
|
||||
mode: u32,
|
||||
inode: u32,
|
||||
}
|
||||
|
||||
impl NodeData {
|
||||
|
||||
fn name_str(&self) -> &str {
|
||||
self.name.to_str()
|
||||
.expect("SyntheticFS: unable to convert name to &str")
|
||||
}
|
||||
|
||||
fn dtype(&self) -> u8 {
|
||||
if self.qid.is_dir() {
|
||||
libc::DT_DIR
|
||||
} else {
|
||||
libc::DT_REG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Node {
|
||||
File(PathBuf, NodeData),
|
||||
MemoryFile(Buffer<&'static [u8]>, NodeData),
|
||||
Dir(BTreeMap<OsString, Node>, NodeData),
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn new_dir(name: &OsStr, mode: u32, inode: u32) -> Node {
|
||||
let mode = mode | libc::S_IFDIR;
|
||||
let data = NodeData::new(name, P9_QTDIR, 0, mode, inode);
|
||||
let entries= BTreeMap::new();
|
||||
Node::Dir(entries, data)
|
||||
}
|
||||
|
||||
fn new_file<S: Into<OsString>>(name: S, mode: u32, inode: u32, size: u64, local: &Path) -> Node {
|
||||
let mode = mode | libc::S_IFREG;
|
||||
let data = NodeData::new(name, P9_QTFILE, size, mode, inode);
|
||||
let local = local.to_path_buf();
|
||||
Node::File(local, data)
|
||||
}
|
||||
|
||||
fn new_memory_file<S: Into<OsString>>(name: S, mode: u32, inode: u32, size: u64, bytes: &'static [u8]) -> Node {
|
||||
let mode = mode | libc::S_IFREG;
|
||||
let data = NodeData::new(name, P9_QTFILE, size, mode, inode);
|
||||
let buffer = Buffer::new(bytes);
|
||||
Node::MemoryFile(buffer, data)
|
||||
}
|
||||
|
||||
fn node_data(&self) -> &NodeData {
|
||||
match self {
|
||||
Node::Dir(_, data) => data,
|
||||
Node::File(_, data) => data,
|
||||
Node::MemoryFile(_, data) => data,
|
||||
}
|
||||
}
|
||||
fn qid(&self) -> Qid {
|
||||
self.node_data().qid
|
||||
}
|
||||
|
||||
fn write_stat(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
self.node_data().write_stat(pp)
|
||||
}
|
||||
|
||||
fn create_directory_entry(&self, offset: u64) -> P9DirEntry {
|
||||
let data = self.node_data();
|
||||
P9DirEntry::new(data.qid, offset, data.dtype(), data.name_str())
|
||||
}
|
||||
|
||||
|
||||
fn entries(&self) -> Option<&BTreeMap<OsString, Node>> {
|
||||
match self {
|
||||
Node::Dir(entries, ..) => Some(entries),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn entries_mut(&mut self) -> Option<&mut BTreeMap<OsString, Node>> {
|
||||
match self {
|
||||
Node::Dir(entries, ..) => Some(entries),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn descend(&self, name: &OsStr) -> io::Result<&Node> {
|
||||
self.entries()
|
||||
.ok_or(rawerr(libc::ENOTDIR))
|
||||
.and_then(|entries|
|
||||
entries.get(name)
|
||||
.ok_or(rawerr(libc::ENOENT)))
|
||||
}
|
||||
|
||||
fn descend_mut(&mut self, name: &OsStr) -> io::Result<&mut Node> {
|
||||
self.entries_mut()
|
||||
.ok_or(rawerr(libc::ENOTDIR))
|
||||
.and_then(|entries|
|
||||
entries.get_mut(name)
|
||||
.ok_or(rawerr(libc::ENOENT)))
|
||||
|
||||
}
|
||||
|
||||
fn mkdir(&mut self, names: &[&OsStr], mode: u32, inodes: &mut Inodes) -> io::Result<()> {
|
||||
if !names.is_empty() {
|
||||
let entries = self.entries_mut().ok_or(rawerr(libc::ENOTDIR))?;
|
||||
match entries.entry(names[0].to_os_string()) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().mkdir(&names[1..], mode, inodes)?;
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
let inode = inodes.next_inode();
|
||||
|
||||
let mut node = Node::new_dir(names[0], mode, inode);
|
||||
node.mkdir(&names[1..], mode, inodes)?;
|
||||
entry.insert(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn populate_directory(&self) -> io::Result<Directory> {
|
||||
match self {
|
||||
Node::Dir(nodes, ..) => {
|
||||
let mut offset = 0;
|
||||
let mut directory = Directory::new();
|
||||
for node in nodes.values() {
|
||||
let entry = node.create_directory_entry(offset);
|
||||
offset = entry.offset();
|
||||
directory.push_entry(entry);
|
||||
}
|
||||
return Ok(directory)
|
||||
},
|
||||
_ => return Err(io::Error::from_raw_os_error(libc::ENOTDIR)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeData {
|
||||
fn new<S: Into<OsString>>(name: S, qtype: u8, size: u64, mode: u32, inode: u32) -> Self {
|
||||
NodeData {
|
||||
name: name.into(),
|
||||
qid: Self::create_qid(qtype, inode),
|
||||
size, mode, inode,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_qid(qtype: u8, inode: u32) -> Qid {
|
||||
let qid_version = Self::generate_qid_version(inode);
|
||||
let qid_path = inode as u64;
|
||||
Qid::new(qtype, qid_version, qid_path)
|
||||
}
|
||||
|
||||
fn generate_qid_version(inode: u32) -> u32 {
|
||||
match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
Ok(v) => v.as_millis() as u32,
|
||||
_ => inode,
|
||||
}
|
||||
}
|
||||
|
||||
fn write_stat(&self, pp: &mut PduParser) -> io::Result<()> {
|
||||
const P9_STATS_BASIC: u64 = 0x000007ff;
|
||||
pp.w64(P9_STATS_BASIC)?;
|
||||
self.qid.write(pp)?;
|
||||
|
||||
pp.w32(self.mode)?;
|
||||
pp.w32(0)?; // uid
|
||||
pp.w32(0)?; // gid
|
||||
pp.w64(1)?; // nlink
|
||||
pp.w64(0)?; // rdev
|
||||
pp.w64(self.size)?; // size
|
||||
pp.w64(0)?; // blksize
|
||||
pp.w64(0)?; // blocks
|
||||
pp.w64(0)?; // atime
|
||||
pp.w64(0)?; // atime nsec
|
||||
pp.w64(0)?; // mtime
|
||||
pp.w64(0)?; // mtime nsec
|
||||
pp.w64(0)?; // ctime
|
||||
pp.w64(0)?; // ctime nsec
|
||||
pp.w64(0)?; // btime
|
||||
pp.w64(0)?; // btime nsec
|
||||
pp.w64(0)?;
|
||||
pp.w64(0)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_INODE: u32 = 1000;
|
||||
#[derive(Clone)]
|
||||
struct Inodes {
|
||||
inodes: HashSet<u32>,
|
||||
current_inode: u32,
|
||||
}
|
||||
|
||||
impl Inodes {
|
||||
fn new() -> Self {
|
||||
Inodes {
|
||||
inodes: HashSet::new(),
|
||||
current_inode: BASE_INODE,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_inode(&mut self) -> u32 {
|
||||
let mut inode = self.current_inode;
|
||||
while self.inodes.contains(&inode) {
|
||||
inode += 1;
|
||||
}
|
||||
self.inodes.insert(inode);
|
||||
self.current_inode = inode + 1;
|
||||
inode
|
||||
}
|
||||
|
||||
fn file_inode(&mut self, path: &Path) -> u32 {
|
||||
let meta = match path.symlink_metadata() {
|
||||
Ok(meta) => meta,
|
||||
Err(_) => return self.next_inode(),
|
||||
};
|
||||
|
||||
let inode = meta.st_ino() as u32;
|
||||
if self.inodes.contains(&inode) {
|
||||
return self.next_inode();
|
||||
}
|
||||
self.inodes.insert(inode);
|
||||
inode
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SyntheticFS {
|
||||
paths_added: HashSet<PathBuf>,
|
||||
root: Node,
|
||||
inodes: Inodes,
|
||||
euid_root: bool,
|
||||
}
|
||||
impl SyntheticFS {
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut inodes = Inodes::new();
|
||||
let root = Node::new_dir("/".as_ref(), 0o755, inodes.next_inode());
|
||||
let euid_root = FileSystem::is_euid_root();
|
||||
|
||||
SyntheticFS {
|
||||
root, inodes, euid_root, paths_added: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn node_count(&self) -> usize {
|
||||
self.inodes.inodes.len()
|
||||
}
|
||||
|
||||
pub fn mkdirs<P: AsRef<Path>>(&mut self, paths: &[P]) {
|
||||
for p in paths {
|
||||
self.mkdir(p, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdir<P: AsRef<Path>>(&mut self, path: P, mode: u32) {
|
||||
let path = path.as_ref();
|
||||
let names = match Self::path_names(path) {
|
||||
Ok(names) => names,
|
||||
Err(_) => {
|
||||
warn!("cannot add directory because path is invalid: {}", path.display());
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(e) = self.root.mkdir(&names, mode, &mut self.inodes) {
|
||||
warn!("failed to create directory {}: {}", path.display(), e);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn add_memory_file<S: Into<OsString>, P: AsRef<Path>>(&mut self, dirpath: P, filename: S, mode: u32, bytes: &'static [u8]) -> io::Result<()> {
|
||||
let dirpath = dirpath.as_ref();
|
||||
let filename = filename.into();
|
||||
self.mkdir(dirpath, 0o755);
|
||||
let inode = self.inodes.next_inode();
|
||||
let node = self.lookup_mut(dirpath)?;
|
||||
let entries = node.entries_mut().ok_or(rawerr(libc::ENOTDIR))?;
|
||||
entries.insert(OsString::from(filename.clone()), Node::new_memory_file(filename, mode, inode, bytes.len() as u64, bytes));
|
||||
Ok(())
|
||||
|
||||
}
|
||||
pub fn add_file<S: Into<OsString>, P: AsRef<Path>, Q: AsRef<Path>>(&mut self, dirpath: P, filename: S, mode: u32, realpath: Q) {
|
||||
let dirpath = dirpath.as_ref();
|
||||
let realpath = realpath.as_ref();
|
||||
let filename = filename.into();
|
||||
if let Err(e) = self._add_file(dirpath, &filename, mode, realpath) {
|
||||
warn!("error adding file {:?} to {}: {}", filename, dirpath.display(), e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _add_file<S: Into<OsString>>(&mut self, dirpath: &Path, filename: S, mode: u32, realpath: &Path) -> io::Result<()> {
|
||||
let filename = filename.into();
|
||||
self.mkdir(dirpath, 0o755);
|
||||
let inode = self.inodes.file_inode(realpath);
|
||||
let node = self.lookup_mut(dirpath)?;
|
||||
let entries = node.entries_mut().ok_or(rawerr(libc::ENOTDIR))?;
|
||||
let meta = realpath.metadata()?;
|
||||
entries.insert(OsString::from(filename.clone()), Node::new_file(filename, mode, inode, meta.len(), realpath));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_ldd_line(line: &str) -> Option<PathBuf> {
|
||||
for s in line.split_whitespace().take(3) {
|
||||
if s.starts_with('/') {
|
||||
let path = Path::new(s);
|
||||
if path.exists() {
|
||||
return Some(path.to_path_buf())
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn add_path(&mut self, path: &Path) -> io::Result<()> {
|
||||
if let (Some(parent), Some(filename)) = (path.parent(), path.file_name()) {
|
||||
let meta = path.metadata()?;
|
||||
let mode = meta.permissions().mode();
|
||||
self.add_file(parent, filename, mode, path);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ldd_command() -> io::Result<Command> {
|
||||
let ldd = Path::new("/usr/bin/ldd");
|
||||
let ldso = Path::new("/usr/lib/ld-linux-x86-64.so.2");
|
||||
|
||||
if ldd.exists() {
|
||||
Ok(Command::new(ldd))
|
||||
} else if ldso.exists() {
|
||||
let mut cmd = Command::new(ldso);
|
||||
cmd.arg("--list");
|
||||
Ok(cmd)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "No ldd binary found"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_library_dependencies<P: AsRef<Path>>(&mut self, execpath: P) -> io::Result<()> {
|
||||
let execpath = execpath.as_ref();
|
||||
let mut cmd = Self::ldd_command()?;
|
||||
let out = cmd
|
||||
.arg(execpath.as_os_str())
|
||||
.stdout(Stdio::piped())
|
||||
.output()?;
|
||||
let s = String::from_utf8(out.stdout).expect("");
|
||||
|
||||
for line in s.lines() {
|
||||
if let Some(path) = Self::parse_ldd_line(line) {
|
||||
if !self.paths_added.contains(&path) {
|
||||
self.add_path(&path)?;
|
||||
self.paths_added.insert(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_executable<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, dirpath: P, filename: &str, realpath: Q) -> io::Result<()> {
|
||||
let realpath = realpath.as_ref();
|
||||
self.add_library_dependencies(realpath)?;
|
||||
|
||||
self.add_file(dirpath, filename, 0o755, realpath);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lookup(&self, path: &Path) -> io::Result<&Node> {
|
||||
let mut current = &self.root;
|
||||
for name in Self::path_names(path)? {
|
||||
current = current.descend(name)?;
|
||||
}
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
fn lookup_mut(&mut self, path: &Path) -> io::Result<&mut Node> {
|
||||
let mut current = &mut self.root;
|
||||
for name in Self::path_names(path)? {
|
||||
current = current.descend_mut(name)?;
|
||||
}
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
fn path_names(path: &Path) -> io::Result<Vec<&OsStr>> {
|
||||
if !path.is_absolute() {
|
||||
return syserr(libc::EINVAL)
|
||||
}
|
||||
Ok(path.components().flat_map(|c| match c {
|
||||
Component::Normal(name) => Some(name),
|
||||
_ => None,
|
||||
}).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystemOps for SyntheticFS {
|
||||
fn read_qid(&self, path: &Path) -> io::Result<Qid> {
|
||||
let node = self.lookup(path)?;
|
||||
Ok(node.qid())
|
||||
}
|
||||
|
||||
fn write_stat(&self, path: &Path, pp: &mut PduParser) -> io::Result<()> {
|
||||
let node = self.lookup(path)?;
|
||||
node.write_stat(pp)
|
||||
}
|
||||
|
||||
fn open(&self, path: &Path, flags: u32) -> io::Result<P9File> {
|
||||
match self.lookup(path)? {
|
||||
Node::File(local, _) => {
|
||||
// XXX filter flags
|
||||
let file = FileSystem::open_with_flags(local, flags, self.euid_root)?;
|
||||
Ok(P9File::from_file(file))
|
||||
},
|
||||
Node::Dir(..) => {
|
||||
Ok(P9File::new_not_a_file())
|
||||
},
|
||||
Node::MemoryFile(buffer,..) => {
|
||||
Ok(P9File::from_buffer(buffer.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create(&self, _path: &Path, _flags: u32, _mode: u32) -> io::Result<P9File> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn write_statfs(&self, _path: &Path, pp: &mut PduParser) -> io::Result<()> {
|
||||
//notify!("write_statfs({})", path.display());
|
||||
let f_files = self.node_count() as u64;
|
||||
pp.w32(0xABCD)?; // f_type
|
||||
pp.w32(512)?; // f_bsize
|
||||
pp.w64(0)?; // f_blocks
|
||||
pp.w64(0)?; // f_bfree
|
||||
pp.w64(0)?; // f_bavail
|
||||
pp.w64(f_files)?; // f_files
|
||||
pp.w64(0)?; // f_ffree
|
||||
pp.w64(0)?; //
|
||||
pp.w32(4096)?; // f_namelen
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn chown(&self, _path: &Path, _uid: u32, _gid: u32) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn set_mode(&self, _path: &Path, _mode: u32) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn touch(&self, _path: &Path, _which: FsTouch, _tv: (u64, u64)) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn truncate(&self, _path: &Path, _size: u64) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn readlink(&self, _path: &Path) -> io::Result<OsString> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn symlink(&self, _target: &Path, _linkpath: &Path) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn link(&self, _target: &Path, _newpath: &Path) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn rename(&self, _from: &Path, _to: &Path) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn remove_file(&self, _path: &Path) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn remove_dir(&self, _path: &Path) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn create_dir(&self, _path: &Path, _mode: u32) -> io::Result<()> {
|
||||
syserr(libc::EROFS)
|
||||
}
|
||||
|
||||
fn readdir_populate(&self, path: &Path) -> io::Result<Directory> {
|
||||
let node = self.lookup(path)?;
|
||||
node.populate_directory()
|
||||
}
|
||||
}
|
||||
|
||||
fn rawerr(errno: i32) -> io::Error {
|
||||
io::Error::from_raw_os_error(errno)
|
||||
}
|
||||
fn syserr<T>(errno: i32) -> io::Result<T> {
|
||||
Err(rawerr(errno))
|
||||
}
|
Loading…
Reference in New Issue
Block a user