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

alamb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new b55e3b187 Canonicalize filesystem paths in user-facing APIs (#2370) 
(#2371)
b55e3b187 is described below

commit b55e3b1874be8737f18c11e6a8497e9724f8d864
Author: Raphael Taylor-Davies <[email protected]>
AuthorDate: Tue Aug 9 15:56:02 2022 +0100

    Canonicalize filesystem paths in user-facing APIs (#2370) (#2371)
    
    * Canonicalize LocalFileSystem::root (#2370)
    
    * Canonicalize paths passed to Path::from_filesystem_path
    
    * Add test
---
 object_store/src/local.rs    | 33 +++++++++++++++++++++++++++++----
 object_store/src/path/mod.rs | 31 +++++++++++++++++++++++--------
 2 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/object_store/src/local.rs b/object_store/src/local.rs
index c5903401e..fd3c3592a 100644
--- a/object_store/src/local.rs
+++ b/object_store/src/local.rs
@@ -18,7 +18,7 @@
 //! An object store implementation for a local filesystem
 use crate::{
     maybe_spawn_blocking,
-    path::{filesystem_path_to_url, Path},
+    path::{absolute_path_to_url, Path},
     GetResult, ListResult, MultipartId, ObjectMeta, ObjectStore, Result,
 };
 use async_trait::async_trait;
@@ -129,6 +129,12 @@ pub(crate) enum Error {
         path: String,
         source: io::Error,
     },
+
+    #[snafu(display("Unable to canonicalize filesystem root: {}", 
path.display()))]
+    UnableToCanonicalize {
+        path: PathBuf,
+        source: io::Error,
+    },
 }
 
 impl From<Error> for super::Error {
@@ -214,17 +220,24 @@ impl LocalFileSystem {
     }
 
     /// Create new filesystem storage with `prefix` applied to all paths
+    ///
+    /// Returns an error if the path does not exist
+    ///
     pub fn new_with_prefix(prefix: impl AsRef<std::path::Path>) -> 
Result<Self> {
+        let path = 
std::fs::canonicalize(&prefix).context(UnableToCanonicalizeSnafu {
+            path: prefix.as_ref(),
+        })?;
+
         Ok(Self {
             config: Arc::new(Config {
-                root: filesystem_path_to_url(prefix)?,
+                root: absolute_path_to_url(path)?,
             }),
         })
     }
 }
 
 impl Config {
-    /// Return filesystem path of the given location
+    /// Return an absolute filesystem path of the given location
     fn path_to_filesystem(&self, location: &Path) -> Result<PathBuf> {
         let mut url = self.root.clone();
         url.path_segments_mut()
@@ -238,8 +251,9 @@ impl Config {
             .map_err(|_| Error::InvalidUrl { url }.into())
     }
 
+    /// Resolves the provided absolute filesystem path to a [`Path`] prefix
     fn filesystem_to_path(&self, location: &std::path::Path) -> Result<Path> {
-        Ok(Path::from_filesystem_path_with_base(
+        Ok(Path::from_absolute_path_with_base(
             location,
             Some(&self.root),
         )?)
@@ -1286,4 +1300,15 @@ mod tests {
         assert_eq!(res.objects.len(), 1);
         assert_eq!(res.objects[0].location.as_ref(), filename);
     }
+
+    #[tokio::test]
+    async fn relative_paths() {
+        LocalFileSystem::new_with_prefix(".").unwrap();
+        LocalFileSystem::new_with_prefix("..").unwrap();
+        LocalFileSystem::new_with_prefix("../..").unwrap();
+
+        let integration = LocalFileSystem::new();
+        let path = Path::from_filesystem_path(".").unwrap();
+        integration.list_with_delimiter(Some(&path)).await.unwrap();
+    }
 }
diff --git a/object_store/src/path/mod.rs b/object_store/src/path/mod.rs
index 5f16d05b5..e5a7b6443 100644
--- a/object_store/src/path/mod.rs
+++ b/object_store/src/path/mod.rs
@@ -162,23 +162,38 @@ impl Path {
 
     /// Convert a filesystem path to a [`Path`] relative to the filesystem root
     ///
-    /// This will return an error if the path contains illegal
-    /// character sequences as defined by [`Path::parse`]
+    /// This will return an error if the path contains illegal character 
sequences
+    /// as defined by [`Path::parse`] or does not exist
+    ///
+    /// Note: this will canonicalize the provided path, resolving any symlinks
     pub fn from_filesystem_path(
         path: impl AsRef<std::path::Path>,
     ) -> Result<Self, Error> {
-        Self::from_filesystem_path_with_base(path, None)
+        let absolute = std::fs::canonicalize(&path).context(CanonicalizeSnafu {
+            path: path.as_ref(),
+        })?;
+
+        Self::from_absolute_path(absolute)
+    }
+
+    /// Convert an absolute filesystem path to a [`Path`] relative to the 
filesystem root
+    ///
+    /// This will return an error if the path contains illegal character 
sequences
+    /// as defined by [`Path::parse`], or `base` is not an absolute path
+    pub fn from_absolute_path(path: impl AsRef<std::path::Path>) -> 
Result<Self, Error> {
+        Self::from_absolute_path_with_base(path, None)
     }
 
     /// Convert a filesystem path to a [`Path`] relative to the provided base
     ///
     /// This will return an error if the path contains illegal character 
sequences
-    /// as defined by [`Path::parse`], or `base` does not refer to a parent 
path of `path`
-    pub(crate) fn from_filesystem_path_with_base(
+    /// as defined by [`Path::parse`], or `base` does not refer to a parent 
path of `path`,
+    /// or `base` is not an absolute path
+    pub(crate) fn from_absolute_path_with_base(
         path: impl AsRef<std::path::Path>,
         base: Option<&Url>,
     ) -> Result<Self, Error> {
-        let url = filesystem_path_to_url(path)?;
+        let url = absolute_path_to_url(path)?;
         let path = match base {
             Some(prefix) => 
url.path().strip_prefix(prefix.path()).ok_or_else(|| {
                 Error::PrefixMismatch {
@@ -293,8 +308,8 @@ where
     }
 }
 
-/// Given a filesystem path convert it to a URL representation
-pub(crate) fn filesystem_path_to_url(
+/// Given an absolute filesystem path convert it to a URL representation 
without canonicalization
+pub(crate) fn absolute_path_to_url(
     path: impl AsRef<std::path::Path>,
 ) -> Result<Url, Error> {
     Url::from_file_path(&path).map_err(|_| Error::InvalidPath {

Reply via email to