Bug 1903381 - Make sure to destroy recovery.zip if decryption fails or does not complete. r=backup-reviewers,kpatenio

Differential Revision: https://phabricator.services.mozilla.com/D214301
This commit is contained in:
Mike Conley
2024-06-21 14:07:28 +00:00
parent 02de0e2758
commit 7b222de590
3 changed files with 227 additions and 17 deletions

View File

@@ -253,7 +253,7 @@ class BinaryReadableStream {
* newlines to indicate when a break between full blocks is, and buffer chunks
* until we see those breaks - only decoding once we have a full block.
*/
class DecoderDecryptorTransformer {
export class DecoderDecryptorTransformer {
#buffer = "";
#decryptor = null;
@@ -329,13 +329,20 @@ class DecoderDecryptorTransformer {
* @returns {Promise<undefined>}
*/
async #processChunk(controller, chunk, isLastChunk = false) {
let bytes = lazy.ArchiveUtils.stringToArray(chunk);
try {
let bytes = lazy.ArchiveUtils.stringToArray(chunk);
if (this.#decryptor) {
let plaintextBytes = await this.#decryptor.decrypt(bytes, isLastChunk);
controller.enqueue(plaintextBytes);
} else {
controller.enqueue(bytes);
if (this.#decryptor) {
let plaintextBytes = await this.#decryptor.decrypt(bytes, isLastChunk);
controller.enqueue(plaintextBytes);
} else {
controller.enqueue(bytes);
}
} catch (e) {
// Something went wrong base64 decoding or decrypting. Tell the controller
// that we're done, so that it can destroy anything that was decoded /
// decrypted already.
controller.error("Corrupted archive.");
}
}
}
@@ -344,7 +351,7 @@ class DecoderDecryptorTransformer {
* A class that lets us construct a WritableStream that writes bytes to a file
* on disk somewhere.
*/
class FileWriterStream {
export class FileWriterStream {
/**
* @type {string}
*/
@@ -360,14 +367,23 @@ class FileWriterStream {
*/
#binStream = null;
/**
* @type {ArchiveDecryptor}
*/
#decryptor = null;
/**
* Constructor for FileWriterStream.
*
* @param {string} destPath
* The path to write the incoming bytes to.
* @param {ArchiveDecryptor|null} decryptor
* An initialized ArchiveDecryptor, if this stream of bytes is presumed to
* be encrypted.
*/
constructor(destPath) {
constructor(destPath, decryptor) {
this.#destPath = destPath;
this.#decryptor = decryptor;
}
/**
@@ -397,9 +413,35 @@ class FileWriterStream {
/**
* Called once the stream of bytes finishes flowing in and closes the stream.
*
* @param {WritableStreamDefaultController} controller
* The controller for the WritableStream.
*/
close() {
close(controller) {
lazy.FileUtils.closeSafeFileOutputStream(this.#outStream);
if (this.#decryptor && !this.#decryptor.isDone()) {
lazy.logConsole.error(
"Decryptor was not done when the stream was closed."
);
controller.error("Corrupted archive.");
}
}
/**
* Called if something went wrong while decoding / decrypting the stream of
* bytes. This destroys any bytes that may have been decoded / decrypted
* prior to the error.
*
* @param {string} reason
* The reported reason for aborting the decoding / decrpytion.
*/
async abort(reason) {
lazy.logConsole.error(`Writing to ${this.#destPath} failed: `, reason);
lazy.FileUtils.closeSafeFileOutputStream(this.#outStream);
await IOUtils.remove(this.#destPath, {
ignoreAbsent: true,
retryReadonly: true,
});
}
}
@@ -1261,7 +1303,7 @@ export class BackupService extends EventTarget {
* The Content-Type of the MIME message.
* @returns {ReadableStream}
*/
async #createBinaryReadableStream(archiveFile, startByteOffset, contentType) {
async createBinaryReadableStream(archiveFile, startByteOffset, contentType) {
let fileInputStream = Cc[
"@mozilla.org/network/file-input-stream;1"
].createInstance(Ci.nsIFileInputStream);
@@ -1424,7 +1466,7 @@ export class BackupService extends EventTarget {
await IOUtils.remove(extractionDestPath, { ignoreAbsent: true });
let archiveFile = await IOUtils.getFile(archivePath);
let archiveStream = await this.#createBinaryReadableStream(
let archiveStream = await this.createBinaryReadableStream(
archiveFile,
startByteOffset,
contentType
@@ -1434,7 +1476,7 @@ export class BackupService extends EventTarget {
new DecoderDecryptorTransformer(decryptor)
);
let fileWriter = new WritableStream(
new FileWriterStream(extractionDestPath)
new FileWriterStream(extractionDestPath, decryptor)
);
await archiveStream.pipeThrough(binaryDecoder).pipeTo(fileWriter);
}