mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-03 11:57:43 +00:00
9d2b8822cf
Co-authored-by: houseme <housemecn@gmail.com>
452 lines
20 KiB
Rust
452 lines
20 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use crate::disk::error::DiskError;
|
|
|
|
pub fn to_file_error(io_err: std::io::Error) -> std::io::Error {
|
|
match io_err.kind() {
|
|
std::io::ErrorKind::NotFound => DiskError::FileNotFound.into(),
|
|
std::io::ErrorKind::PermissionDenied => DiskError::FileAccessDenied.into(),
|
|
std::io::ErrorKind::IsADirectory => DiskError::IsNotRegular.into(),
|
|
std::io::ErrorKind::NotADirectory => DiskError::FileAccessDenied.into(),
|
|
std::io::ErrorKind::DirectoryNotEmpty => DiskError::FileAccessDenied.into(),
|
|
std::io::ErrorKind::UnexpectedEof => DiskError::FaultyDisk.into(),
|
|
std::io::ErrorKind::TooManyLinks => DiskError::TooManyOpenFiles.into(),
|
|
std::io::ErrorKind::InvalidInput => DiskError::FileNotFound.into(),
|
|
std::io::ErrorKind::InvalidData => DiskError::FileCorrupt.into(),
|
|
std::io::ErrorKind::StorageFull => DiskError::DiskFull.into(),
|
|
_ => io_err,
|
|
}
|
|
}
|
|
|
|
pub fn to_volume_error(io_err: std::io::Error) -> std::io::Error {
|
|
match io_err.kind() {
|
|
std::io::ErrorKind::NotFound => DiskError::VolumeNotFound.into(),
|
|
std::io::ErrorKind::PermissionDenied => DiskError::DiskAccessDenied.into(),
|
|
std::io::ErrorKind::DirectoryNotEmpty => DiskError::VolumeNotEmpty.into(),
|
|
std::io::ErrorKind::NotADirectory => DiskError::IsNotRegular.into(),
|
|
std::io::ErrorKind::Other => match io_err.downcast::<DiskError>() {
|
|
Ok(err) => match err {
|
|
DiskError::FileNotFound => DiskError::VolumeNotFound.into(),
|
|
DiskError::FileAccessDenied => DiskError::DiskAccessDenied.into(),
|
|
err => err.into(),
|
|
},
|
|
Err(err) => to_file_error(err),
|
|
},
|
|
_ => to_file_error(io_err),
|
|
}
|
|
}
|
|
|
|
pub fn to_disk_error(io_err: std::io::Error) -> std::io::Error {
|
|
match io_err.kind() {
|
|
std::io::ErrorKind::NotFound => DiskError::DiskNotFound.into(),
|
|
std::io::ErrorKind::PermissionDenied => DiskError::DiskAccessDenied.into(),
|
|
std::io::ErrorKind::Other => match io_err.downcast::<DiskError>() {
|
|
Ok(err) => match err {
|
|
DiskError::FileNotFound => DiskError::DiskNotFound.into(),
|
|
DiskError::VolumeNotFound => DiskError::DiskNotFound.into(),
|
|
DiskError::FileAccessDenied => DiskError::DiskAccessDenied.into(),
|
|
DiskError::VolumeAccessDenied => DiskError::DiskAccessDenied.into(),
|
|
err => err.into(),
|
|
},
|
|
Err(err) => to_volume_error(err),
|
|
},
|
|
_ => to_volume_error(io_err),
|
|
}
|
|
}
|
|
|
|
// only errors from FileSystem operations
|
|
pub fn to_access_error(io_err: std::io::Error, per_err: DiskError) -> std::io::Error {
|
|
match io_err.kind() {
|
|
std::io::ErrorKind::PermissionDenied => per_err.into(),
|
|
std::io::ErrorKind::NotADirectory => per_err.into(),
|
|
std::io::ErrorKind::NotFound => DiskError::VolumeNotFound.into(),
|
|
std::io::ErrorKind::UnexpectedEof => DiskError::FaultyDisk.into(),
|
|
std::io::ErrorKind::Other => match io_err.downcast::<DiskError>() {
|
|
Ok(err) => match err {
|
|
DiskError::DiskAccessDenied => per_err.into(),
|
|
DiskError::FileAccessDenied => per_err.into(),
|
|
DiskError::FileNotFound => DiskError::VolumeNotFound.into(),
|
|
err => err.into(),
|
|
},
|
|
Err(err) => to_volume_error(err),
|
|
},
|
|
_ => to_volume_error(io_err),
|
|
}
|
|
}
|
|
|
|
pub fn to_unformatted_disk_error(io_err: std::io::Error) -> std::io::Error {
|
|
match io_err.kind() {
|
|
std::io::ErrorKind::NotFound => DiskError::UnformattedDisk.into(),
|
|
std::io::ErrorKind::PermissionDenied => DiskError::DiskAccessDenied.into(),
|
|
std::io::ErrorKind::UnexpectedEof => DiskError::UnformattedDisk.into(),
|
|
std::io::ErrorKind::InvalidData => DiskError::UnformattedDisk.into(),
|
|
std::io::ErrorKind::Other => match io_err.downcast::<DiskError>() {
|
|
Ok(err) => match err {
|
|
DiskError::FileNotFound => DiskError::UnformattedDisk.into(),
|
|
DiskError::DiskNotFound => DiskError::UnformattedDisk.into(),
|
|
DiskError::VolumeNotFound => DiskError::UnformattedDisk.into(),
|
|
DiskError::FileAccessDenied => DiskError::DiskAccessDenied.into(),
|
|
DiskError::DiskAccessDenied => DiskError::DiskAccessDenied.into(),
|
|
_ => DiskError::UnformattedDisk.into(),
|
|
},
|
|
Err(_err) => DiskError::UnformattedDisk.into(),
|
|
},
|
|
_ => DiskError::UnformattedDisk.into(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::io::{Error as IoError, ErrorKind};
|
|
|
|
// Helper function to create IO errors with specific kinds
|
|
fn create_io_error(kind: ErrorKind) -> IoError {
|
|
IoError::new(kind, "test error")
|
|
}
|
|
|
|
// Helper function to create IO errors with DiskError as the source
|
|
fn create_io_error_with_disk_error(disk_error: DiskError) -> IoError {
|
|
IoError::other(disk_error)
|
|
}
|
|
|
|
// Helper function to check if an IoError contains a specific DiskError
|
|
fn contains_disk_error(io_error: IoError, expected: DiskError) -> bool {
|
|
if let Ok(disk_error) = io_error.downcast::<DiskError>() {
|
|
std::mem::discriminant(&disk_error) == std::mem::discriminant(&expected)
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_file_error_basic_conversions() {
|
|
// Test NotFound -> FileNotFound
|
|
let result = to_file_error(create_io_error(ErrorKind::NotFound));
|
|
assert!(contains_disk_error(result, DiskError::FileNotFound));
|
|
|
|
// Test PermissionDenied -> FileAccessDenied
|
|
let result = to_file_error(create_io_error(ErrorKind::PermissionDenied));
|
|
assert!(contains_disk_error(result, DiskError::FileAccessDenied));
|
|
|
|
// Test IsADirectory -> IsNotRegular
|
|
let result = to_file_error(create_io_error(ErrorKind::IsADirectory));
|
|
assert!(contains_disk_error(result, DiskError::IsNotRegular));
|
|
|
|
// Test NotADirectory -> FileAccessDenied
|
|
let result = to_file_error(create_io_error(ErrorKind::NotADirectory));
|
|
assert!(contains_disk_error(result, DiskError::FileAccessDenied));
|
|
|
|
// Test DirectoryNotEmpty -> FileAccessDenied
|
|
let result = to_file_error(create_io_error(ErrorKind::DirectoryNotEmpty));
|
|
assert!(contains_disk_error(result, DiskError::FileAccessDenied));
|
|
|
|
// Test UnexpectedEof -> FaultyDisk
|
|
let result = to_file_error(create_io_error(ErrorKind::UnexpectedEof));
|
|
assert!(contains_disk_error(result, DiskError::FaultyDisk));
|
|
|
|
// Test TooManyLinks -> TooManyOpenFiles
|
|
#[cfg(unix)]
|
|
{
|
|
let result = to_file_error(create_io_error(ErrorKind::TooManyLinks));
|
|
assert!(contains_disk_error(result, DiskError::TooManyOpenFiles));
|
|
}
|
|
|
|
// Test InvalidInput -> FileNotFound
|
|
let result = to_file_error(create_io_error(ErrorKind::InvalidInput));
|
|
assert!(contains_disk_error(result, DiskError::FileNotFound));
|
|
|
|
// Test InvalidData -> FileCorrupt
|
|
let result = to_file_error(create_io_error(ErrorKind::InvalidData));
|
|
assert!(contains_disk_error(result, DiskError::FileCorrupt));
|
|
|
|
// Test StorageFull -> DiskFull
|
|
#[cfg(unix)]
|
|
{
|
|
let result = to_file_error(create_io_error(ErrorKind::StorageFull));
|
|
assert!(contains_disk_error(result, DiskError::DiskFull));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_file_error_passthrough_unknown() {
|
|
// Test that unknown error kinds are passed through unchanged
|
|
let original = create_io_error(ErrorKind::Interrupted);
|
|
let result = to_file_error(original);
|
|
assert_eq!(result.kind(), ErrorKind::Interrupted);
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_volume_error_basic_conversions() {
|
|
// Test NotFound -> VolumeNotFound
|
|
let result = to_volume_error(create_io_error(ErrorKind::NotFound));
|
|
assert!(contains_disk_error(result, DiskError::VolumeNotFound));
|
|
|
|
// Test PermissionDenied -> DiskAccessDenied
|
|
let result = to_volume_error(create_io_error(ErrorKind::PermissionDenied));
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
|
|
// Test DirectoryNotEmpty -> VolumeNotEmpty
|
|
let result = to_volume_error(create_io_error(ErrorKind::DirectoryNotEmpty));
|
|
assert!(contains_disk_error(result, DiskError::VolumeNotEmpty));
|
|
|
|
// Test NotADirectory -> IsNotRegular
|
|
let result = to_volume_error(create_io_error(ErrorKind::NotADirectory));
|
|
assert!(contains_disk_error(result, DiskError::IsNotRegular));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_volume_error_other_with_disk_error() {
|
|
// Test Other error kind with FileNotFound DiskError -> VolumeNotFound
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileNotFound);
|
|
let result = to_volume_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::VolumeNotFound));
|
|
|
|
// Test Other error kind with FileAccessDenied DiskError -> DiskAccessDenied
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileAccessDenied);
|
|
let result = to_volume_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
|
|
// Test Other error kind with other DiskError -> passthrough
|
|
let io_error = create_io_error_with_disk_error(DiskError::DiskFull);
|
|
let result = to_volume_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskFull));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_volume_error_fallback_to_file_error() {
|
|
// Test fallback to to_file_error for unknown error kinds
|
|
let result = to_volume_error(create_io_error(ErrorKind::Interrupted));
|
|
assert_eq!(result.kind(), ErrorKind::Interrupted);
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_disk_error_basic_conversions() {
|
|
// Test NotFound -> DiskNotFound
|
|
let result = to_disk_error(create_io_error(ErrorKind::NotFound));
|
|
assert!(contains_disk_error(result, DiskError::DiskNotFound));
|
|
|
|
// Test PermissionDenied -> DiskAccessDenied
|
|
let result = to_disk_error(create_io_error(ErrorKind::PermissionDenied));
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_disk_error_other_with_disk_error() {
|
|
// Test Other error kind with FileNotFound DiskError -> DiskNotFound
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileNotFound);
|
|
let result = to_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskNotFound));
|
|
|
|
// Test Other error kind with VolumeNotFound DiskError -> DiskNotFound
|
|
let io_error = create_io_error_with_disk_error(DiskError::VolumeNotFound);
|
|
let result = to_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskNotFound));
|
|
|
|
// Test Other error kind with FileAccessDenied DiskError -> DiskAccessDenied
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileAccessDenied);
|
|
let result = to_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
|
|
// Test Other error kind with VolumeAccessDenied DiskError -> DiskAccessDenied
|
|
let io_error = create_io_error_with_disk_error(DiskError::VolumeAccessDenied);
|
|
let result = to_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
|
|
// Test Other error kind with other DiskError -> passthrough
|
|
let io_error = create_io_error_with_disk_error(DiskError::DiskFull);
|
|
let result = to_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskFull));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_disk_error_fallback_to_volume_error() {
|
|
// Test fallback to to_volume_error for unknown error kinds
|
|
let result = to_disk_error(create_io_error(ErrorKind::Interrupted));
|
|
assert_eq!(result.kind(), ErrorKind::Interrupted);
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_access_error_basic_conversions() {
|
|
let permission_error = DiskError::FileAccessDenied;
|
|
|
|
// Test PermissionDenied -> specified permission error
|
|
let result = to_access_error(create_io_error(ErrorKind::PermissionDenied), permission_error);
|
|
assert!(contains_disk_error(result, DiskError::FileAccessDenied));
|
|
|
|
// Test NotADirectory -> specified permission error
|
|
let result = to_access_error(create_io_error(ErrorKind::NotADirectory), DiskError::FileAccessDenied);
|
|
assert!(contains_disk_error(result, DiskError::FileAccessDenied));
|
|
|
|
// Test NotFound -> VolumeNotFound
|
|
let result = to_access_error(create_io_error(ErrorKind::NotFound), DiskError::FileAccessDenied);
|
|
assert!(contains_disk_error(result, DiskError::VolumeNotFound));
|
|
|
|
// Test UnexpectedEof -> FaultyDisk
|
|
let result = to_access_error(create_io_error(ErrorKind::UnexpectedEof), DiskError::FileAccessDenied);
|
|
assert!(contains_disk_error(result, DiskError::FaultyDisk));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_access_error_other_with_disk_error() {
|
|
let permission_error = DiskError::VolumeAccessDenied;
|
|
|
|
// Test Other error kind with DiskAccessDenied -> specified permission error
|
|
let io_error = create_io_error_with_disk_error(DiskError::DiskAccessDenied);
|
|
let result = to_access_error(io_error, permission_error);
|
|
assert!(contains_disk_error(result, DiskError::VolumeAccessDenied));
|
|
|
|
// Test Other error kind with FileAccessDenied -> specified permission error
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileAccessDenied);
|
|
let result = to_access_error(io_error, DiskError::VolumeAccessDenied);
|
|
assert!(contains_disk_error(result, DiskError::VolumeAccessDenied));
|
|
|
|
// Test Other error kind with FileNotFound -> VolumeNotFound
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileNotFound);
|
|
let result = to_access_error(io_error, DiskError::VolumeAccessDenied);
|
|
assert!(contains_disk_error(result, DiskError::VolumeNotFound));
|
|
|
|
// Test Other error kind with other DiskError -> passthrough
|
|
let io_error = create_io_error_with_disk_error(DiskError::DiskFull);
|
|
let result = to_access_error(io_error, DiskError::VolumeAccessDenied);
|
|
assert!(contains_disk_error(result, DiskError::DiskFull));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_access_error_fallback_to_volume_error() {
|
|
let permission_error = DiskError::FileAccessDenied;
|
|
|
|
// Test fallback to to_volume_error for unknown error kinds
|
|
let result = to_access_error(create_io_error(ErrorKind::Interrupted), permission_error);
|
|
assert_eq!(result.kind(), ErrorKind::Interrupted);
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_unformatted_disk_error_basic_conversions() {
|
|
// Test NotFound -> UnformattedDisk
|
|
let result = to_unformatted_disk_error(create_io_error(ErrorKind::NotFound));
|
|
assert!(contains_disk_error(result, DiskError::UnformattedDisk));
|
|
|
|
// Test PermissionDenied -> DiskAccessDenied
|
|
let result = to_unformatted_disk_error(create_io_error(ErrorKind::PermissionDenied));
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_unformatted_disk_error_other_with_disk_error() {
|
|
// Test Other error kind with FileNotFound -> UnformattedDisk
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileNotFound);
|
|
let result = to_unformatted_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::UnformattedDisk));
|
|
|
|
// Test Other error kind with DiskNotFound -> UnformattedDisk
|
|
let io_error = create_io_error_with_disk_error(DiskError::DiskNotFound);
|
|
let result = to_unformatted_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::UnformattedDisk));
|
|
|
|
// Test Other error kind with VolumeNotFound -> UnformattedDisk
|
|
let io_error = create_io_error_with_disk_error(DiskError::VolumeNotFound);
|
|
let result = to_unformatted_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::UnformattedDisk));
|
|
|
|
// Test Other error kind with FileAccessDenied -> DiskAccessDenied
|
|
let io_error = create_io_error_with_disk_error(DiskError::FileAccessDenied);
|
|
let result = to_unformatted_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
|
|
// Test Other error kind with DiskAccessDenied -> DiskAccessDenied
|
|
let io_error = create_io_error_with_disk_error(DiskError::DiskAccessDenied);
|
|
let result = to_unformatted_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::DiskAccessDenied));
|
|
|
|
// Test Other error kind with other DiskError -> UnformattedDisk
|
|
let io_error = create_io_error_with_disk_error(DiskError::DiskFull);
|
|
let result = to_unformatted_disk_error(io_error);
|
|
assert!(contains_disk_error(result, DiskError::UnformattedDisk));
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_unformatted_disk_error_recursive_behavior() {
|
|
// Test with non-Other error kind that should be handled without infinite recursion
|
|
let result = to_unformatted_disk_error(create_io_error(ErrorKind::Interrupted));
|
|
// This should not cause infinite recursion and should produce UnformattedDisk
|
|
assert!(contains_disk_error(result, DiskError::UnformattedDisk));
|
|
}
|
|
|
|
#[test]
|
|
fn test_error_chain_conversions() {
|
|
// Test complex error conversion chains
|
|
let original_error = create_io_error(ErrorKind::NotFound);
|
|
|
|
// Chain: NotFound -> FileNotFound (via to_file_error) -> VolumeNotFound (via to_volume_error)
|
|
let file_error = to_file_error(original_error);
|
|
let volume_error = to_volume_error(file_error);
|
|
assert!(contains_disk_error(volume_error, DiskError::VolumeNotFound));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cross_platform_error_kinds() {
|
|
// Test error kinds that may not be available on all platforms
|
|
#[cfg(unix)]
|
|
{
|
|
let result = to_file_error(create_io_error(ErrorKind::TooManyLinks));
|
|
assert!(contains_disk_error(result, DiskError::TooManyOpenFiles));
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
{
|
|
let result = to_file_error(create_io_error(ErrorKind::StorageFull));
|
|
assert!(contains_disk_error(result, DiskError::DiskFull));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_error_conversion_with_different_kinds() {
|
|
// Test multiple error kinds to ensure comprehensive coverage
|
|
let test_cases = vec![
|
|
(ErrorKind::NotFound, DiskError::FileNotFound),
|
|
(ErrorKind::PermissionDenied, DiskError::FileAccessDenied),
|
|
(ErrorKind::IsADirectory, DiskError::IsNotRegular),
|
|
(ErrorKind::InvalidData, DiskError::FileCorrupt),
|
|
];
|
|
|
|
for (kind, expected_disk_error) in test_cases {
|
|
let result = to_file_error(create_io_error(kind));
|
|
assert!(
|
|
contains_disk_error(result, expected_disk_error.clone()),
|
|
"Failed for ErrorKind::{kind:?} -> DiskError::{expected_disk_error:?}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_volume_error_conversion_chain() {
|
|
// Test volume error conversion with different input types
|
|
let test_cases = vec![
|
|
(ErrorKind::NotFound, DiskError::VolumeNotFound),
|
|
(ErrorKind::PermissionDenied, DiskError::DiskAccessDenied),
|
|
(ErrorKind::DirectoryNotEmpty, DiskError::VolumeNotEmpty),
|
|
];
|
|
|
|
for (kind, expected_disk_error) in test_cases {
|
|
let result = to_volume_error(create_io_error(kind));
|
|
assert!(
|
|
contains_disk_error(result, expected_disk_error.clone()),
|
|
"Failed for ErrorKind::{kind:?} -> DiskError::{expected_disk_error:?}"
|
|
);
|
|
}
|
|
}
|
|
}
|