mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 23:47:28 +00:00
fix(ecstore): recover data usage snapshots safely (#4979)
This commit is contained in:
@@ -279,6 +279,17 @@ where
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Read an existing config object without treating an empty payload as absent.
|
||||
/// Callers that validate their own payload format need to distinguish corruption
|
||||
/// from `ConfigNotFound`.
|
||||
pub(crate) async fn read_config_preserve_empty<S>(api: Arc<S>, file: &str) -> Result<Vec<u8>>
|
||||
where
|
||||
S: EcstoreObjectIO,
|
||||
{
|
||||
let (data, _obj) = read_config_with_metadata_inner(api, file, &ObjectOptions::default(), true).await?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub async fn read_config_no_lock<S>(api: Arc<S>, file: &str) -> Result<Vec<u8>>
|
||||
where
|
||||
S: ObjectIO<
|
||||
@@ -304,6 +315,26 @@ where
|
||||
}
|
||||
|
||||
pub async fn read_config_with_metadata<S>(api: Arc<S>, file: &str, opts: &ObjectOptions) -> Result<(Vec<u8>, ObjectInfo)>
|
||||
where
|
||||
S: ObjectIO<
|
||||
Error = Error,
|
||||
RangeSpec = HTTPRangeSpec,
|
||||
HeaderMap = HeaderMap,
|
||||
ObjectOptions = ObjectOptions,
|
||||
ObjectInfo = ObjectInfo,
|
||||
GetObjectReader = GetObjectReader,
|
||||
PutObjectReader = PutObjReader,
|
||||
>,
|
||||
{
|
||||
read_config_with_metadata_inner(api, file, opts, false).await
|
||||
}
|
||||
|
||||
async fn read_config_with_metadata_inner<S>(
|
||||
api: Arc<S>,
|
||||
file: &str,
|
||||
opts: &ObjectOptions,
|
||||
preserve_empty: bool,
|
||||
) -> Result<(Vec<u8>, ObjectInfo)>
|
||||
where
|
||||
S: ObjectIO<
|
||||
Error = Error,
|
||||
@@ -330,7 +361,7 @@ where
|
||||
|
||||
let data = rd.read_all().await?;
|
||||
|
||||
if data.is_empty() {
|
||||
if data.is_empty() && !preserve_empty {
|
||||
return Err(Error::ConfigNotFound);
|
||||
}
|
||||
|
||||
@@ -1596,7 +1627,7 @@ where
|
||||
mod tests {
|
||||
use super::{
|
||||
configs_semantically_equal, decode_server_config_blob, encode_server_config_blob, is_standard_object_server_config,
|
||||
read_config_with_metadata, storage_class_kvs_mut,
|
||||
read_config, read_config_preserve_empty, read_config_with_metadata, storage_class_kvs_mut,
|
||||
};
|
||||
use crate::config::{audit, notify, oidc};
|
||||
use crate::disk::endpoint::Endpoint;
|
||||
@@ -3059,6 +3090,21 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_config_preserve_empty_distinguishes_empty_object() {
|
||||
let store = Arc::new(RecoveryMockStore::new(RecoveryReadState::Blob(Vec::new()), None));
|
||||
|
||||
let err = read_config(store.clone(), "config/empty.json")
|
||||
.await
|
||||
.expect_err("the existing config contract treats empty objects as missing");
|
||||
assert!(matches!(err, Error::ConfigNotFound));
|
||||
|
||||
let data = read_config_preserve_empty(store, "config/empty.json")
|
||||
.await
|
||||
.expect("payload-validating callers must observe the empty object");
|
||||
assert!(data.is_empty());
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl StorageAdminApi for RecoveryMockStore {
|
||||
type BackendInfo = rustfs_madmin::BackendInfo;
|
||||
|
||||
Reference in New Issue
Block a user