diff --git a/Cargo.lock b/Cargo.lock index 978c7fc3f..0ef909d73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3209,6 +3209,7 @@ dependencies = [ "wild", "winapi-util", "windows-sys 0.48.0", + "xattr", "z85", ] diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 44f8bb2d1..557cdc4dd 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -54,6 +54,7 @@ sm3 = { workspace = true, optional = true } [target.'cfg(unix)'.dependencies] walkdir = { workspace = true, optional = true } nix = { workspace = true, features = ["fs", "uio", "zerocopy", "signal"] } +xattr = { workspace = true, optional = true } [dev-dependencies] clap = { workspace = true } @@ -77,6 +78,7 @@ encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] fs = ["dunce", "libc", "winapi-util", "windows-sys"] fsext = ["libc", "time", "windows-sys"] +fsxattr = [ "xattr" ] lines = [] format = ["itertools"] mode = ["libc"] diff --git a/src/uucore/src/lib/features.rs b/src/uucore/src/lib/features.rs index e26de487b..423ff34ba 100644 --- a/src/uucore/src/lib/features.rs +++ b/src/uucore/src/lib/features.rs @@ -46,6 +46,8 @@ pub mod pipes; #[cfg(all(unix, feature = "process"))] pub mod process; +#[cfg(all(unix, not(target_os = "macos"), feature = "fsxattr"))] +pub mod fsxattr; #[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))] pub mod signals; #[cfg(all( diff --git a/src/uucore/src/lib/features/fsxattr.rs b/src/uucore/src/lib/features/fsxattr.rs new file mode 100644 index 000000000..7bda023f9 --- /dev/null +++ b/src/uucore/src/lib/features/fsxattr.rs @@ -0,0 +1,116 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +//! Set of functions to manage xattr on files and dirs +use std::collections::HashMap; +use std::ffi::OsString; +use std::path::Path; + +/// Copies extended attributes (xattrs) from one file or directory to another. +/// +/// # Arguments +/// +/// * `source` - A reference to the source path. +/// * `dest` - A reference to the destination path. +/// +/// # Returns +/// +/// A result indicating success or failure. +pub fn copy_xattrs>(source: P, dest: P) -> std::io::Result<()> { + for attr_name in xattr::list(&source)? { + if let Some(value) = xattr::get(&source, &attr_name)? { + xattr::set(&dest, &attr_name, &value)?; + } + } + Ok(()) +} + +/// Retrieves the extended attributes (xattrs) of a given file or directory. +/// +/// # Arguments +/// +/// * `source` - A reference to the path of the file or directory. +/// +/// # Returns +/// +/// A result containing a HashMap of attributes names and values, or an error. +pub fn retrieve_xattrs>(source: P) -> std::io::Result>> { + let mut attrs = HashMap::new(); + for attr_name in xattr::list(&source)? { + if let Some(value) = xattr::get(&source, &attr_name)? { + attrs.insert(attr_name, value); + } + } + Ok(attrs) +} + +/// Applies extended attributes (xattrs) to a given file or directory. +/// +/// # Arguments +/// +/// * `dest` - A reference to the path of the file or directory. +/// * `xattrs` - A HashMap containing attribute names and their corresponding values. +/// +/// # Returns +/// +/// A result indicating success or failure. +pub fn apply_xattrs>( + dest: P, + xattrs: HashMap>, +) -> std::io::Result<()> { + for (attr, value) in xattrs { + xattr::set(&dest, &attr, &value)?; + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::File; + use tempfile::tempdir; + + #[test] + fn test_copy_xattrs() { + let temp_dir = tempdir().unwrap(); + let source_path = temp_dir.path().join("source.txt"); + let dest_path = temp_dir.path().join("dest.txt"); + + File::create(&source_path).unwrap(); + File::create(&dest_path).unwrap(); + + let test_attr = "user.test"; + let test_value = b"test value"; + xattr::set(&source_path, test_attr, test_value).unwrap(); + + copy_xattrs(&source_path, &dest_path).unwrap(); + + let copied_value = xattr::get(&dest_path, test_attr).unwrap().unwrap(); + assert_eq!(copied_value, test_value); + } + + #[test] + fn test_apply_and_retrieve_xattrs() { + let temp_dir = tempdir().unwrap(); + let file_path = temp_dir.path().join("test_file.txt"); + + File::create(&file_path).unwrap(); + + let mut test_xattrs = HashMap::new(); + let test_attr = "user.test_attr"; + let test_value = b"test value"; + test_xattrs.insert(OsString::from(test_attr), test_value.to_vec()); + apply_xattrs(&file_path, test_xattrs).unwrap(); + + let retrieved_xattrs = retrieve_xattrs(&file_path).unwrap(); + assert!(retrieved_xattrs.contains_key(OsString::from(test_attr).as_os_str())); + assert_eq!( + retrieved_xattrs + .get(OsString::from(test_attr).as_os_str()) + .unwrap(), + test_value + ); + } +} diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 2fc0ae301..6f8400589 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -43,8 +43,6 @@ pub use crate::features::encoding; pub use crate::features::format; #[cfg(feature = "fs")] pub use crate::features::fs; -#[cfg(feature = "fsext")] -pub use crate::features::fsext; #[cfg(feature = "lines")] pub use crate::features::lines; #[cfg(feature = "quoting-style")] @@ -89,6 +87,12 @@ pub use crate::features::utmpx; #[cfg(all(windows, feature = "wide"))] pub use crate::features::wide; +#[cfg(feature = "fsext")] +pub use crate::features::fsext; + +#[cfg(all(unix, not(target_os = "macos"), feature = "fsxattr"))] +pub use crate::features::fsxattr; + //## core functions use std::ffi::OsString;