function standardMetadataValidateBucketAndObj(params, actionImplicitDenies, log, callback) {
    const { authInfo, bucketName, objectKey, versionId, getDeleteMarker, request, withVersionId } = params;
    let requestType = params.requestType;
    if (!Array.isArray(requestType)) {
        requestType = [requestType];
    }
    async.waterfall([
        next => {
            // versionId may be 'null', which asks metadata to fetch the null key specifically
            const getOptions = { versionId };
            if (getDeleteMarker) {
                getOptions.getDeleteMarker = true;
            }
            return metadata.getBucketAndObjectMD(bucketName, objectKey, getOptions, log, (err, getResult) => {
                if (err) {
                    // if some implicit iamAuthzResults, return AccessDenied
                    // before leaking any state information
                    if (actionImplicitDenies && Object.values(actionImplicitDenies).some(v => v === true)) {
                        return next(errors.AccessDenied);
                    }
                    return next(err);
                }
                return next(null, getResult);
            });
        },
        (getResult, next) => {
            const bucket = getResult.bucket ?
                  BucketInfo.deSerialize(getResult.bucket) : undefined;
            if (!bucket) {
                log.debug('bucketAttrs is undefined', {
                    bucket: bucketName,
                    method: 'metadataValidateBucketAndObj',
                });
                return next(errors.NoSuchBucket);
            }
            const validationError = validateBucket(bucket, params, log, actionImplicitDenies);
            if (validationError) {
                return next(validationError, bucket);
            }
            const objMD = getResult.obj ? JSON.parse(getResult.obj) : undefined;
            if (!objMD && versionId === 'null') {
                return getNullVersionFromMaster(bucketName, objectKey, log,
                     (err, nullVer) => next(err, bucket, nullVer));
            }
            return next(null, bucket, objMD);
        },
        (bucket, objMD, next) => {
            const canonicalID = authInfo.getCanonicalID();
            if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, log, request,
                actionImplicitDenies)) {
                log.debug('access denied for user on object', { requestType });
                return next(errors.AccessDenied, bucket);
            }
            return next(null, bucket, objMD);
        },
        (bucket, objMD, next) => {
            const needQuotaCheck = requestType => requestType.some(type => actionNeedQuotaCheck[type] ||
                actionWithDataDeletion[type]);
            const checkQuota = params.checkQuota === undefined ? needQuotaCheck(requestType) : params.checkQuota;
            // withVersionId cover cases when an object is being restored with a specific version ID.
            // In this case, the storage space was already accounted for when the RestoreObject API call
            // was made, so we don't need to add any inflight, but quota must be evaluated.
            if (!checkQuota) {
                return next(null, bucket, objMD);
            }
            const contentLength = processBytesToWrite(request.apiMethod, bucket, versionId,
                request?.parsedContentLength || 0, objMD, params.destObjMD);
            return validateQuotas(request, bucket, request.accountQuotas, requestType, request.apiMethod,
                contentLength, withVersionId, log, err => next(err, bucket, objMD));
        },
    ], (err, bucket, objMD) => {
        if (err) {
            // still return bucket for cors headers
            return callback(err, bucket);
        }
        return callback(null, bucket, objMD);
    });
}
standardMetadataValidateBucketAndObj - retrieve bucket and object md from metadata
and check if user is authorized to access them.
@param {object} params - function parameters
@param {AuthInfo} params.authInfo - AuthInfo class instance, requester's info
@param {string} params.bucketName - name of bucket
@param {string} params.objectKey - name of object
@param {string} [params.versionId] - version id if getting specific version
@param {string} params.requestType - type of request
@param {object} params.request - http request object
@param {boolean} actionImplicitDenies - identity authorization results
@param {RequestLogger} log - request logger
@param {function} callback - callback
@return {undefined} - and call callback with params err, bucket md