Handle locally missing attachments for local backups

This commit is contained in:
ayumi-signal
2025-05-15 08:27:46 -07:00
committed by GitHub
parent 50b34efad0
commit a75a0f9143
3 changed files with 19 additions and 14 deletions

View File

@@ -552,6 +552,7 @@ export async function decryptAttachmentV2ToSink(
`${logId}: Failed to decrypt attachment`, `${logId}: Failed to decrypt attachment`,
Errors.toLogFormat(error) Errors.toLogFormat(error)
); );
sink.end();
throw error; throw error;
} finally { } finally {
await readFd?.close(); await readFd?.close();

View File

@@ -3,6 +3,7 @@
import Long from 'long'; import Long from 'long';
import { BackupLevel } from '@signalapp/libsignal-client/zkgroup'; import { BackupLevel } from '@signalapp/libsignal-client/zkgroup';
import { omit } from 'lodash'; import { omit } from 'lodash';
import { existsSync } from 'node:fs';
import { import {
APPLICATION_OCTET_STREAM, APPLICATION_OCTET_STREAM,
@@ -455,9 +456,15 @@ export async function getLocalBackupFilePointerForAttachment({
getBackupCdnInfo, getBackupCdnInfo,
}); });
// localKey is required to export to the local backup. If it's missing then fall back // If a file disappeared locally (maybe we downloaded it and it disappeared)
// to the filePointer which would have been generated for a remote backup. // or localKey is missing, then we can't export to a local backup.
if (attachment.localKey == null) { // Fallback to the filePointer which would have been generated for a remote backup.
const isAttachmentMissingLocally =
attachment.path == null ||
!existsSync(
window.Signal.Migrations.getAbsoluteAttachmentPath(attachment.path)
);
if (isAttachmentMissingLocally || attachment.localKey == null) {
return { filePointer: remoteFilePointer, updatedAttachment }; return { filePointer: remoteFilePointer, updatedAttachment };
} }

View File

@@ -1,13 +1,12 @@
// Copyright 2025 Signal Messenger, LLC // Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { existsSync } from 'node:fs';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import { import {
type AttachmentType, type AttachmentType,
getAttachmentIdForLogging, getAttachmentIdForLogging,
} from '../types/Attachment'; } from '../types/Attachment';
import * as log from '../logging/log';
import { toLogFormat } from '../types/errors';
import { import {
decryptAndReencryptLocally, decryptAndReencryptLocally,
type ReencryptedAttachmentV2, type ReencryptedAttachmentV2,
@@ -23,15 +22,7 @@ export async function downloadAttachmentFromLocalBackup(
const dataId = `${attachmentId}`; const dataId = `${attachmentId}`;
const logId = `downloadAttachmentFromLocalBackup(${dataId})`; const logId = `downloadAttachmentFromLocalBackup(${dataId})`;
try { return doDownloadFromLocalBackup(attachment, { logId });
return await doDownloadFromLocalBackup(attachment, { logId });
} catch (error) {
log.error(
`${logId}: error when copying from local backup`,
toLogFormat(error)
);
throw new AttachmentPermanentlyUndownloadableError();
}
} }
async function doDownloadFromLocalBackup( async function doDownloadFromLocalBackup(
@@ -49,6 +40,12 @@ async function doDownloadFromLocalBackup(
strictAssert(localBackupPath, `${logId}: missing localBackupPath`); strictAssert(localBackupPath, `${logId}: missing localBackupPath`);
strictAssert(isNumber(size), `${logId}: missing size`); strictAssert(isNumber(size), `${logId}: missing size`);
if (!existsSync(localBackupPath)) {
throw new AttachmentPermanentlyUndownloadableError(
'No file at attachment localBackupPath'
);
}
return decryptAndReencryptLocally({ return decryptAndReencryptLocally({
type: 'local', type: 'local',
ciphertextPath: localBackupPath, ciphertextPath: localBackupPath,