From 43bf8466334e716d05f003c493e18bb830c75fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=AD=A3=E8=B6=85?= Date: Thu, 22 Jan 2026 20:58:03 +0800 Subject: [PATCH] fix: correct max_keys field in list_object_versions response (#1576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 安正超 Co-authored-by: loverustfs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .gitignore | 3 +++ crates/ecstore/src/store_list_objects.rs | 11 +++++++++++ rustfs/src/storage/ecfs.rs | 6 +++--- scripts/s3-tests/implemented_tests.txt | 3 ++- scripts/s3-tests/unimplemented_tests.txt | 1 - 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 998d65cbc..b58b536a8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ artifacts/ *.events *.audit *.snappy +PR_DESCRIPTION.md +IMPLEMENTATION_PLAN.md +scripts/s3-tests/selected_tests.txt diff --git a/crates/ecstore/src/store_list_objects.rs b/crates/ecstore/src/store_list_objects.rs index aae98d48b..b13ef361d 100644 --- a/crates/ecstore/src/store_list_objects.rs +++ b/crates/ecstore/src/store_list_objects.rs @@ -428,6 +428,13 @@ impl ECStore { result.forward_past(opts.marker); } + // Check if list_path returned entries equal to limit, which indicates more objects exist + // This is more accurate than checking get_objects.len() because get_objects may be filtered + // (e.g., directories filtered out), so its length may be less than the actual entries count + // We need to check this before calling from_meta_cache_entries_sorted_versions which consumes entries + let entries_count = list_result.entries.as_ref().map(|e| e.entries().len()).unwrap_or(0); + let limit = opts.limit; + let mut get_objects = ObjectInfo::from_meta_cache_entries_sorted_versions( &list_result.entries.unwrap_or_default(), bucket, @@ -441,7 +448,11 @@ impl ECStore { if max_keys > 0 && get_objects.len() > max_keys as usize { get_objects.truncate(max_keys as usize); true + } else if entries_count >= limit as usize { + // If entries count equals limit, there are more objects + true } else { + // Otherwise, check if there are any objects and no error list_result.err.is_none() && !get_objects.is_empty() } }; diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index 739a4428a..c6a5d2d0b 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -3725,8 +3725,6 @@ impl S3 for FS { }) .collect(); - let key_count = objects.len() as i32; - let common_prefixes = object_infos .prefixes .into_iter() @@ -3753,7 +3751,9 @@ impl S3 for FS { let output = ListObjectVersionsOutput { is_truncated: Some(object_infos.is_truncated), - max_keys: Some(key_count), + // max_keys should be the requested maximum number of keys, not the actual count returned + // Per AWS S3 API spec, this field represents the maximum number of keys that can be returned in the response + max_keys: Some(max_keys), delimiter, name: Some(bucket), prefix: Some(prefix), diff --git a/scripts/s3-tests/implemented_tests.txt b/scripts/s3-tests/implemented_tests.txt index 9df5e88dd..7b5717cae 100644 --- a/scripts/s3-tests/implemented_tests.txt +++ b/scripts/s3-tests/implemented_tests.txt @@ -17,7 +17,7 @@ # - Metadata: User-defined metadata # - Conditional GET: If-Match, If-None-Match, If-Modified-Since # -# Total: 118 tests +# Total: 119 tests test_basic_key_count test_bucket_create_naming_bad_short_one @@ -63,6 +63,7 @@ test_bucket_list_prefix_none test_bucket_list_prefix_not_exist test_bucket_list_prefix_unreadable test_bucket_list_special_prefix +test_bucket_list_delimiter_basic test_bucket_list_delimiter_alt test_bucket_list_delimiter_dot test_bucket_list_delimiter_empty diff --git a/scripts/s3-tests/unimplemented_tests.txt b/scripts/s3-tests/unimplemented_tests.txt index bd7e62973..0e07c386a 100644 --- a/scripts/s3-tests/unimplemented_tests.txt +++ b/scripts/s3-tests/unimplemented_tests.txt @@ -106,7 +106,6 @@ test_versioning_obj_suspend_versions # Teardown issues (list_object_versions on non-versioned buckets) # These tests pass but have cleanup issues with list_object_versions -test_bucket_list_delimiter_basic test_bucket_list_encoding_basic test_bucket_listv2_delimiter_alt test_bucket_listv2_delimiter_basic