mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
review disk
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use std::io::{self, ErrorKind};
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
quorum::CheckErrorFn,
|
||||
@@ -98,6 +100,9 @@ pub enum DiskError {
|
||||
|
||||
#[error("more data was sent than what was advertised")]
|
||||
MoreData,
|
||||
|
||||
#[error("other io err {0}")]
|
||||
IoError(io::Error),
|
||||
}
|
||||
|
||||
impl DiskError {
|
||||
@@ -201,8 +206,61 @@ pub fn clone_err(err: &Error) -> Error {
|
||||
DiskError::CrossDeviceLink => Error::new(DiskError::CrossDeviceLink),
|
||||
DiskError::LessData => Error::new(DiskError::LessData),
|
||||
DiskError::MoreData => Error::new(DiskError::MoreData),
|
||||
DiskError::IoError(ioerr) => Error::msg(ioerr.to_string()),
|
||||
}
|
||||
} else {
|
||||
Error::msg(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn ioerr_to_diskerr(e:io::Error)->DiskError{
|
||||
match e.kind(){
|
||||
io::ErrorKind::NotFound => DiskError::FileNotFound,
|
||||
io::ErrorKind::PermissionDenied => DiskError::FileAccessDenied,
|
||||
// io::ErrorKind::ConnectionRefused => todo!(),
|
||||
// io::ErrorKind::ConnectionReset => todo!(),
|
||||
// io::ErrorKind::HostUnreachable => todo!(),
|
||||
// io::ErrorKind::NetworkUnreachable => todo!(),
|
||||
// io::ErrorKind::ConnectionAborted => todo!(),
|
||||
// io::ErrorKind::NotConnected => todo!(),
|
||||
// io::ErrorKind::AddrInUse => todo!(),
|
||||
// io::ErrorKind::AddrNotAvailable => todo!(),
|
||||
// io::ErrorKind::NetworkDown => todo!(),
|
||||
// io::ErrorKind::BrokenPipe => todo!(),
|
||||
// io::ErrorKind::AlreadyExists => todo!(),
|
||||
// io::ErrorKind::WouldBlock => todo!(),
|
||||
// io::ErrorKind::NotADirectory => DiskError::FileNotFound,
|
||||
// io::ErrorKind::IsADirectory => DiskError::FileNotFound,
|
||||
// io::ErrorKind::DirectoryNotEmpty => DiskError::VolumeNotEmpty,
|
||||
// io::ErrorKind::ReadOnlyFilesystem => todo!(),
|
||||
// io::ErrorKind::FilesystemLoop => todo!(),
|
||||
// io::ErrorKind::StaleNetworkFileHandle => todo!(),
|
||||
// io::ErrorKind::InvalidInput => todo!(),
|
||||
// io::ErrorKind::InvalidData => todo!(),
|
||||
// io::ErrorKind::TimedOut => todo!(),
|
||||
// io::ErrorKind::WriteZero => todo!(),
|
||||
// io::ErrorKind::StorageFull => DiskError::DiskFull,
|
||||
// io::ErrorKind::NotSeekable => todo!(),
|
||||
// io::ErrorKind::FilesystemQuotaExceeded => todo!(),
|
||||
// io::ErrorKind::FileTooLarge => todo!(),
|
||||
// io::ErrorKind::ResourceBusy => todo!(),
|
||||
// io::ErrorKind::ExecutableFileBusy => todo!(),
|
||||
// io::ErrorKind::Deadlock => todo!(),
|
||||
// io::ErrorKind::CrossesDevices => todo!(),
|
||||
// io::ErrorKind::TooManyLinks =>DiskError::TooManyOpenFiles,
|
||||
// io::ErrorKind::InvalidFilename => todo!(),
|
||||
// io::ErrorKind::ArgumentListTooLong => todo!(),
|
||||
// io::ErrorKind::Interrupted => todo!(),
|
||||
// io::ErrorKind::Unsupported => todo!(),
|
||||
// io::ErrorKind::UnexpectedEof => todo!(),
|
||||
// io::ErrorKind::OutOfMemory => todo!(),
|
||||
// io::ErrorKind::Other => todo!(),
|
||||
// TODO: 把不支持的king用字符串处理
|
||||
_ => DiskError::IoError(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn os_is_not_exist(e:io::Error) -> bool{
|
||||
e.kind() == ErrorKind::NotFound
|
||||
}
|
||||
+66
-35
@@ -1,9 +1,11 @@
|
||||
use super::error::{ioerr_to_diskerr, os_is_not_exist};
|
||||
use super::{endpoint::Endpoint, error::DiskError, format::FormatV3};
|
||||
use super::{
|
||||
DeleteOptions, DiskAPI, DiskLocation, FileInfoVersions, FileReader, FileWriter, MetaCacheEntry, ReadMultipleReq,
|
||||
os, DeleteOptions, DiskAPI, DiskLocation, FileInfoVersions, FileReader, FileWriter, MetaCacheEntry, ReadMultipleReq,
|
||||
ReadMultipleResp, ReadOptions, RenameDataResp, UpdateMetadataOpts, VolumeInfo, WalkDirOptions,
|
||||
};
|
||||
use crate::disk::{LocalFileReader, LocalFileWriter, STORAGE_FORMAT_FILE};
|
||||
use crate::utils::path::SLASH_SEPARATOR;
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
file_meta::FileMeta,
|
||||
@@ -93,6 +95,8 @@ impl LocalDisk {
|
||||
last_check: format_last_check,
|
||||
};
|
||||
|
||||
// TODO: DIRECT suport
|
||||
// TODD: DiskInfo
|
||||
let disk = Self {
|
||||
root,
|
||||
endpoint: ep.clone(),
|
||||
@@ -109,6 +113,36 @@ impl LocalDisk {
|
||||
Ok(disk)
|
||||
}
|
||||
|
||||
fn check_path_length(path_name: &str) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_valid_volname(volname: &str) -> bool {
|
||||
if volname.len() < 3 {
|
||||
return false;
|
||||
}
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
// 在 Windows 上,卷名不应该包含保留字符。
|
||||
// 这个正则表达式匹配了不允许的字符。
|
||||
if volname.contains('|')
|
||||
|| volname.contains('<')
|
||||
|| volname.contains('>')
|
||||
|| volname.contains('?')
|
||||
|| volname.contains('*')
|
||||
|| volname.contains(':')
|
||||
|| volname.contains('"')
|
||||
|| volname.contains('\\')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 对于非 Windows 系统,可能需要其他的验证逻辑。
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
async fn check_format_json(&self) -> Result<Metadata> {
|
||||
let md = fs::metadata(&self.format_path).await.map_err(|e| match e.kind() {
|
||||
ErrorKind::NotFound => DiskError::DiskNotFound,
|
||||
@@ -475,7 +509,7 @@ impl DiskAPI for LocalDisk {
|
||||
self.root.clone()
|
||||
}
|
||||
|
||||
fn get_location(&self) -> DiskLocation {
|
||||
fn get_disk_location(&self) -> DiskLocation {
|
||||
DiskLocation {
|
||||
pool_idx: self.endpoint.pool_idx,
|
||||
set_idx: self.endpoint.set_idx,
|
||||
@@ -900,54 +934,51 @@ impl DiskAPI for LocalDisk {
|
||||
async fn make_volumes(&self, volumes: Vec<&str>) -> Result<()> {
|
||||
for vol in volumes {
|
||||
if let Err(e) = self.make_volume(vol).await {
|
||||
match &e.downcast_ref::<DiskError>() {
|
||||
Some(DiskError::VolumeExists) => Ok(()),
|
||||
Some(_) => Err(e),
|
||||
None => Err(e),
|
||||
}?;
|
||||
if !DiskError::VolumeExists.is(&e) {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
// TODO: health check
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn make_volume(&self, volume: &str) -> Result<()> {
|
||||
if !Self::is_valid_volname(volume) {
|
||||
return Err(Error::msg("Invalid arguments specified"));
|
||||
}
|
||||
|
||||
let p = self.get_bucket_path(volume)?;
|
||||
match File::open(&p).await {
|
||||
Ok(_) => (),
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
fs::create_dir_all(&p).await?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => return Err(Error::from(e)),
|
||||
},
|
||||
|
||||
if let Err(err) = utils::fs::access(&p).await {
|
||||
if os_is_not_exist(err) {
|
||||
os::make_dir_all(&p).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::from(DiskError::VolumeExists))
|
||||
}
|
||||
async fn list_volumes(&self) -> Result<Vec<VolumeInfo>> {
|
||||
let mut entries = fs::read_dir(&self.root).await?;
|
||||
|
||||
let mut volumes = Vec::new();
|
||||
|
||||
while let Some(entry) = entries.next_entry().await? {
|
||||
if let Ok(metadata) = entry.metadata().await {
|
||||
// if !metadata.is_dir() {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
|
||||
let created = match metadata.created() {
|
||||
Ok(md) => Some(OffsetDateTime::from(md)),
|
||||
Err(_) => {
|
||||
warn!("Not supported created on this platform");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
volumes.push(VolumeInfo { name, created });
|
||||
let entries = os::read_dir(&self.root, 0).await.map_err(|e| {
|
||||
if DiskError::FileAccessDenied.is(&e) {
|
||||
Error::new(DiskError::DiskAccessDenied)
|
||||
} else if DiskError::FileNotFound.is(&e) {
|
||||
Error::new(DiskError::DiskAccessDenied)
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})?;
|
||||
|
||||
for entry in entries {
|
||||
if utils::path::has_suffix(&entry, SLASH_SEPARATOR) || !Self::is_valid_volname(&entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
volumes.push(VolumeInfo {
|
||||
name: entry,
|
||||
created: None,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(volumes)
|
||||
|
||||
@@ -3,6 +3,7 @@ pub mod error;
|
||||
pub mod format;
|
||||
mod local;
|
||||
mod remote;
|
||||
pub mod os;
|
||||
|
||||
pub const RUSTFS_META_BUCKET: &str = ".rustfs.sys";
|
||||
pub const RUSTFS_META_MULTIPART_BUCKET: &str = ".rustfs.sys/multipart";
|
||||
@@ -56,7 +57,7 @@ pub trait DiskAPI: Debug + Send + Sync + 'static {
|
||||
async fn set_disk_id(&self, id: Option<Uuid>) -> Result<()>;
|
||||
|
||||
fn path(&self) -> PathBuf;
|
||||
fn get_location(&self) -> DiskLocation;
|
||||
fn get_disk_location(&self) -> DiskLocation;
|
||||
|
||||
// Healing
|
||||
// DiskInfo
|
||||
@@ -126,7 +127,7 @@ pub trait DiskAPI: Debug + Send + Sync + 'static {
|
||||
// CleanAbandonedData
|
||||
async fn write_all(&self, volume: &str, path: &str, data: Vec<u8>) -> Result<()>;
|
||||
async fn read_all(&self, volume: &str, path: &str) -> Result<Bytes>;
|
||||
// GetDiskLoc
|
||||
|
||||
}
|
||||
|
||||
pub struct UpdateMetadataOpts {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
use std::path::Path;
|
||||
|
||||
use futures::TryFutureExt;
|
||||
use tokio::fs;
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
utils,
|
||||
};
|
||||
|
||||
use super::error::{ioerr_to_diskerr, DiskError};
|
||||
|
||||
fn check_path_length(path_name: &str) -> Result<()> {
|
||||
// Apple OS X path length is limited to 1016
|
||||
if cfg!(target_os = "macos") && path_name.len() > 1016 {
|
||||
return Err(Error::new(DiskError::FileNameTooLong));
|
||||
}
|
||||
|
||||
// Disallow more than 1024 characters on windows, there
|
||||
// are no known name_max limits on Windows.
|
||||
if cfg!(target_os = "windows") && path_name.len() > 1024 {
|
||||
return Err(Error::new(DiskError::FileNameTooLong));
|
||||
}
|
||||
|
||||
// On Unix we reject paths if they are just '.', '..' or '/'
|
||||
let invalid_paths = [".", "..", "/"];
|
||||
if invalid_paths.contains(&path_name) {
|
||||
return Err(Error::new(DiskError::FileAccessDenied));
|
||||
}
|
||||
|
||||
// Check each path segment length is > 255 on all Unix
|
||||
// platforms, look for this value as NAME_MAX in
|
||||
// /usr/include/linux/limits.h
|
||||
let mut count = 0usize;
|
||||
for c in path_name.chars() {
|
||||
match c {
|
||||
'/' | '\\' if cfg!(target_os = "windows") => count = 0, // Reset
|
||||
_ => {
|
||||
count += 1;
|
||||
if count > 255 {
|
||||
return Err(Error::new(DiskError::FileNameTooLong));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn make_dir_all(path: impl AsRef<Path>) -> Result<()> {
|
||||
check_path_length(path.as_ref().to_string_lossy().to_string().as_str())?;
|
||||
|
||||
utils::fs::make_dir_all(path.as_ref()).map_err(ioerr_to_diskerr).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// read_dir count read limit. when count == 0 unlimit.
|
||||
pub async fn read_dir(path: impl AsRef<Path>, count: usize) -> Result<Vec<String>> {
|
||||
let mut entries = fs::read_dir(path.as_ref()).await?;
|
||||
|
||||
let mut volumes = Vec::new();
|
||||
|
||||
let mut count: i32 = {
|
||||
if count == 0 {
|
||||
-1
|
||||
} else {
|
||||
count as i32
|
||||
}
|
||||
};
|
||||
|
||||
while let Some(entry) = entries.next_entry().await? {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
|
||||
if name == "" || name == "." || name == ".." {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_type = entry.file_type().await?;
|
||||
|
||||
if file_type.is_dir() {
|
||||
count -= 1;
|
||||
volumes.push(format!("{}{}", name, utils::path::SLASH_SEPARATOR));
|
||||
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(volumes)
|
||||
}
|
||||
@@ -125,7 +125,7 @@ impl DiskAPI for RemoteDisk {
|
||||
self.root.clone()
|
||||
}
|
||||
|
||||
fn get_location(&self) -> DiskLocation {
|
||||
fn get_disk_location(&self) -> DiskLocation {
|
||||
DiskLocation {
|
||||
pool_idx: self.endpoint.pool_idx,
|
||||
set_idx: self.endpoint.set_idx,
|
||||
|
||||
+11
-9
@@ -15,10 +15,6 @@ enum QuorumError {
|
||||
Write,
|
||||
}
|
||||
|
||||
pub fn is_file_not_found(e: &Error) -> bool {
|
||||
DiskError::FileNotFound.is(e)
|
||||
}
|
||||
|
||||
pub fn base_ignored_errs() -> Vec<Box<dyn CheckErrorFn>> {
|
||||
vec![
|
||||
Box::new(DiskError::DiskNotFound),
|
||||
@@ -29,14 +25,20 @@ pub fn base_ignored_errs() -> Vec<Box<dyn CheckErrorFn>> {
|
||||
|
||||
// object_op_ignored_errs
|
||||
pub fn object_op_ignored_errs() -> Vec<Box<dyn CheckErrorFn>> {
|
||||
vec![
|
||||
Box::new(DiskError::DiskNotFound),
|
||||
Box::new(DiskError::FaultyDisk),
|
||||
Box::new(DiskError::FaultyRemoteDisk),
|
||||
let mut base = base_ignored_errs();
|
||||
|
||||
|
||||
let ext:Vec<Box<dyn CheckErrorFn>> = vec![
|
||||
// Box::new(DiskError::DiskNotFound),
|
||||
// Box::new(DiskError::FaultyDisk),
|
||||
// Box::new(DiskError::FaultyRemoteDisk),
|
||||
Box::new(DiskError::DiskAccessDenied),
|
||||
Box::new(DiskError::UnformattedDisk),
|
||||
Box::new(DiskError::DiskOngoingReq),
|
||||
]
|
||||
];
|
||||
|
||||
base.extend(ext);
|
||||
base
|
||||
}
|
||||
|
||||
// 用于检查错误是否被忽略的函数
|
||||
|
||||
+32
-1
@@ -1,6 +1,11 @@
|
||||
use std::{fs::Metadata, os::unix::fs::MetadataExt};
|
||||
use std::{fs::Metadata, path::Path};
|
||||
|
||||
use tokio::{fs, io};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn same_file(f1: &Metadata, f2: &Metadata) -> bool {
|
||||
use os::unix::fs::MetadataExt;
|
||||
|
||||
if f1.dev() != f2.dev() {
|
||||
return false;
|
||||
}
|
||||
@@ -22,3 +27,29 @@ pub fn same_file(f1: &Metadata, f2: &Metadata) -> bool {
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn same_file(f1: &Metadata, f2: &Metadata) -> bool {
|
||||
if f1.permissions() != f2.permissions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if f1.file_type() != f2.file_type() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if f1.len() != f2.len() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn access(path: impl AsRef<Path>) -> io::Result<()>{
|
||||
fs::metadata(path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn make_dir_all(path: impl AsRef<Path>) -> io::Result<()>{
|
||||
fs::create_dir_all(path.as_ref()).await
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const GLOBAL_DIR_SUFFIX: &str = "__XLDIR__";
|
||||
|
||||
const SLASH_SEPARATOR: &str = "/";
|
||||
pub const SLASH_SEPARATOR: &str = "/";
|
||||
|
||||
pub fn has_suffix(s: &str, suffix: &str) -> bool {
|
||||
if cfg!(target_os = "windows") {
|
||||
|
||||
Reference in New Issue
Block a user