Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions integrations/dav-server/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,26 @@ impl OpendalFile {
.map_err(convert_error)?;
State::Read(r)
} else if options.write {
let w = op
.writer_with(&path)
.append(options.append)
let capability = op.info().capability();
let mut writer = op.writer_with(&path);

if options.append {
if !capability.write_can_append {
// DAV append requires backend support.
return Err(FsError::NotImplemented);
}
writer = writer.append(true);
}

if options.create_new {
if !capability.write_with_if_not_exists {
// Conditional create requires backend support.
return Err(FsError::NotImplemented);
}
writer = writer.if_not_exists(true);
}

let w = writer
.await
.map_err(convert_error)?
.into_futures_async_write();
Expand Down
117 changes: 92 additions & 25 deletions integrations/dav-server/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ use dav_server::fs::{DavFile, FsStream};
use dav_server::fs::{DavFileSystem, ReadDirMeta};
use futures::FutureExt;
use futures::StreamExt;
use opendal_core::Operator;
use opendal_core::raw::normalize_path;
use opendal_core::{ErrorKind, Operator};
use std::path::Path;

use super::dir::OpendalStream;
Expand Down Expand Up @@ -68,7 +69,24 @@ impl OpendalFs {
}

fn fs_path(&self, path: &DavPath) -> Result<String, FsError> {
String::from_utf8(path.as_bytes().to_vec()).map_err(|_| FsError::GeneralFailure)
String::from_utf8(path.as_bytes().to_vec())
.map(|path| normalize_path(path.as_str()))
.map_err(|_| FsError::GeneralFailure)
}

fn fs_dir_path(&self, path: &DavPath) -> Result<String, FsError> {
Ok(Self::into_dir_path(self.fs_path(path)?))
}

fn into_dir_path(mut path: String) -> String {
if !Self::is_dir_path(&path) {
path.push('/');
}
path
}

fn is_dir_path(path: &str) -> bool {
path.is_empty() || path.ends_with('/')
}
}

Expand All @@ -92,7 +110,7 @@ impl DavFileSystem for OpendalFs {
_meta: ReadDirMeta,
) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>> {
async move {
let path = self.fs_path(path)?;
let path = self.fs_dir_path(path)?;
self.op
.lister(path.as_str())
.await
Expand All @@ -105,21 +123,26 @@ impl DavFileSystem for OpendalFs {
fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
async move {
let path = self.fs_path(path)?;
let opendal_metadata = self.op.stat(path.as_str()).await;
match opendal_metadata {
Ok(metadata) => {
let webdav_metadata = OpendalMetaData::new(metadata);
Ok(Box::new(webdav_metadata) as Box<dyn DavMetaData>)
let metadata = match self.op.stat(path.as_str()).await {
Ok(metadata) => metadata,
Err(e)
if !Self::is_dir_path(&path)
&& matches!(e.kind(), ErrorKind::NotFound | ErrorKind::IsADirectory) =>
{
let path = Self::into_dir_path(path);
self.op.stat(path.as_str()).await.map_err(convert_error)?
}
Err(e) => Err(convert_error(e)),
}
Err(e) => return Err(convert_error(e)),
};

Ok(Box::new(OpendalMetaData::new(metadata)) as Box<dyn DavMetaData>)
}
.boxed()
}

fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
async move {
let path = self.fs_path(path)?;
let path = self.fs_dir_path(path)?;

// check if the parent path is exist.
// During MKCOL processing, a server MUST make the Request-URI a member of its parent collection, unless the Request-URI is "/". If no such ancestor exists, the method MUST fail.
Expand Down Expand Up @@ -161,7 +184,11 @@ impl DavFileSystem for OpendalFs {
}

fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
self.remove_file(path)
async move {
let path = self.fs_dir_path(path)?;
self.op.delete(&path).await.map_err(convert_error)
}
.boxed()
}

fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
Expand All @@ -174,16 +201,54 @@ impl DavFileSystem for OpendalFs {

fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
async move {
let from_path = from
.as_rel_ospath()
.to_str()
.ok_or(FsError::GeneralFailure)?;
let to_path = to.as_rel_ospath().to_str().ok_or(FsError::GeneralFailure)?;
if from.is_collection() {
let _ = self.remove_file(to).await;
let from_path = self.fs_dir_path(from)?;
let to_path = self.fs_dir_path(to)?;

if self
.op
.exists(to_path.as_str())
.await
.map_err(convert_error)?
{
return Err(FsError::Exists);
}

let mut lister = self
.op
.lister_with(from_path.as_str())
.limit(2)
.await
.map_err(convert_error)?;
let mut found = false;
while let Some(entry) = lister.next().await {
let entry = entry.map_err(convert_error)?;
if entry.path() == from_path {
found = true;
} else {
// Only empty directory moves are supported.
return Err(FsError::NotImplemented);
}
}
if !found {
return Err(FsError::NotFound);
}

self.op
.create_dir(to_path.as_str())
.await
.map_err(convert_error)?;
return self
.op
.delete(from_path.as_str())
.await
.map_err(convert_error);
}

let from_path = self.fs_path(from)?;
let to_path = self.fs_path(to)?;
self.op
.rename(from_path, to_path)
.rename(from_path.as_str(), to_path.as_str())
.await
.map_err(convert_error)
}
Expand All @@ -192,13 +257,15 @@ impl DavFileSystem for OpendalFs {

fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
async move {
let from_path = from
.as_rel_ospath()
.to_str()
.ok_or(FsError::GeneralFailure)?;
let to_path = to.as_rel_ospath().to_str().ok_or(FsError::GeneralFailure)?;
if from.is_collection() || to.is_collection() {
// Directory copies are handled by dav-server.
return Err(FsError::NotImplemented);
}

let from_path = self.fs_path(from)?;
let to_path = self.fs_path(to)?;
self.op
.copy(from_path, to_path)
.copy(from_path.as_str(), to_path.as_str())
.await
.map(|_| ())
.map_err(convert_error)
Expand Down
10 changes: 7 additions & 3 deletions integrations/dav-server/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@

pub fn convert_error(opendal_error: opendal_core::Error) -> dav_server::fs::FsError {
match opendal_error.kind() {
opendal_core::ErrorKind::AlreadyExists | opendal_core::ErrorKind::IsSameFile => {
dav_server::fs::FsError::Exists
}
opendal_core::ErrorKind::Unsupported => dav_server::fs::FsError::NotImplemented,
opendal_core::ErrorKind::NotFound => dav_server::fs::FsError::NotFound,
opendal_core::ErrorKind::PermissionDenied
| opendal_core::ErrorKind::IsADirectory
| opendal_core::ErrorKind::NotADirectory => dav_server::fs::FsError::Forbidden,
opendal_core::ErrorKind::AlreadyExists
| opendal_core::ErrorKind::IsSameFile
| opendal_core::ErrorKind::ConditionNotMatch => dav_server::fs::FsError::Exists,
_ => dav_server::fs::FsError::GeneralFailure,
}
}
Loading