Skip to content

Commit 893b18b

Browse files
committed
disallow UploadPartCopy if the source is not restored
Signed-off-by: Utkarsh Srivastava <[email protected]> add tests Signed-off-by: Utkarsh Srivastava <[email protected]>
1 parent 23e6749 commit 893b18b

File tree

2 files changed

+50
-22
lines changed

2 files changed

+50
-22
lines changed

src/sdk/namespace_fs.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,16 @@ class NamespaceFS {
10721072
dbg.warn('NamespaceFS.read_object_stream mismatch version_id', params.version_id, this._get_version_id_by_xattr(stat));
10731073
throw error_utils.new_error_code('MISMATCH_VERSION', 'file version does not match the version we asked for');
10741074
}
1075+
1076+
// Disallow read if the object is in Glacier storage class and isn't restored
1077+
const obj_storage_class = Glacier.storage_class_from_xattr(stat.xattr);
1078+
const obj_restore_status = Glacier.get_restore_status(stat.xattr, new Date(), file_path);
1079+
if (obj_storage_class === s3_utils.STORAGE_CLASS_GLACIER) {
1080+
if (obj_restore_status?.ongoing || !obj_restore_status?.expiry_time) {
1081+
dbg.warn('read_object_stream: object is not restored yet', obj_restore_status);
1082+
throw new S3Error(S3Error.InvalidObjectState);
1083+
}
1084+
}
10751085
break;
10761086
} catch (err) {
10771087
dbg.warn(`NamespaceFS.read_object_stream: retrying retries=${retries} file_path=${file_path}`, err);

src/test/unit_tests/nsfs/test_nsfs_glacier_backend.js

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const { Glacier } = require('../../../sdk/glacier');
2020
const { Semaphore } = require('../../../util/semaphore');
2121
const nb_native = require('../../../util/nb_native');
2222
const { handler: s3_get_bucket } = require('../../../endpoint/s3/ops/s3_get_bucket');
23+
const Stream = require('stream');
2324

2425
const inspect = (x, max_arr = 5) => util.inspect(x, { colors: true, depth: null, maxArrayLength: max_arr });
2526

@@ -80,6 +81,12 @@ function assert_date(date, from, expected, tz = 'LOCAL') {
8081
}
8182
}
8283

84+
class NoOpWriteStream extends Stream.Writable {
85+
_write(chunk, encoding, callback) {
86+
callback();
87+
}
88+
}
89+
8390
/* Justification: Disable max-lines-per-function for test functions
8491
as it is not much helpful in the sense that "describe" function capture
8592
entire test suite instead of being a logical abstraction */
@@ -198,42 +205,53 @@ mocha.describe('nsfs_glacier', function() {
198205
assert(found);
199206
});
200207

201-
mocha.it('restore-object should successfully restore', async function() {
208+
mocha.it('restore-object should successfully restore', async function() {
202209
const now = Date.now();
203210
const data = crypto.randomBytes(100);
204-
const params = {
211+
const params = {
205212
bucket: upload_bkt,
206213
key: restore_key,
207-
storage_class: s3_utils.STORAGE_CLASS_GLACIER,
214+
storage_class: s3_utils.STORAGE_CLASS_GLACIER,
208215
xattr,
209-
days: 1,
216+
days: 1,
210217
source_stream: buffer_utils.buffer_to_read_stream(data)
211218
};
212219

213220
const upload_res = await glacier_ns.upload_object(params, dummy_object_sdk);
214221
console.log('upload_object response', inspect(upload_res));
215222

216-
const restore_res = await glacier_ns.restore_object(params, dummy_object_sdk);
217-
assert(restore_res);
223+
const dummy_stream_writer = new NoOpWriteStream();
218224

219-
// Issue restore
220-
await NamespaceFS.restore_wal._process(async file => {
221-
const fs_context = glacier_ns.prepare_fs_context(dummy_object_sdk);
222-
await backend.restore(fs_context, file);
225+
// Ensure that we can't read the object before restore
226+
await assert.rejects(glacier_ns.read_object_stream(params, dummy_object_sdk, dummy_stream_writer));
223227

224-
// Don't delete the file
225-
return false;
226-
});
228+
const restore_res = await glacier_ns.restore_object(params, dummy_object_sdk);
229+
assert(restore_res);
230+
231+
// Ensure that we can't read the object before restore
232+
await assert.rejects(glacier_ns.read_object_stream(params, dummy_object_sdk, dummy_stream_writer));
233+
234+
// Issue restore
235+
await NamespaceFS.restore_wal._process(async file => {
236+
const fs_context = glacier_ns.prepare_fs_context(dummy_object_sdk);
237+
await backend.restore(fs_context, file);
227238

228-
// Ensure object is restored
229-
const md = await glacier_ns.read_object_md(params, dummy_object_sdk);
239+
// Don't delete the file
240+
return false;
241+
});
230242

231-
assert(!md.restore_status.ongoing);
243+
// Ensure object is restored
244+
const md = await glacier_ns.read_object_md(params, dummy_object_sdk);
232245

233-
const expected_expiry = Glacier.generate_expiry(new Date(), params.days, '', config.NSFS_GLACIER_EXPIRY_TZ);
234-
assert(expected_expiry.getTime() >= md.restore_status.expiry_time.getTime());
235-
assert(now <= md.restore_status.expiry_time.getTime());
236-
});
246+
assert(!md.restore_status.ongoing);
247+
248+
const expected_expiry = Glacier.generate_expiry(new Date(), params.days, '', config.NSFS_GLACIER_EXPIRY_TZ);
249+
assert(expected_expiry.getTime() >= md.restore_status.expiry_time.getTime());
250+
assert(now <= md.restore_status.expiry_time.getTime());
251+
252+
// Ensure that we can't read the object before restore
253+
await assert.doesNotReject(glacier_ns.read_object_stream(params, dummy_object_sdk, dummy_stream_writer));
254+
});
237255

238256
mocha.it('restore-object should not restore failed item', async function() {
239257
const now = Date.now();
@@ -316,10 +334,10 @@ mocha.describe('nsfs_glacier', function() {
316334
assert(failure_stats.xattr[Glacier.XATTR_RESTORE_REQUEST]);
317335
});
318336

319-
mocha.it('_finalize_restore should tolerate deleted objects', async function() {
337+
mocha.it('_finalize_restore should tolerate deleted objects', async function() {
320338
// should not throw error if the path does not exist
321339
await backend._finalize_restore(glacier_ns.prepare_fs_context(dummy_object_sdk), '/path/does/not/exist');
322-
});
340+
});
323341

324342
mocha.it('generate_expiry should round up the expiry', function() {
325343
const now = new Date();

0 commit comments

Comments
 (0)