-
Notifications
You must be signed in to change notification settings - Fork 255
Expand file tree
/
Copy pathstoreObject.js
More file actions
91 lines (87 loc) · 3.81 KB
/
storeObject.js
File metadata and controls
91 lines (87 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
const { errors, jsutil } = require('arsenal');
const { data } = require('../../../data/wrapper');
const { prepareStream, stripTrailingChecksumStream } = require('./prepareStream');
/**
* Check that `hashedStream.completedHash` matches header `stream.contentMD5`
* and delete old data or remove 'hashed' listeners, if applicable.
* @param {object} stream - stream containing the data
* @param {object} hashedStream - instance of MD5Sum
* @param {object} dataRetrievalInfo - object containing the keys of stored data
* @param {number} dataRetrievalInfo.key - key of the stored data
* @param {string} dataRetrievalInfo.dataStoreName - the implName of the data
* @param {object} log - request logger instance
* @param {function} cb - callback to send error or move to next task
* @return {function} - calls callback with arguments:
* error, dataRetrievalInfo, and completedHash (if any)
*/
function checkHashMatchMD5(stream, hashedStream, dataRetrievalInfo, log, cb) {
const contentMD5 = stream.contentMD5;
const completedHash = hashedStream.completedHash;
if (contentMD5 && completedHash && contentMD5 !== completedHash) {
log.debug('contentMD5 and completedHash do not match, deleting data', {
method: 'storeObject::dataStore',
completedHash,
contentMD5,
});
const dataToDelete = [];
dataToDelete.push(dataRetrievalInfo);
return data.batchDelete(dataToDelete, null, null, log, err => {
if (err) {
// failure of batch delete is only logged, client gets the
// error code about the md mismatch
log.error('error deleting old data', { error: err });
}
return cb(errors.BadDigest);
});
}
return cb(null, dataRetrievalInfo, completedHash);
}
/**
* Stores object and responds back with key and storage type
* @param {object} objectContext - object's keyContext for sproxyd Key
* computation (put API)
* @param {object} cipherBundle - cipher bundle that encrypt the data
* @param {object} stream - the stream containing the data
* @param {number} size - data size in the stream
* @param {object | null } streamingV4Params - if v4 auth, object containing
* accessKey, signatureFromRequest, region, scopeDate, timestamp, and
* credentialScope (to be used for streaming v4 auth if applicable)
* @param {BackendInfo} backendInfo - info to determine which data
* backend to use
* @param {RequestLogger} log - the current stream logger
* @param {function} cb - callback containing result for the next task
* @return {undefined}
*/
function dataStore(objectContext, cipherBundle, stream, size,
streamingV4Params, backendInfo, log, cb) {
const cbOnce = jsutil.once(cb);
const dataStreamTmp = prepareStream(stream, streamingV4Params, log, cbOnce);
if (!dataStreamTmp) {
return process.nextTick(() => cb(errors.InvalidArgument));
}
const dataStream = stripTrailingChecksumStream(dataStreamTmp, log, cbOnce);
return data.put(
cipherBundle, dataStream, size, objectContext, backendInfo, log,
(err, dataRetrievalInfo, hashedStream) => {
if (err) {
log.error('error in datastore', {
error: err,
});
return cbOnce(err);
}
if (!dataRetrievalInfo) {
log.fatal('data put returned neither an error nor a key', {
method: 'storeObject::dataStore',
});
return cbOnce(errors.InternalError);
}
log.trace('dataStore: backend stored key', {
dataRetrievalInfo,
});
return checkHashMatchMD5(stream, hashedStream,
dataRetrievalInfo, log, cbOnce);
});
}
module.exports = {
dataStore,
};