mirror of
https://github.com/rustic-rs/rustic.git
synced 2025-10-26 11:18:51 +00:00
Revert "fix(filesystem): Use channels to communicate within webdav filesystem (#1361)"
This reverts commit d79e392b86.
This commit is contained in:
parent
d79e392b86
commit
984c0486e0
@ -3,9 +3,6 @@
|
||||
// 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};
|
||||
@ -16,6 +13,9 @@ 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)]
|
||||
|
||||
@ -3,7 +3,7 @@ use std::os::unix::ffi::OsStrExt;
|
||||
use std::{
|
||||
fmt::{Debug, Formatter},
|
||||
io::SeekFrom,
|
||||
sync::OnceLock,
|
||||
sync::{Arc, OnceLock},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
@ -16,7 +16,6 @@ use dav_server::{
|
||||
},
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
use rustic_core::{
|
||||
repofile::Node,
|
||||
@ -41,7 +40,45 @@ struct DavFsInner<P, S> {
|
||||
file_policy: FilePolicy,
|
||||
}
|
||||
|
||||
impl<P, S: IndexedFull> DavFsInner<P, S> {
|
||||
impl<P, S> Debug for DavFsInner<P, S> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "DavFS")
|
||||
}
|
||||
}
|
||||
|
||||
/// DAV Filesystem implementation.
|
||||
///
|
||||
/// 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<P, S> {
|
||||
inner: Arc<DavFsInner<P, S>>,
|
||||
}
|
||||
|
||||
impl<P, S: IndexedFull> WebDavFS<P, S> {
|
||||
/// Create a new [`WebDavFS`] instance.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `repo` - The [`Repository`] to use
|
||||
/// * `vfs` - The [`Vfs`] to use
|
||||
/// * `file_policy` - The [`FilePolicy`] to use
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new [`WebDavFS`] instance
|
||||
pub(crate) fn new(repo: Repository<P, S>, vfs: Vfs, file_policy: FilePolicy) -> Self {
|
||||
let inner = DavFsInner {
|
||||
repo,
|
||||
vfs,
|
||||
file_policy,
|
||||
};
|
||||
|
||||
Self {
|
||||
inner: Arc::new(inner),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Node`] from the specified [`DavPath`].
|
||||
///
|
||||
/// # Arguments
|
||||
@ -58,8 +95,9 @@ impl<P, S: IndexedFull> DavFsInner<P, S> {
|
||||
///
|
||||
/// [`Tree`]: crate::repofile::Tree
|
||||
fn node_from_path(&self, path: &DavPath) -> Result<Node, FsError> {
|
||||
self.vfs
|
||||
.node_from_path(&self.repo, &path.as_pathbuf())
|
||||
self.inner
|
||||
.vfs
|
||||
.node_from_path(&self.inner.repo, &path.as_pathbuf())
|
||||
.map_err(|_| FsError::GeneralFailure)
|
||||
}
|
||||
|
||||
@ -79,210 +117,31 @@ impl<P, S: IndexedFull> DavFsInner<P, S> {
|
||||
///
|
||||
/// [`Tree`]: crate::repofile::Tree
|
||||
fn dir_entries_from_path(&self, path: &DavPath) -> Result<Vec<Node>, FsError> {
|
||||
self.vfs
|
||||
.dir_entries_from_path(&self.repo, &path.as_pathbuf())
|
||||
self.inner
|
||||
.vfs
|
||||
.dir_entries_from_path(&self.inner.repo, &path.as_pathbuf())
|
||||
.map_err(|_| FsError::GeneralFailure)
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&self, node: &Node, options: OpenOptions) -> Result<OpenFile, FsError> {
|
||||
if options.write
|
||||
|| options.append
|
||||
|| options.truncate
|
||||
|| options.create
|
||||
|| options.create_new
|
||||
{
|
||||
return Err(FsError::Forbidden);
|
||||
impl<P, S: IndexedFull> Clone for WebDavFS<P, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
|
||||
if matches!(self.file_policy, FilePolicy::Forbidden) {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
let open = self
|
||||
.repo
|
||||
.open_file(node)
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
Ok(open)
|
||||
}
|
||||
|
||||
fn read_bytes(
|
||||
&self,
|
||||
file: OpenFile,
|
||||
seek: usize,
|
||||
count: usize,
|
||||
) -> Result<(Bytes, OpenFile), FsError> {
|
||||
let data = self
|
||||
.repo
|
||||
.read_file_at(&file, seek, count)
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
Ok((data, file))
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages used
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum DavFsInnerCommand {
|
||||
Node(DavPath, oneshot::Sender<Result<Node, FsError>>),
|
||||
DirEntries(DavPath, oneshot::Sender<Result<Vec<Node>, FsError>>),
|
||||
Open(
|
||||
Node,
|
||||
OpenOptions,
|
||||
oneshot::Sender<Result<OpenFile, FsError>>,
|
||||
),
|
||||
ReadBytes(
|
||||
OpenFile,
|
||||
usize,
|
||||
usize,
|
||||
oneshot::Sender<Result<(Bytes, OpenFile), FsError>>,
|
||||
),
|
||||
}
|
||||
|
||||
impl<P, S> Debug for DavFsInner<P, S> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "DavFS")
|
||||
}
|
||||
}
|
||||
|
||||
/// DAV Filesystem implementation.
|
||||
///
|
||||
/// 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, Clone)]
|
||||
pub struct WebDavFS {
|
||||
send: mpsc::Sender<DavFsInnerCommand>,
|
||||
}
|
||||
|
||||
impl WebDavFS {
|
||||
/// Create a new [`WebDavFS`] instance.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `repo` - The [`Repository`] to use
|
||||
/// * `vfs` - The [`Vfs`] to use
|
||||
/// * `file_policy` - The [`FilePolicy`] to use
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new [`WebDavFS`] instance
|
||||
pub(crate) fn new<P: Send + 'static, S: IndexedFull + Send + 'static>(
|
||||
repo: Repository<P, S>,
|
||||
vfs: Vfs,
|
||||
file_policy: FilePolicy,
|
||||
) -> Self {
|
||||
let inner = DavFsInner {
|
||||
repo,
|
||||
vfs,
|
||||
file_policy,
|
||||
};
|
||||
|
||||
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`].
|
||||
///
|
||||
/// # 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
|
||||
async fn node_from_path(&self, path: &DavPath) -> Result<Node, FsError> {
|
||||
let (send, rcv) = oneshot::channel();
|
||||
self.send
|
||||
.send(DavFsInnerCommand::Node(path.clone(), send))
|
||||
.await
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
rcv.await.map_err(|_err| FsError::GeneralFailure)?
|
||||
}
|
||||
|
||||
/// Get a list of [`Node`]s from the specified directory path.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - The path to get the [`Tree`] at
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * If the [`Tree`] could not be found
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The list of [`Node`]s at the specified path
|
||||
///
|
||||
/// [`Tree`]: crate::repofile::Tree
|
||||
async fn dir_entries_from_path(&self, path: &DavPath) -> Result<Vec<Node>, FsError> {
|
||||
let (send, rcv) = oneshot::channel();
|
||||
self.send
|
||||
.send(DavFsInnerCommand::DirEntries(path.clone(), send))
|
||||
.await
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
rcv.await.map_err(|_err| FsError::GeneralFailure)?
|
||||
}
|
||||
|
||||
async fn open(&self, node: &Node, options: OpenOptions) -> Result<OpenFile, FsError> {
|
||||
let (send, rcv) = oneshot::channel();
|
||||
self.send
|
||||
.send(DavFsInnerCommand::Open(node.clone(), options, send))
|
||||
.await
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
rcv.await.map_err(|_err| FsError::GeneralFailure)?
|
||||
}
|
||||
async fn read_bytes(
|
||||
&self,
|
||||
file: OpenFile,
|
||||
seek: usize,
|
||||
count: usize,
|
||||
) -> Result<(Bytes, OpenFile), FsError> {
|
||||
let (send, rcv) = oneshot::channel();
|
||||
self.send
|
||||
.send(DavFsInnerCommand::ReadBytes(file, seek, count, send))
|
||||
.await
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
rcv.await.map_err(|_err| FsError::GeneralFailure)?
|
||||
}
|
||||
}
|
||||
|
||||
impl DavFileSystem for WebDavFS {
|
||||
impl<P: Debug + Send + Sync + 'static, S: IndexedFull + Debug + Send + Sync + 'static> DavFileSystem
|
||||
for WebDavFS<P, S>
|
||||
{
|
||||
fn metadata<'a>(&'a self, davpath: &'a DavPath) -> FsFuture<'_, Box<dyn DavMetaData>> {
|
||||
self.symlink_metadata(davpath)
|
||||
}
|
||||
|
||||
fn symlink_metadata<'a>(&'a self, davpath: &'a DavPath) -> FsFuture<'_, Box<dyn DavMetaData>> {
|
||||
async move {
|
||||
let node = self.node_from_path(davpath).await?;
|
||||
let node = self.node_from_path(davpath)?;
|
||||
let meta: Box<dyn DavMetaData> = Box::new(DavFsMetaData(node));
|
||||
Ok(meta)
|
||||
}
|
||||
@ -295,7 +154,7 @@ impl DavFileSystem for WebDavFS {
|
||||
_meta: ReadDirMeta,
|
||||
) -> FsFuture<'_, FsStream<Box<dyn DavDirEntry>>> {
|
||||
async move {
|
||||
let entries = self.dir_entries_from_path(davpath).await?;
|
||||
let entries = self.dir_entries_from_path(davpath)?;
|
||||
let entry_iter = entries.into_iter().map(|e| {
|
||||
let entry: Box<dyn DavDirEntry> = Box::new(DavFsDirEntry(e));
|
||||
Ok(entry)
|
||||
@ -312,13 +171,30 @@ impl DavFileSystem for WebDavFS {
|
||||
options: OpenOptions,
|
||||
) -> FsFuture<'_, Box<dyn DavFile>> {
|
||||
async move {
|
||||
let node = self.node_from_path(path).await?;
|
||||
let file = self.open(&node, options).await?;
|
||||
if options.write
|
||||
|| options.append
|
||||
|| options.truncate
|
||||
|| options.create
|
||||
|| options.create_new
|
||||
{
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
let node = self.node_from_path(path)?;
|
||||
if matches!(self.inner.file_policy, FilePolicy::Forbidden) {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
let open = self
|
||||
.inner
|
||||
.repo
|
||||
.open_file(&node)
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
let file: Box<dyn DavFile> = Box::new(DavFsFile {
|
||||
open: Some(file),
|
||||
seek: 0,
|
||||
fs: self.clone(),
|
||||
node,
|
||||
open,
|
||||
fs: self.inner.clone(),
|
||||
seek: 0,
|
||||
});
|
||||
Ok(file)
|
||||
}
|
||||
@ -358,25 +234,27 @@ impl DavDirEntry for DavFsDirEntry {
|
||||
/// A [`DavFile`] implementation for [`Node`]s.
|
||||
///
|
||||
/// This is a read-only file.
|
||||
struct DavFsFile {
|
||||
struct DavFsFile<P, S> {
|
||||
/// The [`Node`] this file is for
|
||||
node: Node,
|
||||
|
||||
/// The [`OpenFile`] for this file
|
||||
open: Option<OpenFile>,
|
||||
open: OpenFile,
|
||||
|
||||
/// The [`DavFsInner`] this file belongs to
|
||||
fs: Arc<DavFsInner<P, S>>,
|
||||
|
||||
/// The current seek position
|
||||
seek: usize,
|
||||
fs: WebDavFS,
|
||||
}
|
||||
|
||||
impl Debug for DavFsFile {
|
||||
impl<P, S> Debug for DavFsFile<P, S> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "DavFile")
|
||||
}
|
||||
}
|
||||
|
||||
impl DavFile for DavFsFile {
|
||||
impl<P: Debug + Send + Sync, S: IndexedFull + Debug + Send + Sync> DavFile for DavFsFile<P, S> {
|
||||
fn metadata(&mut self) -> FsFuture<'_, Box<dyn DavMetaData>> {
|
||||
async move {
|
||||
let meta: Box<dyn DavMetaData> = Box::new(DavFsMetaData(self.node.clone()));
|
||||
@ -395,16 +273,12 @@ impl DavFile for DavFsFile {
|
||||
|
||||
fn read_bytes(&mut self, count: usize) -> FsFuture<'_, Bytes> {
|
||||
async move {
|
||||
let (data, open) = self
|
||||
let data = self
|
||||
.fs
|
||||
.read_bytes(
|
||||
self.open.take().ok_or(FsError::GeneralFailure)?,
|
||||
self.seek,
|
||||
count,
|
||||
)
|
||||
.await?;
|
||||
.repo
|
||||
.read_file_at(&self.open, self.seek, count)
|
||||
.map_err(|_err| FsError::GeneralFailure)?;
|
||||
self.seek += data.len();
|
||||
self.open = Some(open);
|
||||
Ok(data)
|
||||
}
|
||||
.boxed()
|
||||
@ -418,7 +292,7 @@ impl DavFile for DavFsFile {
|
||||
}
|
||||
SeekFrom::Current(delta) => {
|
||||
self.seek = usize::try_from(
|
||||
i64::try_from(self.seek).expect("i64 should not wrap around") + delta,
|
||||
i64::try_from(self.seek).expect("i64 wrapped around") + delta,
|
||||
)
|
||||
.expect("usize overflow should not happen");
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user