metadataStoreObject(bucketName, dataGetInfo, cipherBundle, params, cb) {
        const { objectKey, authInfo, size, contentMD5, metaHeaders,
            contentType, cacheControl, contentDisposition, contentEncoding,
            expires, multipart, headers, overrideMetadata, log,
            lastModifiedDate, versioning, versionId, uploadId,
            tagging, taggingCopy, replicationInfo, defaultRetention,
            dataStoreName, creationTime, retentionMode, retentionDate,
            legalHold, originOp, updateMicroVersionId, archive, oldReplayId,
            deleteNullKey, amzStorageClass, overheadField, needOplogUpdate,
            restoredEtag, bucketOwnerId } = params;
        log.trace('storing object in metadata');
        assert.strictEqual(typeof bucketName, 'string');
        const md = new ObjectMD();

        if (config.testingMode && metaHeaders && metaHeaders[constants.lastModifiedHeader]) {
            setLastModifiedFromHeader(md, metaHeaders);
        }

        // This should be object creator's canonical ID.
        md.setOwnerId(authInfo.getCanonicalID())
            .setKey(objectKey)
            .setCacheControl(cacheControl)
            .setContentDisposition(contentDisposition)
            .setContentEncoding(contentEncoding)
            .setExpires(expires)
            .setContentLength(size)
            .setContentType(contentType)
            .setContentMd5(contentMD5)
            .setLocation(dataGetInfo)
            // If an IAM user uploads a resource, the owner should be the parent
            // account. http://docs.aws.amazon.com/AmazonS3/
            // latest/dev/access-control-overview.html
            .setOwnerDisplayName(authInfo.getAccountDisplayName())
            .setDataStoreName(dataStoreName)
            // CreationTime needs to be carried over so that it remains static
            .setCreationTime(creationTime)
            .setOriginOp(originOp);
        // Sending in last modified date in object put copy since need
        // to return the exact date in the response
        if (lastModifiedDate) {
            md.setLastModified(lastModifiedDate);
        }
        if (cipherBundle) {
            md.setAmzServerSideEncryption(cipherBundle.algorithm);
            // configuredMasterKeyId takes precedence
            if (cipherBundle.configuredMasterKeyId || cipherBundle.masterKeyId) {
                md.setAmzEncryptionKeyId(cipherBundle.configuredMasterKeyId || cipherBundle.masterKeyId);
            }
        }
        if (headers && headers['x-amz-website-redirect-location']) {
            md.setRedirectLocation(headers['x-amz-website-redirect-location']);
        }
        if (headers) {
            // Stores retention information if object has its own retention
            // configuration or default retention configuration from its bucket
            const headerMode = headers['x-amz-object-lock-mode'];
            const headerDate = headers['x-amz-object-lock-retain-until-date'];
            const headerLegalHold = headers['x-amz-object-lock-legal-hold'];
            const objectRetention = headers && headerMode && headerDate;
            const objectLegalHold = headers && headerLegalHold;
            if (objectRetention || defaultRetention || objectLegalHold) {
                setObjectLockInformation(headers, md, defaultRetention);
            }
        }
        if (replicationInfo) {
            md.setReplicationInfo(replicationInfo);
        }
        // options to send to metadata to create or overwrite versions
        // when putting the object MD
        const options = {};
        if (versioning) {
            options.versioning = versioning;
        }
        if (versionId || versionId === '') {
            options.versionId = versionId;
        }
        if (needOplogUpdate) {
            options.needOplogUpdate = true;
            options.originOp = needOplogUpdate;
        }
        if (uploadId) {
            md.setUploadId(uploadId);
            options.replayId = uploadId;
        }
        // update microVersionId when overwriting metadata.
        if (updateMicroVersionId) {
            md.updateMicroVersionId();
        }
        // update restore
        if (archive) {
            md.setAmzStorageClass(amzStorageClass);
            md.setArchive(new ObjectMDArchive(
                archive.archiveInfo,
                archive.restoreRequestedAt,
                archive.restoreRequestedDays,
                archive.restoreCompletedAt,
                archive.restoreWillExpireAt));
            md.setAmzRestore({
                'ongoing-request': false,
                'expiry-date': archive.restoreWillExpireAt,
                'content-md5': restoredEtag,
            });
        }

        if (oldReplayId) {
            options.oldReplayId = oldReplayId;
        }

        if (deleteNullKey) {
            options.deleteNullKey = deleteNullKey;
        }

        if (overheadField) {
            options.overheadField = overheadField;
        }

        // information to store about the version and the null version id
        // in the object metadata

        // NOTE nullVersionId and nullUploadId are only maintained in
        // v0 metadata compatibility mode
        const { isNull, nullVersionId, nullUploadId, isDeleteMarker } = params;
        md.setIsNull(isNull)
            .setNullVersionId(nullVersionId)
            .setNullUploadId(nullUploadId)
            .setIsDeleteMarker(isDeleteMarker);
        if (versionId && versionId !== 'null') {
            md.setVersionId(versionId);
        }
        if (isNull && !config.nullVersionCompatMode) {
            md.setIsNull2(true);
        }
        if (taggingCopy) {
            // If copying tags to an object (taggingCopy) we do not
            // need to validate them again
            md.setTags(taggingCopy);
        } else if (tagging) {
            const validationTagRes = parseTagFromQuery(tagging);
            if (validationTagRes instanceof Error) {
                log.debug('tag validation failed', {
                    error: validationTagRes,
                    method: 'metadataStoreObject',
                });
                return process.nextTick(() => cb(validationTagRes));
            }
            md.setTags(validationTagRes);
        }

        // Store user provided metadata.
        // For multipart upload this also serves to transfer
        // over metadata originally sent with the initiation
        // of the multipart upload (for instance, ACL's).
        // Do NOT move this to before
        // the assignments of metadata above since this loop
        // will reassign some of the above values with the ones
        // from the intiation of the multipart upload
        // (for instance, storage class)
        md.setUserMetadata(metaHeaders);
        if (overrideMetadata) {
            md.overrideMetadataValues(overrideMetadata);
        }
        if (retentionMode && retentionDate) {
            md.setRetentionMode(retentionMode);
            md.setRetentionDate(retentionDate);
        }
        if (legalHold) {
            md.setLegalHold(legalHold);
        }
        if (params.acl) {
            // In case of a restore we dont pass ACL in the headers
            // but we take them from the old metadata
            md.setAcl(params.acl);
        }

        if (bucketOwnerId) {
            md.setBucketOwnerId(bucketOwnerId);
        }

        log.trace('object metadata', { omVal: md.getValue() });
        // If this is not the completion of a multipart upload or
        // the creation of a delete marker, parse the headers to
        // get the ACL's if any
        return async.waterfall([
            callback => {
                if (multipart || md.getIsDeleteMarker()) {
                    return callback();
                }
                const parseAclParams = {
                    headers,
                    resourceType: 'object',
                    acl: md.getAcl(),
                    log,
                };
                log.trace('parsing acl from headers');
                acl.parseAclFromHeaders(parseAclParams, (err, parsedACL) => {
                    if (err) {
                        log.debug('error parsing acl', { error: err });
                        return callback(err);
                    }
                    md.setAcl(parsedACL);
                    return callback();
                });
                return null;
            },
            callback => metadata.putObjectMD(bucketName, objectKey, md,
                    options, log, callback),
        ], (err, data) => {
            if (err) {
                log.error('error from metadata', { error: err });
                return cb(err);
            }
            log.trace('object successfully stored in metadata');
            // if versioning is enabled, data will be returned from metadata
            // as JSON containing a versionId which some APIs will need sent
            // back to them
            let versionId;
            if (data) {
                if (params.isNull && params.isDeleteMarker) {
                    versionId = 'null';
                } else if (!params.isNull) {
                    versionId = JSON.parse(data).versionId;
                }
            }
            return cb(err, {
                lastModified: md.getLastModified(),
                tags: md.getTags(),
                contentMD5,
                versionId,
            });
        });
    }
Stores object location, custom headers, version etc.
@param {object} bucketName - bucket in which metadata is stored
@param {array} dataGetInfo - array of objects with information to
retrieve data or null if 0 bytes object
@param {object} cipherBundle - server side encryption information
@param {object} params - custom built object containing resource details.
@param {function} cb - callback containing result for the next task
@return {function} executes callback with err or ETag as arguments