From d79e392b86c79d73a91b7fe15b10a535d1110e9c Mon Sep 17 00:00:00 2001 From: aawsome <37850842+aawsome@users.noreply.github.com> Date: Sat, 23 Nov 2024 17:07:33 +0100 Subject: [PATCH] fix(filesystem): Use channels to communicate within webdav filesystem (#1361) Solves the webdav problem reported in #1181 --------- Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> Co-authored-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/commands/webdav.rs | 6 +- src/commands/webdav/webdavfs.rs | 254 ++++++++++++++++++++++++-------- 2 files changed, 193 insertions(+), 67 deletions(-) diff --git a/src/commands/webdav.rs b/src/commands/webdav.rs index 90b79072..aa06d390 100644 --- a/src/commands/webdav.rs +++ b/src/commands/webdav.rs @@ -3,6 +3,9 @@ // ignore markdown clippy lints as we use doc-comments to generate clap help texts #![allow(clippy::doc_markdown)] +mod webdavfs; +use webdavfs::WebDavFS; + use std::net::ToSocketAddrs; use crate::{repository::CliIndexedRepo, status_err, Application, RusticConfig, RUSTIC_APP}; @@ -13,9 +16,6 @@ use dav_server::{warp::dav_handler, DavHandler}; use serde::{Deserialize, Serialize}; use rustic_core::vfs::{FilePolicy, IdenticalSnapshot, Latest, Vfs}; -use webdavfs::WebDavFS; - -mod webdavfs; #[derive(Clone, Command, Default, Debug, clap::Parser, Serialize, Deserialize, Merge)] #[serde(default, rename_all = "kebab-case", deny_unknown_fields)] diff --git a/src/commands/webdav/webdavfs.rs b/src/commands/webdav/webdavfs.rs index 0c3ebb54..87146ad7 100644 --- a/src/commands/webdav/webdavfs.rs +++ b/src/commands/webdav/webdavfs.rs @@ -3,7 +3,7 @@ use std::os::unix::ffi::OsStrExt; use std::{ fmt::{Debug, Formatter}, io::SeekFrom, - sync::{Arc, OnceLock}, + sync::OnceLock, time::SystemTime, }; @@ -16,6 +16,7 @@ use dav_server::{ }, }; use futures::FutureExt; +use tokio::sync::{mpsc, oneshot}; use rustic_core::{ repofile::Node, @@ -40,6 +41,102 @@ struct DavFsInner
{ file_policy: FilePolicy, } +impl
DavFsInner
{
+ /// Get a [`Node`] from the specified [`DavPath`].
+ ///
+ /// # Arguments
+ ///
+ /// * `path` - The path to get the [`Tree`] at
+ ///
+ /// # Errors
+ ///
+ /// * If the [`Tree`] could not be found
+ ///
+ /// # Returns
+ ///
+ /// The [`Node`] at the specified path
+ ///
+ /// [`Tree`]: crate::repofile::Tree
+ fn node_from_path(&self, path: &DavPath) -> Result Debug for DavFsInner {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "DavFS")
@@ -50,12 +147,12 @@ impl Debug for DavFsInner {
///
/// This is the main entry point for the DAV filesystem.
/// It implements [`DavFileSystem`] and can be used to serve a [`Repository`] via DAV.
-#[derive(Debug)]
-pub struct WebDavFS {
- inner: Arc WebDavFS {
+impl WebDavFS {
/// Create a new [`WebDavFS`] instance.
///
/// # Arguments
@@ -67,16 +164,44 @@ impl WebDavFS {
/// # Returns
///
/// A new [`WebDavFS`] instance
- pub(crate) fn new(repo: Repository , vfs: Vfs, file_policy: FilePolicy) -> Self {
+ pub(crate) fn new ,
+ vfs: Vfs,
+ file_policy: FilePolicy,
+ ) -> Self {
let inner = DavFsInner {
repo,
vfs,
file_policy,
};
- Self {
- inner: Arc::new(inner),
- }
+ let (send, mut rcv) = mpsc::channel(1);
+
+ let _ = std::thread::spawn(move || -> Result<_, FsError> {
+ while let Some(task) = rcv.blocking_recv() {
+ match task {
+ DavFsInnerCommand::Node(path, res) => {
+ res.send(inner.node_from_path(&path))
+ .map_err(|_err| FsError::GeneralFailure)?;
+ }
+ DavFsInnerCommand::DirEntries(path, res) => {
+ res.send(inner.dir_entries_from_path(&path))
+ .map_err(|_err| FsError::GeneralFailure)?;
+ }
+ DavFsInnerCommand::Open(path, open_options, res) => {
+ res.send(inner.open(&path, open_options))
+ .map_err(|_err| FsError::GeneralFailure)?;
+ }
+ DavFsInnerCommand::ReadBytes(file, seek, count, res) => {
+ res.send(inner.read_bytes(file, seek, count))
+ .map_err(|_err| FsError::GeneralFailure)?;
+ }
+ }
+ }
+ Ok(())
+ });
+
+ Self { send }
}
/// Get a [`Node`] from the specified [`DavPath`].
@@ -94,11 +219,13 @@ impl WebDavFS {
/// The [`Node`] at the specified path
///
/// [`Tree`]: crate::repofile::Tree
- fn node_from_path(&self, path: &DavPath) -> Result WebDavFS {
/// The list of [`Node`]s at the specified path
///
/// [`Tree`]: crate::repofile::Tree
- fn dir_entries_from_path(&self, path: &DavPath) -> Result Clone for WebDavFS {
- fn clone(&self) -> Self {
- Self {
- inner: self.inner.clone(),
- }
+ async fn open(&self, node: &Node, options: OpenOptions) -> Result
-{
+impl DavFileSystem for WebDavFS {
fn metadata<'a>(&'a self, davpath: &'a DavPath) -> FsFuture<'_, Box {
+struct DavFsFile {
/// The [`Node`] this file is for
node: Node,
/// The [`OpenFile`] for this file
- open: OpenFile,
-
- /// The [`DavFsInner`] this file belongs to
- fs: Arc Debug for DavFsFile {
+impl Debug for DavFsFile {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "DavFile")
}
}
-impl {
+impl DavFile for DavFsFile {
fn metadata(&mut self) -> FsFuture<'_, Box