From 9b07b0c83cbc9873d2ae80acf4195aaaf30f831b Mon Sep 17 00:00:00 2001 From: Siyao Meng <50227127+smengcl@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:44:58 -0700 Subject: [PATCH 1/5] HDDS-15961. Resolve linked bucket source properties consistently --- .../hadoop/ozone/om/helpers/OmBucketInfo.java | 1 + .../ozone/om/helpers/TestOmBucketInfo.java | 21 ++++ .../apache/hadoop/ozone/om/OzoneManager.java | 12 +++ .../ozone/om/TestBucketManagerImpl.java | 95 +++++++++++++++++++ 4 files changed, 129 insertions(+) diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java index 463b9de0d95f..09a31883dd19 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java @@ -407,6 +407,7 @@ public Builder toBuilder() { public OmBucketInfo withOperationalPropertiesFrom(OmBucketInfo source) { return toBuilder() .setDefaultReplicationConfig(source.getDefaultReplicationConfig()) + .setBucketEncryptionKey(source.getEncryptionKeyInfo()) .setIsVersionEnabled(source.getIsVersionEnabled()) .setStorageType(source.getStorageType()) .setQuotaInBytes(source.getQuotaInBytes()) diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmBucketInfo.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmBucketInfo.java index 5f816ff4c20a..3cd3d53508d3 100644 --- a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmBucketInfo.java +++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/helpers/TestOmBucketInfo.java @@ -147,6 +147,27 @@ public void testWithOperationalPropertiesFromPreservesLinkIdentity() { assertEquals("linkValue", resolvedLink.getMetadata().get("linkKey")); } + @Test + public void testWithOperationalPropertiesFromCopiesEncryptionInfo() { + BucketEncryptionKeyInfo encryptionKeyInfo = + new BucketEncryptionKeyInfo.Builder().setKeyName("key1").build(); + OmBucketInfo source = OmBucketInfo.newBuilder() + .setVolumeName("vol1") + .setBucketName("source") + .setBucketEncryptionKey(encryptionKeyInfo) + .build(); + OmBucketInfo link = OmBucketInfo.newBuilder() + .setVolumeName("vol1") + .setBucketName("link") + .setSourceVolume("vol1") + .setSourceBucket("source") + .build(); + + OmBucketInfo resolvedLink = link.withOperationalPropertiesFrom(source); + + assertEquals(encryptionKeyInfo, resolvedLink.getEncryptionKeyInfo()); + } + @Test public void getProtobufMessageEC() { OmBucketInfo omBucketInfo = diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index 709c59da16b8..be3022612f72 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -3061,6 +3061,18 @@ private OmBucketInfo enrichLinkBucketInfo( if (resolvedBucket.isDangling()) { return bucketInfo; } + if (getAclsEnabled()) { + try { + omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, + ACLType.READ, resolvedBucket.realVolume(), + resolvedBucket.realBucket(), null); + } catch (OMException e) { + if (e.getResult() == PERMISSION_DENIED) { + return bucketInfo; + } + throw e; + } + } OmBucketInfo realBucket = getResolvedSourceBucket(resolvedBucket, resolvedSourceCache); return bucketInfo.withOperationalPropertiesFrom(realBucket); } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java index 1755fb433c42..7d4c2806e570 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java @@ -18,18 +18,29 @@ package org.apache.hadoop.ozone.om; import static java.util.Collections.singletonMap; +import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ; +import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET; +import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.hadoop.crypto.key.KeyProvider; @@ -41,7 +52,9 @@ import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.StorageType; import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor; +import org.apache.hadoop.hdds.scm.HddsWhiteboxTestUtils; import org.apache.hadoop.hdds.server.ServerUtils; +import org.apache.hadoop.ozone.audit.AuditMessage; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes; import org.apache.hadoop.ozone.om.helpers.BucketEncryptionKeyInfo; @@ -540,4 +553,86 @@ public void testListBucketsResolvesFsoAndObsLinkLayouts() throws Exception { assertEquals(volume, listedObsLink.getSourceVolume()); assertEquals("obs-source", listedObsLink.getSourceBucket()); } + + @Test + void testGetBucketInfoDoesNotCopySourcePropertiesWithoutReadAccess() throws Exception { + String linkVolume = volumeName(); + String sourceVolume = volumeName(); + OmBucketInfo link = createLinkBucketInfo(linkVolume, sourceVolume); + OmBucketInfo source = createSourceBucketInfo(sourceVolume); + BucketManager bucketManager = mock(BucketManager.class); + when(bucketManager.getBucketInfo(linkVolume, "link")).thenReturn(link); + when(bucketManager.getBucketInfo(sourceVolume, "source")).thenReturn(source); + OmMetadataReader metadataReader = mock(OmMetadataReader.class); + denySourceRead(metadataReader, sourceVolume); + OzoneManager omSpy = createAclEnabledOmSpy(bucketManager, metadataReader); + + OmBucketInfo result = omSpy.getBucketInfo(linkVolume, "link"); + + assertSame(link, result); + verify(metadataReader).checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); + } + + @Test + void testListBucketsDoesNotCopySourcePropertiesWithoutReadAccess() throws Exception { + String linkVolume = volumeName(); + String sourceVolume = volumeName(); + OmBucketInfo link = createLinkBucketInfo(linkVolume, sourceVolume); + OmBucketInfo source = createSourceBucketInfo(sourceVolume); + OmBucketInfo regular = OmBucketInfo.newBuilder() + .setVolumeName(linkVolume) + .setBucketName("regular") + .build(); + BucketManager bucketManager = mock(BucketManager.class); + when(bucketManager.listBuckets(linkVolume, "", "", 100, false)) + .thenReturn(new ArrayList<>(Arrays.asList(link, regular))); + when(bucketManager.getBucketInfo(sourceVolume, "source")).thenReturn(source); + OmMetadataReader metadataReader = mock(OmMetadataReader.class); + denySourceRead(metadataReader, sourceVolume); + OzoneManager omSpy = createAclEnabledOmSpy(bucketManager, metadataReader); + + List result = omSpy.listBuckets(linkVolume, "", "", 100, false); + + assertSame(link, result.get(0)); + assertSame(regular, result.get(1)); + verify(metadataReader).checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); + } + + private static OmBucketInfo createLinkBucketInfo(String linkVolume, String sourceVolume) { + return OmBucketInfo.newBuilder() + .setVolumeName(linkVolume) + .setBucketName("link") + .setSourceVolume(sourceVolume) + .setSourceBucket("source") + .addAllMetadata(singletonMap("linkKey", "linkValue")) + .build(); + } + + private static OmBucketInfo createSourceBucketInfo(String sourceVolume) { + return OmBucketInfo.newBuilder() + .setVolumeName(sourceVolume) + .setBucketName("source") + .addAllMetadata(singletonMap("sourceKey", "sourceValue")) + .build(); + } + + private static void denySourceRead(OmMetadataReader metadataReader, String sourceVolume) throws IOException { + doAnswer(invocation -> { + if (sourceVolume.equals(invocation.getArgument(3)) && "source".equals(invocation.getArgument(4))) { + throw new OMException("denied", ResultCodes.PERMISSION_DENIED); + } + return null; + }).when(metadataReader).checkAcls(any(), any(), any(), any(), any(), any()); + } + + private OzoneManager createAclEnabledOmSpy(BucketManager bucketManager, OmMetadataReader metadataReader) { + OzoneManager omSpy = spy(omTestManagers.getOzoneManager()); + HddsWhiteboxTestUtils.setInternalState(omSpy, "bucketManager", bucketManager); + HddsWhiteboxTestUtils.setInternalState(omSpy, "omMetadataReader", metadataReader); + doReturn(true).when(omSpy).getAclsEnabled(); + AuditMessage auditMessage = mock(AuditMessage.class); + when(auditMessage.getOp()).thenReturn("READ_BUCKET"); + doReturn(auditMessage).when(omSpy).buildAuditMessageForSuccess(any(), anyMap()); + return omSpy; + } } From 4b099a0583dd488e043315178bb490fe153b334c Mon Sep 17 00:00:00 2001 From: Siyao Meng <50227127+smengcl@users.noreply.github.com> Date: Fri, 24 Jul 2026 02:02:57 -0700 Subject: [PATCH 2/5] HDDS-15961. Reuse source access checks in listBuckets --- .../apache/hadoop/ozone/om/OzoneManager.java | 28 +++++++++--------- .../ozone/om/TestBucketManagerImpl.java | 29 +++++++++++++++++++ 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index be3022612f72..f1b44bc427e3 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -3061,20 +3061,10 @@ private OmBucketInfo enrichLinkBucketInfo( if (resolvedBucket.isDangling()) { return bucketInfo; } - if (getAclsEnabled()) { - try { - omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, - ACLType.READ, resolvedBucket.realVolume(), - resolvedBucket.realBucket(), null); - } catch (OMException e) { - if (e.getResult() == PERMISSION_DENIED) { - return bucketInfo; - } - throw e; - } - } OmBucketInfo realBucket = getResolvedSourceBucket(resolvedBucket, resolvedSourceCache); - return bucketInfo.withOperationalPropertiesFrom(realBucket); + return realBucket != null + ? bucketInfo.withOperationalPropertiesFrom(realBucket) + : bucketInfo; } private OmBucketInfo getResolvedSourceBucket( @@ -3090,6 +3080,18 @@ private OmBucketInfo getResolvedSourceBucket( return cachedSource; } } + if (getAclsEnabled()) { + try { + omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, + ACLType.READ, resolvedBucket.realVolume(), + resolvedBucket.realBucket(), null); + } catch (OMException e) { + if (e.getResult() == PERMISSION_DENIED) { + return null; + } + throw e; + } + } OmBucketInfo realBucket = bucketManager.getBucketInfo( resolvedBucket.realVolume(), resolvedBucket.realBucket()); diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java index 7d4c2806e570..43e47706ee80 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -598,6 +599,34 @@ void testListBucketsDoesNotCopySourcePropertiesWithoutReadAccess() throws Except verify(metadataReader).checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); } + @Test + void testListBucketsChecksSourceReadAccessOncePerSource() throws Exception { + String linkVolume = volumeName(); + String sourceVolume = volumeName(); + OmBucketInfo firstLink = createLinkBucketInfo(linkVolume, sourceVolume) + .toBuilder() + .setBucketName("link1") + .build(); + OmBucketInfo secondLink = createLinkBucketInfo(linkVolume, sourceVolume) + .toBuilder() + .setBucketName("link2") + .build(); + OmBucketInfo source = createSourceBucketInfo(sourceVolume); + BucketManager bucketManager = mock(BucketManager.class); + when(bucketManager.listBuckets(linkVolume, "", "", 100, false)) + .thenReturn(new ArrayList<>(Arrays.asList(firstLink, secondLink))); + when(bucketManager.getBucketInfo(sourceVolume, "source")).thenReturn(source); + OmMetadataReader metadataReader = mock(OmMetadataReader.class); + OzoneManager omSpy = createAclEnabledOmSpy(bucketManager, metadataReader); + + List result = omSpy.listBuckets(linkVolume, "", "", 100, false); + + assertEquals("sourceValue", result.get(0).getMetadata().get("sourceKey")); + assertEquals("sourceValue", result.get(1).getMetadata().get("sourceKey")); + verify(metadataReader, times(1)) + .checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); + } + private static OmBucketInfo createLinkBucketInfo(String linkVolume, String sourceVolume) { return OmBucketInfo.newBuilder() .setVolumeName(linkVolume) From 18abc0b34915205fa92fe0a0f0421cb5572cee14 Mon Sep 17 00:00:00 2001 From: Siyao Meng <50227127+smengcl@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:45:01 -0700 Subject: [PATCH 3/5] findbugs --- .../hadoop/ozone/om/TestBucketManagerImpl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java index 43e47706ee80..dde4627e0013 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java @@ -570,8 +570,11 @@ void testGetBucketInfoDoesNotCopySourcePropertiesWithoutReadAccess() throws Exce OmBucketInfo result = omSpy.getBucketInfo(linkVolume, "link"); - assertSame(link, result); + assertThat(result.getMetadata()) + .containsEntry("linkKey", "linkValue") + .doesNotContainKey("sourceKey"); verify(metadataReader).checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); + verify(bucketManager, times(1)).getBucketInfo(sourceVolume, "source"); } @Test @@ -594,9 +597,12 @@ void testListBucketsDoesNotCopySourcePropertiesWithoutReadAccess() throws Except List result = omSpy.listBuckets(linkVolume, "", "", 100, false); - assertSame(link, result.get(0)); + assertThat(result.get(0).getMetadata()) + .containsEntry("linkKey", "linkValue") + .doesNotContainKey("sourceKey"); assertSame(regular, result.get(1)); verify(metadataReader).checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); + verify(bucketManager, times(1)).getBucketInfo(sourceVolume, "source"); } @Test @@ -658,7 +664,7 @@ private OzoneManager createAclEnabledOmSpy(BucketManager bucketManager, OmMetada OzoneManager omSpy = spy(omTestManagers.getOzoneManager()); HddsWhiteboxTestUtils.setInternalState(omSpy, "bucketManager", bucketManager); HddsWhiteboxTestUtils.setInternalState(omSpy, "omMetadataReader", metadataReader); - doReturn(true).when(omSpy).getAclsEnabled(); + when(omSpy.getAclsEnabled()).thenReturn(true); AuditMessage auditMessage = mock(AuditMessage.class); when(auditMessage.getOp()).thenReturn("READ_BUCKET"); doReturn(auditMessage).when(omSpy).buildAuditMessageForSuccess(any(), anyMap()); From 8d8a1c0b260dfa06002eabf044f7738e66203a1c Mon Sep 17 00:00:00 2001 From: Siyao Meng <50227127+smengcl@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:08:10 -0700 Subject: [PATCH 4/5] Address comments --- .../hadoop/ozone/om/helpers/OmBucketInfo.java | 12 ++++++------ .../apache/hadoop/ozone/om/OzoneManager.java | 17 ++++------------- .../hadoop/ozone/om/TestBucketManagerImpl.java | 17 +++++++++-------- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java index 09a31883dd19..639dbaa9f392 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmBucketInfo.java @@ -406,19 +406,19 @@ public Builder toBuilder() { */ public OmBucketInfo withOperationalPropertiesFrom(OmBucketInfo source) { return toBuilder() - .setDefaultReplicationConfig(source.getDefaultReplicationConfig()) - .setBucketEncryptionKey(source.getEncryptionKeyInfo()) - .setIsVersionEnabled(source.getIsVersionEnabled()) .setStorageType(source.getStorageType()) - .setQuotaInBytes(source.getQuotaInBytes()) - .setQuotaInNamespace(source.getQuotaInNamespace()) + .setIsVersionEnabled(source.getIsVersionEnabled()) + .setBucketEncryptionKey(source.getEncryptionKeyInfo()) .setUsedBytes(source.getUsedBytes()) .setUsedNamespace(source.getUsedNamespace()) + .setQuotaInBytes(source.getQuotaInBytes()) + .setQuotaInNamespace(source.getQuotaInNamespace()) .setSnapshotUsedBytes(source.getSnapshotUsedBytes()) .setSnapshotUsedNamespace(source.getSnapshotUsedNamespace()) - .addAllMetadata(source.getMetadata()) .setBucketLayout(source.getBucketLayout()) + .setDefaultReplicationConfig(source.getDefaultReplicationConfig()) .setTags(source.getTags()) + .addAllMetadata(source.getMetadata()) .build(); } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index f1b44bc427e3..9897a26e7366 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -3062,9 +3062,7 @@ private OmBucketInfo enrichLinkBucketInfo( return bucketInfo; } OmBucketInfo realBucket = getResolvedSourceBucket(resolvedBucket, resolvedSourceCache); - return realBucket != null - ? bucketInfo.withOperationalPropertiesFrom(realBucket) - : bucketInfo; + return bucketInfo.withOperationalPropertiesFrom(realBucket); } private OmBucketInfo getResolvedSourceBucket( @@ -3081,16 +3079,9 @@ private OmBucketInfo getResolvedSourceBucket( } } if (getAclsEnabled()) { - try { - omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, - ACLType.READ, resolvedBucket.realVolume(), - resolvedBucket.realBucket(), null); - } catch (OMException e) { - if (e.getResult() == PERMISSION_DENIED) { - return null; - } - throw e; - } + omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, + ACLType.READ, resolvedBucket.realVolume(), + resolvedBucket.realBucket(), null); } OmBucketInfo realBucket = bucketManager.getBucketInfo( resolvedBucket.realVolume(), diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java index dde4627e0013..b9f6121fdbfe 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java @@ -31,7 +31,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -556,7 +556,7 @@ public void testListBucketsResolvesFsoAndObsLinkLayouts() throws Exception { } @Test - void testGetBucketInfoDoesNotCopySourcePropertiesWithoutReadAccess() throws Exception { + void testGetBucketInfoFailsWithoutSourceReadAccess() throws Exception { String linkVolume = volumeName(); String sourceVolume = volumeName(); OmBucketInfo link = createLinkBucketInfo(linkVolume, sourceVolume); @@ -568,11 +568,10 @@ void testGetBucketInfoDoesNotCopySourcePropertiesWithoutReadAccess() throws Exce denySourceRead(metadataReader, sourceVolume); OzoneManager omSpy = createAclEnabledOmSpy(bucketManager, metadataReader); - OmBucketInfo result = omSpy.getBucketInfo(linkVolume, "link"); + OMException exception = assertThrows(OMException.class, + () -> omSpy.getBucketInfo(linkVolume, "link")); - assertThat(result.getMetadata()) - .containsEntry("linkKey", "linkValue") - .doesNotContainKey("sourceKey"); + assertEquals(ResultCodes.PERMISSION_DENIED, exception.getResult()); verify(metadataReader).checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); verify(bucketManager, times(1)).getBucketInfo(sourceVolume, "source"); } @@ -666,8 +665,10 @@ private OzoneManager createAclEnabledOmSpy(BucketManager bucketManager, OmMetada HddsWhiteboxTestUtils.setInternalState(omSpy, "omMetadataReader", metadataReader); when(omSpy.getAclsEnabled()).thenReturn(true); AuditMessage auditMessage = mock(AuditMessage.class); - when(auditMessage.getOp()).thenReturn("READ_BUCKET"); - doReturn(auditMessage).when(omSpy).buildAuditMessageForSuccess(any(), anyMap()); + lenient().when(auditMessage.getOp()).thenReturn("READ_BUCKET"); + lenient().doReturn(auditMessage).when(omSpy).buildAuditMessageForSuccess(any(), anyMap()); + lenient().doReturn(auditMessage).when(omSpy) + .buildAuditMessageForFailure(any(), anyMap(), any(Throwable.class)); return omSpy; } } From 4b2707f36b7f1058903c4fe61e43b813ea2d2591 Mon Sep 17 00:00:00 2001 From: Siyao Meng <50227127+smengcl@users.noreply.github.com> Date: Fri, 24 Jul 2026 16:37:43 -0700 Subject: [PATCH 5/5] Restore contract --- .../apache/hadoop/ozone/om/OzoneManager.java | 17 +++++++++++++---- .../hadoop/ozone/om/TestBucketManagerImpl.java | 9 +++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index 9897a26e7366..f1b44bc427e3 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -3062,7 +3062,9 @@ private OmBucketInfo enrichLinkBucketInfo( return bucketInfo; } OmBucketInfo realBucket = getResolvedSourceBucket(resolvedBucket, resolvedSourceCache); - return bucketInfo.withOperationalPropertiesFrom(realBucket); + return realBucket != null + ? bucketInfo.withOperationalPropertiesFrom(realBucket) + : bucketInfo; } private OmBucketInfo getResolvedSourceBucket( @@ -3079,9 +3081,16 @@ private OmBucketInfo getResolvedSourceBucket( } } if (getAclsEnabled()) { - omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, - ACLType.READ, resolvedBucket.realVolume(), - resolvedBucket.realBucket(), null); + try { + omMetadataReader.checkAcls(ResourceType.BUCKET, StoreType.OZONE, + ACLType.READ, resolvedBucket.realVolume(), + resolvedBucket.realBucket(), null); + } catch (OMException e) { + if (e.getResult() == PERMISSION_DENIED) { + return null; + } + throw e; + } } OmBucketInfo realBucket = bucketManager.getBucketInfo( resolvedBucket.realVolume(), diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java index b9f6121fdbfe..6252836b57de 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java @@ -556,7 +556,7 @@ public void testListBucketsResolvesFsoAndObsLinkLayouts() throws Exception { } @Test - void testGetBucketInfoFailsWithoutSourceReadAccess() throws Exception { + void testGetBucketInfoReturnsLinkWithoutSourceReadAccess() throws Exception { String linkVolume = volumeName(); String sourceVolume = volumeName(); OmBucketInfo link = createLinkBucketInfo(linkVolume, sourceVolume); @@ -568,10 +568,11 @@ void testGetBucketInfoFailsWithoutSourceReadAccess() throws Exception { denySourceRead(metadataReader, sourceVolume); OzoneManager omSpy = createAclEnabledOmSpy(bucketManager, metadataReader); - OMException exception = assertThrows(OMException.class, - () -> omSpy.getBucketInfo(linkVolume, "link")); + OmBucketInfo result = omSpy.getBucketInfo(linkVolume, "link"); - assertEquals(ResultCodes.PERMISSION_DENIED, exception.getResult()); + assertThat(result.getMetadata()) + .containsEntry("linkKey", "linkValue") + .doesNotContainKey("sourceKey"); verify(metadataReader).checkAcls(BUCKET, OZONE, READ, sourceVolume, "source", null); verify(bucketManager, times(1)).getBucketInfo(sourceVolume, "source"); }