This is an automated email from the ASF dual-hosted git repository.

ivandika3 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new d1eee36a502 HDDS-15618. Fix ListObjects to include single 
whitespace-only S3 object keys (#10589)
d1eee36a502 is described below

commit d1eee36a502793f0fa095668ab47e01e8b7c7c52
Author: Gargi Jaiswal <[email protected]>
AuthorDate: Mon Jun 29 14:58:22 2026 +0530

    HDDS-15618. Fix ListObjects to include single whitespace-only S3 object 
keys (#10589)
---
 .../hadoop/ozone/s3/awssdk/S3SDKTestUtils.java     |  9 ++++++++
 .../ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java   | 22 +++++++++++++++++++
 .../ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java   | 22 +++++++++++++++++++
 .../hadoop/ozone/om/OmMetadataManagerImpl.java     |  2 +-
 .../hadoop/ozone/om/TestOmMetadataManager.java     | 25 ++++++++++++++++++++++
 5 files changed, 79 insertions(+), 1 deletion(-)

diff --git 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/S3SDKTestUtils.java
 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/S3SDKTestUtils.java
index ec42a0d7b4f..ec3793c68d1 100644
--- 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/S3SDKTestUtils.java
+++ 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/S3SDKTestUtils.java
@@ -25,6 +25,8 @@
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -38,6 +40,13 @@
  */
 public final class S3SDKTestUtils {
 
+  /**
+   * Key names from ceph s3-tests {@code test_bucket_create_special_key_names}.
+   */
+  public static final List<String> S3_SPECIAL_KEY_NAMES = 
Collections.unmodifiableList(
+      Arrays.asList(" ", "\"",
+          "$", "%", "&", "'", "<", ">", "_", "_ ", "_ _", "__"));
+
   public static final Pattern UPLOAD_ID_PATTERN = 
Pattern.compile("<UploadId>(.+?)</UploadId>");
 
   private S3SDKTestUtils() {
diff --git 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
index 5de874b51d0..e810a8786b9 100644
--- 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
+++ 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
@@ -1316,6 +1316,28 @@ public void testListObjectsManyV2() throws Exception {
     testListObjectsMany(true);
   }
 
+  @Test
+  public void testListObjectsSpecialKeyNames() throws Exception {
+    final String bucketName = getBucketName("special-keys");
+    final String content = "x";
+    s3Client.createBucket(bucketName);
+
+    for (String keyName : S3SDKTestUtils.S3_SPECIAL_KEY_NAMES) {
+      InputStream is = new 
ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
+      s3Client.putObject(bucketName, keyName, is, new ObjectMetadata());
+      try (S3Object object = s3Client.getObject(bucketName, keyName)) {
+        assertEquals(content, IOUtils.toString(object.getObjectContent(), 
StandardCharsets.UTF_8));
+      }
+    }
+
+    ObjectListing listObjectsResponse = s3Client.listObjects(
+        new ListObjectsRequest().withBucketName(bucketName));
+    List<String> listedKeys = listObjectsResponse.getObjectSummaries().stream()
+        .map(S3ObjectSummary::getKey)
+        .collect(Collectors.toList());
+    assertEquals(S3SDKTestUtils.S3_SPECIAL_KEY_NAMES, listedKeys);
+  }
+
   private void testListObjectsMany(boolean isListV2) throws Exception {
     final String bucketName = getBucketName();
     s3Client.createBucket(bucketName);
diff --git 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
index ff46b739d9f..d807be57483 100644
--- 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
+++ 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
@@ -993,6 +993,28 @@ public void testListObjectsManyV2() throws Exception {
     testListObjectsMany(true);
   }
 
+  @Test
+  public void testListObjectsSpecialKeyNamesV2() throws Exception {
+    final String bucketName = getBucketName("special-keys");
+    final String content = "x";
+    s3Client.createBucket(b -> b.bucket(bucketName));
+
+    for (String keyName : S3SDKTestUtils.S3_SPECIAL_KEY_NAMES) {
+      s3Client.putObject(b -> b.bucket(bucketName).key(keyName),
+          RequestBody.fromString(content));
+      ResponseBytes<GetObjectResponse> objectBytes = s3Client.getObjectAsBytes(
+          b -> b.bucket(bucketName).key(keyName));
+      assertEquals(content, objectBytes.asUtf8String());
+    }
+
+    ListObjectsV2Response listObjectsResponse = s3Client.listObjectsV2(
+        ListObjectsV2Request.builder().bucket(bucketName).build());
+    List<String> listedKeys = listObjectsResponse.contents().stream()
+        .map(S3Object::key)
+        .collect(Collectors.toList());
+    assertEquals(S3SDKTestUtils.S3_SPECIAL_KEY_NAMES, listedKeys);
+  }
+
   private void testListObjectsMany(boolean isListV2) throws Exception {
     final String bucketName = getBucketName();
     s3Client.createBucket(b -> b.bucket(bucketName));
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
index b76e5aa5262..69b1ef67bfe 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
@@ -640,7 +640,7 @@ public String getOzoneKey(String volume, String bucket, 
String key) {
     StringBuilder builder = new StringBuilder()
         .append(OM_KEY_PREFIX).append(volume)
         .append(OM_KEY_PREFIX).append(bucket); // TODO : Throw if the Bucket 
is null?
-    if (StringUtils.isNotBlank(key)) {
+    if (StringUtils.isNotEmpty(key)) {
       builder.append(OM_KEY_PREFIX);
       if (!key.equals(OM_KEY_PREFIX)) {
         builder.append(key);
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmMetadataManager.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmMetadataManager.java
index fc2a9ca78b0..17131e0c7db 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmMetadataManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmMetadataManager.java
@@ -1294,4 +1294,29 @@ public void testGetMultipartUploadKeys() throws 
Exception {
 
     assertEquals(25, noPagination.size());
   }
+
+  @Test
+  public void testListKeysSpecialKeyNames() throws Exception {
+    List<String> keyNames = Arrays.asList(" ", "\"",
+        "$", "%", "&", "'", "<", ">", "_", "_ ", "_ _", "__");
+
+    String volumeName = "volumeA";
+    String bucketName = "bucketA";
+    OMRequestTestUtils.addVolumeToDB(volumeName, omMetadataManager);
+    addBucketsToCache(volumeName, bucketName);
+
+    assertEquals("/volumeA/bucketA/ ",
+        omMetadataManager.getOzoneKey(volumeName, bucketName, " "));
+
+    for (int i = 0; i < keyNames.size(); i++) {
+      addKeysToOM(volumeName, bucketName, keyNames.get(i), i);
+    }
+
+    List<String> listedKeys = omMetadataManager.listKeys(volumeName, 
bucketName,
+        null, null, 100).getKeys().stream()
+        .map(OmKeyInfo::getKeyName)
+        .collect(Collectors.toList());
+
+    assertEquals(keyNames, listedKeys);
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to