This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch add-nextcloud-test-for-webdav
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/add-nextcloud-test-for-webdav 
by this push:
     new fd3989899 Check parent first
fd3989899 is described below

commit fd398989984e66f15102cce2c5a422b8acf042ee
Author: Xuanwo <[email protected]>
AuthorDate: Thu Jul 13 17:39:24 2023 +0800

    Check parent first
    
    Signed-off-by: Xuanwo <[email protected]>
---
 core/src/services/webdav/backend.rs | 72 +++++++++++++++++++++++--------------
 1 file changed, 45 insertions(+), 27 deletions(-)

diff --git a/core/src/services/webdav/backend.rs 
b/core/src/services/webdav/backend.rs
index 353c96c1b..9288b25c8 100644
--- a/core/src/services/webdav/backend.rs
+++ b/core/src/services/webdav/backend.rs
@@ -16,6 +16,7 @@
 // under the License.
 
 use std::collections::HashMap;
+use std::collections::VecDeque;
 use std::fmt::Debug;
 use std::fmt::Formatter;
 
@@ -299,7 +300,27 @@ impl Accessor for WebdavBackend {
         self.ensure_parent_path(path).await?;
 
         let abs_path = build_abs_path(&self.root, path);
-        self.create_internal(&abs_path).await
+
+        let resp = self
+            .webdav_mkcol(&abs_path, None, None, AsyncBody::Empty)
+            .await?;
+
+        let status = resp.status();
+
+        match status {
+            StatusCode::CREATED
+            | StatusCode::OK
+            // `File exists` will return `Method Not Allowed`
+            | StatusCode::METHOD_NOT_ALLOWED
+            // create existing dir will return conflict
+            | StatusCode::CONFLICT
+            // create existing file will return no_content
+            | StatusCode::NO_CONTENT => {
+                resp.into_body().consume().await?;
+                Ok(RpCreateDir::default())
+            }
+            _ => Err(parse_error(resp).await?),
+        }
     }
 
     async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, 
Self::Reader)> {
@@ -642,14 +663,12 @@ impl WebdavBackend {
         self.client.send(req).await
     }
 
-    async fn create_internal(&self, abs_path: &str) -> Result<RpCreateDir> {
-        let resp = if abs_path.ends_with('/') {
-            self.webdav_mkcol(abs_path, None, None, AsyncBody::Empty)
-                .await?
-        } else {
-            self.webdav_put(abs_path, Some(0), None, None, AsyncBody::Empty)
-                .await?
-        };
+    async fn create_dir_internal(&self, path: &str) -> Result<()> {
+        let abs_path = build_abs_path(&self.root, path);
+
+        let resp = self
+            .webdav_mkcol(&abs_path, None, None, AsyncBody::Empty)
+            .await?;
 
         let status = resp.status();
 
@@ -663,32 +682,31 @@ impl WebdavBackend {
             // create existing file will return no_content
             | StatusCode::NO_CONTENT => {
                 resp.into_body().consume().await?;
-                Ok(RpCreateDir::default())
+                Ok(())
             }
             _ => Err(parse_error(resp).await?),
         }
     }
 
-    async fn ensure_parent_path(&self, path: &str) -> Result<()> {
-        if path == "/" {
-            return Ok(());
-        }
-
-        // create dir recursively, split path by `/` and create each dir 
except the last one
-        let abs_path = build_abs_path(&self.root, path);
-        let abs_path = abs_path.as_str();
-        let mut parts: Vec<&str> = abs_path.split('/').filter(|x| 
!x.is_empty()).collect();
-        if !parts.is_empty() {
-            parts.pop();
+    async fn ensure_parent_path(&self, mut path: &str) -> Result<()> {
+        let mut dirs = VecDeque::default();
+
+        while path != "/" {
+            // check path first.
+            let parent = get_parent(path);
+            match self.stat(path, OpStat::default()).await {
+                Ok(_) => break,
+                Err(err) if err.kind() != ErrorKind::NotFound => return 
Err(err),
+                _ => {
+                    dirs.push_front(path);
+                    path = parent
+                }
+            }
         }
 
-        let mut sub_path = String::new();
-        for sub_part in parts {
-            let sub_path_with_slash = sub_part.to_owned() + "/";
-            sub_path.push_str(&sub_path_with_slash);
-            self.create_internal(&sub_path).await?;
+        for dir in dirs {
+            self.create_dir_internal(dir).await?;
         }
-
         Ok(())
     }
 }

Reply via email to