@@ -195,6 +195,8 @@ class TapeCloudUtils {
195
195
}
196
196
197
197
class TapeCloudGlacier extends Glacier {
198
+ static LOG_DELIM = ' -- ' ;
199
+
198
200
/**
199
201
* @param {nb.NativeFSContext } fs_context
200
202
* @param {LogFile } log_file
@@ -204,8 +206,14 @@ class TapeCloudGlacier extends Glacier {
204
206
async stage_migrate ( fs_context , log_file , failure_recorder ) {
205
207
dbg . log2 ( 'TapeCloudGlacier.stage_migrate starting for' , log_file . log_path ) ;
206
208
209
+ // Wrap failure recorder to make sure we correctly encode the entries
210
+ // before appending them to the failure log
211
+ const encoded_failure_recorder = async failure => failure_recorder ( this . encode_log ( failure ) ) ;
212
+
207
213
try {
208
214
await log_file . collect ( Glacier . MIGRATE_STAGE_WAL_NAME , async ( entry , batch_recorder ) => {
215
+ entry = this . decode_log ( entry ) ;
216
+
209
217
let entry_fh ;
210
218
let should_migrate = true ;
211
219
try {
@@ -234,7 +242,7 @@ class TapeCloudGlacier extends Glacier {
234
242
// Can't really do anything if this fails - provider
235
243
// needs to make sure that appropriate error handling
236
244
// is being done there
237
- await failure_recorder ( entry ) ;
245
+ await encoded_failure_recorder ( entry ) ;
238
246
return ;
239
247
}
240
248
@@ -244,14 +252,14 @@ class TapeCloudGlacier extends Glacier {
244
252
// Mark the file staged
245
253
try {
246
254
await entry_fh . replacexattr ( fs_context , { [ Glacier . XATTR_STAGE_MIGRATE ] : Date . now ( ) . toString ( ) } ) ;
247
- await batch_recorder ( entry ) ;
255
+ await batch_recorder ( this . encode_log ( entry ) ) ;
248
256
} catch ( error ) {
249
257
dbg . error ( 'failed to mark the entry migrate staged' , error ) ;
250
258
251
259
// Can't really do anything if this fails - provider
252
260
// needs to make sure that appropriate error handling
253
261
// is being done there
254
- await failure_recorder ( entry ) ;
262
+ await encoded_failure_recorder ( entry ) ;
255
263
} finally {
256
264
entry_fh ?. close ( fs_context ) ;
257
265
}
@@ -272,16 +280,23 @@ class TapeCloudGlacier extends Glacier {
272
280
*/
273
281
async migrate ( fs_context , log_file , failure_recorder ) {
274
282
dbg . log2 ( 'TapeCloudGlacier.migrate starting for' , log_file . log_path ) ;
283
+
284
+ // Wrap failure recorder to make sure we correctly encode the entries
285
+ // before appending them to the failure log
286
+ const encoded_failure_recorder = async failure => failure_recorder ( this . encode_log ( failure ) ) ;
287
+
275
288
try {
276
289
// This will throw error only if our eeadm error handler
277
290
// panics as well and at that point it's okay to
278
291
// not handle the error and rather keep the log file around
279
- await this . _migrate ( log_file . log_path , failure_recorder ) ;
292
+ await this . _migrate ( log_file . log_path , encoded_failure_recorder ) ;
280
293
281
294
// Un-stage all the files - We don't need to deal with the cases
282
295
// where some files have migrated and some have not as that is
283
296
// not important for staging/un-staging.
284
297
await log_file . collect_and_process ( async entry => {
298
+ entry = this . decode_log ( entry ) ;
299
+
285
300
let fh ;
286
301
try {
287
302
fh = await nb_native ( ) . fs . open ( fs_context , entry ) ;
@@ -297,7 +312,7 @@ class TapeCloudGlacier extends Glacier {
297
312
// Add the enty to the failure log - This could be wasteful as it might
298
313
// add entries which have already been migrated but this is a better
299
314
// retry.
300
- await failure_recorder ( entry ) ;
315
+ await encoded_failure_recorder ( entry ) ;
301
316
} finally {
302
317
await fh ?. close ( fs_context ) ;
303
318
}
@@ -319,8 +334,14 @@ class TapeCloudGlacier extends Glacier {
319
334
async stage_restore ( fs_context , log_file , failure_recorder ) {
320
335
dbg . log2 ( 'TapeCloudGlacier.stage_restore starting for' , log_file . log_path ) ;
321
336
337
+ // Wrap failure recorder to make sure we correctly encode the entries
338
+ // before appending them to the failure log
339
+ const encoded_failure_recorder = async failure => failure_recorder ( this . encode_log ( failure ) ) ;
340
+
322
341
try {
323
342
await log_file . collect ( Glacier . RESTORE_STAGE_WAL_NAME , async ( entry , batch_recorder ) => {
343
+ entry = this . decode_log ( entry ) ;
344
+
324
345
let fh ;
325
346
try {
326
347
fh = await nb_native ( ) . fs . open ( fs_context , entry ) ;
@@ -347,9 +368,9 @@ class TapeCloudGlacier extends Glacier {
347
368
// 3. If we read corrupt value then either the file is getting staged or is
348
369
// getting un-staged - In either case we must requeue.
349
370
if ( stat . xattr [ Glacier . XATTR_STAGE_MIGRATE ] ) {
350
- await failure_recorder ( entry ) ;
371
+ await encoded_failure_recorder ( entry ) ;
351
372
} else {
352
- await batch_recorder ( entry ) ;
373
+ await batch_recorder ( this . encode_log ( entry ) ) ;
353
374
}
354
375
} catch ( error ) {
355
376
if ( error . code === 'ENOENT' ) {
@@ -361,7 +382,7 @@ class TapeCloudGlacier extends Glacier {
361
382
'adding log entry' , entry ,
362
383
'to failure recorder due to error' , error ,
363
384
) ;
364
- await failure_recorder ( entry ) ;
385
+ await encoded_failure_recorder ( entry ) ;
365
386
} finally {
366
387
await fh ?. close ( fs_context ) ;
367
388
}
@@ -383,25 +404,32 @@ class TapeCloudGlacier extends Glacier {
383
404
async restore ( fs_context , log_file , failure_recorder ) {
384
405
dbg . log2 ( 'TapeCloudGlacier.restore starting for' , log_file . log_path ) ;
385
406
407
+ // Wrap failure recorder to make sure we correctly encode the entries
408
+ // before appending them to the failure log
409
+ const encoded_failure_recorder = async failure => failure_recorder ( this . encode_log ( failure ) ) ;
410
+
386
411
try {
387
412
const success = await this . _recall (
388
413
log_file . log_path ,
389
414
async entry_path => {
415
+ entry_path = this . decode_log ( entry_path ) ;
390
416
dbg . log2 ( 'TapeCloudGlacier.restore.partial_failure - entry:' , entry_path ) ;
391
- await failure_recorder ( entry_path ) ;
417
+ await encoded_failure_recorder ( entry_path ) ;
392
418
} ,
393
419
async entry_path => {
420
+ entry_path = this . decode_log ( entry_path ) ;
394
421
dbg . log2 ( 'TapeCloudGlacier.restore.partial_success - entry:' , entry_path ) ;
395
- await this . _finalize_restore ( fs_context , entry_path , failure_recorder ) ;
422
+ await this . _finalize_restore ( fs_context , entry_path , encoded_failure_recorder ) ;
396
423
}
397
424
) ;
398
425
399
426
// We will iterate through the entire log file iff and we get a success message from
400
427
// the recall call.
401
428
if ( success ) {
402
429
await log_file . collect_and_process ( async ( entry_path , batch_recorder ) => {
430
+ entry_path = this . decode_log ( entry_path ) ;
403
431
dbg . log2 ( 'TapeCloudGlacier.restore.batch - entry:' , entry_path ) ;
404
- await this . _finalize_restore ( fs_context , entry_path , failure_recorder ) ;
432
+ await this . _finalize_restore ( fs_context , entry_path , encoded_failure_recorder ) ;
405
433
} ) ;
406
434
}
407
435
@@ -425,6 +453,25 @@ class TapeCloudGlacier extends Glacier {
425
453
return result . toLowerCase ( ) . trim ( ) === 'true' ;
426
454
}
427
455
456
+ /**
457
+ *
458
+ * @param {string } data
459
+ * @returns
460
+ */
461
+ encode_log ( data ) {
462
+ const encoded = data . replace ( / \\ / g, '\\\\' ) . replace ( / \n / g, '\\n' ) ;
463
+ return `${ TapeCloudGlacier . LOG_DELIM } ${ encoded } ` ;
464
+ }
465
+
466
+ /**
467
+ *
468
+ * @param {string } data
469
+ * @returns
470
+ */
471
+ decode_log ( data ) {
472
+ return data . substring ( TapeCloudGlacier . LOG_DELIM . length ) . replace ( / \\ n / g, '\n' ) . replace ( / \\ \\ / g, '\\' ) ;
473
+ }
474
+
428
475
// ============= PRIVATE FUNCTIONS =============
429
476
430
477
/**
0 commit comments