mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-31 09:18:28 +00:00
fix:handle null version ID in delete and return version_id in get_object (#1479)
Signed-off-by: houseme <housemecn@gmail.com> Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
@@ -2491,6 +2491,23 @@ impl S3 for FS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let versioned = BucketVersioningSys::prefix_enabled(&bucket, &key).await;
|
||||||
|
|
||||||
|
// Get version_id from object info
|
||||||
|
// If versioning is enabled and version_id exists in object info, return it
|
||||||
|
// If version_id is Uuid::nil(), return "null" string (AWS S3 convention)
|
||||||
|
let output_version_id = if versioned {
|
||||||
|
info.version_id.map(|vid| {
|
||||||
|
if vid == Uuid::nil() {
|
||||||
|
"null".to_string()
|
||||||
|
} else {
|
||||||
|
vid.to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let output = GetObjectOutput {
|
let output = GetObjectOutput {
|
||||||
body,
|
body,
|
||||||
content_length: Some(response_content_length),
|
content_length: Some(response_content_length),
|
||||||
@@ -2511,6 +2528,7 @@ impl S3 for FS {
|
|||||||
checksum_sha256,
|
checksum_sha256,
|
||||||
checksum_crc64nvme,
|
checksum_crc64nvme,
|
||||||
checksum_type,
|
checksum_type,
|
||||||
|
version_id: output_version_id,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -65,13 +65,23 @@ pub async fn del_opts(
|
|||||||
|
|
||||||
let vid = vid.map(|v| v.as_str().trim().to_owned());
|
let vid = vid.map(|v| v.as_str().trim().to_owned());
|
||||||
|
|
||||||
if let Some(ref id) = vid
|
// Handle AWS S3 special case: "null" string represents null version ID
|
||||||
&& *id != Uuid::nil().to_string()
|
// When VersionId='null' is specified, it means delete the object with null version ID
|
||||||
&& let Err(err) = Uuid::parse_str(id.as_str())
|
let vid = if let Some(ref id) = vid {
|
||||||
{
|
if id.eq_ignore_ascii_case("null") {
|
||||||
error!("del_opts: invalid version id: {} error: {}", id, err);
|
// Convert "null" to Uuid::nil() string representation
|
||||||
return Err(StorageError::InvalidVersionID(bucket.to_owned(), object.to_owned(), id.clone()));
|
Some(Uuid::nil().to_string())
|
||||||
}
|
} else {
|
||||||
|
// Validate UUID format for other version IDs
|
||||||
|
if *id != Uuid::nil().to_string() && Uuid::parse_str(id.as_str()).is_err() {
|
||||||
|
error!("del_opts: invalid version id: {} error: invalid UUID format", id);
|
||||||
|
return Err(StorageError::InvalidVersionID(bucket.to_owned(), object.to_owned(), id.clone()));
|
||||||
|
}
|
||||||
|
Some(id.clone())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut opts = put_opts_from_headers(headers, metadata.clone()).map_err(|err| {
|
let mut opts = put_opts_from_headers(headers, metadata.clone()).map_err(|err| {
|
||||||
error!("del_opts: invalid argument: {} error: {}", object, err);
|
error!("del_opts: invalid argument: {} error: {}", object, err);
|
||||||
@@ -704,6 +714,16 @@ mod tests {
|
|||||||
assert!(!opts.delete_prefix);
|
assert!(!opts.delete_prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_del_opts_with_null_version_id() {
|
||||||
|
let headers = create_test_headers();
|
||||||
|
let metadata = create_test_metadata();
|
||||||
|
let result = del_opts("test-bucket", "test-object", Some("null".to_string()), &headers, metadata.clone()).await;
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let result = del_opts("test-bucket", "test-object", Some("NULL".to_string()), &headers, metadata.clone()).await;
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_get_opts_basic() {
|
async fn test_get_opts_basic() {
|
||||||
let headers = create_test_headers();
|
let headers = create_test_headers();
|
||||||
|
|||||||
Reference in New Issue
Block a user