Skip to content

Commit e8311fb

Browse files
committed
CLDSRV-908: populate dataStoreETag on the CopyObject recompute path
The recompute path writes the destination through plain data.put, which (unlike data.copyObject, used by the propagate path) does not return a dataStoreETag, so the destination's location entry was stored without one. Nothing breaks visibly — partNumber readers fall back to treating the copy as a single part — but it left the recompute path as the only data-writing path producing legacy ETag-less locations and dropped the written-bytes MD5 that integrity/audit tooling relies on. Read the MD5 from data.put's hashedStream callback argument (completedHash, which the arsenal wrapper guarantees is set before the callback fires) and stamp the location as 1:<md5>, matching createAndStoreObject. Falls back to dataRetrievalInfo.dataStoreETag for external backends. Adds a unit test asserting the recomputed destination location carries the 1:-prefixed MD5.
1 parent e6ae5ae commit e8311fb

2 files changed

Lines changed: 25 additions & 2 deletions

File tree

lib/api/objectCopy.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ function _recomputeChecksumAndStore(log, request, requestedAlgo, sourceObjMD, da
234234
const doPut = cipherBundle => data.put(
235235
cipherBundle, checksumStream, storeMetadataParams.size,
236236
dataStoreContext, backendInfoDest, log,
237-
(err, dataRetrievalInfo) => {
237+
(err, dataRetrievalInfo, hashedStream) => {
238238
if (err) {
239239
return done(err);
240240
}
@@ -244,13 +244,16 @@ function _recomputeChecksumAndStore(log, request, requestedAlgo, sourceObjMD, da
244244
value: checksumStream.digest,
245245
type: 'FULL_OBJECT',
246246
};
247+
// Prefix the part number like createAndStoreObject does, so
248+
// partNumber-based reads see the copy as a single-part object.
249+
const dataStoreETag = dataRetrievalInfo.dataStoreETag || hashedStream?.completedHash;
247250
const putResult = {
248251
key: dataRetrievalInfo.key,
249252
size: storeMetadataParams.size,
250253
start: 0,
251254
dataStoreName: dataRetrievalInfo.dataStoreName,
252255
dataStoreType: dataRetrievalInfo.dataStoreType,
253-
dataStoreETag: dataRetrievalInfo.dataStoreETag,
256+
dataStoreETag: dataStoreETag ? `1:${dataStoreETag}` : undefined,
254257
dataStoreVersionId:
255258
dataRetrievalInfo.dataStoreVersionId,
256259
};

tests/unit/api/objectCopy.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const assert = require('assert');
22
const async = require('async');
3+
const crypto = require('crypto');
34
const { Readable } = require('stream');
45
const { storage, versioning } = require('arsenal');
56
const sinon = require('sinon');
@@ -974,6 +975,25 @@ describe('objectCopy checksum recompute', () => {
974975
}, done);
975976
});
976977

978+
it('should store a part-prefixed dataStoreETag on the recomputed destination location', done => {
979+
// The recompute path writes the destination through data.put, which
980+
// does not return a dataStoreETag; it must be filled in from the MD5
981+
// of the streamed bytes, prefixed like createAndStoreObject does.
982+
const expectedMD5 = crypto.createHash('md5').update(objData[0]).digest('hex');
983+
const req = _createObjectCopyRequest(destBucketName, {
984+
'x-amz-checksum-algorithm': 'SHA256',
985+
});
986+
objectCopy(authInfo, req, sourceBucketName, objectKey, undefined, log, err => {
987+
assert.ifError(err);
988+
metadata.getObjectMD(destBucketName, objectKey, {}, log, (err, md) => {
989+
assert.ifError(err);
990+
assert.strictEqual(md.location[0].dataStoreETag, `1:${expectedMD5}`,
991+
'recomputed destination location should carry a 1:-prefixed MD5 dataStoreETag');
992+
done();
993+
});
994+
});
995+
});
996+
977997
it('should reject an unknown x-amz-checksum-algorithm value with InvalidRequest', done => {
978998
const req = _createObjectCopyRequest(destBucketName, {
979999
'x-amz-checksum-algorithm': 'GARBAGE',

0 commit comments

Comments
 (0)