mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-16 17:20:33 +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 {
|
||||
body,
|
||||
content_length: Some(response_content_length),
|
||||
@@ -2511,6 +2528,7 @@ impl S3 for FS {
|
||||
checksum_sha256,
|
||||
checksum_crc64nvme,
|
||||
checksum_type,
|
||||
version_id: output_version_id,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
@@ -65,13 +65,23 @@ pub async fn del_opts(
|
||||
|
||||
let vid = vid.map(|v| v.as_str().trim().to_owned());
|
||||
|
||||
if let Some(ref id) = vid
|
||||
&& *id != Uuid::nil().to_string()
|
||||
&& let Err(err) = Uuid::parse_str(id.as_str())
|
||||
{
|
||||
error!("del_opts: invalid version id: {} error: {}", id, err);
|
||||
return Err(StorageError::InvalidVersionID(bucket.to_owned(), object.to_owned(), id.clone()));
|
||||
}
|
||||
// Handle AWS S3 special case: "null" string represents null version ID
|
||||
// When VersionId='null' is specified, it means delete the object with null version ID
|
||||
let vid = if let Some(ref id) = vid {
|
||||
if id.eq_ignore_ascii_case("null") {
|
||||
// Convert "null" to Uuid::nil() string representation
|
||||
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| {
|
||||
error!("del_opts: invalid argument: {} error: {}", object, err);
|
||||
@@ -704,6 +714,16 @@ mod tests {
|
||||
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]
|
||||
async fn test_get_opts_basic() {
|
||||
let headers = create_test_headers();
|
||||
|
||||
Reference in New Issue
Block a user