mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
fix(window): Compatible with Windows Path (#2691)
Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
+12
-17
@@ -52,7 +52,7 @@ use rmp_serde::Serializer;
|
||||
use rustfs_common::defer;
|
||||
use rustfs_common::heal_channel::HealOpts;
|
||||
use rustfs_filemeta::{FileInfoVersions, MetaCacheEntries, MetaCacheEntry, MetadataResolutionParams};
|
||||
use rustfs_utils::path::{SLASH_SEPARATOR, encode_dir_object, path_join};
|
||||
use rustfs_utils::path::{encode_dir_object, path_join, path_to_bucket_object, path_to_bucket_object_with_base_path};
|
||||
use rustfs_workers::workers::Workers;
|
||||
use s3s::dto::{BucketLifecycleConfiguration, DefaultRetention, ReplicationConfiguration};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -986,25 +986,11 @@ impl PoolMeta {
|
||||
}
|
||||
|
||||
pub fn path2_bucket_object(name: &str) -> (String, String) {
|
||||
path2_bucket_object_with_base_path("", name)
|
||||
path_to_bucket_object(name)
|
||||
}
|
||||
|
||||
pub fn path2_bucket_object_with_base_path(base_path: &str, path: &str) -> (String, String) {
|
||||
// Trim the base path and leading slash
|
||||
let trimmed_path = path
|
||||
.strip_prefix(base_path)
|
||||
.unwrap_or(path)
|
||||
.strip_prefix(SLASH_SEPARATOR)
|
||||
.unwrap_or(path);
|
||||
// Find the position of the first '/'
|
||||
let Some(pos) = trimmed_path.find(SLASH_SEPARATOR) else {
|
||||
return (trimmed_path.to_string(), "".to_string());
|
||||
};
|
||||
// Split into bucket and prefix
|
||||
let bucket = &trimmed_path[0..pos];
|
||||
let prefix = &trimmed_path[pos + 1..]; // +1 to skip the '/' character if it exists
|
||||
|
||||
(bucket.to_string(), prefix.to_string())
|
||||
path_to_bucket_object_with_base_path(base_path, path)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
@@ -3746,4 +3732,13 @@ mod pools_tests {
|
||||
.contains("failed to start decommission routines: scheduled 1 of 2 expected workers")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_path2_bucket_object_with_base_path_supports_windows_separators() {
|
||||
let (bucket, object) = super::path2_bucket_object_with_base_path("C:\\data", "C:\\data\\my-bucket\\nested\\object.txt");
|
||||
|
||||
assert_eq!(bucket, "my-bucket");
|
||||
assert_eq!(object, "nested/object.txt");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,10 @@ use rustfs_ecstore::set_disk::SetDisks;
|
||||
use rustfs_ecstore::store_api::{BucketInfo, BucketOperations, BucketOptions, ObjectInfo};
|
||||
use rustfs_ecstore::{StorageAPI, error::Result, store::ECStore};
|
||||
use rustfs_filemeta::FileMeta;
|
||||
use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf};
|
||||
use rustfs_utils::path::path_join_buf;
|
||||
use s3s::dto::{BucketLifecycleConfiguration, ReplicationConfiguration};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
use time::OffsetDateTime;
|
||||
@@ -69,6 +70,13 @@ fn finalize_nsscanner_result(results: &[DataUsageCache], first_err: Option<Error
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_xl_meta_path(path: &str) -> bool {
|
||||
Path::new(path)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.is_some_and(|name| name == STORAGE_FORMAT_FILE)
|
||||
}
|
||||
|
||||
async fn persist_and_publish_cache_snapshot<S: StorageAPI>(
|
||||
store: Arc<S>,
|
||||
updates: &mpsc::Sender<DataUsageCache>,
|
||||
@@ -540,7 +548,7 @@ impl ScannerIODisk for Disk {
|
||||
async fn get_size(&self, mut item: ScannerItem) -> Result<SizeSummary> {
|
||||
let done_object = Metrics::time(Metric::ScanObject);
|
||||
|
||||
if !item.path.ends_with(&format!("{SLASH_SEPARATOR}{STORAGE_FORMAT_FILE}")) {
|
||||
if !is_xl_meta_path(&item.path) {
|
||||
return Err(StorageError::other("skip file".to_string()));
|
||||
}
|
||||
|
||||
@@ -741,4 +749,15 @@ mod tests {
|
||||
.expect_err("all failed sets should bubble first error");
|
||||
assert!(err.to_string().contains("set failed"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn is_xl_meta_path_accepts_windows_separator() {
|
||||
assert!(is_xl_meta_path("D:\\data\\bucket\\object\\xl.meta"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_xl_meta_path_accepts_forward_separator() {
|
||||
assert!(is_xl_meta_path("/data/bucket/object/xl.meta"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,12 @@ pub fn retain_slash(s: &str) -> String {
|
||||
|
||||
/// Checks if string `s` starts with `prefix` using case-insensitive comparison.
|
||||
pub fn strings_has_prefix_fold(s: &str, prefix: &str) -> bool {
|
||||
s.len() >= prefix.len() && (s[..prefix.len()] == *prefix || s[..prefix.len()].eq_ignore_ascii_case(prefix))
|
||||
if s.starts_with(prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
s.get(..prefix.len())
|
||||
.is_some_and(|s_prefix| s_prefix.eq_ignore_ascii_case(prefix))
|
||||
}
|
||||
|
||||
/// Checks if string `s` starts with `prefix`.
|
||||
@@ -875,4 +880,16 @@ mod tests {
|
||||
assert_eq!(bucket, "bucket");
|
||||
assert_eq!(object, "object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_path_to_bucket_object_with_base_path_handles_unicode_without_panicking() {
|
||||
let (bucket, object) = path_to_bucket_object_with_base_path(
|
||||
"D:\\Github\\rustfs\\target\\volumes\\test1",
|
||||
"s3-test-bucket/中文/日本語/한글-9cd5599a-f8eb-4e24-9df7-32ecd8d8ad1f",
|
||||
);
|
||||
|
||||
assert_eq!(bucket, "s3-test-bucket");
|
||||
assert_eq!(object, "中文/日本語/한글-9cd5599a-f8eb-4e24-9df7-32ecd8d8ad1f");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,13 +506,12 @@ pub fn parse_ellipses_range(pattern: &str) -> Result<Vec<String>> {
|
||||
/// ```
|
||||
///
|
||||
pub fn strings_has_prefix_fold(s: &str, prefix: &str) -> bool {
|
||||
if s.len() < prefix.len() {
|
||||
return false;
|
||||
if s.starts_with(prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let s_prefix = &s[..prefix.len()];
|
||||
// Test match with case first, then case-insensitive
|
||||
s_prefix == prefix || s_prefix.to_lowercase() == prefix.to_lowercase()
|
||||
s.get(..prefix.len())
|
||||
.is_some_and(|s_prefix| s_prefix.eq_ignore_ascii_case(prefix))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -872,4 +871,13 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_strings_has_prefix_fold_handles_unicode_without_panicking() {
|
||||
assert!(!strings_has_prefix_fold(
|
||||
"s3-test-bucket/中文/日本語/한글-9cd5599a-f8eb-4e24-9df7-32ecd8d8ad1f",
|
||||
"D:\\Github\\rustfs\\target\\volumes\\test1",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user