dav_server/
fs.rs

1//! Contains the structs and traits that define a filesystem backend.
2//!
3//! You only need this if you are going to implement your own
4//! filesystem backend. Otherwise, just use 'LocalFs' or 'MemFs'.
5//!
6use std::fmt::Debug;
7use std::io::SeekFrom;
8use std::pin::Pin;
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use dyn_clone::{DynClone, clone_trait_object};
12use futures_util::{Future, FutureExt, Stream, TryFutureExt, future};
13use http::StatusCode;
14
15use crate::davpath::DavPath;
16
17macro_rules! notimplemented {
18    ($method:expr_2021) => {
19        Err(FsError::NotImplemented)
20    };
21}
22
23macro_rules! notimplemented_fut {
24    ($method:expr_2021) => {
25        Box::pin(future::ready(Err(FsError::NotImplemented)))
26    };
27}
28
29/// Errors generated by a filesystem implementation.
30///
31/// These are more result-codes than errors, really.
32#[derive(Debug, Clone, Copy, PartialEq)]
33pub enum FsError {
34    /// Operation not implemented (501)
35    NotImplemented,
36    /// Something went wrong (500)
37    GeneralFailure,
38    /// tried to create something, but it existed (405 / 412) (yes, 405. RFC4918 says so)
39    Exists,
40    /// File / Directory not found (404)
41    NotFound,
42    /// Not allowed (403)
43    Forbidden,
44    /// Out of space (507)
45    InsufficientStorage,
46    /// Symbolic link loop detected (ELOOP) (508)
47    LoopDetected,
48    /// The path is too long (ENAMETOOLONG) (414)
49    PathTooLong,
50    /// The file being PUT is too large (413)
51    TooLarge,
52    /// Trying to MOVE over a mount boundary (EXDEV) (502)
53    IsRemote,
54}
55/// The Result type.
56pub type FsResult<T> = std::result::Result<T, FsError>;
57
58#[cfg(any(feature = "memfs", feature = "localfs"))]
59impl From<&std::io::Error> for FsError {
60    fn from(e: &std::io::Error) -> Self {
61        use std::io::ErrorKind;
62
63        if let Some(errno) = e.raw_os_error() {
64            // specific errors.
65            match errno {
66                #[cfg(unix)]
67                libc::EMLINK | libc::ENOSPC | libc::EDQUOT => return FsError::InsufficientStorage,
68                #[cfg(windows)]
69                libc::EMLINK | libc::ENOSPC => return FsError::InsufficientStorage,
70                libc::EFBIG => return FsError::TooLarge,
71                libc::EACCES | libc::EPERM => return FsError::Forbidden,
72                libc::ENOTEMPTY | libc::EEXIST => return FsError::Exists,
73                libc::ELOOP => return FsError::LoopDetected,
74                libc::ENAMETOOLONG => return FsError::PathTooLong,
75                libc::ENOTDIR => return FsError::Forbidden,
76                libc::EISDIR => return FsError::Forbidden,
77                libc::EROFS => return FsError::Forbidden,
78                libc::ENOENT => return FsError::NotFound,
79                libc::ENOSYS => return FsError::NotImplemented,
80                libc::EXDEV => return FsError::IsRemote,
81                _ => {}
82            }
83        } else {
84            // not an OS error - must be "not implemented"
85            // (e.g. metadata().created() on systems without st_crtime)
86            return FsError::NotImplemented;
87        }
88        // generic mappings for-whatever is left.
89        match e.kind() {
90            ErrorKind::NotFound => FsError::NotFound,
91            ErrorKind::PermissionDenied => FsError::Forbidden,
92            _ => FsError::GeneralFailure,
93        }
94    }
95}
96
97#[cfg(any(feature = "memfs", feature = "localfs"))]
98impl From<std::io::Error> for FsError {
99    fn from(e: std::io::Error) -> Self {
100        (&e).into()
101    }
102}
103/// A webdav property.
104#[derive(Debug, Clone)]
105pub struct DavProp {
106    /// Name of the property.
107    pub name: String,
108    /// XML prefix.
109    pub prefix: Option<String>,
110    /// XML namespace.
111    pub namespace: Option<String>,
112    /// Value of the property as raw XML. Use DavProp::new() to create your custom props
113    pub xml: Option<Vec<u8>>,
114}
115
116impl DavProp {
117    /// Create XML property with name, prefix, namespace and value
118    pub fn new(name: String, prefix: String, namespace: String, value: String) -> DavProp {
119        DavProp {
120            name: name.clone(),
121            prefix: Some(prefix.clone()),
122            namespace: Some(namespace.clone()),
123            xml: Some(
124                format!(
125                    "<{prefix}:{name} xmlns:{prefix}=\"{namespace}\">{value}</{prefix}:{name}>"
126                )
127                .into_bytes(),
128            ),
129        }
130    }
131}
132
133/// Future returned by almost all of the DavFileSystem methods.
134pub type FsFuture<'a, T> = Pin<Box<dyn Future<Output = FsResult<T>> + Send + 'a>>;
135/// Convenience alias for a boxed Stream.
136pub type FsStream<T> = Pin<Box<dyn Stream<Item = FsResult<T>> + Send>>;
137
138/// Used as argument to the read_dir() method.
139/// It is:
140///
141/// - an optimization hint (the implementation may call metadata() and
142///   store the result in the returned directory entry)
143/// - a way to get metadata instead of symlink_metadata from
144///   the directory entry.
145///
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum ReadDirMeta {
148    /// DavDirEntry.metadata() behaves as metadata()
149    Data,
150    /// DavDirEntry.metadata() behaves as symlink_metadata()
151    DataSymlink,
152    /// No optimizations, otherwise like DataSymlink.
153    None,
154}
155
156/// File system without access control.
157pub trait DavFileSystem {
158    /// Open a file.
159    fn open<'a>(
160        &'a self,
161        path: &'a DavPath,
162        options: OpenOptions,
163    ) -> FsFuture<'a, Box<dyn DavFile>>;
164
165    /// Lists entries within a directory.
166    fn read_dir<'a>(
167        &'a self,
168        path: &'a DavPath,
169        meta: ReadDirMeta,
170    ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>>;
171
172    /// Return the metadata of a file or directory.
173    fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>>;
174
175    /// Return the metadata of a file, directory or symbolic link.
176    ///
177    /// Differs from [`metadata`][Self::metadata] that if the path is a symbolic link,
178    /// it return the metadata for the link itself, not for the thing
179    /// it points to.
180    ///
181    /// The default implementation returns [`FsError::NotImplemented`].
182    #[allow(unused_variables)]
183    fn symlink_metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
184        self.metadata(path)
185    }
186
187    /// Create a directory.
188    ///
189    /// The default implementation returns [`FsError::NotImplemented`].
190    #[allow(unused_variables)]
191    fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
192        notimplemented_fut!("create_dir")
193    }
194
195    /// Remove a directory.
196    ///
197    /// The default implementation returns [`FsError::NotImplemented`].
198    #[allow(unused_variables)]
199    fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
200        notimplemented_fut!("remove_dir")
201    }
202
203    /// Remove a file.
204    ///
205    /// The default implementation returns [`FsError::NotImplemented`].
206    #[allow(unused_variables)]
207    fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
208        notimplemented_fut!("remove_file")
209    }
210
211    /// Rename a file or directory.
212    ///
213    /// Source and destination must be the same type (file/dir).
214    /// If the destination already exists and is a file, it
215    /// should be replaced. If it is a directory it should give
216    /// an error.
217    ///
218    /// The default implementation returns [`FsError::NotImplemented`].
219    #[allow(unused_variables)]
220    fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
221        notimplemented_fut!("rename")
222    }
223
224    /// Copy a file.
225    ///
226    /// Should also copy the DAV properties, if properties
227    /// are implemented.
228    ///
229    /// The default implementation returns [`FsError::NotImplemented`].
230    #[allow(unused_variables)]
231    fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
232        notimplemented_fut!("copy")
233    }
234
235    /// Set the access time of a file / directory.
236    ///
237    /// The default implementation returns [`FsError::NotImplemented`].
238    #[doc(hidden)]
239    #[allow(unused_variables)]
240    fn set_accessed<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<'a, ()> {
241        notimplemented_fut!("set_accessed")
242    }
243
244    /// Set the modified time of a file / directory.
245    ///
246    /// The default implementation returns [`FsError::NotImplemented`].
247    #[doc(hidden)]
248    #[allow(unused_variables)]
249    fn set_modified<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<'a, ()> {
250        notimplemented_fut!("set_modified")
251    }
252
253    /// Indicator that tells if this filesystem driver supports DAV properties.
254    ///
255    /// The default implementation returns `false`.
256    #[allow(unused_variables)]
257    fn have_props<'a>(
258        &'a self,
259        path: &'a DavPath,
260    ) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
261        Box::pin(future::ready(false))
262    }
263
264    /// Patch the DAV properties of a node (add/remove props).
265    ///
266    /// The default implementation returns [`FsError::NotImplemented`].
267    #[allow(unused_variables)]
268    fn patch_props<'a>(
269        &'a self,
270        path: &'a DavPath,
271        patch: Vec<(bool, DavProp)>,
272    ) -> FsFuture<'a, Vec<(StatusCode, DavProp)>> {
273        notimplemented_fut!("patch_props")
274    }
275
276    /// List/get the DAV properties of a node.
277    ///
278    /// The default implementation returns [`FsError::NotImplemented`].
279    #[allow(unused_variables)]
280    fn get_props<'a>(&'a self, path: &'a DavPath, do_content: bool) -> FsFuture<'a, Vec<DavProp>> {
281        notimplemented_fut!("get_props")
282    }
283
284    /// Get one specific named property of a node.
285    ///
286    /// The default implementation returns [`FsError::NotImplemented`].
287    #[allow(unused_variables)]
288    fn get_prop<'a>(&'a self, path: &'a DavPath, prop: DavProp) -> FsFuture<'a, Vec<u8>> {
289        notimplemented_fut!("get_prop")
290    }
291
292    /// Get quota of this filesystem (used/total space).
293    ///
294    /// The first value returned is the amount of space used,
295    /// the second optional value is the total amount of space
296    /// (used + available).
297    ///
298    /// The default implementation returns [`FsError::NotImplemented`].
299    #[allow(unused_variables)]
300    fn get_quota(&'_ self) -> FsFuture<'_, (u64, Option<u64>)> {
301        notimplemented_fut!("get_quota")
302    }
303}
304
305/// File system with access control. Type parameter `C` (credentials) represents
306/// the authentication and authorization information required for accessing the file system.
307/// This can include various forms of credentials such as:
308///
309/// - auth tokens,
310/// - login and password hash combinations,
311/// - encryption keys.
312///
313/// Additionally, it may encapsulate context, scope or metadata related to the request,
314/// such as:
315///
316/// - a set of file system permissions granted to a user,
317/// - regionally-defined access scope.
318///
319/// All [HTTP security considerations][HTTP security] also [apply][WebDAV security]
320/// to WebDAV, so you should follow the same authorization best practices as you would
321/// for regular HTTP server.
322///
323/// If credentials `C` include a username, you may want
324/// to [specify it as principal][crate::DavConfig::principal]
325/// to the [`DavHandler`][crate::DavHandler] builder so that the locks user requests
326/// have a known owner.
327///
328/// For file systems without access control, implement simpler [`DavFileSystem`] trait,
329/// for which there's a blanket implementation of `GuardedFileSystem<()>`.
330///
331/// See also the [auth example].
332///
333/// [HTTP security]: https://datatracker.ietf.org/doc/html/rfc2616#section-15
334/// [WebDAV security]: https://datatracker.ietf.org/doc/html/rfc4918#section-20
335/// [auth example]: https://github.com/messense/dav-server-rs/blob/main/examples/auth.rs
336pub trait GuardedFileSystem<C>: Send + Sync + DynClone
337where
338    C: Clone + Send + Sync + 'static,
339{
340    /// Open a file.
341    fn open<'a>(
342        &'a self,
343        path: &'a DavPath,
344        options: OpenOptions,
345        credentials: &'a C,
346    ) -> FsFuture<'a, Box<dyn DavFile>>;
347
348    /// Lists entries within a directory.
349    fn read_dir<'a>(
350        &'a self,
351        path: &'a DavPath,
352        meta: ReadDirMeta,
353        credentials: &'a C,
354    ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>>;
355
356    /// Return the metadata of a file or directory.
357    fn metadata<'a>(
358        &'a self,
359        path: &'a DavPath,
360        credentials: &'a C,
361    ) -> FsFuture<'a, Box<dyn DavMetaData>>;
362
363    /// Return the metadata of a file, directory or symbolic link.
364    ///
365    /// Differs from [`metadata`][Self::metadata] that if the path is a symbolic link,
366    /// it return the metadata for the link itself, not for the thing
367    /// it points to.
368    ///
369    /// The default implementation returns [`FsError::NotImplemented`].
370    #[allow(unused_variables)]
371    fn symlink_metadata<'a>(
372        &'a self,
373        path: &'a DavPath,
374        credentials: &'a C,
375    ) -> FsFuture<'a, Box<dyn DavMetaData>> {
376        self.metadata(path, credentials)
377    }
378
379    /// Create a directory.
380    ///
381    /// The default implementation returns [`FsError::NotImplemented`].
382    #[allow(unused_variables)]
383    fn create_dir<'a>(&'a self, path: &'a DavPath, credentials: &'a C) -> FsFuture<'a, ()> {
384        notimplemented_fut!("create_dir")
385    }
386
387    /// Remove a directory.
388    ///
389    /// The default implementation returns [`FsError::NotImplemented`].
390    #[allow(unused_variables)]
391    fn remove_dir<'a>(&'a self, path: &'a DavPath, credentials: &'a C) -> FsFuture<'a, ()> {
392        notimplemented_fut!("remove_dir")
393    }
394
395    /// Remove a file.
396    ///
397    /// The default implementation returns [`FsError::NotImplemented`].
398    #[allow(unused_variables)]
399    fn remove_file<'a>(&'a self, path: &'a DavPath, credentials: &'a C) -> FsFuture<'a, ()> {
400        notimplemented_fut!("remove_file")
401    }
402
403    /// Rename a file or directory.
404    ///
405    /// Source and destination must be the same type (file/dir).
406    /// If the destination already exists and is a file, it
407    /// should be replaced. If it is a directory it should give
408    /// an error.
409    ///
410    /// The default implementation returns [`FsError::NotImplemented`].
411    #[allow(unused_variables)]
412    fn rename<'a>(
413        &'a self,
414        from: &'a DavPath,
415        to: &'a DavPath,
416        credentials: &'a C,
417    ) -> FsFuture<'a, ()> {
418        notimplemented_fut!("rename")
419    }
420
421    /// Copy a file.
422    ///
423    /// Should also copy the DAV properties, if properties
424    /// are implemented.
425    ///
426    /// The default implementation returns [`FsError::NotImplemented`].
427    #[allow(unused_variables)]
428    fn copy<'a>(
429        &'a self,
430        from: &'a DavPath,
431        to: &'a DavPath,
432        credentials: &'a C,
433    ) -> FsFuture<'a, ()> {
434        notimplemented_fut!("copy")
435    }
436
437    /// Set the access time of a file / directory.
438    ///
439    /// The default implementation returns [`FsError::NotImplemented`].
440    #[doc(hidden)]
441    #[allow(unused_variables)]
442    fn set_accessed<'a>(
443        &'a self,
444        path: &'a DavPath,
445        tm: SystemTime,
446        credentials: &C,
447    ) -> FsFuture<'a, ()> {
448        notimplemented_fut!("set_accessed")
449    }
450
451    /// Set the modified time of a file / directory.
452    ///
453    /// The default implementation returns [`FsError::NotImplemented`].
454    #[doc(hidden)]
455    #[allow(unused_variables)]
456    fn set_modified<'a>(
457        &'a self,
458        path: &'a DavPath,
459        tm: SystemTime,
460        credentials: &'a C,
461    ) -> FsFuture<'a, ()> {
462        notimplemented_fut!("set_mofified")
463    }
464
465    /// Indicator that tells if this filesystem driver supports DAV properties.
466    ///
467    /// The default implementation returns `false`.
468    #[allow(unused_variables)]
469    fn have_props<'a>(
470        &'a self,
471        path: &'a DavPath,
472        credentials: &'a C,
473    ) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
474        Box::pin(future::ready(false))
475    }
476
477    /// Patch the DAV properties of a node (add/remove props).
478    ///
479    /// The default implementation returns [`FsError::NotImplemented`].
480    #[allow(unused_variables)]
481    fn patch_props<'a>(
482        &'a self,
483        path: &'a DavPath,
484        patch: Vec<(bool, DavProp)>,
485        credentials: &'a C,
486    ) -> FsFuture<'a, Vec<(StatusCode, DavProp)>> {
487        notimplemented_fut!("patch_props")
488    }
489
490    /// List/get the DAV properties of a node.
491    ///
492    /// The default implementation returns [`FsError::NotImplemented`].
493    #[allow(unused_variables)]
494    fn get_props<'a>(
495        &'a self,
496        path: &'a DavPath,
497        do_content: bool,
498        credentials: &'a C,
499    ) -> FsFuture<'a, Vec<DavProp>> {
500        notimplemented_fut!("get_props")
501    }
502
503    /// Get one specific named property of a node.
504    ///
505    /// The default implementation returns [`FsError::NotImplemented`].
506    #[allow(unused_variables)]
507    fn get_prop<'a>(
508        &'a self,
509        path: &'a DavPath,
510        prop: DavProp,
511        credentials: &'a C,
512    ) -> FsFuture<'a, Vec<u8>> {
513        notimplemented_fut!("get_prop")
514    }
515
516    /// Get quota of this filesystem (used/total space).
517    ///
518    /// The first value returned is the amount of space used,
519    /// the second optional value is the total amount of space
520    /// (used + available).
521    ///
522    /// The default implementation returns [`FsError::NotImplemented`].
523    #[allow(unused_variables)]
524    fn get_quota<'a>(&'a self, credentials: &'a C) -> FsFuture<'a, (u64, Option<u64>)> {
525        notimplemented_fut!("get_quota")
526    }
527}
528
529clone_trait_object! {<C> GuardedFileSystem<C>}
530
531impl<Fs: DavFileSystem + Clone + Send + Sync> GuardedFileSystem<()> for Fs {
532    fn open<'a>(
533        &'a self,
534        path: &'a DavPath,
535        options: OpenOptions,
536        _credentials: &(),
537    ) -> FsFuture<'a, Box<dyn DavFile>> {
538        DavFileSystem::open(self, path, options)
539    }
540
541    fn read_dir<'a>(
542        &'a self,
543        path: &'a DavPath,
544        meta: ReadDirMeta,
545        _credentials: &(),
546    ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>> {
547        DavFileSystem::read_dir(self, path, meta)
548    }
549
550    fn metadata<'a>(
551        &'a self,
552        path: &'a DavPath,
553        _credentials: &(),
554    ) -> FsFuture<'a, Box<dyn DavMetaData>> {
555        DavFileSystem::metadata(self, path)
556    }
557
558    fn symlink_metadata<'a>(
559        &'a self,
560        path: &'a DavPath,
561        _credentials: &(),
562    ) -> FsFuture<'a, Box<dyn DavMetaData>> {
563        DavFileSystem::symlink_metadata(self, path)
564    }
565
566    fn create_dir<'a>(&'a self, path: &'a DavPath, _credentials: &()) -> FsFuture<'a, ()> {
567        DavFileSystem::create_dir(self, path)
568    }
569
570    fn remove_dir<'a>(&'a self, path: &'a DavPath, _credentials: &()) -> FsFuture<'a, ()> {
571        DavFileSystem::remove_dir(self, path)
572    }
573
574    fn remove_file<'a>(&'a self, path: &'a DavPath, _credentials: &()) -> FsFuture<'a, ()> {
575        DavFileSystem::remove_file(self, path)
576    }
577
578    fn rename<'a>(
579        &'a self,
580        from: &'a DavPath,
581        to: &'a DavPath,
582        _credentials: &(),
583    ) -> FsFuture<'a, ()> {
584        DavFileSystem::rename(self, from, to)
585    }
586
587    fn copy<'a>(
588        &'a self,
589        from: &'a DavPath,
590        to: &'a DavPath,
591        _credentials: &(),
592    ) -> FsFuture<'a, ()> {
593        DavFileSystem::copy(self, from, to)
594    }
595
596    fn set_accessed<'a>(
597        &'a self,
598        path: &'a DavPath,
599        tm: SystemTime,
600        _credentials: &(),
601    ) -> FsFuture<'a, ()> {
602        DavFileSystem::set_accessed(self, path, tm)
603    }
604
605    fn set_modified<'a>(
606        &'a self,
607        path: &'a DavPath,
608        tm: SystemTime,
609        _credentials: &(),
610    ) -> FsFuture<'a, ()> {
611        DavFileSystem::set_modified(self, path, tm)
612    }
613
614    fn have_props<'a>(
615        &'a self,
616        path: &'a DavPath,
617        _credentials: &(),
618    ) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
619        DavFileSystem::have_props(self, path)
620    }
621
622    fn patch_props<'a>(
623        &'a self,
624        path: &'a DavPath,
625        patch: Vec<(bool, DavProp)>,
626        _credentials: &(),
627    ) -> FsFuture<'a, Vec<(StatusCode, DavProp)>> {
628        DavFileSystem::patch_props(self, path, patch)
629    }
630
631    fn get_props<'a>(
632        &'a self,
633        path: &'a DavPath,
634        do_content: bool,
635        _credentials: &(),
636    ) -> FsFuture<'a, Vec<DavProp>> {
637        DavFileSystem::get_props(self, path, do_content)
638    }
639
640    fn get_prop<'a>(
641        &'a self,
642        path: &'a DavPath,
643        prop: DavProp,
644        _credentials: &(),
645    ) -> FsFuture<'a, Vec<u8>> {
646        DavFileSystem::get_prop(self, path, prop)
647    }
648
649    fn get_quota(&'_ self, _credentials: &()) -> FsFuture<'_, (u64, Option<u64>)> {
650        DavFileSystem::get_quota(self)
651    }
652}
653
654/// One directory entry (or child node).
655pub trait DavDirEntry: Send + Sync {
656    /// Name of the entry.
657    fn name(&self) -> Vec<u8>;
658
659    /// Metadata of the entry.
660    fn metadata(&'_ self) -> FsFuture<'_, Box<dyn DavMetaData>>;
661
662    /// Default implementation of `is_dir` just returns `metadata()?.is_dir()`.
663    /// Implementations can override this if their `metadata()` method is
664    /// expensive and there is a cheaper way to provide the same info
665    /// (e.g. dirent.d_type in unix filesystems).
666    fn is_dir(&'_ self) -> FsFuture<'_, bool> {
667        Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_dir())))
668    }
669
670    /// Likewise. Default: `!is_dir()`.
671    fn is_file(&'_ self) -> FsFuture<'_, bool> {
672        Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_file())))
673    }
674
675    /// Likewise. Default: `false`.
676    fn is_symlink(&'_ self) -> FsFuture<'_, bool> {
677        Box::pin(
678            self.metadata()
679                .and_then(|meta| future::ok(meta.is_symlink())),
680        )
681    }
682}
683
684/// A `DavFile` is the equivalent of `std::fs::File`, should be
685/// readable/writeable/seekable, and be able to return its metadata.
686pub trait DavFile: Debug + Send + Sync {
687    fn metadata(&'_ mut self) -> FsFuture<'_, Box<dyn DavMetaData>>;
688    fn write_buf(&'_ mut self, buf: Box<dyn bytes::Buf + Send>) -> FsFuture<'_, ()>;
689    fn write_bytes(&'_ mut self, buf: bytes::Bytes) -> FsFuture<'_, ()>;
690    fn read_bytes(&'_ mut self, count: usize) -> FsFuture<'_, bytes::Bytes>;
691    fn seek(&'_ mut self, pos: SeekFrom) -> FsFuture<'_, u64>;
692    fn flush(&'_ mut self) -> FsFuture<'_, ()>;
693    fn redirect_url(&'_ mut self) -> FsFuture<'_, Option<String>> {
694        future::ready(Ok(None)).boxed()
695    }
696}
697
698/// File metadata. Basically type, length, and some timestamps.
699pub trait DavMetaData: Debug + Send + Sync + DynClone {
700    /// Size of the file.
701    fn len(&self) -> u64;
702    /// `Modified` timestamp.
703    fn modified(&self) -> FsResult<SystemTime>;
704    /// File or directory (aka collection).
705    fn is_dir(&self) -> bool;
706
707    /// Simplistic implementation of `etag()`
708    ///
709    /// Returns a simple etag that basically is `\<length\>-\<timestamp_in_ms\>`
710    /// with the numbers in hex. Enough for most implementations.
711    fn etag(&self) -> Option<String> {
712        if let Ok(t) = self.modified()
713            && let Ok(t) = t.duration_since(UNIX_EPOCH)
714        {
715            let t = t.as_secs() * 1000000 + t.subsec_nanos() as u64 / 1000;
716            let tag = if self.is_file() && self.len() > 0 {
717                format!("{:x}-{:x}", self.len(), t)
718            } else {
719                format!("{t:x}")
720            };
721            return Some(tag);
722        }
723        None
724    }
725
726    /// Is this a file and not a directory. Default: `!is_dir()`.
727    fn is_file(&self) -> bool {
728        !self.is_dir()
729    }
730
731    /// Is this a symbolic link. Default: false.
732    fn is_symlink(&self) -> bool {
733        false
734    }
735
736    /// Last access time. Default: `FsError::NotImplemented`.
737    fn accessed(&self) -> FsResult<SystemTime> {
738        notimplemented!("access time")
739    }
740
741    /// Creation time. Default: `FsError::NotImplemented`.
742    fn created(&self) -> FsResult<SystemTime> {
743        notimplemented!("creation time")
744    }
745
746    /// Inode change time (ctime). Default: `FsError::NotImplemented`.
747    fn status_changed(&self) -> FsResult<SystemTime> {
748        notimplemented!("status change time")
749    }
750
751    /// Is file executable (unix: has "x" mode bit). Default: `FsError::NotImplemented`.
752    fn executable(&self) -> FsResult<bool> {
753        notimplemented!("executable")
754    }
755
756    // Is empty file
757    fn is_empty(&self) -> bool {
758        self.len() == 0
759    }
760}
761
762clone_trait_object! {DavMetaData}
763
764/// OpenOptions for `open()`.
765#[derive(Debug, Clone, Default)]
766pub struct OpenOptions {
767    /// open for reading
768    pub read: bool,
769    /// open for writing
770    pub write: bool,
771    /// open in write-append mode
772    pub append: bool,
773    /// truncate file first when writing
774    pub truncate: bool,
775    /// create file if it doesn't exist
776    pub create: bool,
777    /// must create new file, fail if it already exists.
778    pub create_new: bool,
779    /// write file total size
780    pub size: Option<u64>,
781    /// checksum, owncloud extension
782    pub checksum: Option<String>,
783}
784
785impl OpenOptions {
786    #[allow(dead_code)]
787    pub(crate) fn new() -> OpenOptions {
788        OpenOptions {
789            read: false,
790            write: false,
791            append: false,
792            truncate: false,
793            create: false,
794            create_new: false,
795            size: None,
796            checksum: None,
797        }
798    }
799
800    pub(crate) fn read() -> OpenOptions {
801        OpenOptions {
802            read: true,
803            write: false,
804            append: false,
805            truncate: false,
806            create: false,
807            create_new: false,
808            size: None,
809            checksum: None,
810        }
811    }
812
813    pub(crate) fn write() -> OpenOptions {
814        OpenOptions {
815            read: false,
816            write: true,
817            append: false,
818            truncate: false,
819            create: false,
820            create_new: false,
821            size: None,
822            checksum: None,
823        }
824    }
825}
826
827impl std::error::Error for FsError {
828    fn description(&self) -> &str {
829        "DavFileSystem error"
830    }
831    fn cause(&self) -> Option<&dyn std::error::Error> {
832        None
833    }
834}
835
836impl std::fmt::Display for FsError {
837    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
838        write!(f, "{self:?}")
839    }
840}