Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hadoop-ozone/dist/src/main/smoketest/s3/mpu_lib.robot
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Upload MPU part
IF '${expected_rc}' == '0'
Should contain ${result} ETag
${etag} = Execute echo '${result}' | jq -r '.ETag'
${etag} = Replace String ${etag} \" ${EMPTY}
${md5sum} = Execute md5sum ${file} | awk '{print $1}'
Should Be Equal As Strings ${etag} ${md5sum}
RETURN ${etag}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
import org.apache.hadoop.ozone.s3.MultiS3GatewayService;
import org.apache.hadoop.ozone.s3.S3ClientFactory;
import org.apache.hadoop.ozone.s3.awssdk.S3SDKTestUtils;
import org.apache.hadoop.ozone.s3.endpoint.ObjectEndpoint;
import org.apache.hadoop.ozone.s3.endpoint.S3Owner;
import org.apache.hadoop.ozone.s3.util.S3Consts;
import org.apache.hadoop.security.UserGroupInformation;
Expand Down Expand Up @@ -1231,7 +1232,7 @@ private void completeMPU(String keyName, String uploadId, List<PartETag> complet
for (PartETag part : completedParts) {
completionXml.append(" <Part>\n");
completionXml.append(" <PartNumber>").append(part.getPartNumber()).append("</PartNumber>\n");
completionXml.append(" <ETag>").append(part.getETag()).append("</ETag>\n");
completionXml.append(" <ETag>").append(ObjectEndpoint.stripQuotes(part.getETag())).append("</ETag>\n");
completionXml.append(" </Part>\n");
}
completionXml.append("</CompleteMultipartUpload>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import org.apache.hadoop.ozone.s3.MultiS3GatewayService;
import org.apache.hadoop.ozone.s3.S3ClientFactory;
import org.apache.hadoop.ozone.s3.awssdk.S3SDKTestUtils;
import org.apache.hadoop.ozone.s3.endpoint.ObjectEndpoint;
import org.apache.hadoop.ozone.s3.endpoint.S3Owner;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.ozone.test.OzoneTestBase;
Expand Down Expand Up @@ -970,7 +971,7 @@ private String buildCompleteMultipartUploadXml(List<CompletedPart> parts) {
for (CompletedPart part : parts) {
xml.append(" <Part>\n");
xml.append(" <PartNumber>").append(part.partNumber()).append("</PartNumber>\n");
xml.append(" <ETag>").append(part.eTag()).append("</ETag>\n");
xml.append(" <ETag>").append(ObjectEndpoint.stripQuotes(part.eTag())).append("</ETag>\n");
xml.append(" </Part>\n");
}
xml.append("</CompleteMultipartUpload>");
Expand Down Expand Up @@ -1142,11 +1143,11 @@ private List<CompletedPart> uploadParts(String bucketName, String key, String up
RequestBody.fromByteBuffer(bb));

assertEquals(DatatypeConverter.printHexBinary(
calculateDigest(fileInputStream, 0, partSize)).toLowerCase(), partResponse.eTag());
calculateDigest(fileInputStream, 0, partSize)).toLowerCase(), ObjectEndpoint.stripQuotes(partResponse.eTag()));

CompletedPart part = CompletedPart.builder()
.partNumber(partNumber)
.eTag(partResponse.eTag())
.eTag(ObjectEndpoint.stripQuotes(partResponse.eTag()))
.build();
completedParts.add(part);

Expand Down Expand Up @@ -1643,7 +1644,7 @@ public void testCompleteMultipartUpload() {

CompletedMultipartUpload completedUpload = CompletedMultipartUpload.builder()
.parts(
CompletedPart.builder().partNumber(1).eTag(uploadPartResponse.eTag()).build()
CompletedPart.builder().partNumber(1).eTag(ObjectEndpoint.stripQuotes(uploadPartResponse.eTag())).build()
).build();


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ public Response completeMultipartUpload(@PathParam("bucket") String bucket,
S3Owner.verifyBucketOwnerCondition(headers, bucket, ozoneBucket.getOwner());

for (CompleteMultipartUploadRequest.Part part : partList) {
partsMap.put(part.getPartNumber(), part.getETag());
partsMap.put(part.getPartNumber(), stripQuotes(part.getETag()));
}
if (LOG.isDebugEnabled()) {
LOG.debug("Parts map {}", partsMap);
Expand Down Expand Up @@ -1065,6 +1065,10 @@ private Response createMultipartKey(OzoneVolume volume, OzoneBucket ozoneBucket,
new byte[getIOBufferSize(length)]);
ozoneOutputStream.getMetadata()
.putAll(sourceKeyDetails.getMetadata());
String raw = ozoneOutputStream.getMetadata().get(ETAG);
if (raw != null) {
ozoneOutputStream.getMetadata().put(ETAG, stripQuotes(raw));
}

Copilot AI Nov 4, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent ETag handling: The stripQuotes logic is only applied in the 'else' branch (lines 1070-1073) but not in the 'if (range != null)' branch (lines 1049-1059). Both branches copy source key metadata using putAll(), which means both should handle potentially quoted ETags consistently.

Copilot uses AI. Check for mistakes.
outputStream = ozoneOutputStream;
}
}
Expand Down Expand Up @@ -1099,6 +1103,7 @@ private Response createMultipartKey(OzoneVolume volume, OzoneBucket ozoneBucket,
if (StringUtils.isEmpty(eTag)) {
eTag = omMultipartCommitUploadPartInfo.getPartName();
}
eTag = wrapInQuotes(eTag);

if (copyHeader != null) {
getMetrics().updateCopyObjectSuccessStats(startNanos);
Expand Down Expand Up @@ -1518,6 +1523,13 @@ public boolean isDatastreamEnabled() {
return datastreamEnabled;
}

public static String stripQuotes(String value) {
if (value == null) {
return null;
}
return value.replaceAll("^\\\"|\\\"$", "");
}

static String wrapInQuotes(String value) {
return "\"" + value + "\"";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ public static Response createMultipartKey(OzoneBucket ozoneBucket, String key,
}
throw ex;
}
return Response.ok().header(OzoneConsts.ETAG, eTag).build();
return Response.ok()
.header(OzoneConsts.ETAG, ObjectEndpoint.wrapInQuotes(eTag))
.build();
}
}