mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 15:37:02 +00:00
fix(windows): handle zfs volume paths (#3157)
* fix(windows): handle zfs volume paths * fix(windows): address volume path review * refactor(windows): share fallback path resolution --------- Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
@@ -19,6 +19,23 @@ use std::{fmt::Display, path::Path};
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub(crate) fn windows_fallback_local_path(
|
||||||
|
path: &str,
|
||||||
|
canonicalize_error: &std::io::Error,
|
||||||
|
context: &'static str,
|
||||||
|
) -> std::io::Result<std::path::PathBuf> {
|
||||||
|
let absolute = Path::new(path).absolutize()?.to_path_buf();
|
||||||
|
tracing::warn!(
|
||||||
|
path = %path,
|
||||||
|
canonicalize_error = %canonicalize_error,
|
||||||
|
resolved = ?absolute,
|
||||||
|
context = context,
|
||||||
|
"using windows fallback path resolution for local endpoint"
|
||||||
|
);
|
||||||
|
Ok(absolute)
|
||||||
|
}
|
||||||
|
|
||||||
/// enum for endpoint type.
|
/// enum for endpoint type.
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub enum EndpointType {
|
pub enum EndpointType {
|
||||||
|
|||||||
@@ -356,20 +356,60 @@ impl Debug for LocalDisk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve the local disk root path from an endpoint path.
|
||||||
|
///
|
||||||
|
/// Tries `canonicalize` first (fast path). On Windows, if canonicalization reports
|
||||||
|
/// `NotFound` for paths that may still be valid mount roots, falls back to
|
||||||
|
/// `absolutize` + metadata check to accept valid local directory roots that
|
||||||
|
/// don't support full canonicalization.
|
||||||
|
fn resolve_local_disk_root(ep_path: &str) -> Result<PathBuf> {
|
||||||
|
match rustfs_utils::canonicalize(ep_path) {
|
||||||
|
Ok(path) => Ok(path),
|
||||||
|
Err(err) => {
|
||||||
|
if err.kind() != ErrorKind::NotFound {
|
||||||
|
return Err(to_file_error(err).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
// On Windows, canonicalize can fail for ZFS volumes, junction points,
|
||||||
|
// subst drives, and other non-standard filesystem mounts. Try a fallback
|
||||||
|
// path resolution using absolutize + metadata check.
|
||||||
|
let absolute = match crate::disk::endpoint::windows_fallback_local_path(ep_path, &err, "local disk root") {
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(DiskError::VolumeNotFound);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match std::fs::metadata(&absolute) {
|
||||||
|
Ok(metadata) => {
|
||||||
|
if !metadata.is_dir() {
|
||||||
|
return Err(DiskError::DiskNotDir);
|
||||||
|
}
|
||||||
|
return Ok(absolute);
|
||||||
|
}
|
||||||
|
Err(meta_err) => {
|
||||||
|
if meta_err.kind() == ErrorKind::NotFound {
|
||||||
|
return Err(DiskError::VolumeNotFound);
|
||||||
|
}
|
||||||
|
return Err(to_file_error(meta_err).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
Err(DiskError::VolumeNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LocalDisk {
|
impl LocalDisk {
|
||||||
pub async fn new(ep: &Endpoint, cleanup: bool) -> Result<Self> {
|
pub async fn new(ep: &Endpoint, cleanup: bool) -> Result<Self> {
|
||||||
debug!("Creating local disk");
|
debug!("Creating local disk");
|
||||||
// Use optimized path resolution instead of absolutize() for better performance
|
let root = resolve_local_disk_root(&ep.get_file_path())?;
|
||||||
// Use dunce::canonicalize instead of std::fs::canonicalize to avoid UNC paths on Windows
|
|
||||||
let root = match rustfs_utils::canonicalize(ep.get_file_path()) {
|
|
||||||
Ok(path) => path,
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() == ErrorKind::NotFound {
|
|
||||||
return Err(DiskError::VolumeNotFound);
|
|
||||||
}
|
|
||||||
return Err(to_file_error(e).into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ensure_data_usage_layout(&root).await.map_err(DiskError::from)?;
|
ensure_data_usage_layout(&root).await.map_err(DiskError::from)?;
|
||||||
|
|
||||||
|
|||||||
@@ -689,8 +689,39 @@ fn validate_local_physical_disk_independence(pools: &[Endpoints]) -> Result<()>
|
|||||||
let canonical = match rustfs_utils::canonicalize(path) {
|
let canonical = match rustfs_utils::canonicalize(path) {
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(err) if err.kind() == ErrorKind::NotFound => {
|
Err(err) if err.kind() == ErrorKind::NotFound => {
|
||||||
missing_paths.push(path.clone());
|
// On Windows, canonicalize can fail for ZFS volumes, junction points,
|
||||||
continue;
|
// subst drives, and other non-standard mounts. Try absolutize as fallback.
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
match crate::disk::endpoint::windows_fallback_local_path(path, &err, "disk independence validation") {
|
||||||
|
Ok(absolute) => {
|
||||||
|
let abs_path = absolute.to_string_lossy().into_owned();
|
||||||
|
match rustfs_utils::os::get_physical_device_ids(&abs_path) {
|
||||||
|
Ok(ids) => {
|
||||||
|
for device_id in ids {
|
||||||
|
device_paths.entry(device_id).or_default().insert(abs_path.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(device_err) => {
|
||||||
|
return Err(Error::other(format!(
|
||||||
|
"failed to inspect physical disk for local endpoint '{abs_path}' after fallback path resolution: {device_err}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(fallback_err) => {
|
||||||
|
return Err(Error::other(format!(
|
||||||
|
"failed to resolve local endpoint path '{path}' for disk validation: {err}; fallback resolution failed: {fallback_err}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
missing_paths.push(path.clone());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(Error::other(format!(
|
return Err(Error::other(format!(
|
||||||
|
|||||||
@@ -423,9 +423,30 @@ pub fn find_ellipses_patterns(arg: &str) -> Result<ArgPattern> {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
pub fn has_ellipses<T: AsRef<str>>(s: &[T]) -> bool {
|
pub fn has_ellipses<T: AsRef<str>>(s: &[T]) -> bool {
|
||||||
let pattern = [ELLIPSES, OPEN_BRACES, CLOSE_BRACES];
|
s.iter().any(|v| {
|
||||||
|
let v = v.as_ref();
|
||||||
|
|
||||||
s.iter().any(|v| pattern.iter().any(|p| v.as_ref().contains(p)))
|
let mut remaining = v;
|
||||||
|
while let Some(start) = remaining.find(OPEN_BRACES) {
|
||||||
|
let after_start = &remaining[start + OPEN_BRACES.len()..];
|
||||||
|
let Some(end) = after_start.find(CLOSE_BRACES) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
// Dotted brace content covers both valid ellipsis patterns like
|
||||||
|
// {1...64} and invalid attempts like {1..64}, while excluding
|
||||||
|
// Windows Volume{GUID} paths (GUIDs use hyphens, not dots).
|
||||||
|
if after_start[..end].contains('.') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
remaining = &after_start[end + CLOSE_BRACES.len()..];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bare `...` without braces (e.g. "1...64") is still an ellipsis attempt.
|
||||||
|
if v.contains(ELLIPSES) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses an ellipses range pattern of following style
|
/// Parses an ellipses range pattern of following style
|
||||||
@@ -569,6 +590,10 @@ mod tests {
|
|||||||
),
|
),
|
||||||
(15, vec!["mydisk-{a...z}{1...20}"], true),
|
(15, vec!["mydisk-{a...z}{1...20}"], true),
|
||||||
(16, vec!["mydisk-{1...4}{1..2.}"], true),
|
(16, vec!["mydisk-{1...4}{1..2.}"], true),
|
||||||
|
// Windows Volume{GUID} paths must not match; GUIDs use hyphens, not dots.
|
||||||
|
(17, vec![r"\\?\Volume{9425a190-4bb3-11f1-86de-803253b6a8b8}\"], false),
|
||||||
|
(18, vec![r"//./Volume{9425a190-4bb3-11f1-86de-803253b6a8b8}/"], false),
|
||||||
|
(19, vec![r"\\?\Volume{9425a190-4bb3-11f1-86de-803253b6a8b8}\disk-{1..4}"], true),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (i, args, expected) in test_cases {
|
for (i, args, expected) in test_cases {
|
||||||
|
|||||||
Reference in New Issue
Block a user