Skip to content

Commit f3fc883

Browse files
committed
Rebase on main
Signed-off-by: simonsan <[email protected]>
1 parent 3d13f2e commit f3fc883

File tree

2 files changed

+193
-67
lines changed

2 files changed

+193
-67
lines changed

src/commands/webdav.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// ignore markdown clippy lints as we use doc-comments to generate clap help texts
44
#![allow(clippy::doc_markdown)]
55

6+
mod webdavfs;
7+
use webdavfs::WebDavFS;
8+
69
use std::net::ToSocketAddrs;
710

811
use crate::{repository::CliIndexedRepo, status_err, Application, RusticConfig, RUSTIC_APP};
@@ -13,9 +16,6 @@ use dav_server::{warp::dav_handler, DavHandler};
1316
use serde::{Deserialize, Serialize};
1417

1518
use rustic_core::vfs::{FilePolicy, IdenticalSnapshot, Latest, Vfs};
16-
use webdavfs::WebDavFS;
17-
18-
mod webdavfs;
1919

2020
#[derive(Clone, Command, Default, Debug, clap::Parser, Serialize, Deserialize, Merge)]
2121
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]

src/commands/webdav/webdavfs.rs

+190-64
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::os::unix::ffi::OsStrExt;
33
use std::{
44
fmt::{Debug, Formatter},
55
io::SeekFrom,
6-
sync::{Arc, OnceLock},
6+
sync::OnceLock,
77
time::SystemTime,
88
};
99

@@ -16,9 +16,10 @@ use dav_server::{
1616
},
1717
};
1818
use futures::FutureExt;
19+
use tokio::sync::{mpsc, oneshot};
1920

2021
use rustic_core::{
21-
repofile::Node,
22+
refile::Node,
2223
vfs::{FilePolicy, OpenFile, Vfs},
2324
IndexedFull, Repository,
2425
};
@@ -40,6 +41,102 @@ struct DavFsInner<P, S> {
4041
file_policy: FilePolicy,
4142
}
4243

44+
impl<P, S: IndexedFull> DavFsInner<P, S> {
45+
/// Get a [`Node`] from the specified [`DavPath`].
46+
///
47+
/// # Arguments
48+
///
49+
/// * `path` - The path to get the [`Tree`] at
50+
///
51+
/// # Errors
52+
///
53+
/// * If the [`Tree`] could not be found
54+
///
55+
/// # Returns
56+
///
57+
/// The [`Node`] at the specified path
58+
///
59+
/// [`Tree`]: crate::repofile::Tree
60+
fn node_from_path(&self, path: &DavPath) -> Result<Node, FsError> {
61+
self.vfs
62+
.node_from_path(&self.repo, &path.as_pathbuf())
63+
.map_err(|_| FsError::GeneralFailure)
64+
}
65+
66+
/// Get a list of [`Node`]s from the specified directory path.
67+
///
68+
/// # Arguments
69+
///
70+
/// * `path` - The path to get the [`Tree`] at
71+
///
72+
/// # Errors
73+
///
74+
/// * If the [`Tree`] could not be found
75+
///
76+
/// # Returns
77+
///
78+
/// The list of [`Node`]s at the specified path
79+
///
80+
/// [`Tree`]: crate::repofile::Tree
81+
fn dir_entries_from_path(&self, path: &DavPath) -> Result<Vec<Node>, FsError> {
82+
self.vfs
83+
.dir_entries_from_path(&self.repo, &path.as_pathbuf())
84+
.map_err(|_| FsError::GeneralFailure)
85+
}
86+
87+
fn open(&self, node: &Node, options: OpenOptions) -> Result<OpenFile, FsError> {
88+
if options.write
89+
|| options.append
90+
|| options.truncate
91+
|| options.create
92+
|| options.create_new
93+
{
94+
return Err(FsError::Forbidden);
95+
}
96+
97+
if matches!(self.file_policy, FilePolicy::Forbidden) {
98+
return Err(FsError::Forbidden);
99+
}
100+
101+
let open = self
102+
.repo
103+
.open_file(node)
104+
.map_err(|_err| FsError::GeneralFailure)?;
105+
Ok(open)
106+
}
107+
108+
fn read_bytes(
109+
&self,
110+
file: OpenFile,
111+
seek: usize,
112+
count: usize,
113+
) -> Result<(Bytes, OpenFile), FsError> {
114+
let data = self
115+
.repo
116+
.read_file_at(&file, seek, count)
117+
.map_err(|_err| FsError::GeneralFailure)?;
118+
Ok((data, file))
119+
}
120+
}
121+
122+
/// Messages used
123+
#[allow(clippy::large_enum_variant)]
124+
enum DavFsInnerCommand {
125+
Node(DavPath, oneshot::Sender<Result<Node, FsError>>),
126+
DirEntries(DavPath, oneshot::Sender<Result<Vec<Node>, FsError>>),
127+
Open(
128+
Node,
129+
OpenOptions,
130+
oneshot::Sender<Result<OpenFile, FsError>>,
131+
),
132+
ReadBytes(
133+
OpenFile,
134+
usize,
135+
usize,
136+
oneshot::Sender<Result<(Bytes, OpenFile), FsError>>,
137+
),
138+
}
139+
43140
impl<P, S> Debug for DavFsInner<P, S> {
44141
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
45142
write!(f, "DavFS")
@@ -50,12 +147,12 @@ impl<P, S> Debug for DavFsInner<P, S> {
50147
///
51148
/// This is the main entry point for the DAV filesystem.
52149
/// It implements [`DavFileSystem`] and can be used to serve a [`Repository`] via DAV.
53-
#[derive(Debug)]
54-
pub struct WebDavFS<P, S> {
55-
inner: Arc<DavFsInner<P, S>>,
150+
#[derive(Debug, Clone)]
151+
pub struct WebDavFS {
152+
send: mpsc::Sender<DavFsInnerCommand>,
56153
}
57154

58-
impl<P, S: IndexedFull> WebDavFS<P, S> {
155+
impl WebDavFS {
59156
/// Create a new [`WebDavFS`] instance.
60157
///
61158
/// # Arguments
@@ -67,16 +164,44 @@ impl<P, S: IndexedFull> WebDavFS<P, S> {
67164
/// # Returns
68165
///
69166
/// A new [`WebDavFS`] instance
70-
pub(crate) fn new(repo: Repository<P, S>, vfs: Vfs, file_policy: FilePolicy) -> Self {
167+
pub(crate) fn new<P: Send + 'static, S: IndexedFull + Send + 'static>(
168+
repo: Repository<P, S>,
169+
vfs: Vfs,
170+
file_policy: FilePolicy,
171+
) -> Self {
71172
let inner = DavFsInner {
72173
repo,
73174
vfs,
74175
file_policy,
75176
};
76177

77-
Self {
78-
inner: Arc::new(inner),
79-
}
178+
let (send, mut rcv) = mpsc::channel(1);
179+
180+
let _ = std::thread::spawn(move || -> Result<_, FsError> {
181+
while let Some(task) = rcv.blocking_recv() {
182+
match task {
183+
DavFsInnerCommand::Node(path, res) => {
184+
res.send(inner.node_from_path(&path))
185+
.map_err(|_err| FsError::GeneralFailure)?;
186+
}
187+
DavFsInnerCommand::DirEntries(path, res) => {
188+
res.send(inner.dir_entries_from_path(&path))
189+
.map_err(|_err| FsError::GeneralFailure)?;
190+
}
191+
DavFsInnerCommand::Open(path, open_options, res) => {
192+
res.send(inner.open(&path, open_options))
193+
.map_err(|_err| FsError::GeneralFailure)?;
194+
}
195+
DavFsInnerCommand::ReadBytes(file, seek, count, res) => {
196+
res.send(inner.read_bytes(file, seek, count))
197+
.map_err(|_err| FsError::GeneralFailure)?;
198+
}
199+
}
200+
}
201+
Ok(())
202+
});
203+
204+
Self { send }
80205
}
81206

82207
/// Get a [`Node`] from the specified [`DavPath`].
@@ -94,11 +219,13 @@ impl<P, S: IndexedFull> WebDavFS<P, S> {
94219
/// The [`Node`] at the specified path
95220
///
96221
/// [`Tree`]: crate::repofile::Tree
97-
fn node_from_path(&self, path: &DavPath) -> Result<Node, FsError> {
98-
self.inner
99-
.vfs
100-
.node_from_path(&self.inner.repo, &path.as_pathbuf())
101-
.map_err(|_| FsError::GeneralFailure)
222+
async fn node_from_path(&self, path: &DavPath) -> Result<Node, FsError> {
223+
let (send, rcv) = oneshot::channel();
224+
self.send
225+
.send(DavFsInnerCommand::Node(path.clone(), send))
226+
.await
227+
.map_err(|_err| FsError::GeneralFailure)?;
228+
rcv.await.map_err(|_err| FsError::GeneralFailure)?
102229
}
103230

104231
/// Get a list of [`Node`]s from the specified directory path.
@@ -116,32 +243,46 @@ impl<P, S: IndexedFull> WebDavFS<P, S> {
116243
/// The list of [`Node`]s at the specified path
117244
///
118245
/// [`Tree`]: crate::repofile::Tree
119-
fn dir_entries_from_path(&self, path: &DavPath) -> Result<Vec<Node>, FsError> {
120-
self.inner
121-
.vfs
122-
.dir_entries_from_path(&self.inner.repo, &path.as_pathbuf())
123-
.map_err(|_| FsError::GeneralFailure)
246+
async fn dir_entries_from_path(&self, path: &DavPath) -> Result<Vec<Node>, FsError> {
247+
let (send, rcv) = oneshot::channel();
248+
self.send
249+
.send(DavFsInnerCommand::DirEntries(path.clone(), send))
250+
.await
251+
.map_err(|_err| FsError::GeneralFailure)?;
252+
rcv.await.map_err(|_err| FsError::GeneralFailure)?
124253
}
125-
}
126254

127-
impl<P, S: IndexedFull> Clone for WebDavFS<P, S> {
128-
fn clone(&self) -> Self {
129-
Self {
130-
inner: self.inner.clone(),
131-
}
255+
async fn open(&self, node: &Node, options: OpenOptions) -> Result<OpenFile, FsError> {
256+
let (send, rcv) = oneshot::channel();
257+
self.send
258+
.send(DavFsInnerCommand::Open(node.clone(), options, send))
259+
.await
260+
.map_err(|_err| FsError::GeneralFailure)?;
261+
rcv.await.map_err(|_err| FsError::GeneralFailure)?
262+
}
263+
async fn read_bytes(
264+
&self,
265+
file: OpenFile,
266+
seek: usize,
267+
count: usize,
268+
) -> Result<(Bytes, OpenFile), FsError> {
269+
let (send, rcv) = oneshot::channel();
270+
self.send
271+
.send(DavFsInnerCommand::ReadBytes(file, seek, count, send))
272+
.await
273+
.map_err(|_err| FsError::GeneralFailure)?;
274+
rcv.await.map_err(|_err| FsError::GeneralFailure)?
132275
}
133276
}
134277

135-
impl<P: Debug + Send + Sync + 'static, S: IndexedFull + Debug + Send + Sync + 'static> DavFileSystem
136-
for WebDavFS<P, S>
137-
{
278+
impl DavFileSystem for WebDavFS {
138279
fn metadata<'a>(&'a self, davpath: &'a DavPath) -> FsFuture<'_, Box<dyn DavMetaData>> {
139280
self.symlink_metadata(davpath)
140281
}
141282

142283
fn symlink_metadata<'a>(&'a self, davpath: &'a DavPath) -> FsFuture<'_, Box<dyn DavMetaData>> {
143284
async move {
144-
let node = self.node_from_path(davpath)?;
285+
let node = self.node_from_path(davpath).await?;
145286
let meta: Box<dyn DavMetaData> = Box::new(DavFsMetaData(node));
146287
Ok(meta)
147288
}
@@ -154,7 +295,7 @@ impl<P: Debug + Send + Sync + 'static, S: IndexedFull + Debug + Send + Sync + 's
154295
_meta: ReadDirMeta,
155296
) -> FsFuture<'_, FsStream<Box<dyn DavDirEntry>>> {
156297
async move {
157-
let entries = self.dir_entries_from_path(davpath)?;
298+
let entries = self.dir_entries_from_path(davpath).await?;
158299
let entry_iter = entries.into_iter().map(|e| {
159300
let entry: Box<dyn DavDirEntry> = Box::new(DavFsDirEntry(e));
160301
Ok(entry)
@@ -171,30 +312,13 @@ impl<P: Debug + Send + Sync + 'static, S: IndexedFull + Debug + Send + Sync + 's
171312
options: OpenOptions,
172313
) -> FsFuture<'_, Box<dyn DavFile>> {
173314
async move {
174-
if options.write
175-
|| options.append
176-
|| options.truncate
177-
|| options.create
178-
|| options.create_new
179-
{
180-
return Err(FsError::Forbidden);
181-
}
182-
183-
let node = self.node_from_path(path)?;
184-
if matches!(self.inner.file_policy, FilePolicy::Forbidden) {
185-
return Err(FsError::Forbidden);
186-
}
187-
188-
let open = self
189-
.inner
190-
.repo
191-
.open_file(&node)
192-
.map_err(|_err| FsError::GeneralFailure)?;
315+
let node = self.node_from_path(path).await?;
316+
let file = self.open(&node, options).await?;
193317
let file: Box<dyn DavFile> = Box::new(DavFsFile {
194-
node,
195-
open,
196-
fs: self.inner.clone(),
318+
open: Some(file),
197319
seek: 0,
320+
fs: self.clone(),
321+
node,
198322
});
199323
Ok(file)
200324
}
@@ -234,27 +358,25 @@ impl DavDirEntry for DavFsDirEntry {
234358
/// A [`DavFile`] implementation for [`Node`]s.
235359
///
236360
/// This is a read-only file.
237-
struct DavFsFile<P, S> {
361+
struct DavFsFile {
238362
/// The [`Node`] this file is for
239363
node: Node,
240364

241365
/// The [`OpenFile`] for this file
242-
open: OpenFile,
243-
244-
/// The [`DavFsInner`] this file belongs to
245-
fs: Arc<DavFsInner<P, S>>,
366+
open: Option<OpenFile>,
246367

247368
/// The current seek position
248369
seek: usize,
370+
fs: WebDavFS,
249371
}
250372

251-
impl<P, S> Debug for DavFsFile<P, S> {
373+
impl Debug for DavFsFile {
252374
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
253375
write!(f, "DavFile")
254376
}
255377
}
256378

257-
impl<P: Debug + Send + Sync, S: IndexedFull + Debug + Send + Sync> DavFile for DavFsFile<P, S> {
379+
impl DavFile for DavFsFile {
258380
fn metadata(&mut self) -> FsFuture<'_, Box<dyn DavMetaData>> {
259381
async move {
260382
let meta: Box<dyn DavMetaData> = Box::new(DavFsMetaData(self.node.clone()));
@@ -273,12 +395,16 @@ impl<P: Debug + Send + Sync, S: IndexedFull + Debug + Send + Sync> DavFile for D
273395

274396
fn read_bytes(&mut self, count: usize) -> FsFuture<'_, Bytes> {
275397
async move {
276-
let data = self
398+
let (data, open) = self
277399
.fs
278-
.repo
279-
.read_file_at(&self.open, self.seek, count)
280-
.map_err(|_err| FsError::GeneralFailure)?;
400+
.read_bytes(
401+
self.open.take().ok_or(FsError::GeneralFailure)?,
402+
self.seek,
403+
count,
404+
)
405+
.await?;
281406
self.seek += data.len();
407+
self.open = Some(open);
282408
Ok(data)
283409
}
284410
.boxed()

0 commit comments

Comments
 (0)