Bug 1890591 - Add some utilities to BackupResource for copying files and Sqlite databases. r=backup-reviewers,kpatenio

Differential Revision: https://phabricator.services.mozilla.com/D207155
This commit is contained in:
Mike Conley
2024-04-15 14:47:20 +00:00
parent ff23c68395
commit 79d94225c6
5 changed files with 195 additions and 72 deletions

View File

@@ -2,6 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
});
// Convert from bytes to kilobytes (not kibibytes).
export const BYTES_IN_KB = 1000;
@@ -129,6 +135,65 @@ export class BackupResource {
return size;
}
/**
* Copy a set of SQLite databases safely from a source directory to a
* destination directory. A new read-only connection is opened for each
* database, and then a backup is created.
*
* @param {string} sourcePath
* Path to the source directory of the SQLite databases.
* @param {string} destPath
* Path to the destination directory where the SQLite databases should be
* copied to.
* @param {Array<string>} sqliteDatabases
* An array of filenames of the SQLite databases to copy.
* @returns {Promise<undefined>}
*/
static async copySqliteDatabases(sourcePath, destPath, sqliteDatabases) {
for (let fileName of sqliteDatabases) {
let sourceFilePath = PathUtils.join(sourcePath, fileName);
let destFilePath = PathUtils.join(destPath, fileName);
let connection;
try {
connection = await lazy.Sqlite.openConnection({
path: sourceFilePath,
readOnly: true,
});
await connection.backup(destFilePath);
} finally {
await connection.close();
}
}
}
/**
* A helper function to copy a set of files from a source directory to a
* destination directory. Callers should ensure that the source files can be
* copied safely before invoking this function. Files that do not exist will
* be ignored. Callers that wish to copy SQLite databases should use
* copySqliteDatabases() instead.
*
* @param {string} sourcePath
* Path to the source directory of the files to be copied.
* @param {string} destPath
* Path to the destination directory where the files should be
* copied to.
* @param {string[]} fileNames
* An array of filenames of the files to copy.
* @returns {Promise<undefined>}
*/
static async copyFiles(sourcePath, destPath, fileNames) {
for (let fileName of fileNames) {
let sourceFilePath = PathUtils.join(sourcePath, fileName);
let destFilePath = PathUtils.join(destPath, fileName);
if (await IOUtils.exists(sourceFilePath)) {
await IOUtils.copy(sourceFilePath, destFilePath, { recursive: true });
}
}
}
constructor() {}
/**

View File

@@ -4,12 +4,6 @@
import { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
});
/**
* Class representing miscellaneous files for telemetry, site storage,
* media device origin mapping, chrome privileged IndexedDB databases,
@@ -30,33 +24,14 @@ export class MiscDataBackupResource extends BackupResource {
"enumerate_devices.txt",
"SiteSecurityServiceState.bin",
];
for (let fileName of files) {
let sourcePath = PathUtils.join(profilePath, fileName);
let destPath = PathUtils.join(stagingPath, fileName);
if (await IOUtils.exists(sourcePath)) {
await IOUtils.copy(sourcePath, destPath, { recursive: true });
}
}
await BackupResource.copyFiles(profilePath, stagingPath, files);
const sqliteDatabases = ["protections.sqlite"];
for (let fileName of sqliteDatabases) {
let sourcePath = PathUtils.join(profilePath, fileName);
let destPath = PathUtils.join(stagingPath, fileName);
let connection;
try {
connection = await lazy.Sqlite.openConnection({
path: sourcePath,
readOnly: true,
});
await connection.backup(destPath);
} finally {
await connection.close();
}
}
await BackupResource.copySqliteDatabases(
profilePath,
stagingPath,
sqliteDatabases
);
// Bug 1890585 - we don't currently have the ability to copy the
// chrome-privileged IndexedDB databases under storage/permanent/chrome, so

View File

@@ -10,7 +10,6 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(
@@ -60,22 +59,12 @@ export class PlacesBackupResource extends BackupResource {
return { bookmarksOnly: true };
}
for (let fileName of sqliteDatabases) {
let sourcePath = PathUtils.join(profilePath, fileName);
let destPath = PathUtils.join(stagingPath, fileName);
let connection;
await BackupResource.copySqliteDatabases(
profilePath,
stagingPath,
sqliteDatabases
);
try {
connection = await lazy.Sqlite.openConnection({
path: sourcePath,
readOnly: true,
});
await connection.backup(destPath);
} finally {
await connection.close();
}
}
return null;
}

View File

@@ -3,7 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
import { Sqlite } from "resource://gre/modules/Sqlite.sys.mjs";
/**
* Class representing files that modify preferences and permissions within a user profile.
@@ -28,32 +27,14 @@ export class PreferencesBackupResource extends BackupResource {
"user.js",
"chrome",
];
for (let fileName of simpleCopyFiles) {
let sourcePath = PathUtils.join(profilePath, fileName);
let destPath = PathUtils.join(stagingPath, fileName);
if (await IOUtils.exists(sourcePath)) {
await IOUtils.copy(sourcePath, destPath, { recursive: true });
}
}
await BackupResource.copyFiles(profilePath, stagingPath, simpleCopyFiles);
const sqliteDatabases = ["permissions.sqlite", "content-prefs.sqlite"];
for (let fileName of sqliteDatabases) {
let sourcePath = PathUtils.join(profilePath, fileName);
let destPath = PathUtils.join(stagingPath, fileName);
let connection;
try {
connection = await Sqlite.openConnection({
path: sourcePath,
});
await connection.backup(destPath);
} finally {
await connection.close();
}
}
await BackupResource.copySqliteDatabases(
profilePath,
stagingPath,
sqliteDatabases
);
// prefs.js is a special case - we have a helper function to flush the
// current prefs state to disk off of the main thread.