pub mod endpoint; pub mod error; pub mod format; pub mod io; pub mod local; pub mod os; pub mod remote; pub const RUSTFS_META_BUCKET: &str = ".rustfs.sys"; pub const RUSTFS_META_MULTIPART_BUCKET: &str = ".rustfs.sys/multipart"; pub const RUSTFS_META_TMP_BUCKET: &str = ".rustfs.sys/tmp"; pub const RUSTFS_META_TMP_DELETED_BUCKET: &str = ".rustfs.sys/tmp/.trash"; pub const BUCKET_META_PREFIX: &str = "buckets"; pub const FORMAT_CONFIG_FILE: &str = "format.json"; pub const STORAGE_FORMAT_FILE: &str = "xl.meta"; pub const STORAGE_FORMAT_FILE_BACKUP: &str = "xl.meta.bkp"; use crate::{ bucket::{metadata_sys::get_versioning_config, versioning::VersioningApi}, error::{Error, Result}, file_meta::{merge_file_meta_versions, FileMeta, FileMetaShallowVersion, VersionType}, heal::{ data_scanner::ShouldSleepFn, data_usage_cache::{DataUsageCache, DataUsageEntry}, heal_commands::{HealScanMode, HealingTracker}, }, store_api::{FileInfo, ObjectInfo, RawFileInfo}, utils::path::SLASH_SEPARATOR, }; use endpoint::Endpoint; use error::DiskError; use local::LocalDisk; use madmin::info_commands::DiskMetrics; use remote::RemoteDisk; use serde::{Deserialize, Serialize}; use std::{cmp::Ordering, fmt::Debug, path::PathBuf, sync::Arc}; use time::OffsetDateTime; use tokio::{ io::{AsyncRead, AsyncWrite}, sync::mpsc::Sender, }; use tracing::info; use tracing::warn; use uuid::Uuid; pub type DiskStore = Arc; #[derive(Debug)] pub enum Disk { Local(Box), Remote(Box), } #[async_trait::async_trait] impl DiskAPI for Disk { fn to_string(&self) -> String { match self { Disk::Local(local_disk) => local_disk.to_string(), Disk::Remote(remote_disk) => remote_disk.to_string(), } } fn is_local(&self) -> bool { match self { Disk::Local(local_disk) => local_disk.is_local(), Disk::Remote(remote_disk) => remote_disk.is_local(), } } fn host_name(&self) -> String { match self { Disk::Local(local_disk) => local_disk.host_name(), Disk::Remote(remote_disk) => remote_disk.host_name(), } } async fn is_online(&self) -> bool { match self { Disk::Local(local_disk) => local_disk.is_online().await, Disk::Remote(remote_disk) => remote_disk.is_online().await, } } fn endpoint(&self) -> Endpoint { match self { Disk::Local(local_disk) => local_disk.endpoint(), Disk::Remote(remote_disk) => remote_disk.endpoint(), } } async fn close(&self) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.close().await, Disk::Remote(remote_disk) => remote_disk.close().await, } } fn path(&self) -> PathBuf { match self { Disk::Local(local_disk) => local_disk.path(), Disk::Remote(remote_disk) => remote_disk.path(), } } fn get_disk_location(&self) -> DiskLocation { match self { Disk::Local(local_disk) => local_disk.get_disk_location(), Disk::Remote(remote_disk) => remote_disk.get_disk_location(), } } async fn get_disk_id(&self) -> Result> { match self { Disk::Local(local_disk) => local_disk.get_disk_id().await, Disk::Remote(remote_disk) => remote_disk.get_disk_id().await, } } async fn set_disk_id(&self, id: Option) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.set_disk_id(id).await, Disk::Remote(remote_disk) => remote_disk.set_disk_id(id).await, } } async fn read_all(&self, volume: &str, path: &str) -> Result> { match self { Disk::Local(local_disk) => local_disk.read_all(volume, path).await, Disk::Remote(remote_disk) => remote_disk.read_all(volume, path).await, } } async fn write_all(&self, volume: &str, path: &str, data: Vec) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.write_all(volume, path, data).await, Disk::Remote(remote_disk) => remote_disk.write_all(volume, path, data).await, } } async fn delete(&self, volume: &str, path: &str, opt: DeleteOptions) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.delete(volume, path, opt).await, Disk::Remote(remote_disk) => remote_disk.delete(volume, path, opt).await, } } async fn verify_file(&self, volume: &str, path: &str, fi: &FileInfo) -> Result { match self { Disk::Local(local_disk) => local_disk.verify_file(volume, path, fi).await, Disk::Remote(remote_disk) => remote_disk.verify_file(volume, path, fi).await, } } async fn check_parts(&self, volume: &str, path: &str, fi: &FileInfo) -> Result { match self { Disk::Local(local_disk) => local_disk.check_parts(volume, path, fi).await, Disk::Remote(remote_disk) => remote_disk.check_parts(volume, path, fi).await, } } async fn rename_part(&self, src_volume: &str, src_path: &str, dst_volume: &str, dst_path: &str, meta: Vec) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.rename_part(src_volume, src_path, dst_volume, dst_path, meta).await, Disk::Remote(remote_disk) => { remote_disk .rename_part(src_volume, src_path, dst_volume, dst_path, meta) .await } } } async fn rename_file(&self, src_volume: &str, src_path: &str, dst_volume: &str, dst_path: &str) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.rename_file(src_volume, src_path, dst_volume, dst_path).await, Disk::Remote(remote_disk) => remote_disk.rename_file(src_volume, src_path, dst_volume, dst_path).await, } } async fn create_file(&self, _origvolume: &str, volume: &str, path: &str, _file_size: usize) -> Result { match self { Disk::Local(local_disk) => local_disk.create_file(_origvolume, volume, path, _file_size).await, Disk::Remote(remote_disk) => remote_disk.create_file(_origvolume, volume, path, _file_size).await, } } async fn append_file(&self, volume: &str, path: &str) -> Result { match self { Disk::Local(local_disk) => local_disk.append_file(volume, path).await, Disk::Remote(remote_disk) => remote_disk.append_file(volume, path).await, } } async fn read_file(&self, volume: &str, path: &str) -> Result { match self { Disk::Local(local_disk) => local_disk.read_file(volume, path).await, Disk::Remote(remote_disk) => remote_disk.read_file(volume, path).await, } } async fn read_file_stream(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result { match self { Disk::Local(local_disk) => local_disk.read_file_stream(volume, path, offset, length).await, Disk::Remote(remote_disk) => remote_disk.read_file_stream(volume, path, offset, length).await, } } async fn list_dir(&self, _origvolume: &str, volume: &str, _dir_path: &str, _count: i32) -> Result> { match self { Disk::Local(local_disk) => local_disk.list_dir(_origvolume, volume, _dir_path, _count).await, Disk::Remote(remote_disk) => remote_disk.list_dir(_origvolume, volume, _dir_path, _count).await, } } async fn walk_dir(&self, opts: WalkDirOptions, wr: &mut W) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.walk_dir(opts, wr).await, Disk::Remote(remote_disk) => remote_disk.walk_dir(opts, wr).await, } } async fn rename_data( &self, src_volume: &str, src_path: &str, fi: FileInfo, dst_volume: &str, dst_path: &str, ) -> Result { match self { Disk::Local(local_disk) => local_disk.rename_data(src_volume, src_path, fi, dst_volume, dst_path).await, Disk::Remote(remote_disk) => remote_disk.rename_data(src_volume, src_path, fi, dst_volume, dst_path).await, } } async fn make_volumes(&self, volumes: Vec<&str>) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.make_volumes(volumes).await, Disk::Remote(remote_disk) => remote_disk.make_volumes(volumes).await, } } async fn make_volume(&self, volume: &str) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.make_volume(volume).await, Disk::Remote(remote_disk) => remote_disk.make_volume(volume).await, } } async fn list_volumes(&self) -> Result> { match self { Disk::Local(local_disk) => local_disk.list_volumes().await, Disk::Remote(remote_disk) => remote_disk.list_volumes().await, } } async fn stat_volume(&self, volume: &str) -> Result { match self { Disk::Local(local_disk) => local_disk.stat_volume(volume).await, Disk::Remote(remote_disk) => remote_disk.stat_volume(volume).await, } } async fn delete_paths(&self, volume: &str, paths: &[&str]) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.delete_paths(volume, paths).await, Disk::Remote(remote_disk) => remote_disk.delete_paths(volume, paths).await, } } async fn update_metadata(&self, volume: &str, path: &str, fi: FileInfo, opts: &UpdateMetadataOpts) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.update_metadata(volume, path, fi, opts).await, Disk::Remote(remote_disk) => remote_disk.update_metadata(volume, path, fi, opts).await, } } async fn write_metadata(&self, _org_volume: &str, volume: &str, path: &str, fi: FileInfo) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.write_metadata(_org_volume, volume, path, fi).await, Disk::Remote(remote_disk) => remote_disk.write_metadata(_org_volume, volume, path, fi).await, } } #[tracing::instrument(level = "debug", skip(self))] async fn read_version( &self, _org_volume: &str, volume: &str, path: &str, version_id: &str, opts: &ReadOptions, ) -> Result { match self { Disk::Local(local_disk) => local_disk.read_version(_org_volume, volume, path, version_id, opts).await, Disk::Remote(remote_disk) => remote_disk.read_version(_org_volume, volume, path, version_id, opts).await, } } async fn read_xl(&self, volume: &str, path: &str, read_data: bool) -> Result { match self { Disk::Local(local_disk) => local_disk.read_xl(volume, path, read_data).await, Disk::Remote(remote_disk) => remote_disk.read_xl(volume, path, read_data).await, } } async fn delete_version( &self, volume: &str, path: &str, fi: FileInfo, force_del_marker: bool, opts: DeleteOptions, ) -> Result<()> { match self { Disk::Local(local_disk) => local_disk.delete_version(volume, path, fi, force_del_marker, opts).await, Disk::Remote(remote_disk) => remote_disk.delete_version(volume, path, fi, force_del_marker, opts).await, } } async fn delete_versions( &self, volume: &str, versions: Vec, opts: DeleteOptions, ) -> Result>> { match self { Disk::Local(local_disk) => local_disk.delete_versions(volume, versions, opts).await, Disk::Remote(remote_disk) => remote_disk.delete_versions(volume, versions, opts).await, } } async fn read_multiple(&self, req: ReadMultipleReq) -> Result> { match self { Disk::Local(local_disk) => local_disk.read_multiple(req).await, Disk::Remote(remote_disk) => remote_disk.read_multiple(req).await, } } async fn delete_volume(&self, volume: &str) -> Result<()> { info!("delete_volume, volume: {}", volume); match self { Disk::Local(local_disk) => local_disk.delete_volume(volume).await, Disk::Remote(remote_disk) => remote_disk.delete_volume(volume).await, } } async fn disk_info(&self, opts: &DiskInfoOptions) -> Result { match self { Disk::Local(local_disk) => local_disk.disk_info(opts).await, Disk::Remote(remote_disk) => remote_disk.disk_info(opts).await, } } async fn ns_scanner( &self, cache: &DataUsageCache, updates: Sender, scan_mode: HealScanMode, we_sleep: ShouldSleepFn, ) -> Result { info!("ns_scanner"); match self { Disk::Local(local_disk) => local_disk.ns_scanner(cache, updates, scan_mode, we_sleep).await, Disk::Remote(remote_disk) => remote_disk.ns_scanner(cache, updates, scan_mode, we_sleep).await, } } async fn healing(&self) -> Option { match self { Disk::Local(local_disk) => local_disk.healing().await, Disk::Remote(remote_disk) => remote_disk.healing().await, } } } pub async fn new_disk(ep: &endpoint::Endpoint, opt: &DiskOption) -> Result { if ep.is_local { let s = local::LocalDisk::new(ep, opt.cleanup).await?; Ok(Arc::new(Disk::Local(Box::new(s)))) } else { let remote_disk = remote::RemoteDisk::new(ep, opt).await?; Ok(Arc::new(Disk::Remote(Box::new(remote_disk)))) } } pub type FileReader = Box; pub type FileWriter = Box; #[async_trait::async_trait] pub trait DiskAPI: Debug + Send + Sync + 'static { fn to_string(&self) -> String; async fn is_online(&self) -> bool; fn is_local(&self) -> bool; // LastConn fn host_name(&self) -> String; fn endpoint(&self) -> Endpoint; async fn close(&self) -> Result<()>; async fn get_disk_id(&self) -> Result>; async fn set_disk_id(&self, id: Option) -> Result<()>; fn path(&self) -> PathBuf; fn get_disk_location(&self) -> DiskLocation; // Healing // DiskInfo // NSScanner // Volume operations. async fn make_volume(&self, volume: &str) -> Result<()>; async fn make_volumes(&self, volume: Vec<&str>) -> Result<()>; async fn list_volumes(&self) -> Result>; async fn stat_volume(&self, volume: &str) -> Result; async fn delete_volume(&self, volume: &str) -> Result<()>; // 并发边读边写 w <- MetaCacheEntry async fn walk_dir(&self, opts: WalkDirOptions, wr: &mut W) -> Result<()>; // Metadata operations async fn delete_version( &self, volume: &str, path: &str, fi: FileInfo, force_del_marker: bool, opts: DeleteOptions, ) -> Result<()>; async fn delete_versions( &self, volume: &str, versions: Vec, opts: DeleteOptions, ) -> Result>>; async fn delete_paths(&self, volume: &str, paths: &[&str]) -> Result<()>; async fn write_metadata(&self, org_volume: &str, volume: &str, path: &str, fi: FileInfo) -> Result<()>; async fn update_metadata(&self, volume: &str, path: &str, fi: FileInfo, opts: &UpdateMetadataOpts) -> Result<()>; async fn read_version( &self, org_volume: &str, volume: &str, path: &str, version_id: &str, opts: &ReadOptions, ) -> Result; async fn read_xl(&self, volume: &str, path: &str, read_data: bool) -> Result; async fn rename_data( &self, src_volume: &str, src_path: &str, file_info: FileInfo, dst_volume: &str, dst_path: &str, ) -> Result; // File operations. // 读目录下的所有文件、目录 async fn list_dir(&self, origvolume: &str, volume: &str, dir_path: &str, count: i32) -> Result>; async fn read_file(&self, volume: &str, path: &str) -> Result; async fn read_file_stream(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result; async fn append_file(&self, volume: &str, path: &str) -> Result; async fn create_file(&self, origvolume: &str, volume: &str, path: &str, file_size: usize) -> Result; // ReadFileStream async fn rename_file(&self, src_volume: &str, src_path: &str, dst_volume: &str, dst_path: &str) -> Result<()>; async fn rename_part(&self, src_volume: &str, src_path: &str, dst_volume: &str, dst_path: &str, meta: Vec) -> Result<()>; async fn delete(&self, volume: &str, path: &str, opt: DeleteOptions) -> Result<()>; // VerifyFile async fn verify_file(&self, volume: &str, path: &str, fi: &FileInfo) -> Result; // CheckParts async fn check_parts(&self, volume: &str, path: &str, fi: &FileInfo) -> Result; // StatInfoFile // ReadParts async fn read_multiple(&self, req: ReadMultipleReq) -> Result>; // CleanAbandonedData async fn write_all(&self, volume: &str, path: &str, data: Vec) -> Result<()>; async fn read_all(&self, volume: &str, path: &str) -> Result>; async fn disk_info(&self, opts: &DiskInfoOptions) -> Result; async fn ns_scanner( &self, cache: &DataUsageCache, updates: Sender, scan_mode: HealScanMode, we_sleep: ShouldSleepFn, ) -> Result; async fn healing(&self) -> Option; } #[derive(Debug, Default, Serialize, Deserialize)] pub struct CheckPartsResp { pub results: Vec, } #[derive(Debug, Serialize, Deserialize, Default)] pub struct UpdateMetadataOpts { pub no_persistence: bool, } pub struct DiskLocation { pub pool_idx: Option, pub set_idx: Option, pub disk_idx: Option, } impl DiskLocation { pub fn valid(&self) -> bool { self.pool_idx.is_some() && self.set_idx.is_some() && self.disk_idx.is_some() } } #[derive(Debug, Default, Serialize, Deserialize)] pub struct DiskInfoOptions { pub disk_id: String, pub metrics: bool, pub noop: bool, } #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct DiskInfo { pub total: u64, pub free: u64, pub used: u64, pub used_inodes: u64, pub free_inodes: u64, pub major: u64, pub minor: u64, pub nr_requests: u64, pub fs_type: String, pub root_disk: bool, pub healing: bool, pub scanning: bool, pub endpoint: String, pub mount_path: String, pub id: String, pub rotational: bool, pub metrics: DiskMetrics, pub error: String, } #[derive(Clone, Debug, Default)] pub struct Info { pub total: u64, pub free: u64, pub used: u64, pub files: u64, pub ffree: u64, pub fstype: String, pub major: u64, pub minor: u64, pub name: String, pub rotational: bool, pub nrrequests: u64, } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct FileInfoVersions { // Name of the volume. pub volume: String, // Name of the file. pub name: String, // Represents the latest mod time of the // latest version. pub latest_mod_time: Option, pub versions: Vec, pub free_versions: Vec, } impl FileInfoVersions { pub fn find_version_index(&self, v: &str) -> Option { if v.is_empty() { return None; } let vid = Uuid::parse_str(v).unwrap_or(Uuid::nil()); self.versions.iter().position(|v| v.version_id == Some(vid)) } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct WalkDirOptions { // Bucket to scanner pub bucket: String, // Directory inside the bucket. pub base_dir: String, // Do a full recursive scan. pub recursive: bool, // ReportNotFound will return errFileNotFound if all disks reports the BaseDir cannot be found. pub report_notfound: bool, // FilterPrefix will only return results with given prefix within folder. // Should never contain a slash. pub filter_prefix: Option, // ForwardTo will forward to the given object path. pub forward_to: Option, // Limit the number of returned objects if > 0. pub limit: i32, // DiskID contains the disk ID of the disk. // Leave empty to not check disk ID. pub disk_id: String, } #[derive(Clone, Debug, Default)] pub struct MetadataResolutionParams { pub dir_quorum: usize, pub obj_quorum: usize, pub requested_versions: usize, pub bucket: String, pub strict: bool, pub candidates: Vec>, } #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] pub struct MetaCacheEntry { // name is the full name of the object including prefixes pub name: String, // Metadata. If none is present it is not an object but only a prefix. // Entries without metadata will only be present in non-recursive scans. pub metadata: Vec, // cached contains the metadata if decoded. pub cached: Option, // Indicates the entry can be reused and only one reference to metadata is expected. pub reusable: bool, } impl MetaCacheEntry { pub fn marshal_msg(&self) -> Result> { let mut wr = Vec::new(); rmp::encode::write_bool(&mut wr, true)?; rmp::encode::write_str(&mut wr, &self.name)?; rmp::encode::write_bin(&mut wr, &self.metadata)?; Ok(wr) } pub fn is_dir(&self) -> bool { self.metadata.is_empty() && self.name.ends_with('/') } pub fn is_in_dir(&self, dir: &str, separator: &str) -> bool { if dir.is_empty() { let idx = self.name.find(separator); return idx.is_none() || idx.unwrap() == self.name.len() - separator.len(); } let ext = self.name.trim_start_matches(dir); if ext.len() != self.name.len() { let idx = ext.find(separator); return idx.is_none() || idx.unwrap() == ext.len() - separator.len(); } false } pub fn is_object(&self) -> bool { !self.metadata.is_empty() } pub fn is_object_dir(&self) -> bool { !self.metadata.is_empty() && self.name.ends_with(SLASH_SEPARATOR) } pub fn is_latest_delete_marker(&mut self) -> bool { if let Some(cached) = &self.cached { if cached.versions.is_empty() { return true; } return cached.versions[0].header.version_type == VersionType::Delete; } if !FileMeta::is_xl2_v1_format(&self.metadata) { return false; } match FileMeta::check_xl2_v1(&self.metadata) { Ok((meta, _, _)) => { if !meta.is_empty() { return FileMeta::is_latest_delete_marker(meta); } } Err(_) => return true, } match self.xl_meta() { Ok(res) => { if res.versions.is_empty() { return true; } res.versions[0].header.version_type == VersionType::Delete } Err(_) => true, } } #[tracing::instrument(level = "debug", skip(self))] pub fn to_fileinfo(&self, bucket: &str) -> Result { if self.is_dir() { return Ok(FileInfo { volume: bucket.to_owned(), name: self.name.clone(), ..Default::default() }); } if self.cached.is_some() { let fm = self.cached.as_ref().unwrap(); if fm.versions.is_empty() { return Ok(FileInfo { volume: bucket.to_owned(), name: self.name.clone(), deleted: true, is_latest: true, mod_time: Some(OffsetDateTime::UNIX_EPOCH), ..Default::default() }); } let fi = fm.into_fileinfo(bucket, self.name.as_str(), "", false, false)?; return Ok(fi); } let mut fm = FileMeta::new(); fm.unmarshal_msg(&self.metadata)?; let fi = fm.into_fileinfo(bucket, self.name.as_str(), "", false, false)?; return Ok(fi); } pub fn file_info_versions(&self, bucket: &str) -> Result { if self.is_dir() { return Ok(FileInfoVersions { volume: bucket.to_string(), name: self.name.clone(), versions: vec![FileInfo { volume: bucket.to_string(), name: self.name.clone(), ..Default::default() }], ..Default::default() }); } let mut fm = FileMeta::new(); fm.unmarshal_msg(&self.metadata)?; fm.into_file_info_versions(bucket, self.name.as_str(), false) } pub fn matches(&self, other: &MetaCacheEntry, strict: bool) -> Result<(Option, bool)> { let mut prefer = None; if self.name != other.name { if self.name < other.name { return Ok((Some(self.clone()), false)); } return Ok((Some(other.clone()), false)); } if other.is_dir() || self.is_dir() { if self.is_dir() { return Ok((Some(self.clone()), other.is_dir())); } return Ok((Some(other.clone()), other.is_dir() == self.is_dir())); } let self_vers = match &self.cached { Some(file_meta) => file_meta.clone(), None => FileMeta::load(&self.metadata)?, }; let other_vers = match &other.cached { Some(file_meta) => file_meta.clone(), None => FileMeta::load(&other.metadata)?, }; if self_vers.versions.len() != other_vers.versions.len() { match self_vers.lastest_mod_time().cmp(&other_vers.lastest_mod_time()) { Ordering::Greater => { return Ok((Some(self.clone()), false)); } Ordering::Less => { return Ok((Some(self.clone()), false)); } _ => {} } if self_vers.versions.len() > other_vers.versions.len() { return Ok((Some(self.clone()), false)); } return Ok((Some(self.clone()), false)); } for (s_version, o_version) in self_vers.versions.iter().zip(other_vers.versions.iter()) { if s_version.header != o_version.header { if s_version.header.has_ec() != o_version.header.has_ec() { // One version has EC and the other doesn't - may have been written later. // Compare without considering EC. let (mut a, mut b) = (s_version.header.clone(), o_version.header.clone()); (a.ec_n, a.ec_m, b.ec_n, b.ec_m) = (0, 0, 0, 0); if a == b { continue; } } if !strict && s_version.header.matches_not_strict(&o_version.header) { if prefer.is_none() { if s_version.header.sorts_before(&o_version.header) { prefer = Some(self.clone()); } else { prefer = Some(other.clone()); } } continue; } if prefer.is_some() { return Ok((prefer, false)); } if s_version.header.sorts_before(&o_version.header) { return Ok((Some(self.clone()), false)); } return Ok((Some(other.clone()), false)); } } if prefer.is_none() { prefer = Some(self.clone()); } Ok((prefer, true)) } pub fn xl_meta(&mut self) -> Result { if self.is_dir() { return Err(Error::new(DiskError::FileNotFound)); } if let Some(meta) = &self.cached { Ok(meta.clone()) } else { if self.metadata.is_empty() { return Err(Error::new(DiskError::FileNotFound)); } let meta = FileMeta::load(&self.metadata)?; self.cached = Some(meta.clone()); Ok(meta) } } } #[derive(Debug, Default)] pub struct MetaCacheEntries(pub Vec>); impl MetaCacheEntries { #[allow(clippy::should_implement_trait)] pub fn as_ref(&self) -> &[Option] { &self.0 } pub fn resolve(&self, mut params: MetadataResolutionParams) -> Result> { if self.0.is_empty() { return Ok(None); } let mut dir_exists = 0; let mut selected = None; params.candidates.clear(); let mut objs_agree = 0; let mut objs_valid = 0; for entry in self.0.iter().flatten() { if entry.name.is_empty() { continue; } if entry.is_dir() { dir_exists += 1; selected = Some(entry.clone()); continue; } objs_valid += 1; match &entry.cached { Some(file_meta) => { params.candidates.push(file_meta.versions.clone()); } None => { params.candidates.push(FileMeta::load(&entry.metadata)?.versions); } } if selected.is_none() { selected = Some(entry.clone()); objs_agree = 1; continue; } if let (Some(prefer), true) = entry.matches(selected.as_ref().unwrap(), params.strict)? { selected = Some(prefer); objs_agree += 1; continue; } } // Return dir entries, if enough... if selected.is_some() && selected.as_ref().unwrap().is_dir() && dir_exists >= params.dir_quorum { return Ok(selected); } // If we would never be able to reach read quorum. if objs_valid < params.obj_quorum { return Ok(None); } // If all objects agree. if selected.is_some() && objs_agree == objs_valid { return Ok(selected); } // If cached is nil we shall skip the entry. if selected.is_none() || (selected.is_some() && selected.as_ref().unwrap().cached.is_none()) { return Ok(None); } // Merge if we have disagreement. // Create a new merged result. selected = Some(MetaCacheEntry { name: selected.as_ref().unwrap().name.clone(), cached: Some(FileMeta { meta_ver: selected.as_ref().unwrap().cached.as_ref().unwrap().meta_ver, ..Default::default() }), reusable: true, ..Default::default() }); selected.as_mut().unwrap().cached.as_mut().unwrap().versions = merge_file_meta_versions(params.obj_quorum, params.strict, params.requested_versions, ¶ms.candidates); if selected.as_ref().unwrap().cached.as_ref().unwrap().versions.is_empty() { return Ok(None); } selected.as_mut().unwrap().metadata = selected.as_ref().unwrap().cached.as_ref().unwrap().marshal_msg()?; Ok(selected) } pub fn first_found(&self) -> (Option, usize) { (self.0.iter().find(|x| x.is_some()).cloned().unwrap_or_default(), self.0.len()) } } #[derive(Debug, Default)] pub struct MetaCacheEntriesSortedResult { pub entries: Option, pub err: Option, } // impl MetaCacheEntriesSortedResult { // pub fn entriy_list(&self) -> Vec<&MetaCacheEntry> { // if let Some(entries) = &self.entries { // entries.entries() // } else { // Vec::new() // } // } // } #[derive(Debug, Default)] pub struct MetaCacheEntriesSorted { pub o: MetaCacheEntries, pub list_id: Option, pub reuse: bool, pub last_skipped_entry: Option, } impl MetaCacheEntriesSorted { pub fn entries(&self) -> Vec<&MetaCacheEntry> { let entries: Vec<&MetaCacheEntry> = self.o.0.iter().flatten().collect(); entries } pub fn forward_past(&mut self, marker: Option) { if let Some(val) = marker { // TODO: reuse if let Some(idx) = self.o.0.iter().flatten().position(|v| v.name > val) { self.o.0 = self.o.0.split_off(idx); } } } pub async fn file_infos(&self, bucket: &str, prefix: &str, delimiter: Option) -> Vec { let vcfg = get_versioning_config(bucket).await.ok(); let mut objects = Vec::with_capacity(self.o.as_ref().len()); let mut prev_prefix = ""; for entry in self.o.as_ref().iter().flatten() { if entry.is_object() { if let Some(delimiter) = &delimiter { if let Some(idx) = entry.name.trim_start_matches(prefix).find(delimiter) { let idx = prefix.len() + idx + delimiter.len(); if let Some(curr_prefix) = entry.name.get(0..idx) { if curr_prefix == prev_prefix { continue; } prev_prefix = curr_prefix; objects.push(ObjectInfo { is_dir: true, bucket: bucket.to_owned(), name: curr_prefix.to_owned(), ..Default::default() }); } continue; } } if let Ok(fi) = entry.to_fileinfo(bucket) { // TODO:VersionPurgeStatus let versioned = vcfg.clone().map(|v| v.0.versioned(&entry.name)).unwrap_or_default(); objects.push(fi.to_object_info(bucket, &entry.name, versioned)); } continue; } if entry.is_dir() { if let Some(delimiter) = &delimiter { if let Some(idx) = entry.name.trim_start_matches(prefix).find(delimiter) { let idx = prefix.len() + idx + delimiter.len(); if let Some(curr_prefix) = entry.name.get(0..idx) { if curr_prefix == prev_prefix { continue; } prev_prefix = curr_prefix; objects.push(ObjectInfo { is_dir: true, bucket: bucket.to_owned(), name: curr_prefix.to_owned(), ..Default::default() }); } } } } } objects } pub async fn file_info_versions( &self, bucket: &str, prefix: &str, delimiter: Option, after_v: Option, ) -> Vec { let vcfg = get_versioning_config(bucket).await.ok(); let mut objects = Vec::with_capacity(self.o.as_ref().len()); let mut prev_prefix = ""; let mut after_v = after_v; for entry in self.o.as_ref().iter().flatten() { if entry.is_object() { if let Some(delimiter) = &delimiter { if let Some(idx) = entry.name.trim_start_matches(prefix).find(delimiter) { let idx = prefix.len() + idx + delimiter.len(); if let Some(curr_prefix) = entry.name.get(0..idx) { if curr_prefix == prev_prefix { continue; } prev_prefix = curr_prefix; objects.push(ObjectInfo { is_dir: true, bucket: bucket.to_owned(), name: curr_prefix.to_owned(), ..Default::default() }); } continue; } } let mut fiv = match entry.file_info_versions(bucket) { Ok(res) => res, Err(_err) => { // continue; } }; let fi_versions = 'c: { if let Some(after_val) = &after_v { if let Some(idx) = fiv.find_version_index(after_val) { after_v = None; break 'c fiv.versions.split_off(idx + 1); } after_v = None; break 'c fiv.versions; } else { break 'c fiv.versions; } }; for fi in fi_versions.into_iter() { // VersionPurgeStatus let versioned = vcfg.clone().map(|v| v.0.versioned(&entry.name)).unwrap_or_default(); objects.push(fi.to_object_info(bucket, &entry.name, versioned)); } continue; } if entry.is_dir() { if let Some(delimiter) = &delimiter { if let Some(idx) = entry.name.trim_start_matches(prefix).find(delimiter) { let idx = prefix.len() + idx + delimiter.len(); if let Some(curr_prefix) = entry.name.get(0..idx) { if curr_prefix == prev_prefix { continue; } prev_prefix = curr_prefix; objects.push(ObjectInfo { is_dir: true, bucket: bucket.to_owned(), name: curr_prefix.to_owned(), ..Default::default() }); } } } } } objects } } #[derive(Clone, Debug, Default)] pub struct DiskOption { pub cleanup: bool, pub health_check: bool, } #[derive(Debug, Default, Serialize, Deserialize)] pub struct RenameDataResp { pub old_data_dir: Option, pub sign: Option>, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct DeleteOptions { pub recursive: bool, pub immediate: bool, pub undo_write: bool, pub old_data_dir: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReadMultipleReq { pub bucket: String, pub prefix: String, pub files: Vec, pub max_size: usize, pub metadata_only: bool, pub abort404: bool, pub max_results: usize, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct ReadMultipleResp { pub bucket: String, pub prefix: String, pub file: String, pub exists: bool, pub error: String, pub data: Vec, pub mod_time: Option, } // impl Default for ReadMultipleResp { // fn default() -> Self { // Self { // bucket: String::new(), // prefix: String::new(), // file: String::new(), // exists: false, // error: String::new(), // data: Vec::new(), // mod_time: OffsetDateTime::UNIX_EPOCH, // } // } // } #[derive(Debug, Deserialize, Serialize)] pub struct VolumeInfo { pub name: String, pub created: Option, } #[derive(Deserialize, Serialize, Debug, Default)] pub struct ReadOptions { pub incl_free_versions: bool, pub read_data: bool, pub healing: bool, } // pub struct FileWriter { // pub inner: Pin>, // } // impl AsyncWrite for FileWriter { // fn poll_write( // mut self: Pin<&mut Self>, // cx: &mut std::task::Context<'_>, // buf: &[u8], // ) -> std::task::Poll> { // Pin::new(&mut self.inner).poll_write(cx, buf) // } // fn poll_flush( // mut self: Pin<&mut Self>, // cx: &mut std::task::Context<'_>, // ) -> std::task::Poll> { // Pin::new(&mut self.inner).poll_flush(cx) // } // fn poll_shutdown( // mut self: Pin<&mut Self>, // cx: &mut std::task::Context<'_>, // ) -> std::task::Poll> { // Pin::new(&mut self.inner).poll_shutdown(cx) // } // } // impl FileWriter { // pub fn new(inner: W) -> Self // where // W: AsyncWrite + Send + Sync + 'static, // { // Self { inner: Box::pin(inner) } // } // } // #[derive(Debug)] // pub struct BufferWriter { // pub inner: Vec, // } // impl BufferWriter { // pub fn new(inner: Vec) -> Self { // Self { inner } // } // #[allow(clippy::should_implement_trait)] // pub fn as_ref(&self) -> &[u8] { // self.inner.as_ref() // } // } // #[async_trait::async_trait] // impl Writer for BufferWriter { // fn as_any(&self) -> &dyn Any { // self // } // async fn write(&mut self, buf: &[u8]) -> Result<()> { // let _ = self.inner.write(buf).await?; // self.inner.flush().await?; // Ok(()) // } // } // #[derive(Debug)] // pub struct LocalFileWriter { // pub inner: File, // } // impl LocalFileWriter { // pub fn new(inner: File) -> Self { // Self { inner } // } // } // #[async_trait::async_trait] // impl Writer for LocalFileWriter { // fn as_any(&self) -> &dyn Any { // self // } // async fn write(&mut self, buf: &[u8]) -> Result<()> { // let _ = self.inner.write(buf).await?; // self.inner.flush().await?; // Ok(()) // } // } // type NodeClient = NodeServiceClient< // InterceptedService) -> Result, Status> + Send + Sync + 'static>>, // >; // #[derive(Debug)] // pub struct RemoteFileWriter { // pub endpoint: Endpoint, // pub volume: String, // pub path: String, // pub is_append: bool, // tx: Sender, // resp_stream: Streaming, // } // impl RemoteFileWriter { // pub async fn new(endpoint: Endpoint, volume: String, path: String, is_append: bool, mut client: NodeClient) -> Result { // let (tx, rx) = mpsc::channel(128); // let in_stream = ReceiverStream::new(rx); // let response = client.write_stream(in_stream).await.unwrap(); // let resp_stream = response.into_inner(); // Ok(Self { // endpoint, // volume, // path, // is_append, // tx, // resp_stream, // }) // } // } // #[async_trait::async_trait] // impl Writer for RemoteFileWriter { // fn as_any(&self) -> &dyn Any { // self // } // async fn write(&mut self, buf: &[u8]) -> Result<()> { // let request = WriteRequest { // disk: self.endpoint.to_string(), // volume: self.volume.to_string(), // path: self.path.to_string(), // is_append: self.is_append, // data: buf.to_vec(), // }; // self.tx.send(request).await?; // if let Some(resp) = self.resp_stream.next().await { // // match resp { // // Ok(resp) => { // // if resp.success { // // info!("write stream success"); // // } else { // // info!("write stream failed: {}", resp.error_info.unwrap_or("".to_string())); // // } // // } // // Err(_err) => { // // } // // } // let resp = resp?; // if resp.success { // info!("write stream success"); // } else { // return if let Some(err) = &resp.error { // Err(proto_err_to_err(err)) // } else { // Err(Error::from_string("")) // }; // } // } else { // let error_info = "can not get response"; // info!("write stream failed: {}", error_info); // return Err(Error::from_string(error_info)); // } // Ok(()) // } // } // #[async_trait::async_trait] // pub trait Reader { // async fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result; // // async fn seek(&mut self, offset: usize) -> Result<()>; // // async fn read_exact(&mut self, buf: &mut [u8]) -> Result; // } // #[async_trait::async_trait] // impl Reader for FileReader { // async fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result { // match self { // Self::Local(reader) => reader.read_at(offset, buf).await, // Self::Remote(reader) => reader.read_at(offset, buf).await, // Self::Buffer(reader) => reader.read_at(offset, buf).await, // Self::Http(reader) => reader.read_at(offset, buf).await, // } // } // // async fn seek(&mut self, offset: usize) -> Result<()> { // // match self { // // Self::Local(reader) => reader.seek(offset).await, // // Self::Remote(reader) => reader.seek(offset).await, // // Self::Buffer(reader) => reader.seek(offset).await, // // } // // } // // async fn read_exact(&mut self, buf: &mut [u8]) -> Result { // // match self { // // Self::Local(reader) => reader.read_exact(buf).await, // // Self::Remote(reader) => reader.read_exact(buf).await, // // Self::Buffer(reader) => reader.read_exact(buf).await, // // } // // } // } // #[derive(Debug)] // pub struct BufferReader { // pub inner: Cursor>, // remaining: usize, // } // impl BufferReader { // pub fn new(inner: Vec, offset: usize, read_length: usize) -> Self { // let mut cur = Cursor::new(inner); // cur.set_position(offset as u64); // Self { // inner: cur, // remaining: offset + read_length, // } // } // } // impl AsyncRead for BufferReader { // #[tracing::instrument(level = "debug", skip(self, buf))] // fn poll_read( // mut self: Pin<&mut Self>, // cx: &mut std::task::Context<'_>, // buf: &mut tokio::io::ReadBuf<'_>, // ) -> std::task::Poll> { // match Pin::new(&mut self.inner).poll_read(cx, buf) { // Poll::Ready(Ok(_)) => { // if self.inner.position() as usize >= self.remaining { // self.remaining -= buf.filled().len(); // Poll::Ready(Ok(())) // } else { // Poll::Pending // } // } // Poll::Ready(Err(err)) => Poll::Ready(Err(err)), // Poll::Pending => Poll::Pending, // } // } // } // #[async_trait::async_trait] // impl Reader for BufferReader { // #[tracing::instrument(level = "debug", skip(self, buf))] // async fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result { // if self.pos != offset { // self.inner.set_position(offset as u64); // } // self.inner.read_exact(buf).await?; // self.pos += buf.len(); // Ok(buf.len()) // } // // #[tracing::instrument(level = "debug", skip(self))] // // async fn seek(&mut self, offset: usize) -> Result<()> { // // if self.pos != offset { // // self.inner.set_position(offset as u64); // // } // // Ok(()) // // } // // #[tracing::instrument(level = "debug", skip(self))] // // async fn read_exact(&mut self, buf: &mut [u8]) -> Result { // // let bytes_read = self.inner.read_exact(buf).await?; // // self.pos += buf.len(); // // Ok(bytes_read) // // } // } // #[derive(Debug)] // pub struct LocalFileReader { // pub inner: File, // // pos: usize, // } // impl LocalFileReader { // pub fn new(inner: File) -> Self { // Self { inner } // } // } // #[async_trait::async_trait] // impl Reader for LocalFileReader { // #[tracing::instrument(level = "debug", skip(self, buf))] // async fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result { // if self.pos != offset { // self.inner.seek(SeekFrom::Start(offset as u64)).await?; // self.pos = offset; // } // self.inner.read_exact(buf).await?; // self.pos += buf.len(); // Ok(buf.len()) // } // // #[tracing::instrument(level = "debug", skip(self))] // // async fn seek(&mut self, offset: usize) -> Result<()> { // // if self.pos != offset { // // self.inner.seek(SeekFrom::Start(offset as u64)).await?; // // self.pos = offset; // // } // // Ok(()) // // } // // #[tracing::instrument(level = "debug", skip(self, buf))] // // async fn read_exact(&mut self, buf: &mut [u8]) -> Result { // // let bytes_read = self.inner.read_exact(buf).await?; // // self.pos += buf.len(); // // Ok(bytes_read) // // } // } // impl AsyncRead for LocalFileReader { // #[tracing::instrument(level = "debug", skip(self, buf))] // fn poll_read( // mut self: Pin<&mut Self>, // cx: &mut std::task::Context<'_>, // buf: &mut tokio::io::ReadBuf<'_>, // ) -> std::task::Poll> { // Pin::new(&mut self.inner).poll_read(cx, buf) // } // } // #[derive(Debug)] // pub struct RemoteFileReader { // pub endpoint: Endpoint, // pub volume: String, // pub path: String, // tx: Sender, // resp_stream: Streaming, // } // impl RemoteFileReader { // pub async fn new(endpoint: Endpoint, volume: String, path: String, mut client: NodeClient) -> Result { // let (tx, rx) = mpsc::channel(128); // let in_stream = ReceiverStream::new(rx); // let response = client.read_at(in_stream).await.unwrap(); // let resp_stream = response.into_inner(); // Ok(Self { // endpoint, // volume, // path, // tx, // resp_stream, // }) // } // } // #[async_trait::async_trait] // impl Reader for RemoteFileReader { // async fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result { // let request = ReadAtRequest { // disk: self.endpoint.to_string(), // volume: self.volume.to_string(), // path: self.path.to_string(), // offset: offset.try_into().unwrap(), // // length: length.try_into().unwrap(), // length: buf.len().try_into().unwrap(), // }; // self.tx.send(request).await?; // if let Some(resp) = self.resp_stream.next().await { // let resp = resp?; // if resp.success { // info!("read at stream success"); // buf.copy_from_slice(&resp.data); // Ok(resp.read_size.try_into().unwrap()) // } else { // return if let Some(err) = &resp.error { // Err(proto_err_to_err(err)) // } else { // Err(Error::from_string("")) // }; // } // } else { // let error_info = "can not get response"; // info!("read at stream failed: {}", error_info); // Err(Error::from_string(error_info)) // } // } // // async fn seek(&mut self, _offset: usize) -> Result<()> { // // unimplemented!() // // } // // async fn read_exact(&mut self, _buf: &mut [u8]) -> Result { // // unimplemented!() // // } // } // impl AsyncRead for RemoteFileReader { // #[tracing::instrument(level = "debug", skip(self, buf))] // fn poll_read( // mut self: Pin<&mut Self>, // cx: &mut std::task::Context<'_>, // buf: &mut tokio::io::ReadBuf<'_>, // ) -> std::task::Poll> { // unimplemented!("poll_read") // } // }