Skip to content

Commit 0a5f3cc

Browse files
Refactor backup import/export options
1 parent 6447021 commit 0a5f3cc

17 files changed

+530
-507
lines changed

ts/jobs/AttachmentDownloadManager.preload.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
shouldAttachmentEndUpInRemoteBackup,
3939
getUndownloadedAttachmentSignature,
4040
isIncremental,
41-
hasRequiredInformationForBackup,
41+
hasRequiredInformationForRemoteBackup,
4242
} from '../util/Attachment.std.js';
4343
import type { ReadonlyMessageAttributesType } from '../model-types.d.ts';
4444
import { backupsService } from '../services/backups/index.preload.js';
@@ -310,7 +310,7 @@ export class AttachmentDownloadManager extends JobManager<CoreAttachmentDownload
310310

311311
if (
312312
source === AttachmentDownloadSource.BACKUP_IMPORT_WITH_MEDIA &&
313-
!hasRequiredInformationForBackup(attachment)
313+
!hasRequiredInformationForRemoteBackup(attachment)
314314
) {
315315
source = AttachmentDownloadSource.BACKUP_IMPORT_NO_MEDIA;
316316
}

ts/jobs/AttachmentLocalBackupManager.preload.ts

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
/* eslint-disable max-classes-per-file */
44

55
import { existsSync } from 'node:fs';
6-
import { PassThrough } from 'node:stream';
7-
import { constants as FS_CONSTANTS, copyFile, mkdir } from 'node:fs/promises';
6+
import {
7+
constants as FS_CONSTANTS,
8+
copyFile,
9+
mkdir,
10+
rename,
11+
} from 'node:fs/promises';
812

913
import * as durations from '../util/durations/index.std.js';
1014
import { createLogger } from '../logging/log.std.js';
@@ -14,6 +18,7 @@ import { redactGenericText } from '../util/privacy.node.js';
1418
import {
1519
getAbsoluteAttachmentPath,
1620
getAbsoluteAttachmentPath as doGetAbsoluteAttachmentPath,
21+
getAbsoluteTempPath,
1722
} from '../util/migrations.preload.js';
1823
import {
1924
JobManager,
@@ -36,6 +41,7 @@ import {
3641
getLocalBackupDirectoryForMediaName,
3742
getLocalBackupPathForMediaName,
3843
} from '../services/backups/util/localBackup.node.js';
44+
import { createName } from '../util/attachmentPath.node.js';
3945

4046
const log = createLogger('AttachmentLocalBackupManager');
4147

@@ -219,7 +225,7 @@ async function runAttachmentBackupJobInner(
219225
log.info(`${logId}: starting`);
220226

221227
const { backupsBaseDir, mediaName } = job;
222-
const { localKey, path, size } = job.data;
228+
const { path } = job.data;
223229

224230
if (!path) {
225231
throw new AttachmentPermanentlyMissingError('No path property');
@@ -230,45 +236,26 @@ async function runAttachmentBackupJobInner(
230236
throw new AttachmentPermanentlyMissingError('No file at provided path');
231237
}
232238

233-
if (!localKey) {
234-
throw new Error('No localKey property, required for test decryption');
235-
}
236-
237239
const localBackupFileDir = getLocalBackupDirectoryForMediaName({
238240
backupsBaseDir,
239241
mediaName,
240242
});
241243
await mkdir(localBackupFileDir, { recursive: true });
242244

243-
const localBackupFilePath = getLocalBackupPathForMediaName({
245+
const destinationLocalBackupFilePath = getLocalBackupPathForMediaName({
244246
backupsBaseDir,
245247
mediaName,
246248
});
247249

248-
// TODO: Add check in local FS to prevent double backup
249-
250250
// File is already encrypted with localKey, so we just have to copy it to the backup dir
251-
const attachmentPath = getAbsoluteAttachmentPath(path);
251+
const sourceAttachmentPath = getAbsoluteAttachmentPath(path);
252+
const tempPath = getAbsoluteTempPath(createName());
253+
254+
// A unique constraint on the DB table should enforce that only one job is writing to
255+
// the same mediaName at a time, but just to be safe, we copy to temp file and rename to
256+
// ensure the atomicity of the copy operation
252257

253258
// Set COPYFILE_FICLONE for Copy on Write (OS dependent, gracefully falls back to copy)
254-
await copyFile(
255-
attachmentPath,
256-
localBackupFilePath,
257-
FS_CONSTANTS.COPYFILE_FICLONE
258-
);
259-
260-
// TODO: Optimize this check -- it can be expensive to test decrypt on every export
261-
log.info(`${logId}: Verifying file in local backup`);
262-
const sink = new PassThrough();
263-
sink.resume();
264-
await decryptAttachmentV2ToSink(
265-
{
266-
ciphertextPath: localBackupFilePath,
267-
idForLogging: 'AttachmentLocalBackupManager',
268-
keysBase64: localKey,
269-
size,
270-
type: 'local',
271-
},
272-
sink
273-
);
259+
await copyFile(sourceAttachmentPath, tempPath, FS_CONSTANTS.COPYFILE_FICLONE);
260+
await rename(tempPath, destinationLocalBackupFilePath);
274261
}

0 commit comments

Comments
 (0)