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
commit a75a0f9143
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 19 additions and 14 deletions

View file

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

View file

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

View file

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