Skip to main content

dav_server/
memfs.rs

1//! Simple in-memory filesystem.
2//!
3//! This implementation has state, so if you create a
4//! new instance in a handler(), it will be empty every time.
5//!
6//! This means you have to create the instance once, using `MemFs::new`, store
7//! it in your handler struct, and clone() it every time you pass
8//! it to the DavHandler. As a MemFs struct is just a handle, cloning is cheap.
9use std::collections::HashMap;
10use std::io::{Error, ErrorKind, SeekFrom};
11use std::sync::{Arc, Mutex};
12use std::time::SystemTime;
13
14use bytes::{Buf, Bytes};
15use futures_util::{
16    StreamExt, future,
17    future::{BoxFuture, FutureExt},
18};
19use http::StatusCode;
20
21use crate::davpath::DavPath;
22use crate::fs::*;
23use crate::tree;
24
25type Tree = tree::Tree<Vec<u8>, MemFsNode>;
26
27/// Ephemeral in-memory filesystem.
28#[derive(Debug)]
29pub struct MemFs {
30    tree: Arc<Mutex<Tree>>,
31}
32
33#[derive(Debug, Clone)]
34enum MemFsNode {
35    Dir(MemFsDirNode),
36    File(MemFsFileNode),
37}
38
39#[derive(Debug, Clone)]
40struct MemFsDirNode {
41    props: HashMap<String, DavProp>,
42    mtime: SystemTime,
43    crtime: SystemTime,
44}
45
46#[derive(Debug, Clone)]
47struct MemFsFileNode {
48    props: HashMap<String, DavProp>,
49    mtime: SystemTime,
50    crtime: SystemTime,
51    data: Vec<u8>,
52}
53
54#[derive(Debug, Clone)]
55struct MemFsDirEntry {
56    mtime: SystemTime,
57    crtime: SystemTime,
58    is_dir: bool,
59    name: Vec<u8>,
60    size: u64,
61}
62
63#[derive(Debug)]
64struct MemFsFile {
65    tree: Arc<Mutex<Tree>>,
66    node_id: u64,
67    pos: usize,
68    append: bool,
69}
70
71impl MemFs {
72    /// Create a new "memfs" filesystem.
73    pub fn new() -> Box<MemFs> {
74        #[allow(unused_mut)]
75        let mut tree = Tree::new(MemFsNode::new_dir());
76
77        #[cfg(feature = "caldav")]
78        {
79            tree.add_child(
80                tree::ROOT_ID,
81                b"calendars".to_vec(),
82                MemFsNode::new_dir(),
83                false,
84            )
85            .unwrap();
86        }
87
88        #[cfg(feature = "carddav")]
89        {
90            tree.add_child(
91                tree::ROOT_ID,
92                b"addressbooks".to_vec(),
93                MemFsNode::new_dir(),
94                false,
95            )
96            .unwrap();
97        }
98
99        Box::new(MemFs {
100            tree: Arc::new(Mutex::new(tree)),
101        })
102    }
103
104    fn do_open(
105        &self,
106        tree: &mut Tree,
107        path: &[u8],
108        options: OpenOptions,
109    ) -> FsResult<Box<dyn DavFile>> {
110        let node_id = match tree.lookup(path) {
111            Ok(n) => {
112                if options.create_new {
113                    return Err(FsError::Exists);
114                }
115                n
116            }
117            Err(FsError::NotFound) => {
118                if !options.create {
119                    return Err(FsError::NotFound);
120                }
121                let parent_id = tree.lookup_parent(path)?;
122                tree.add_child(parent_id, file_name(path), MemFsNode::new_file(), true)?
123            }
124            Err(e) => return Err(e),
125        };
126        let node = tree.get_node_mut(node_id).unwrap();
127        if node.is_dir() {
128            return Err(FsError::Forbidden);
129        }
130        if options.truncate {
131            node.as_file_mut()?.data.truncate(0);
132            node.update_mtime(SystemTime::now());
133        }
134        Ok(Box::new(MemFsFile {
135            tree: self.tree.clone(),
136            node_id,
137            pos: 0,
138            append: options.append,
139        }))
140    }
141}
142
143impl Clone for MemFs {
144    fn clone(&self) -> Self {
145        MemFs {
146            tree: Arc::clone(&self.tree),
147        }
148    }
149}
150
151impl DavFileSystem for MemFs {
152    fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
153        async move {
154            let tree = &*self.tree.lock().unwrap();
155            let node_id = tree.lookup(path.as_bytes())?;
156            let meta = tree.get_node(node_id)?.as_dirent(path.as_bytes());
157            Ok(Box::new(meta) as Box<dyn DavMetaData>)
158        }
159        .boxed()
160    }
161
162    fn read_dir<'a>(
163        &'a self,
164        path: &'a DavPath,
165        _meta: ReadDirMeta,
166    ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>> {
167        async move {
168            let tree = &*self.tree.lock().unwrap();
169            let node_id = tree.lookup(path.as_bytes())?;
170            if !tree.get_node(node_id)?.is_dir() {
171                return Err(FsError::Forbidden);
172            }
173            let mut v: Vec<Box<dyn DavDirEntry>> = Vec::new();
174            for (name, dnode_id) in tree.get_children(node_id)? {
175                if let Ok(node) = tree.get_node(dnode_id) {
176                    v.push(Box::new(node.as_dirent(&name)));
177                }
178            }
179            let strm = futures_util::stream::iter(v).map(Ok);
180            Ok(Box::pin(strm) as FsStream<Box<dyn DavDirEntry>>)
181        }
182        .boxed()
183    }
184
185    fn open<'a>(
186        &'a self,
187        path: &'a DavPath,
188        options: OpenOptions,
189    ) -> FsFuture<'a, Box<dyn DavFile>> {
190        async move {
191            let tree = &mut *self.tree.lock().unwrap();
192            self.do_open(tree, path.as_bytes(), options)
193        }
194        .boxed()
195    }
196
197    fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
198        async move {
199            trace!("FS: create_dir {path:?}");
200            let tree = &mut *self.tree.lock().unwrap();
201            let path = path.as_bytes();
202            let parent_id = tree.lookup_parent(path)?;
203            tree.add_child(parent_id, file_name(path), MemFsNode::new_dir(), false)?;
204            tree.get_node_mut(parent_id)?
205                .update_mtime(SystemTime::now());
206            Ok(())
207        }
208        .boxed()
209    }
210
211    fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
212        async move {
213            let tree = &mut *self.tree.lock().unwrap();
214            let parent_id = tree.lookup_parent(path.as_bytes())?;
215            let node_id = tree.lookup(path.as_bytes())?;
216            tree.delete_node(node_id)?;
217            tree.get_node_mut(parent_id)?
218                .update_mtime(SystemTime::now());
219            Ok(())
220        }
221        .boxed()
222    }
223
224    fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
225        async move {
226            let tree = &mut *self.tree.lock().unwrap();
227            let parent_id = tree.lookup_parent(path.as_bytes())?;
228            let node_id = tree.lookup(path.as_bytes())?;
229            tree.delete_node(node_id)?;
230            tree.get_node_mut(parent_id)?
231                .update_mtime(SystemTime::now());
232            Ok(())
233        }
234        .boxed()
235    }
236
237    fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
238        async move {
239            let tree = &mut *self.tree.lock().unwrap();
240            let node_id = tree.lookup(from.as_bytes())?;
241            let parent_id = tree.lookup_parent(from.as_bytes())?;
242            let dst_id = tree.lookup_parent(to.as_bytes())?;
243            tree.move_node(node_id, dst_id, file_name(to.as_bytes()), true)?;
244            tree.get_node_mut(parent_id)?
245                .update_mtime(SystemTime::now());
246            tree.get_node_mut(dst_id)?.update_mtime(SystemTime::now());
247            Ok(())
248        }
249        .boxed()
250    }
251
252    fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
253        async move {
254            let tree = &mut *self.tree.lock().unwrap();
255
256            // source must exist.
257            let snode_id = tree.lookup(from.as_bytes())?;
258
259            // make sure destination exists, create if needed.
260            {
261                let mut oo = OpenOptions::write();
262                oo.create = true;
263                self.do_open(tree, to.as_bytes(), oo)?;
264            }
265            let dnode_id = tree.lookup(to.as_bytes())?;
266
267            // copy.
268            let mut data = (*tree.get_node_mut(snode_id)?).clone();
269            match data {
270                MemFsNode::Dir(ref mut d) => d.crtime = SystemTime::now(),
271                MemFsNode::File(ref mut f) => f.crtime = SystemTime::now(),
272            }
273            *tree.get_node_mut(dnode_id)? = data;
274
275            Ok(())
276        }
277        .boxed()
278    }
279
280    fn have_props<'a>(&'a self, _path: &'a DavPath) -> BoxFuture<'a, bool> {
281        future::ready(true).boxed()
282    }
283
284    fn patch_props<'a>(
285        &'a self,
286        path: &'a DavPath,
287        mut patch: Vec<(bool, DavProp)>,
288    ) -> FsFuture<'a, Vec<(StatusCode, DavProp)>> {
289        async move {
290            let tree = &mut *self.tree.lock().unwrap();
291            let node_id = tree.lookup(path.as_bytes())?;
292            let node = tree.get_node_mut(node_id)?;
293            let props = node.get_props_mut();
294
295            let mut res = Vec::new();
296
297            for (set, p) in patch.drain(..) {
298                let prop = cloneprop(&p);
299                let status = if set {
300                    props.insert(propkey(&p.namespace, &p.name), p);
301                    StatusCode::OK
302                } else {
303                    props.remove(&propkey(&p.namespace, &p.name));
304                    // the below map was added to signify if the remove succeeded or
305                    // failed. however it seems that removing non-existant properties
306                    // always succeed, so just return success.
307                    //  .map(|_| StatusCode::OK).unwrap_or(StatusCode::NOT_FOUND)
308                    StatusCode::OK
309                };
310                res.push((status, prop));
311            }
312            Ok(res)
313        }
314        .boxed()
315    }
316
317    fn get_props<'a>(&'a self, path: &'a DavPath, do_content: bool) -> FsFuture<'a, Vec<DavProp>> {
318        async move {
319            let tree = &mut *self.tree.lock().unwrap();
320            let node_id = tree.lookup(path.as_bytes())?;
321            let node = tree.get_node(node_id)?;
322            let mut res = Vec::new();
323            for p in node.get_props().values() {
324                res.push(if do_content { p.clone() } else { cloneprop(p) });
325            }
326            Ok(res)
327        }
328        .boxed()
329    }
330
331    fn get_prop<'a>(&'a self, path: &'a DavPath, prop: DavProp) -> FsFuture<'a, Vec<u8>> {
332        async move {
333            let tree = &mut *self.tree.lock().unwrap();
334            let node_id = tree.lookup(path.as_bytes())?;
335            let node = tree.get_node(node_id)?;
336            let p = node
337                .get_props()
338                .get(&propkey(&prop.namespace, &prop.name))
339                .ok_or(FsError::NotFound)?;
340            p.xml.clone().ok_or(FsError::NotFound)
341        }
342        .boxed()
343    }
344}
345
346// small helper.
347fn propkey(ns: &Option<String>, name: &str) -> String {
348    ns.to_owned().as_ref().unwrap_or(&"".to_string()).clone() + name
349}
350
351// small helper.
352fn cloneprop(p: &DavProp) -> DavProp {
353    DavProp {
354        name: p.name.clone(),
355        namespace: p.namespace.clone(),
356        prefix: p.prefix.clone(),
357        xml: None,
358    }
359}
360
361impl DavDirEntry for MemFsDirEntry {
362    fn metadata(&'_ self) -> FsFuture<'_, Box<dyn DavMetaData>> {
363        let meta = (*self).clone();
364        Box::pin(future::ok(Box::new(meta) as Box<dyn DavMetaData>))
365    }
366
367    fn name(&self) -> Vec<u8> {
368        self.name.clone()
369    }
370}
371
372impl DavFile for MemFsFile {
373    fn metadata(&'_ mut self) -> FsFuture<'_, Box<dyn DavMetaData>> {
374        async move {
375            let tree = &*self.tree.lock().unwrap();
376            let node = tree.get_node(self.node_id)?;
377            let meta = node.as_dirent(b"");
378            Ok(Box::new(meta) as Box<dyn DavMetaData>)
379        }
380        .boxed()
381    }
382
383    fn read_bytes(&'_ mut self, count: usize) -> FsFuture<'_, Bytes> {
384        async move {
385            let tree = &*self.tree.lock().unwrap();
386            let node = tree.get_node(self.node_id)?;
387            let file = node.as_file()?;
388            let curlen = file.data.len();
389            let mut start = self.pos;
390            let mut end = self.pos + count;
391            if start > curlen {
392                start = curlen
393            }
394            if end > curlen {
395                end = curlen
396            }
397            let cnt = end - start;
398            self.pos += cnt;
399            Ok(Bytes::copy_from_slice(&file.data[start..end]))
400        }
401        .boxed()
402    }
403
404    fn write_bytes(&'_ mut self, buf: Bytes) -> FsFuture<'_, ()> {
405        async move {
406            let tree = &mut *self.tree.lock().unwrap();
407            let node = tree.get_node_mut(self.node_id)?;
408            let file = node.as_file_mut()?;
409            if self.append {
410                self.pos = file.data.len();
411            }
412            let end = self.pos + buf.len();
413            if end > file.data.len() {
414                file.data.resize(end, 0);
415            }
416            file.data[self.pos..end].copy_from_slice(&buf);
417            self.pos = end;
418            Ok(())
419        }
420        .boxed()
421    }
422
423    fn write_buf(&'_ mut self, mut buf: Box<dyn Buf + Send>) -> FsFuture<'_, ()> {
424        async move {
425            let tree = &mut *self.tree.lock().unwrap();
426            let node = tree.get_node_mut(self.node_id)?;
427            let file = node.as_file_mut()?;
428            if self.append {
429                self.pos = file.data.len();
430            }
431            let end = self.pos + buf.remaining();
432            if end > file.data.len() {
433                file.data.resize(end, 0);
434            }
435            while buf.has_remaining() {
436                let b = buf.chunk();
437                let len = b.len();
438                file.data[self.pos..self.pos + len].copy_from_slice(b);
439                buf.advance(len);
440                self.pos += len;
441            }
442            Ok(())
443        }
444        .boxed()
445    }
446
447    fn flush(&'_ mut self) -> FsFuture<'_, ()> {
448        future::ok(()).boxed()
449    }
450
451    fn seek(&'_ mut self, pos: SeekFrom) -> FsFuture<'_, u64> {
452        async move {
453            let (start, offset): (u64, i64) = match pos {
454                SeekFrom::Start(npos) => {
455                    self.pos = npos as usize;
456                    return Ok(npos);
457                }
458                SeekFrom::Current(npos) => (self.pos as u64, npos),
459                SeekFrom::End(npos) => {
460                    let tree = &*self.tree.lock().unwrap();
461                    let node = tree.get_node(self.node_id)?;
462                    let curlen = node.as_file()?.data.len() as u64;
463                    (curlen, npos)
464                }
465            };
466            if offset < 0 {
467                if -offset as u64 > start {
468                    return Err(Error::new(ErrorKind::InvalidInput, "invalid seek").into());
469                }
470                self.pos = (start - (-offset as u64)) as usize;
471            } else {
472                self.pos = (start + offset as u64) as usize;
473            }
474            Ok(self.pos as u64)
475        }
476        .boxed()
477    }
478}
479
480impl DavMetaData for MemFsDirEntry {
481    fn len(&self) -> u64 {
482        self.size
483    }
484
485    fn created(&self) -> FsResult<SystemTime> {
486        Ok(self.crtime)
487    }
488
489    fn modified(&self) -> FsResult<SystemTime> {
490        Ok(self.mtime)
491    }
492
493    fn is_dir(&self) -> bool {
494        self.is_dir
495    }
496
497    #[cfg(feature = "caldav")]
498    fn is_calendar(&self, path: &DavPath) -> bool {
499        crate::caldav::is_path_in_caldav_directory(path)
500    }
501
502    #[cfg(feature = "carddav")]
503    fn is_addressbook(&self, path: &DavPath) -> bool {
504        crate::carddav::is_path_in_carddav_directory(path)
505    }
506}
507
508impl MemFsNode {
509    fn new_dir() -> MemFsNode {
510        MemFsNode::Dir(MemFsDirNode {
511            crtime: SystemTime::now(),
512            mtime: SystemTime::now(),
513            props: HashMap::new(),
514        })
515    }
516
517    fn new_file() -> MemFsNode {
518        MemFsNode::File(MemFsFileNode {
519            crtime: SystemTime::now(),
520            mtime: SystemTime::now(),
521            props: HashMap::new(),
522            data: Vec::new(),
523        })
524    }
525
526    // helper to create MemFsDirEntry from a node.
527    fn as_dirent(&self, name: &[u8]) -> MemFsDirEntry {
528        let (is_dir, size, mtime, crtime) = match *self {
529            MemFsNode::File(ref file) => (false, file.data.len() as u64, file.mtime, file.crtime),
530            MemFsNode::Dir(ref dir) => (true, 0, dir.mtime, dir.crtime),
531        };
532        MemFsDirEntry {
533            name: name.to_vec(),
534            mtime,
535            crtime,
536            is_dir,
537            size,
538        }
539    }
540
541    fn update_mtime(&mut self, tm: std::time::SystemTime) {
542        match *self {
543            MemFsNode::Dir(ref mut d) => d.mtime = tm,
544            MemFsNode::File(ref mut f) => f.mtime = tm,
545        }
546    }
547
548    fn is_dir(&self) -> bool {
549        match *self {
550            MemFsNode::Dir(_) => true,
551            MemFsNode::File(_) => false,
552        }
553    }
554
555    fn as_file(&self) -> FsResult<&MemFsFileNode> {
556        match *self {
557            MemFsNode::File(ref n) => Ok(n),
558            _ => Err(FsError::Forbidden),
559        }
560    }
561
562    fn as_file_mut(&mut self) -> FsResult<&mut MemFsFileNode> {
563        match *self {
564            MemFsNode::File(ref mut n) => Ok(n),
565            _ => Err(FsError::Forbidden),
566        }
567    }
568
569    fn get_props(&self) -> &HashMap<String, DavProp> {
570        match *self {
571            MemFsNode::File(ref n) => &n.props,
572            MemFsNode::Dir(ref d) => &d.props,
573        }
574    }
575
576    fn get_props_mut(&mut self) -> &mut HashMap<String, DavProp> {
577        match *self {
578            MemFsNode::File(ref mut n) => &mut n.props,
579            MemFsNode::Dir(ref mut d) => &mut d.props,
580        }
581    }
582}
583
584trait TreeExt {
585    fn lookup_segs(&self, segs: Vec<&[u8]>) -> FsResult<u64>;
586    fn lookup(&self, path: &[u8]) -> FsResult<u64>;
587    fn lookup_parent(&self, path: &[u8]) -> FsResult<u64>;
588}
589
590impl TreeExt for Tree {
591    fn lookup_segs(&self, segs: Vec<&[u8]>) -> FsResult<u64> {
592        let mut node_id = tree::ROOT_ID;
593        let mut is_dir = true;
594        for seg in segs.into_iter() {
595            if !is_dir {
596                return Err(FsError::Forbidden);
597            }
598            if self.get_node(node_id)?.is_dir() {
599                node_id = self.get_child(node_id, seg)?;
600            } else {
601                is_dir = false;
602            }
603        }
604        Ok(node_id)
605    }
606
607    fn lookup(&self, path: &[u8]) -> FsResult<u64> {
608        self.lookup_segs(
609            path.split(|&c| c == b'/')
610                .filter(|s| !s.is_empty())
611                .collect(),
612        )
613    }
614
615    // pop the last segment off the path, do a lookup, then
616    // check if the result is a directory.
617    fn lookup_parent(&self, path: &[u8]) -> FsResult<u64> {
618        let mut segs: Vec<&[u8]> = path
619            .split(|&c| c == b'/')
620            .filter(|s| !s.is_empty())
621            .collect();
622        segs.pop();
623        let node_id = self.lookup_segs(segs)?;
624        if !self.get_node(node_id)?.is_dir() {
625            return Err(FsError::Forbidden);
626        }
627        Ok(node_id)
628    }
629}
630
631// helper
632fn file_name(path: &[u8]) -> Vec<u8> {
633    path.split(|&c| c == b'/')
634        .rfind(|s| !s.is_empty())
635        .unwrap_or(b"")
636        .to_vec()
637}