mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 14:23:13 +00:00
@@ -401,3 +401,20 @@ pub fn convert_access_error(e: io::Error, per_err: DiskError) -> Error {
|
||||
|
||||
Error::new(e)
|
||||
}
|
||||
|
||||
pub fn is_all_not_found(errs: &[Option<Error>]) -> bool {
|
||||
for err in errs.iter() {
|
||||
if let Some(err) = err {
|
||||
if let Some(err) = err.downcast_ref::<DiskError>() {
|
||||
match err {
|
||||
DiskError::FileNotFound | DiskError::VolumeNotFound | &DiskError::FileVersionNotFound =>{
|
||||
continue;
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
!errs.is_empty()
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::error::{Error, Result};
|
||||
use crate::global::{GLOBAL_IsErasureSD, GLOBAL_RootDiskThreshold};
|
||||
use crate::utils::fs::{lstat, O_APPEND, O_CREATE, O_RDONLY, O_WRONLY};
|
||||
use crate::utils::path::{clean, has_suffix, SLASH_SEPARATOR};
|
||||
use crate::utils::stat_linux::get_info;
|
||||
use crate::utils::os::get_info;
|
||||
use crate::{
|
||||
file_meta::FileMeta,
|
||||
store_api::{FileInfo, RawFileInfo},
|
||||
|
||||
@@ -8,7 +8,7 @@ use tokio::fs;
|
||||
use crate::{
|
||||
disk::error::{is_sys_err_not_dir, is_sys_err_path_not_found, os_is_not_exist},
|
||||
error::{Error, Result},
|
||||
utils::{self, stat_linux::same_disk},
|
||||
utils::{self, os::same_disk},
|
||||
};
|
||||
|
||||
use super::error::{os_err_to_file_err, os_is_exist, DiskError};
|
||||
|
||||
@@ -27,6 +27,16 @@ pub const HEAL_ITEM_BUCKET: &str = "bucket";
|
||||
pub const HEAL_ITEM_BUCKET_METADATA: &str = "bucket-metadata";
|
||||
pub const HEAL_ITEM_OBJECT: &str = "object";
|
||||
|
||||
pub const DRIVE_STATE_OK: &str = "ok";
|
||||
pub const DRIVE_STATE_OFFLINE: &str = "offline";
|
||||
pub const DRIVE_STATE_CORRUPT: &str = "corrupt";
|
||||
pub const DRIVE_STATE_MISSING: &str = "missing";
|
||||
pub const DRIVE_STATE_PERMISSION: &str = "permission-denied";
|
||||
pub const DRIVE_STATE_FAULTY: &str = "faulty";
|
||||
pub const DRIVE_STATE_ROOT_MOUNT: &str = "root-mount";
|
||||
pub const DRIVE_STATE_UNKNOWN: &str = "unknown";
|
||||
pub const DRIVE_STATE_UNFORMATTED: &str = "unformatted"; // only returned by disk
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct HealOpts {
|
||||
pub recursive: bool,
|
||||
@@ -41,10 +51,10 @@ pub struct HealOpts {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct HealDriveInfo {
|
||||
uuid: String,
|
||||
endpoint: String,
|
||||
state: String,
|
||||
pub struct HealDriveInfo {
|
||||
pub uuid: String,
|
||||
pub endpoint: String,
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
||||
@@ -4,5 +4,5 @@ pub mod fs;
|
||||
pub mod hash;
|
||||
pub mod net;
|
||||
pub mod path;
|
||||
pub mod stat_linux;
|
||||
pub mod os;
|
||||
pub mod wildcard;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
mod unix;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use linux::get_info;
|
||||
pub use linux::same_disk;
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
pub use unix::get_info;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::get_info;
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::disk::Info;
|
||||
use nix::sys::{statfs::statfs, stat::stat};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::path::Path;
|
||||
|
||||
/// returns total and free bytes available in a directory, e.g. `/`.
|
||||
pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<Info> {
|
||||
let stat = statfs(p.as_ref())?;
|
||||
|
||||
let bsize = stat.block_size() as u64;
|
||||
let bfree = stat.blocks_free() as u64;
|
||||
let bavail = stat.blocks_available() as u64;
|
||||
let blocks = stat.blocks() as u64;
|
||||
|
||||
let reserved = match bfree.checked_sub(bavail) {
|
||||
Some(reserved) => reserved,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"detected f_bavail space ({}) > f_bfree space ({}), fs corruption at ({}). please run 'fsck'",
|
||||
bavail,
|
||||
bfree,
|
||||
p.as_ref().display()
|
||||
),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let total = match blocks.checked_sub(reserved) {
|
||||
Some(total) => total * bsize,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"detected reserved space ({}) > blocks space ({}), fs corruption at ({}). please run 'fsck'",
|
||||
reserved,
|
||||
blocks,
|
||||
p.as_ref().display()
|
||||
),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let free = bavail * bsize;
|
||||
let used = match total.checked_sub(free) {
|
||||
Some(used) => used,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"detected free space ({}) > total drive space ({}), fs corruption at ({}). please run 'fsck'",
|
||||
free,
|
||||
total,
|
||||
p.as_ref().display()
|
||||
),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Info {
|
||||
total,
|
||||
free,
|
||||
used,
|
||||
files: stat.files(),
|
||||
ffree: stat.files_free(),
|
||||
fstype: stat.filesystem_type_name().to_string(),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn same_disk(disk1: &str, disk2: &str) -> Result<bool> {
|
||||
let stat1 = stat(disk1)?;
|
||||
let stat2 = stat(disk2)?;
|
||||
|
||||
Ok(stat1.st_dev == stat2.st_dev)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
use crate::disk::Info;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
use std::mem;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
use winapi::shared::minwindef::{DWORD, MAX_PATH};
|
||||
use winapi::shared::ntdef::ULARGE_INTEGER;
|
||||
use winapi::um::fileapi::{GetDiskFreeSpaceExW, GetDiskFreeSpaceW, GetVolumeInformationW, GetVolumePathNameW};
|
||||
use winapi::um::winnt::{LPCWSTR, WCHAR};
|
||||
|
||||
/// returns total and free bytes available in a directory, e.g. `C:\`.
|
||||
pub fn get_info(p: impl AsRef<Path>) -> Result<Info> {
|
||||
let path_wide: Vec<WCHAR> = p
|
||||
.as_ref()
|
||||
.canonicalize()?
|
||||
.into_os_string()
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0)) // Null-terminate the string
|
||||
.collect();
|
||||
|
||||
let mut lp_free_bytes_available: ULARGE_INTEGER = unsafe { mem::zeroed() };
|
||||
let mut lp_total_number_of_bytes: ULARGE_INTEGER = unsafe { mem::zeroed() };
|
||||
let mut lp_total_number_of_free_bytes: ULARGE_INTEGER = unsafe { mem::zeroed() };
|
||||
|
||||
let success = unsafe {
|
||||
GetDiskFreeSpaceExW(
|
||||
path_wide.as_ptr(),
|
||||
&mut lp_free_bytes_available,
|
||||
&mut lp_total_number_of_bytes,
|
||||
&mut lp_total_number_of_free_bytes,
|
||||
)
|
||||
};
|
||||
if success == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
let total = unsafe { *lp_total_number_of_bytes.QuadPart() };
|
||||
let free = unsafe { *lp_total_number_of_free_bytes.QuadPart() };
|
||||
|
||||
if free > total {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
"detected free space ({}) > total drive space ({}), fs corruption at ({}). please run 'fsck'",
|
||||
free,
|
||||
total,
|
||||
p.as_ref().display()
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
let mut lp_sectors_per_cluster: DWORD = 0;
|
||||
let mut lp_bytes_per_sector: DWORD = 0;
|
||||
let mut lp_number_of_free_clusters: DWORD = 0;
|
||||
let mut lp_total_number_of_clusters: DWORD = 0;
|
||||
|
||||
let success = unsafe {
|
||||
GetDiskFreeSpaceW(
|
||||
path_wide.as_ptr(),
|
||||
&mut lp_sectors_per_cluster,
|
||||
&mut lp_bytes_per_sector,
|
||||
&mut lp_number_of_free_clusters,
|
||||
&mut lp_total_number_of_clusters,
|
||||
)
|
||||
};
|
||||
if success == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(Info {
|
||||
total,
|
||||
free,
|
||||
used: total - free,
|
||||
files: lp_total_number_of_clusters as u64,
|
||||
ffree: lp_number_of_free_clusters as u64,
|
||||
fstype: get_fs_type(&path_wide)?,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
/// returns leading volume name.
|
||||
fn get_volume_name(v: &[WCHAR]) -> Result<LPCWSTR> {
|
||||
let volume_name_size: DWORD = MAX_PATH as _;
|
||||
let mut lp_volume_name_buffer: [WCHAR; MAX_PATH] = [0; MAX_PATH];
|
||||
|
||||
let success = unsafe { GetVolumePathNameW(v.as_ptr(), lp_volume_name_buffer.as_mut_ptr(), volume_name_size) };
|
||||
|
||||
if success == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(lp_volume_name_buffer.as_ptr())
|
||||
}
|
||||
|
||||
fn utf16_to_string(v: &[WCHAR]) -> String {
|
||||
let len = v.iter().position(|&x| x == 0).unwrap_or(v.len());
|
||||
String::from_utf16_lossy(&v[..len])
|
||||
}
|
||||
|
||||
/// returns the filesystem type of the underlying mounted filesystem
|
||||
fn get_fs_type(p: &[WCHAR]) -> Result<String> {
|
||||
let path = get_volume_name(p)?;
|
||||
|
||||
let volume_name_size: DWORD = MAX_PATH as _;
|
||||
let n_file_system_name_size: DWORD = MAX_PATH as _;
|
||||
|
||||
let mut lp_volume_serial_number: DWORD = 0;
|
||||
let mut lp_maximum_component_length: DWORD = 0;
|
||||
let mut lp_file_system_flags: DWORD = 0;
|
||||
|
||||
let mut lp_volume_name_buffer: [WCHAR; MAX_PATH] = [0; MAX_PATH];
|
||||
let mut lp_file_system_name_buffer: [WCHAR; MAX_PATH] = [0; MAX_PATH];
|
||||
|
||||
let success = unsafe {
|
||||
GetVolumeInformationW(
|
||||
path,
|
||||
lp_volume_name_buffer.as_mut_ptr(),
|
||||
volume_name_size,
|
||||
&mut lp_volume_serial_number,
|
||||
&mut lp_maximum_component_length,
|
||||
&mut lp_file_system_flags,
|
||||
lp_file_system_name_buffer.as_mut_ptr(),
|
||||
n_file_system_name_size,
|
||||
)
|
||||
};
|
||||
|
||||
if success == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(utf16_to_string(&lp_file_system_name_buffer))
|
||||
}
|
||||
|
||||
pub fn same_disk(disk1: &str, disk2: &str) -> Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
Reference in New Issue
Block a user