use std::collections::HashMap;
use std::io::{Error, ErrorKind, SeekFrom};
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use futures::{
future,
future::{BoxFuture, FutureExt},
};
use http::StatusCode;
use crate::fs::*;
use crate::tree;
use crate::webpath::WebPath;
type Tree = tree::Tree<Vec<u8>, MemFsNode>;
#[derive(Debug)]
pub struct MemFs {
tree: Arc<Mutex<Tree>>,
}
#[derive(Debug, Clone)]
enum MemFsNode {
Dir(MemFsDirNode),
File(MemFsFileNode),
}
#[derive(Debug, Clone)]
struct MemFsDirNode {
props: HashMap<String, DavProp>,
mtime: SystemTime,
crtime: SystemTime,
}
#[derive(Debug, Clone)]
struct MemFsFileNode {
props: HashMap<String, DavProp>,
mtime: SystemTime,
crtime: SystemTime,
data: Vec<u8>,
}
#[derive(Debug, Clone)]
struct MemFsDirEntry {
mtime: SystemTime,
crtime: SystemTime,
is_dir: bool,
name: Vec<u8>,
size: u64,
}
#[derive(Debug)]
struct MemFsFile {
tree: Arc<Mutex<Tree>>,
node_id: u64,
pos: usize,
append: bool,
}
impl MemFs {
pub fn new() -> Box<MemFs> {
let root = MemFsNode::new_dir();
Box::new(MemFs {
tree: Arc::new(Mutex::new(Tree::new(root))),
})
}
fn do_open(&self, tree: &mut Tree, path: &[u8], options: OpenOptions) -> FsResult<Box<dyn DavFile>> {
let node_id = match tree.lookup(path) {
Ok(n) => {
if options.create_new {
return Err(FsError::Exists);
}
n
},
Err(FsError::NotFound) => {
if !options.create {
return Err(FsError::NotFound);
}
let parent_id = tree.lookup_parent(path)?;
tree.add_child(parent_id, file_name(path), MemFsNode::new_file(), true)?
},
Err(e) => return Err(e),
};
let node = tree.get_node_mut(node_id).unwrap();
if node.is_dir() {
return Err(FsError::Forbidden);
}
if options.truncate {
node.as_file_mut()?.data.truncate(0);
node.update_mtime(SystemTime::now());
}
Ok(Box::new(MemFsFile {
tree: self.tree.clone(),
node_id: node_id,
pos: 0,
append: options.append,
}))
}
}
impl Clone for MemFs {
fn clone(&self) -> Self {
MemFs {
tree: Arc::clone(&self.tree),
}
}
}
impl DavFileSystem for MemFs {
fn metadata<'a>(&'a self, path: &'a WebPath) -> FsFuture<Box<dyn DavMetaData>> {
async move {
let tree = &*self.tree.lock().unwrap();
let node_id = tree.lookup(path.as_bytes())?;
let meta = tree.get_node(node_id)?.as_dirent(path.as_bytes());
Ok(Box::new(meta) as Box<dyn DavMetaData>)
}
.boxed()
}
fn read_dir<'a>(
&'a self,
path: &'a WebPath,
_meta: ReadDirMeta,
) -> FsFuture<FsStream<Box<dyn DavDirEntry>>>
{
async move {
let tree = &*self.tree.lock().unwrap();
let node_id = tree.lookup(path.as_bytes())?;
if !tree.get_node(node_id)?.is_dir() {
return Err(FsError::Forbidden);
}
let mut v: Vec<Box<dyn DavDirEntry>> = Vec::new();
for (name, dnode_id) in tree.get_children(node_id)? {
if let Ok(node) = tree.get_node(dnode_id) {
v.push(Box::new(node.as_dirent(&name)));
}
}
let strm = futures::stream::iter(v.into_iter());
Ok(Box::pin(strm) as FsStream<Box<dyn DavDirEntry>>)
}
.boxed()
}
fn open<'a>(&'a self, path: &'a WebPath, options: OpenOptions) -> FsFuture<Box<dyn DavFile>> {
async move {
let tree = &mut *self.tree.lock().unwrap();
self.do_open(tree, path.as_bytes(), options)
}
.boxed()
}
fn create_dir<'a>(&'a self, path: &'a WebPath) -> FsFuture<()> {
async move {
debug!("FS: create_dir {:?}", path);
let tree = &mut *self.tree.lock().unwrap();
let path = path.as_bytes();
let parent_id = tree.lookup_parent(path)?;
tree.add_child(parent_id, file_name(path), MemFsNode::new_dir(), false)?;
tree.get_node_mut(parent_id)?.update_mtime(SystemTime::now());
Ok(())
}
.boxed()
}
fn remove_file<'a>(&'a self, path: &'a WebPath) -> FsFuture<()> {
async move {
let tree = &mut *self.tree.lock().unwrap();
let parent_id = tree.lookup_parent(path.as_bytes())?;
let node_id = tree.lookup(path.as_bytes())?;
tree.delete_node(node_id)?;
tree.get_node_mut(parent_id)?.update_mtime(SystemTime::now());
Ok(())
}
.boxed()
}
fn remove_dir<'a>(&'a self, path: &'a WebPath) -> FsFuture<()> {
async move {
let tree = &mut *self.tree.lock().unwrap();
let parent_id = tree.lookup_parent(path.as_bytes())?;
let node_id = tree.lookup(path.as_bytes())?;
tree.delete_node(node_id)?;
tree.get_node_mut(parent_id)?.update_mtime(SystemTime::now());
Ok(())
}
.boxed()
}
fn rename<'a>(&'a self, from: &'a WebPath, to: &'a WebPath) -> FsFuture<()> {
async move {
let tree = &mut *self.tree.lock().unwrap();
let node_id = tree.lookup(from.as_bytes())?;
let parent_id = tree.lookup_parent(from.as_bytes())?;
let dst_id = tree.lookup_parent(to.as_bytes())?;
tree.move_node(node_id, dst_id, file_name(to.as_bytes()), true)?;
tree.get_node_mut(parent_id)?.update_mtime(SystemTime::now());
tree.get_node_mut(dst_id)?.update_mtime(SystemTime::now());
Ok(())
}
.boxed()
}
fn copy<'a>(&'a self, from: &'a WebPath, to: &'a WebPath) -> FsFuture<()> {
async move {
let tree = &mut *self.tree.lock().unwrap();
let snode_id = tree.lookup(from.as_bytes())?;
{
let mut oo = OpenOptions::write();
oo.create = true;
self.do_open(tree, to.as_bytes(), oo)?;
}
let dnode_id = tree.lookup(to.as_bytes())?;
let mut data = (*tree.get_node_mut(snode_id)?).clone();
match data {
MemFsNode::Dir(ref mut d) => d.crtime = SystemTime::now(),
MemFsNode::File(ref mut f) => f.crtime = SystemTime::now(),
}
*tree.get_node_mut(dnode_id)? = data;
Ok(())
}
.boxed()
}
fn have_props<'a>(&'a self, _path: &'a WebPath) -> BoxFuture<'a, bool> {
future::ready(true).boxed()
}
fn patch_props<'a>(
&'a self,
path: &'a WebPath,
mut patch: Vec<(bool, DavProp)>,
) -> FsFuture<Vec<(StatusCode, DavProp)>>
{
async move {
let tree = &mut *self.tree.lock().unwrap();
let node_id = tree.lookup(path.as_bytes())?;
let node = tree.get_node_mut(node_id)?;
let props = node.get_props_mut();
let mut res = Vec::new();
let patch = patch.drain(..).collect::<Vec<_>>();
for (set, p) in patch.into_iter() {
let prop = cloneprop(&p);
let status = if set {
props.insert(propkey(&p.namespace, &p.name), p);
StatusCode::OK
} else {
props.remove(&propkey(&p.namespace, &p.name));
StatusCode::OK
};
res.push((status, prop));
}
Ok(res)
}
.boxed()
}
fn get_props<'a>(&'a self, path: &'a WebPath, do_content: bool) -> FsFuture<Vec<DavProp>> {
async move {
let tree = &mut *self.tree.lock().unwrap();
let node_id = tree.lookup(path.as_bytes())?;
let node = tree.get_node(node_id)?;
let mut res = Vec::new();
for (_, p) in node.get_props() {
res.push(if do_content { p.clone() } else { cloneprop(p) });
}
Ok(res)
}
.boxed()
}
fn get_prop<'a>(&'a self, path: &'a WebPath, prop: DavProp) -> FsFuture<Vec<u8>> {
async move {
let tree = &mut *self.tree.lock().unwrap();
let node_id = tree.lookup(path.as_bytes())?;
let node = tree.get_node(node_id)?;
let p = node
.get_props()
.get(&propkey(&prop.namespace, &prop.name))
.ok_or(FsError::NotFound)?;
Ok(p.xml.clone().ok_or(FsError::NotFound)?)
}
.boxed()
}
}
fn propkey(ns: &Option<String>, name: &str) -> String {
ns.to_owned().as_ref().unwrap_or(&"".to_string()).clone() + name
}
fn cloneprop(p: &DavProp) -> DavProp {
DavProp {
name: p.name.clone(),
namespace: p.namespace.clone(),
prefix: p.prefix.clone(),
xml: None,
}
}
impl DavDirEntry for MemFsDirEntry {
fn metadata<'a>(&'a self) -> FsFuture<Box<dyn DavMetaData>> {
let meta = (*self).clone();
Box::pin(future::ok(Box::new(meta) as Box<dyn DavMetaData>))
}
fn name(&self) -> Vec<u8> {
self.name.clone()
}
}
impl DavFile for MemFsFile {
fn metadata<'a>(&'a self) -> FsFuture<Box<dyn DavMetaData>> {
async move {
let tree = &*self.tree.lock().unwrap();
let node = tree.get_node(self.node_id)?;
let meta = node.as_dirent(b"");
Ok(Box::new(meta) as Box<dyn DavMetaData>)
}
.boxed()
}
fn read_bytes<'a>(&'a mut self, buf: &'a mut [u8]) -> FsFuture<usize> {
async move {
let tree = &*self.tree.lock().unwrap();
let node = tree.get_node(self.node_id)?;
let file = node.as_file()?;
let curlen = file.data.len();
let mut start = self.pos;
let mut end = self.pos + buf.len();
if start > curlen {
start = curlen
}
if end > curlen {
end = curlen
}
let cnt = end - start;
buf[..cnt].copy_from_slice(&file.data[start..end]);
Ok(cnt)
}
.boxed()
}
fn write_bytes<'a>(&'a mut self, buf: &'a [u8]) -> FsFuture<usize> {
async move {
let tree = &mut *self.tree.lock().unwrap();
let node = tree.get_node_mut(self.node_id)?;
let file = node.as_file_mut()?;
let start = if self.append { file.data.len() } else { self.pos };
let end = start + buf.len();
if end > file.data.len() {
file.data.resize(end, 0);
}
file.data[start..end].copy_from_slice(buf);
Ok(end - start)
}
.boxed()
}
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> FsFuture<()> {
async move {
self.write_bytes(buf).await?;
Ok(())
}
.boxed()
}
fn flush<'a>(&'a mut self) -> FsFuture<()> {
future::ok(()).boxed()
}
fn seek<'a>(&'a mut self, pos: SeekFrom) -> FsFuture<u64> {
async move {
let (start, offset): (u64, i64) = match pos {
SeekFrom::Start(npos) => {
self.pos = npos as usize;
return Ok(npos);
},
SeekFrom::Current(npos) => (self.pos as u64, npos),
SeekFrom::End(npos) => {
let tree = &*self.tree.lock().unwrap();
let node = tree.get_node(self.node_id)?;
let curlen = node.as_file()?.data.len() as u64;
(curlen, npos)
},
};
if offset < 0 {
if -offset as u64 > start {
return Err(Error::new(ErrorKind::InvalidInput, "invalid seek").into());
}
self.pos = (start - (-offset as u64)) as usize;
} else {
self.pos = (start + offset as u64) as usize;
}
Ok(self.pos as u64)
}
.boxed()
}
}
impl DavMetaData for MemFsDirEntry {
fn len(&self) -> u64 {
self.size
}
fn created(&self) -> FsResult<SystemTime> {
Ok(self.crtime)
}
fn modified(&self) -> FsResult<SystemTime> {
Ok(self.mtime)
}
fn is_dir(&self) -> bool {
self.is_dir
}
}
impl MemFsNode {
fn new_dir() -> MemFsNode {
MemFsNode::Dir(MemFsDirNode {
crtime: SystemTime::now(),
mtime: SystemTime::now(),
props: HashMap::new(),
})
}
fn new_file() -> MemFsNode {
MemFsNode::File(MemFsFileNode {
crtime: SystemTime::now(),
mtime: SystemTime::now(),
props: HashMap::new(),
data: Vec::new(),
})
}
fn as_dirent(&self, name: &[u8]) -> MemFsDirEntry {
let (is_dir, size, mtime, crtime) = match self {
&MemFsNode::File(ref file) => (false, file.data.len() as u64, file.mtime, file.crtime),
&MemFsNode::Dir(ref dir) => (true, 0, dir.mtime, dir.crtime),
};
MemFsDirEntry {
name: name.to_vec(),
mtime: mtime,
crtime: crtime,
is_dir: is_dir,
size: size as u64,
}
}
fn update_mtime(&mut self, tm: std::time::SystemTime) {
match self {
&mut MemFsNode::Dir(ref mut d) => d.mtime = tm,
&mut MemFsNode::File(ref mut f) => f.mtime = tm,
}
}
fn is_dir(&self) -> bool {
match self {
&MemFsNode::Dir(_) => true,
&MemFsNode::File(_) => false,
}
}
fn as_file(&self) -> FsResult<&MemFsFileNode> {
match self {
&MemFsNode::File(ref n) => Ok(n),
_ => Err(FsError::Forbidden),
}
}
fn as_file_mut(&mut self) -> FsResult<&mut MemFsFileNode> {
match self {
&mut MemFsNode::File(ref mut n) => Ok(n),
_ => Err(FsError::Forbidden),
}
}
fn get_props(&self) -> &HashMap<String, DavProp> {
match self {
&MemFsNode::File(ref n) => &n.props,
&MemFsNode::Dir(ref d) => &d.props,
}
}
fn get_props_mut(&mut self) -> &mut HashMap<String, DavProp> {
match self {
&mut MemFsNode::File(ref mut n) => &mut n.props,
&mut MemFsNode::Dir(ref mut d) => &mut d.props,
}
}
}
trait TreeExt {
fn lookup_segs(&self, segs: Vec<&[u8]>) -> FsResult<u64>;
fn lookup(&self, path: &[u8]) -> FsResult<u64>;
fn lookup_parent(&self, path: &[u8]) -> FsResult<u64>;
}
impl TreeExt for Tree {
fn lookup_segs(&self, segs: Vec<&[u8]>) -> FsResult<u64> {
let mut node_id = tree::ROOT_ID;
let mut is_dir = true;
for seg in segs.into_iter() {
if !is_dir {
return Err(FsError::Forbidden);
}
if self.get_node(node_id)?.is_dir() {
node_id = self.get_child(node_id, seg)?;
} else {
is_dir = false;
}
}
Ok(node_id)
}
fn lookup(&self, path: &[u8]) -> FsResult<u64> {
self.lookup_segs(path.split(|&c| c == b'/').filter(|s| s.len() > 0).collect())
}
fn lookup_parent(&self, path: &[u8]) -> FsResult<u64> {
let mut segs: Vec<&[u8]> = path.split(|&c| c == b'/').filter(|s| s.len() > 0).collect();
segs.pop();
let node_id = self.lookup_segs(segs)?;
if !self.get_node(node_id)?.is_dir() {
return Err(FsError::Forbidden);
}
Ok(node_id)
}
}
fn file_name(path: &[u8]) -> Vec<u8> {
path.split(|&c| c == b'/')
.filter(|s| s.len() > 0)
.last()
.unwrap_or(b"")
.to_vec()
}