function _prepMetadata(request, sourceObjMD, headers, sourceIsDestination,
    authInfo, objectKey, sourceBucketMD, destBucketMD, sourceVersionId, log) {
    let whichMetadata = headers['x-amz-metadata-directive'];
    // Default is COPY
    whichMetadata = whichMetadata === undefined ? 'COPY' : whichMetadata;
    if (whichMetadata !== 'COPY' && whichMetadata !== 'REPLACE') {
        return { error: errors.InvalidArgument };
    }
    let whichTagging = headers['x-amz-tagging-directive'];
    // Default is COPY
    whichTagging = whichTagging === undefined ? 'COPY' : whichTagging;
    if (whichTagging !== 'COPY' && whichTagging !== 'REPLACE') {
        return { error: errorInstances.InvalidArgument
          .customizeDescription('Unknown tagging directive') };
    }
    const overrideMetadata = {};
    if (headers['x-amz-server-side-encryption']) {
        overrideMetadata['x-amz-server-side-encryption'] =
            headers['x-amz-server-side-encryption'];
    }
    if (headers['x-amz-storage-class']) {  // TODO: remove in CLDSRV-639
        overrideMetadata['x-amz-storage-class'] =
            headers['x-amz-storage-class'];
    }
    if (headers['x-amz-website-redirect-location']) {
        overrideMetadata['x-amz-website-redirect-location'] =
            headers['x-amz-website-redirect-location'];
    }
    const retentionHeaders = headers['x-amz-object-lock-mode']
        && headers['x-amz-object-lock-retain-until-date'];
    const legalHoldHeader = headers['x-amz-object-lock-legal-hold'];
    if ((retentionHeaders || legalHoldHeader)
        && !destBucketMD.isObjectLockEnabled()) {
        return { error: errorInstances.InvalidRequest.customizeDescription(
            'Bucket is missing ObjectLockConfiguration') };
    }
    // Cannot copy from same source and destination if no MD
    // changed and no source version id
    if (sourceIsDestination && whichMetadata === 'COPY' &&
        Object.keys(overrideMetadata).length === 0 && !sourceVersionId) {
        return { error: errorInstances.InvalidRequest.customizeDescription('This copy' +
            ' request is illegal because it is trying to copy an ' +
            'object to itself without changing the object\'s metadata, ' +
            'storage class, website redirect location or encryption ' +
            'attributes.') };
    }
    // If COPY, pull all x-amz-meta keys/values from source object
    // Otherwise, pull all x-amz-meta keys/values from request headers
    const userMetadata = whichMetadata === 'COPY' ?
        getMetaHeaders(sourceObjMD) :
        getMetaHeaders(headers);
    if (userMetadata instanceof Error) {
        log.debug('user metadata validation failed', {
            error: userMetadata,
            method: 'objectCopy',
        });
        return { error: userMetadata };
    }
    // if the request occurs within a Zenko deployment, we place a user-metadata
    // field on the object
    applyZenkoUserMD(userMetadata);
    // If metadataDirective is:
    // - 'COPY' and source object has a location constraint in its metadata
    // we use the bucket destination location constraint
    if (whichMetadata === 'COPY'
        && userMetadata[locationHeader]
        && destBucketMD.getLocationConstraint()) {
        userMetadata[locationHeader] = destBucketMD.getLocationConstraint();
    }
    const backendInfoObjSource = locationConstraintCheck(request,
        sourceObjMD, sourceBucketMD, log);
    if (backendInfoObjSource.err) {
        return { error: backendInfoObjSource.err };
    }
    const sourceLocationConstraintName = backendInfoObjSource.controllingLC;

    const backendInfoObjDest = locationConstraintCheck(request,
        userMetadata, destBucketMD, log);
    if (backendInfoObjDest.err) {
        return { error: backendInfoObjSource.err };
    }
    const destLocationConstraintName = backendInfoObjDest.controllingLC;

    // If location constraint header is not included, locations match
    const locationMatch =
    sourceLocationConstraintName === destLocationConstraintName;

    // If tagging directive is REPLACE but you don't specify any
    // tags in the request, the destination object will
    // not have any tags.
    // If tagging directive is COPY but the source object does not have tags,
    // the destination object will not have any tags.
    let tagging;
    let taggingCopy;
    if (whichTagging === 'COPY') {
        taggingCopy = sourceObjMD.tags || {};
    } else {
        tagging = headers['x-amz-tagging'] || '';
    }

    // If COPY, pull the necessary headers from source object
    // Otherwise, pull them from request headers
    const headersToStoreSource = whichMetadata === 'COPY' ?
        sourceObjMD : headers;

    const storeMetadataParams = {
        objectKey,
        log,
        headers,
        authInfo,
        metaHeaders: userMetadata,
        size: sourceObjMD['content-length'],
        contentType: headersToStoreSource['content-type'],
        contentMD5: sourceObjMD['content-md5'],
        cacheControl: headersToStoreSource['cache-control'],
        contentDisposition: headersToStoreSource['content-disposition'],
        contentEncoding:
            removeAWSChunked(headersToStoreSource['content-encoding']),
        dataStoreName: destLocationConstraintName,
        expires: headersToStoreSource.expires,
        overrideMetadata,
        lastModifiedDate: new Date().toJSON(),
        tagging,
        taggingCopy,
        replicationInfo: getReplicationInfo(config,
            objectKey, destBucketMD, false, sourceObjMD['content-length']),
        locationMatch,
        originOp: 's3:ObjectCreated:Copy',
    };

    const defaultRetentionConfig = destBucketMD.getObjectLockConfiguration();
    if (defaultRetentionConfig && !legalHoldHeader) {
        storeMetadataParams.defaultRetention = defaultRetentionConfig;
    }

    // In case whichMetadata === 'REPLACE' but contentType is undefined in copy
    // request headers, make sure to keep the original header instead
    if (!storeMetadataParams.contentType) {
        storeMetadataParams.contentType = sourceObjMD['content-type'];
    }

    if (authInfo.getCanonicalID() !== destBucketMD.getOwner()) {
        storeMetadataParams.bucketOwnerId = destBucketMD.getOwner();
    }

    return { storeMetadataParams, sourceLocationConstraintName,
      backendInfoDest: backendInfoObjDest.backendInfo };
}
Preps metadata to be saved (based on copy or replace request header)
@param {object} request - request
@param {object} sourceObjMD - object md of source object
@param {object} headers - request headers
@param {boolean} sourceIsDestination - whether or not source is same as
destination
@param {AuthInfo} authInfo - authInfo from Vault
@param {string} objectKey - destination key name
@param {object} sourceBucketMD - bucket metadata of source bucket
@param {object} destBucketMD - bucket metadata of bucket being copied to
@param {string} sourceVersionId - versionId of source object for copy
@param {object} log - logger object
@return {object}
- (storeMetadataParams
- sourceLocationConstraintName {string} - location type of the source
- OR error