This is an automated email from the ASF dual-hosted git repository.
dentiny pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 868837c9e feat(fs): copy with metadata (#7535)
868837c9e is described below
commit 868837c9ec2e5e29e7bf97cf8e00a24bf0424766
Author: dentiny <[email protected]>
AuthorDate: Wed Jun 24 09:59:29 2026 -0700
feat(fs): copy with metadata (#7535)
---
core/Cargo.lock | 1 +
core/services/fs/Cargo.toml | 4 ++++
core/services/fs/src/core.rs | 47 +++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/core/Cargo.lock b/core/Cargo.lock
index 457874deb..17a511f37 100644
--- a/core/Cargo.lock
+++ b/core/Cargo.lock
@@ -7016,6 +7016,7 @@ dependencies = [
"log",
"opendal-core",
"serde",
+ "tempfile",
"tokio",
"xattr",
]
diff --git a/core/services/fs/Cargo.toml b/core/services/fs/Cargo.toml
index 6e66f250d..06ea4d623 100644
--- a/core/services/fs/Cargo.toml
+++ b/core/services/fs/Cargo.toml
@@ -41,3 +41,7 @@ tokio = { workspace = true, features = ["fs",
"rt-multi-thread"] }
[target.'cfg(unix)'.dependencies]
xattr = "1"
+
+[dev-dependencies]
+tempfile = "3"
+tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
diff --git a/core/services/fs/src/core.rs b/core/services/fs/src/core.rs
index e6b45f579..e6c54edeb 100644
--- a/core/services/fs/src/core.rs
+++ b/core/services/fs/src/core.rs
@@ -224,7 +224,20 @@ impl FsCore {
.ensure_write_abs_path(&self.root, to.trim_end_matches('/'))
.await?;
- tokio::fs::copy(from, to).await.map_err(new_std_io_error)?;
+ tokio::fs::copy(&from, &to)
+ .await
+ .map_err(new_std_io_error)?;
+
+ // only *nix supports `write_with_user_metadata`
+ #[cfg(unix)]
+ {
+ if let Ok(user_meta) = Self::get_user_metadata(&from) {
+ if !user_meta.is_empty() {
+ Self::set_user_metadata(&to, &user_meta)?;
+ }
+ }
+ }
+
Ok(())
}
@@ -321,3 +334,35 @@ mod error {
}
pub(super) use error::*;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[cfg(unix)]
+ #[tokio::test]
+ async fn test_fs_backend_copy_preserves_user_metadata() {
+ use opendal_core::Operator;
+
+ let temp_dir = tempfile::TempDir::new().unwrap();
+ let root = temp_dir.path();
+
+ let src = "src_meta.txt";
+ let dst = "dst_meta.txt";
+
+ let src_path = root.join(src);
+ let dst_path = root.join(dst);
+
+ std::fs::File::create(&src_path).unwrap();
+
+ let mut meta = HashMap::new();
+ meta.insert("key".to_string(), "preserved123".to_string());
+ FsCore::set_user_metadata(&src_path, &meta).unwrap();
+
+ let op =
Operator::new(crate::Fs::default().root(root.to_str().unwrap())).unwrap();
+ op.copy(src, dst).await.unwrap();
+
+ let got = FsCore::get_user_metadata(&dst_path).unwrap();
+ assert_eq!(got.get("key").map(String::as_str), Some("preserved123"));
+ }
+}