mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-17 15:23:36 +01:00
Improve ref counting when deduplicating attachments on disk
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
@@ -1444,12 +1444,20 @@ type WritableInterface = {
|
||||
plaintextHash,
|
||||
version,
|
||||
contentType,
|
||||
messageId,
|
||||
}: {
|
||||
plaintextHash: string;
|
||||
version: number;
|
||||
contentType: MIMEType;
|
||||
messageId: string;
|
||||
}) => ExistingAttachmentData | undefined;
|
||||
_protectAttachmentPathFromDeletion: (path: string) => void;
|
||||
_protectAttachmentPathFromDeletion: ({
|
||||
path,
|
||||
messageId,
|
||||
}: {
|
||||
path: string;
|
||||
messageId: string;
|
||||
}) => void;
|
||||
resetProtectedAttachmentPaths: () => void;
|
||||
|
||||
removeAll: () => void;
|
||||
|
||||
@@ -2932,12 +2932,19 @@ function getAndProtectExistingAttachmentPath(
|
||||
plaintextHash,
|
||||
version,
|
||||
contentType,
|
||||
}: { plaintextHash: string; version: number; contentType: string }
|
||||
messageId,
|
||||
}: {
|
||||
plaintextHash: string;
|
||||
version: number;
|
||||
contentType: string;
|
||||
messageId: string;
|
||||
}
|
||||
): ExistingAttachmentData | undefined {
|
||||
if (!isValidPlaintextHash(plaintextHash)) {
|
||||
logger.error('getAndProtectExistingAttachmentPath: Invalid plaintextHash');
|
||||
return;
|
||||
}
|
||||
|
||||
if (version < 2) {
|
||||
logger.error(
|
||||
'getAndProtectExistingAttachmentPath: Invalid version',
|
||||
@@ -2985,8 +2992,8 @@ function getAndProtectExistingAttachmentPath(
|
||||
(${existingData.thumbnailPath}),
|
||||
(${existingData.screenshotPath})
|
||||
)
|
||||
INSERT OR REPLACE INTO attachments_protected_from_deletion(path)
|
||||
SELECT path
|
||||
INSERT OR REPLACE INTO attachments_protected_from_deletion(path, messageId)
|
||||
SELECT path, ${messageId}
|
||||
FROM existingMessageAttachmentPaths
|
||||
WHERE path IS NOT NULL;
|
||||
`;
|
||||
@@ -2997,11 +3004,13 @@ function getAndProtectExistingAttachmentPath(
|
||||
|
||||
function _protectAttachmentPathFromDeletion(
|
||||
db: WritableDB,
|
||||
path: string
|
||||
{ path, messageId }: { path: string; messageId: string }
|
||||
): void {
|
||||
const [protectQuery, protectParams] = sql`
|
||||
INSERT OR REPLACE INTO attachments_protected_from_deletion(path)
|
||||
VALUES (${path});
|
||||
INSERT OR REPLACE INTO attachments_protected_from_deletion
|
||||
(path, messageId)
|
||||
VALUES
|
||||
(${path}, ${messageId});
|
||||
`;
|
||||
db.prepare(protectQuery).run(protectParams);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import type { WritableDB } from '../Interface.std.js';
|
||||
|
||||
export default function updateToSchemaVersion1660(db: WritableDB): void {
|
||||
db.exec(`
|
||||
DROP TABLE attachments_protected_from_deletion;
|
||||
|
||||
CREATE TABLE attachments_protected_from_deletion (
|
||||
path TEXT NOT NULL,
|
||||
messageId TEXT NOT NULL,
|
||||
PRIMARY KEY (path, messageId)
|
||||
) STRICT;
|
||||
`);
|
||||
|
||||
db.exec(`
|
||||
DROP TRIGGER stop_protecting_attachments_after_update;
|
||||
|
||||
CREATE TRIGGER stop_protecting_attachments_after_update
|
||||
AFTER UPDATE OF path, thumbnailPath, screenshotPath, backupThumbnailPath
|
||||
ON message_attachments
|
||||
WHEN
|
||||
OLD.path IS NOT NEW.path OR
|
||||
OLD.thumbnailPath IS NOT NEW.thumbnailPath OR
|
||||
OLD.screenshotPath IS NOT NEW.screenshotPath OR
|
||||
OLD.backupThumbnailPath IS NOT NEW.backupThumbnailPath
|
||||
BEGIN
|
||||
DELETE FROM attachments_protected_from_deletion
|
||||
WHERE
|
||||
messageId IS NEW.messageId
|
||||
AND path IN (
|
||||
NEW.path,
|
||||
NEW.thumbnailPath,
|
||||
NEW.screenshotPath,
|
||||
NEW.backupThumbnailPath
|
||||
);
|
||||
END;
|
||||
`);
|
||||
|
||||
db.exec(`
|
||||
DROP TRIGGER stop_protecting_attachments_after_insert;
|
||||
|
||||
CREATE TRIGGER stop_protecting_attachments_after_insert
|
||||
AFTER INSERT
|
||||
ON message_attachments
|
||||
BEGIN
|
||||
DELETE FROM attachments_protected_from_deletion
|
||||
WHERE
|
||||
messageId IS NEW.messageId
|
||||
AND path IN (
|
||||
NEW.path,
|
||||
NEW.thumbnailPath,
|
||||
NEW.screenshotPath,
|
||||
NEW.backupThumbnailPath
|
||||
);
|
||||
END;
|
||||
`);
|
||||
}
|
||||
@@ -142,6 +142,7 @@ import updateToSchemaVersion1620 from './1620-sort-bigger-media.std.js';
|
||||
import updateToSchemaVersion1630 from './1630-message-pin-message-data.std.js';
|
||||
import updateToSchemaVersion1640 from './1640-key-transparency.std.js';
|
||||
import updateToSchemaVersion1650 from './1650-protected-attachments.std.js';
|
||||
import updateToSchemaVersion1660 from './1660-protected-attachments-non-unique.std.js';
|
||||
|
||||
import { DataWriter } from '../Server.node.js';
|
||||
|
||||
@@ -1644,6 +1645,7 @@ export const SCHEMA_VERSIONS: ReadonlyArray<SchemaUpdateType> = [
|
||||
{ version: 1630, update: updateToSchemaVersion1630 },
|
||||
{ version: 1640, update: updateToSchemaVersion1640 },
|
||||
{ version: 1650, update: updateToSchemaVersion1650 },
|
||||
{ version: 1660, update: updateToSchemaVersion1660 },
|
||||
];
|
||||
|
||||
export class DBVersionFromFutureError extends Error {
|
||||
|
||||
Reference in New Issue
Block a user