From de59cd2deb19cb7aa880f4438aaa3709cbce5c13 Mon Sep 17 00:00:00 2001 From: "shiro.lee" Date: Fri, 5 Jul 2024 11:47:13 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96endpoint=20new?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 7 +++++ ecstore/Cargo.toml | 1 + ecstore/src/disks_layout.rs | 15 +++++++--- ecstore/src/ellipses.rs | 24 ++++++++++++--- ecstore/src/endpoint.rs | 60 +++++++++++++++++++++---------------- 5 files changed, 73 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 704d1422..c1ab118f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,6 +319,7 @@ dependencies = [ "lazy_static", "netif", "path-absolutize", + "path-clean", "reed-solomon-erasure", "regex", "rmp-serde", @@ -882,6 +883,12 @@ dependencies = [ "path-dedot", ] +[[package]] +name = "path-clean" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" + [[package]] name = "path-dedot" version = "3.1.1" diff --git a/ecstore/Cargo.toml b/ecstore/Cargo.toml index 538ed8b4..de95f7a6 100644 --- a/ecstore/Cargo.toml +++ b/ecstore/Cargo.toml @@ -33,6 +33,7 @@ tokio-util = { version = "0.7.11", features = ["io"] } s3s = "0.10.0" crc32fast = "1.4.2" siphasher = "1.0.1" +path-clean = "1.0.1" [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/ecstore/src/disks_layout.rs b/ecstore/src/disks_layout.rs index 772c48fd..88364c00 100644 --- a/ecstore/src/disks_layout.rs +++ b/ecstore/src/disks_layout.rs @@ -283,11 +283,11 @@ fn possible_set_counts_with_symmetry(set_counts: &[usize], arg_patterns: &[ArgPa for &ss in set_counts { let mut symmetry = false; for arg_pattern in arg_patterns { - for p in arg_pattern.inner.iter() { - if p.seq.len() > ss { - symmetry = (p.seq.len() % ss) == 0; + for p in arg_pattern.as_ref().iter() { + if p.len() > ss { + symmetry = (p.len() % ss) == 0; } else { - symmetry = (ss % p.seq.len()) == 0; + symmetry = (ss % p.len()) == 0; } } } @@ -520,6 +520,13 @@ mod test { ]], success: true, }, + TestCase { + num: 15, + args: vec!["https://node{1...3}.example.net/mnt/drive{1...8}"], + total_sizes: vec![24], + indexes: vec![vec![12, 12]], + success: true, + }, ]; for test_case in test_cases { diff --git a/ecstore/src/ellipses.rs b/ecstore/src/ellipses.rs index 170ec0d7..fdb647ea 100644 --- a/ecstore/src/ellipses.rs +++ b/ecstore/src/ellipses.rs @@ -16,9 +16,9 @@ const ELLIPSES: &str = "..."; /// associated prefix and suffixes. #[derive(Debug, Default, PartialEq, Eq)] pub struct Pattern { - pub prefix: String, - pub suffix: String, - pub seq: Vec, + pub(crate) prefix: String, + pub(crate) suffix: String, + pub(crate) seq: Vec, } impl Pattern { @@ -37,12 +37,28 @@ impl Pattern { ret } + + pub fn len(&self) -> usize { + self.seq.len() + } } /// contains a list of patterns provided in the input. #[derive(Debug, PartialEq, Eq)] pub struct ArgPattern { - pub inner: Vec, + inner: Vec, +} + +impl AsRef> for ArgPattern { + fn as_ref(&self) -> &Vec { + &self.inner + } +} + +impl AsMut> for ArgPattern { + fn as_mut(&mut self) -> &mut Vec { + &mut self.inner + } } impl ArgPattern { diff --git a/ecstore/src/endpoint.rs b/ecstore/src/endpoint.rs index 0a2e4b33..03f39d14 100644 --- a/ecstore/src/endpoint.rs +++ b/ecstore/src/endpoint.rs @@ -2,6 +2,7 @@ use super::disks_layout::DisksLayout; use super::error::{Error, Result}; use super::utils::net; use path_absolutize::Absolutize; +use path_clean::PathClean; use std::collections::HashSet; use std::fmt::Display; use std::{collections::HashMap, path::Path, usize}; @@ -70,20 +71,18 @@ impl TryFrom<&str> for Endpoint { /// Performs the conversion. fn try_from(value: &str) -> Result { /// check whether given path is not empty. - fn is_empty_path(path: &str) -> bool { - ["", "/", "\\"].iter().any(|&v| v.eq(path)) + fn is_empty_path(path: impl AsRef) -> bool { + ["", "/", "\\"].iter().any(|&v| Path::new(v).eq(path.as_ref())) } if is_empty_path(value) { return Err(Error::from_string("empty or root endpoint is not supported")); } - // TODO What if the value passed in is something like d:\data\rustfs - let mut is_local = false; let url = match Url::parse(value) { #[allow(unused_mut)] - Ok(mut url) => { + Ok(mut url) if url.has_host() => { // URL style of endpoint. // Valid URL style endpoint is // - Scheme field must contain "http" or "https" @@ -96,7 +95,7 @@ impl TryFrom<&str> for Endpoint { return Err(Error::from_string("invalid URL endpoint format")); } - if is_empty_path(url.path()) { + if is_empty_path(Path::new(url.path()).clean()) { return Err(Error::from_string("empty or root endpoint is not supported")); } @@ -124,31 +123,20 @@ impl TryFrom<&str> for Endpoint { url } + Ok(_) => { + // like d:/foo + is_local = true; + url_parse_from_file_path(value)? + } Err(e) => match e { ParseError::InvalidPort => { return Err(Error::from_string("invalid URL endpoint format: port number must be between 1 to 65535")) } ParseError::EmptyHost => return Err(Error::from_string("invalid URL endpoint format: empty host name")), ParseError::RelativeUrlWithoutBase => { - // Only check if the arg is an ip address and ask for scheme since its absent. - // localhost, example.com, any FQDN cannot be disambiguated from a regular file path such as - // /mnt/export1. So we go ahead and start the rustfs server in FS modes in these cases. - if net::is_socket_addr(value) { - return Err(Error::from_string("invalid URL endpoint format: missing scheme http or https")); - } - - let file_path = match Path::new(value).absolutize() { - Ok(path) => path, - Err(err) => return Err(Error::from_string(format!("absolute path failed: {}", err))), - }; - - match Url::from_file_path(file_path) { - Ok(url) => { - is_local = true; - url - } - Err(_) => return Err(Error::from_string("Convert a file path into an URL failed")), - } + // like /foo + is_local = true; + url_parse_from_file_path(value)? } _ => return Err(Error::from_string(format!("invalid URL endpoint format: {}", e))), }, @@ -503,6 +491,26 @@ impl EndpointServerPools { } } +/// parse a file path into an URL. +fn url_parse_from_file_path(value: &str) -> Result { + // Only check if the arg is an ip address and ask for scheme since its absent. + // localhost, example.com, any FQDN cannot be disambiguated from a regular file path such as + // /mnt/export1. So we go ahead and start the rustfs server in FS modes in these cases. + if net::is_socket_addr(value) { + return Err(Error::from_string("invalid URL endpoint format: missing scheme http or https")); + } + + let file_path = match Path::new(value).absolutize() { + Ok(path) => path, + Err(err) => return Err(Error::from_string(format!("absolute path failed: {}", err))), + }; + + match Url::from_file_path(file_path) { + Ok(url) => Ok(url), + Err(_) => return Err(Error::from_string("Convert a file path into an URL failed")), + } +} + #[cfg(test)] mod test { use super::*; @@ -519,7 +527,7 @@ mod test { let u2 = url::Url::parse("https://example.org/path").unwrap(); let u4 = url::Url::parse("http://192.168.253.200/path").unwrap(); - let root_slash_foo = url::Url::from_file_path("d:/foo").unwrap(); + let root_slash_foo = url::Url::from_file_path("/foo").unwrap(); let test_cases = [ TestCase {