Skip to main content

dav_server/
memls.rs

1//! Simple in-memory locksystem.
2//!
3//! This implementation has state - 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 `MemLs::new`, store
7//! it in your handler struct, and clone() it every time you pass
8//! it to the DavHandler. As a MemLs struct is just a handle, cloning is cheap.
9use std::collections::HashMap;
10use std::future;
11use std::sync::{Arc, Mutex};
12use std::time::{Duration, SystemTime};
13
14use futures_util::FutureExt;
15use uuid::Uuid;
16use xmltree::Element;
17
18use crate::davpath::DavPath;
19use crate::fs::FsResult;
20use crate::ls::*;
21use crate::tree;
22
23type Tree = tree::Tree<Vec<u8>, Vec<DavLock>>;
24
25/// Ephemeral in-memory LockSystem.
26#[derive(Debug, Clone)]
27pub struct MemLs(Arc<Mutex<MemLsInner>>);
28
29#[derive(Debug)]
30struct MemLsInner {
31    tree: Tree,
32    #[allow(dead_code)]
33    locks: HashMap<Vec<u8>, u64>,
34}
35
36impl MemLs {
37    /// Create a new "memls" locksystem.
38    pub fn new() -> Box<MemLs> {
39        let inner = MemLsInner {
40            tree: Tree::new(Vec::new()),
41            locks: HashMap::new(),
42        };
43        Box::new(MemLs(Arc::new(Mutex::new(inner))))
44    }
45}
46
47impl DavLockSystem for MemLs {
48    fn lock(
49        &'_ self,
50        path: &DavPath,
51        principal: Option<&str>,
52        owner: Option<&Element>,
53        timeout: Option<Duration>,
54        shared: bool,
55        deep: bool,
56    ) -> LsFuture<'_, Result<DavLock, DavLock>> {
57        let inner = &mut *self.0.lock().unwrap();
58
59        // any locks in the path?
60        let rc = check_locks_to_path(&inner.tree, path, None, true, &Vec::new(), shared);
61        trace!("lock: check_locks_to_path: {rc:?}");
62        if let Err(err) = rc {
63            return future::ready(Err(err)).boxed();
64        }
65
66        // if it's a deep lock we need to check if there are locks furter along the path.
67        if deep {
68            let rc = check_locks_from_path(&inner.tree, path, None, true, &Vec::new(), shared);
69            trace!("lock: check_locks_from_path: {rc:?}");
70            if let Err(err) = rc {
71                return future::ready(Err(err)).boxed();
72            }
73        }
74
75        // create lock.
76        let node = get_or_create_path_node(&mut inner.tree, path);
77        let timeout_at = timeout.map(|d| SystemTime::now() + d);
78        let lock = DavLock {
79            token: Uuid::new_v4().urn().to_string(),
80            path: Box::new(path.clone()),
81            principal: principal.map(|s| s.to_string()),
82            owner: owner.map(|o| Box::new(o.clone())),
83            timeout_at,
84            timeout,
85            shared,
86            deep,
87        };
88        trace!("lock {} created", &lock.token);
89        let slock = lock.clone();
90        node.push(slock);
91        future::ready(Ok(lock)).boxed()
92    }
93
94    fn unlock(&'_ self, path: &DavPath, token: &str) -> LsFuture<'_, Result<(), ()>> {
95        let inner = &mut *self.0.lock().unwrap();
96        let node_id = match lookup_lock(&inner.tree, path, token) {
97            None => {
98                trace!("unlock: {token} not found at {path}");
99                return future::ready(Err(())).boxed();
100            }
101            Some(n) => n,
102        };
103        let len = {
104            let node = inner.tree.get_node_mut(node_id).unwrap();
105            let idx = node.iter().position(|n| n.token.as_str() == token).unwrap();
106            node.remove(idx);
107            node.len()
108        };
109        if len == 0 {
110            inner.tree.delete_node(node_id).ok();
111        }
112        future::ready(Ok(())).boxed()
113    }
114
115    fn refresh(
116        &'_ self,
117        path: &DavPath,
118        token: &str,
119        timeout: Option<Duration>,
120    ) -> LsFuture<'_, Result<DavLock, ()>> {
121        trace!("refresh lock {token}");
122        let inner = &mut *self.0.lock().unwrap();
123        let node_id = match lookup_lock(&inner.tree, path, token) {
124            None => {
125                trace!("lock not found");
126                return future::ready(Err(())).boxed();
127            }
128            Some(n) => n,
129        };
130        let node = inner.tree.get_node_mut(node_id).unwrap();
131        let idx = node.iter().position(|n| n.token.as_str() == token).unwrap();
132        let lock = &mut node[idx];
133        let timeout_at = timeout.map(|d| SystemTime::now() + d);
134        lock.timeout = timeout;
135        lock.timeout_at = timeout_at;
136        future::ready(Ok(lock.clone())).boxed()
137    }
138
139    fn check(
140        &'_ self,
141        path: &DavPath,
142        principal: Option<&str>,
143        ignore_principal: bool,
144        deep: bool,
145        submitted_tokens: Vec<&str>,
146    ) -> LsFuture<'_, Result<(), DavLock>> {
147        let inner = &*self.0.lock().unwrap();
148        let _st = submitted_tokens.clone();
149        let rc = check_locks_to_path(
150            &inner.tree,
151            path,
152            principal,
153            ignore_principal,
154            &submitted_tokens,
155            false,
156        );
157        trace!("check: check_lock_to_path: {_st:?}: {rc:?}");
158        if let Err(err) = rc {
159            return future::ready(Err(err)).boxed();
160        }
161
162        // if it's a deep lock we need to check if there are locks furter along the path.
163        if deep {
164            let rc = check_locks_from_path(
165                &inner.tree,
166                path,
167                principal,
168                ignore_principal,
169                &submitted_tokens,
170                false,
171            );
172            trace!("check: check_locks_from_path: {rc:?}");
173            if let Err(err) = rc {
174                return future::ready(Err(err)).boxed();
175            }
176        }
177        future::ready(Ok(())).boxed()
178    }
179
180    fn discover(&'_ self, path: &DavPath) -> LsFuture<'_, Vec<DavLock>> {
181        let inner = &*self.0.lock().unwrap();
182        future::ready(list_locks(&inner.tree, path)).boxed()
183    }
184
185    fn delete(&'_ self, path: &DavPath) -> LsFuture<'_, Result<(), ()>> {
186        let inner = &mut *self.0.lock().unwrap();
187        if let Some(node_id) = lookup_node(&inner.tree, path) {
188            inner.tree.delete_subtree(node_id).ok();
189        }
190        future::ready(Ok(())).boxed()
191    }
192}
193
194// check if there are any locks along the path.
195fn check_locks_to_path(
196    tree: &Tree,
197    path: &DavPath,
198    principal: Option<&str>,
199    ignore_principal: bool,
200    submitted_tokens: &[&str],
201    shared_ok: bool,
202) -> Result<(), DavLock> {
203    // path segments
204    let segs = path_to_segs(path, true);
205    let last_seg = segs.len() - 1;
206
207    // state
208    let mut holds_lock = false;
209    let mut first_lock_seen: Option<&DavLock> = None;
210
211    // walk over path segments starting at root.
212    let mut node_id = tree::ROOT_ID;
213    for (i, seg) in segs.into_iter().enumerate() {
214        node_id = match get_child(tree, node_id, seg) {
215            Ok(n) => n,
216            Err(_) => break,
217        };
218        let node_locks = match tree.get_node(node_id) {
219            Ok(n) => n,
220            Err(_) => break,
221        };
222
223        for nl in node_locks {
224            if i < last_seg && !nl.deep {
225                continue;
226            }
227            if submitted_tokens.iter().any(|t| &nl.token == t)
228                && (ignore_principal || principal == nl.principal.as_deref())
229            {
230                // fine, we hold this lock.
231                holds_lock = true;
232            } else {
233                // exclusive locks are fatal.
234                if !nl.shared {
235                    return Err(nl.to_owned());
236                }
237                // remember first shared lock seen.
238                if !shared_ok {
239                    first_lock_seen.get_or_insert(nl);
240                }
241            }
242        }
243    }
244
245    // return conflicting lock on error.
246    if !holds_lock && let Some(first_lock_seen) = first_lock_seen {
247        return Err(first_lock_seen.to_owned());
248    }
249
250    Ok(())
251}
252
253// See if there are locks in any path below this collection.
254fn check_locks_from_path(
255    tree: &Tree,
256    path: &DavPath,
257    principal: Option<&str>,
258    ignore_principal: bool,
259    submitted_tokens: &[&str],
260    shared_ok: bool,
261) -> Result<(), DavLock> {
262    let node_id = match lookup_node(tree, path) {
263        Some(id) => id,
264        None => return Ok(()),
265    };
266    check_locks_from_node(
267        tree,
268        node_id,
269        principal,
270        ignore_principal,
271        submitted_tokens,
272        shared_ok,
273    )
274}
275
276// See if there are locks in any nodes below this node.
277fn check_locks_from_node(
278    tree: &Tree,
279    node_id: u64,
280    principal: Option<&str>,
281    ignore_principal: bool,
282    submitted_tokens: &[&str],
283    shared_ok: bool,
284) -> Result<(), DavLock> {
285    let node_locks = match tree.get_node(node_id) {
286        Ok(n) => n,
287        Err(_) => return Ok(()),
288    };
289    for nl in node_locks {
290        if (!nl.shared || !shared_ok)
291            && (!submitted_tokens.iter().any(|t| t == &nl.token)
292                || (!ignore_principal && principal != nl.principal.as_deref()))
293        {
294            return Err(nl.to_owned());
295        }
296    }
297    if let Ok(children) = tree.get_children(node_id) {
298        for (_, node_id) in children {
299            check_locks_from_node(
300                tree,
301                node_id,
302                principal,
303                ignore_principal,
304                submitted_tokens,
305                shared_ok,
306            )?;
307        }
308    }
309    Ok(())
310}
311
312// Find or create node.
313fn get_or_create_path_node<'a>(tree: &'a mut Tree, path: &DavPath) -> &'a mut Vec<DavLock> {
314    let mut node_id = tree::ROOT_ID;
315    for seg in path_to_segs(path, false) {
316        node_id = match tree.get_child(node_id, seg) {
317            Ok(n) => n,
318            Err(_) => tree
319                .add_child(node_id, seg.to_vec(), Vec::new(), false)
320                .unwrap(),
321        };
322    }
323    tree.get_node_mut(node_id).unwrap()
324}
325
326// Find lock in path.
327fn lookup_lock(tree: &Tree, path: &DavPath, token: &str) -> Option<u64> {
328    trace!("lookup_lock: {token}");
329
330    let mut node_id = tree::ROOT_ID;
331    for seg in path_to_segs(path, true) {
332        trace!(
333            "lookup_lock: node {} seg {}",
334            node_id,
335            String::from_utf8_lossy(seg)
336        );
337        node_id = match get_child(tree, node_id, seg) {
338            Ok(n) => n,
339            Err(_) => break,
340        };
341        let node = tree.get_node(node_id).unwrap();
342        trace!("lookup_lock: locks here: {:?}", &node);
343        if node.iter().any(|n| n.token == token) {
344            return Some(node_id);
345        }
346    }
347    trace!("lookup_lock: fail");
348    None
349}
350
351// Find node ID for path.
352fn lookup_node(tree: &Tree, path: &DavPath) -> Option<u64> {
353    let mut node_id = tree::ROOT_ID;
354    for seg in path_to_segs(path, false) {
355        node_id = match tree.get_child(node_id, seg) {
356            Ok(n) => n,
357            Err(_) => return None,
358        };
359    }
360    Some(node_id)
361}
362
363// Find all locks in a path
364fn list_locks(tree: &Tree, path: &DavPath) -> Vec<DavLock> {
365    let mut locks = Vec::new();
366
367    let mut node_id = tree::ROOT_ID;
368    if let Ok(node) = tree.get_node(node_id) {
369        locks.extend_from_slice(node);
370    }
371    for seg in path_to_segs(path, false) {
372        node_id = match tree.get_child(node_id, seg) {
373            Ok(n) => n,
374            Err(_) => break,
375        };
376        if let Ok(node) = tree.get_node(node_id) {
377            locks.extend_from_slice(node);
378        }
379    }
380    locks
381}
382
383fn path_to_segs(path: &DavPath, include_root: bool) -> Vec<&[u8]> {
384    let path = path.as_bytes();
385    let mut segs: Vec<&[u8]> = path
386        .split(|&c| c == b'/')
387        .filter(|s| !s.is_empty())
388        .collect();
389    if include_root {
390        segs.insert(0, b"");
391    }
392    segs
393}
394
395fn get_child(tree: &Tree, node_id: u64, seg: &[u8]) -> FsResult<u64> {
396    if seg.is_empty() {
397        return Ok(node_id);
398    }
399    tree.get_child(node_id, seg)
400}