Bug 1481819 - Make Safe Browsing directory-based file copy interruptible. r=francois

A directory-based file copy without checkpoint to abort may take lots
of time to finish. This cause an issue that if firefox is shutting down
and try to close an ongoing update thread, main-thread may be blocked
for a long time.

This patch adds a wrapper for copying an entire directory, within this
wrapper, we use file-based copy and add checkpoints to let update thread
has a chance to abort.

Differential Revision: https://phabricator.services.mozilla.com/D3414

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dimi Lee 2018-09-18 14:29:20 +00:00
Родитель 926304f225
Коммит e05a4185a6
2 изменённых файлов: 65 добавлений и 12 удалений

Просмотреть файл

@ -794,16 +794,12 @@ Classifier::ApplyUpdatesBackground(TableUpdateArray& aUpdates,
nsresult rv; nsresult rv;
// Check point 1: Copying file takes time so we check here. // Check point 1: Copying files takes time so we check |mUpdateInterrupted|
if (mUpdateInterrupted) { // inside CopyInUseDirForUpdate().
LOG(("Update is interrupted. Don't copy files."));
return NS_OK;
}
rv = CopyInUseDirForUpdate(); // i.e. mUpdatingDirectory will be setup. rv = CopyInUseDirForUpdate(); // i.e. mUpdatingDirectory will be setup.
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
LOG(("Failed to copy in-use directory for update.")); LOG(("Failed to copy in-use directory for update."));
return rv; return (rv == NS_ERROR_ABORT) ? NS_OK : rv;
} }
LOG(("Applying %zu table updates.", aUpdates.Length())); LOG(("Applying %zu table updates.", aUpdates.Length()));
@ -1096,6 +1092,64 @@ Classifier::DumpFailedUpdate()
#endif // MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES #endif // MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
/**
* This function copies the files one by one to the destination folder.
* Before copying a file, it checks |mUpdateInterrupted| and returns
* NS_ERROR_ABORT if the flag is set.
*/
nsresult
Classifier::CopyDirectoryInterruptible(nsCOMPtr<nsIFile>& aDestDir, nsCOMPtr<nsIFile>& aSourceDir)
{
nsCOMPtr<nsIDirectoryEnumerator> entries;
nsresult rv = aSourceDir->GetDirectoryEntries(getter_AddRefs(entries));
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(entries);
nsCOMPtr<nsIFile> source;
while (NS_SUCCEEDED(rv = entries->GetNextFile(getter_AddRefs(source))) &&
source) {
if (mUpdateInterrupted) {
LOG(("Update is interrupted. Aborting the directory copy"));
return NS_ERROR_ABORT;
}
bool isDirectory;
rv = source->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
if (isDirectory) {
// If it is a directory, recursively copy the files inside the directory.
nsAutoCString leaf;
source->GetNativeLeafName(leaf);
MOZ_ASSERT(!leaf.IsEmpty());
nsCOMPtr<nsIFile> dest;
aDestDir->Clone(getter_AddRefs(dest));
dest->AppendNative(leaf);
NS_ENSURE_SUCCESS(rv, rv);
rv = CopyDirectoryInterruptible(dest, source);
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = source->CopyToNative(aDestDir, EmptyCString());
NS_ENSURE_SUCCESS(rv, rv);
}
}
// If the destination directory doesn't exist in the end, it means that the
// source directory is empty, we should copy the directory here.
bool exist;
rv = aDestDir->Exists(&exist);
NS_ENSURE_SUCCESS(rv, rv);
if (!exist) {
rv = aDestDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult nsresult
Classifier::CopyInUseDirForUpdate() Classifier::CopyInUseDirForUpdate()
{ {
@ -1104,17 +1158,15 @@ Classifier::CopyInUseDirForUpdate()
// We copy everything from in-use directory to a temporary directory // We copy everything from in-use directory to a temporary directory
// for updating. // for updating.
nsCString updatingDirName;
nsresult rv = mUpdatingDirectory->GetNativeLeafName(updatingDirName);
NS_ENSURE_SUCCESS(rv, rv);
// Remove the destination directory first (just in case) the do the copy. // Remove the destination directory first (just in case) the do the copy.
mUpdatingDirectory->Remove(true); mUpdatingDirectory->Remove(true);
if (!mRootStoreDirectoryForUpdate) { if (!mRootStoreDirectoryForUpdate) {
LOG(("mRootStoreDirectoryForUpdate is null.")); LOG(("mRootStoreDirectoryForUpdate is null."));
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
} }
rv = mRootStoreDirectoryForUpdate->CopyToNative(nullptr, updatingDirName);
nsresult rv = CopyDirectoryInterruptible(mUpdatingDirectory,
mRootStoreDirectoryForUpdate);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;

Просмотреть файл

@ -134,6 +134,7 @@ private:
nsresult RecoverBackups(); nsresult RecoverBackups();
nsresult CleanToDelete(); nsresult CleanToDelete();
nsresult CopyInUseDirForUpdate(); nsresult CopyInUseDirForUpdate();
nsresult CopyDirectoryInterruptible(nsCOMPtr<nsIFile>& aDestDir, nsCOMPtr<nsIFile>& aSourceDir);
nsresult RegenActiveTables(); nsresult RegenActiveTables();
void MergeNewLookupCaches(); // Merge mNewLookupCaches into mLookupCaches. void MergeNewLookupCaches(); // Merge mNewLookupCaches into mLookupCaches.