/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using mozilla::dom::quota::Client; using mozilla::dom::quota::CloneFileAndAppend; using mozilla::dom::quota::GetDirEntryKind; using mozilla::dom::quota::nsIFileKind; using mozilla::dom::quota::QuotaManager; using mozilla::dom::quota::QuotaObject;
namespace {
// Const variable for generate padding size. // XXX This will be tweaked to something more meaningful in Bug 1383656. const int64_t kRoundUpNumber = 20480;
// At the moment, the encrypted stream block size is assumed to be unchangeable // between encrypting and decrypting blobs. This assumptions holds as long as we // only encrypt in private browsing mode, but when we support encryption for // persistent storage, this needs to be changed.
constexpr uint32_t kEncryptedStreamBlockSize = 4096;
// The alogrithm for generating padding refers to the mitigation approach in // https://github.com/whatwg/storage/issues/31. // First, generate a random number between 0 and 100kB. // Next, round up the sum of random number and response size to the nearest // 20kB. // Finally, the virtual padding size will be the result minus the response size.
int64_t BodyGeneratePadding(int64_t aBodyFileSize, uint32_t aPaddingInfo);
// Some file systems have poor performance when there are too many files // in a single directory. Mitigate this issue by spreading the body // files out into sub-directories. We use the last byte of the ID for // the name of the sub-directory.
QM_TRY(MOZ_TO_RESULT(cacheDir->Append(IntToString(aId.m3[7]))));
// Callers call this function without checking if the directory already // exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we // just want to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the // reports.
QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF( // Expression.
MOZ_TO_RESULT(cacheDir->Create(nsIFile::DIRECTORY_TYPE, 0755)), // Predicate.
IsSpecificError<NS_ERROR_FILE_ALREADY_EXISTS>, // Fallback.
ErrToDefaultOk<>));
// Callers call this function without checking if the directory already // exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we // just want to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the // reports.
QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF( // Expression.
MOZ_TO_RESULT(bodyDir->Create(nsIFile::DIRECTORY_TYPE, 0755)), // Predicate.
IsSpecificError<NS_ERROR_FILE_ALREADY_EXISTS>, // Fallback.
ErrToDefaultOk<>));
// It's fine to not notify the QuotaManager that the path has been changed, // because its path will be updated and its size will be recalculated when // opening file next time.
QM_TRY(MOZ_TO_RESULT(tmpFile->RenameTo(nullptr, finalFileName)));
// Iterate over all the intermediate morgue subdirs
QM_TRY(quota::CollectEachFile(
*dir,
[&aDirectoryMetadata, &aKnownBodyIdList]( const nsCOMPtr<nsIFile>& subdir) -> Result<Ok, nsresult> {
QM_TRY_INSPECT(constauto& dirEntryKind, GetDirEntryKind(*subdir));
switch (dirEntryKind) { case nsIFileKind::ExistsAsDirectory: { constauto removeOrphanedFiles =
[&aDirectoryMetadata, &aKnownBodyIdList](
nsIFile& bodyFile, const nsACString& leafName) -> Result<bool, nsresult> { // Finally, parse the uuid out of the name. If it fails to parse, // then ignore the file. auto cleanup = MakeScopeExit([&aDirectoryMetadata, &bodyFile] {
DebugOnly<nsresult> result =
RemoveNsIFile(aDirectoryMetadata, bodyFile);
MOZ_ASSERT(NS_SUCCEEDED(result));
});
if (!aKnownBodyIdList.Contains(id)) { returntrue;
}
cleanup.release();
returnfalse;
};
// QM_OR_ELSE_WARN_IF is not used here since we just want to log // NS_ERROR_FILE_FS_CORRUPTED result and not spam the reports (even // a warning in the reports is not desired).
QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF( // Expression.
MOZ_TO_RESULT(BodyTraverseFiles(aDirectoryMetadata, *subdir,
removeOrphanedFiles, /* aCanRemoveFiles */ true, /* aTrackQuota */ true)), // Predicate.
IsSpecificError<NS_ERROR_FILE_FS_CORRUPTED>, // Fallback. We treat NS_ERROR_FILE_FS_CORRUPTED as if the // directory did not exist at all.
ErrToDefaultOk<>)); break;
}
case nsIFileKind::ExistsAsFile: { // If a file got in here somehow, try to remove it and move on
DebugOnly<nsresult> result =
RemoveNsIFile(aDirectoryMetadata, *subdir, /* aTrackQuota */ false);
MOZ_ASSERT(NS_SUCCEEDED(result)); break;
}
case nsIFileKind::DoesNotExist: // Ignore files that got removed externally while iterating. break;
}
// Callers call this function without checking if the file already exists // (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we just want // to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the reports. // // TODO: In theory if this file exists, then Context::~Context should have // cleaned it up, but obviously we can crash and not clean it up, which is // the whole point of the marker file. In that case, we'll realize the marker // file exists in SetupAction::RunSyncWithDBOnTarget and do some cleanup, but // we won't delete the marker file, so if we see this marker file, it is part // of our standard operating procedure to redundantly try and create the // marker here. We currently treat this as idempotent usage, but we could // make sure to delete the marker file when handling the existing marker // file in SetupAction::RunSyncWithDBOnTarget and change // QM_OR_ELSE_LOG_VERBOSE_IF to QM_OR_ELSE_WARN_IF in the end.
QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF( // Expression.
MOZ_TO_RESULT(marker->Create(nsIFile::NORMAL_FILE_TYPE, 0644)), // Predicate.
IsSpecificError<NS_ERROR_FILE_ALREADY_EXISTS>, // Fallback.
ErrToDefaultOk<>));
// Note, we don't need to fsync here. We only care about actually // writing the marker if later modifications to the Cache are // actually flushed to the disk. If the OS crashes before the marker // is written then we are ensured no other changes to the Cache were // flushed either.
DebugOnly<nsresult> result =
RemoveNsIFile(aDirectoryMetadata, *marker, /* aTrackQuota */ false);
MOZ_ASSERT(NS_SUCCEEDED(result));
// Again, no fsync is necessary. If the OS crashes before the file // removal is flushed, then the Cache will search for stale data on // startup. This will cause the next Cache access to be a bit slow, but // it seems appropriate after an OS crash.
nsresult RemoveNsIFileRecursively( const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata, nsIFile& aFile, constbool aTrackQuota) { // XXX This assertion proves that we can remove aTrackQuota and just check // aClientMetadata
MOZ_DIAGNOSTIC_ASSERT_IF(aTrackQuota, aDirectoryMetadata);
switch (dirEntryKind) { case nsIFileKind::ExistsAsDirectory: // Unfortunately, we need to traverse all the entries and delete files one // by // one to update their usages to the QuotaManager.
QM_TRY(quota::CollectEachFile(
aFile,
[&aDirectoryMetadata, &aTrackQuota]( const nsCOMPtr<nsIFile>& file) -> Result<Ok, nsresult> {
QM_TRY(MOZ_TO_RESULT(RemoveNsIFileRecursively(aDirectoryMetadata,
*file, aTrackQuota)));
return Ok{};
}));
// In the end, remove the folder
QM_TRY(MOZ_TO_RESULT(aFile.Remove(/* recursive */ false)));
break;
case nsIFileKind::ExistsAsFile: return RemoveNsIFile(aDirectoryMetadata, aFile, aTrackQuota);
case nsIFileKind::DoesNotExist: // Ignore files that got removed externally while iterating. break;
}
return NS_OK;
}
nsresult RemoveNsIFile(const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
nsIFile& aFile, constbool aTrackQuota) { // XXX This assertion proves that we can remove aTrackQuota and just check // aClientMetadata
MOZ_DIAGNOSTIC_ASSERT_IF(aTrackQuota, aDirectoryMetadata);
QM_TRY_INSPECT( const int64_t& currentPaddingSize,
([directoryPaddingGetResult, &aBaseDir, &aConn, aIncreaseSize,
aDecreaseSize]() -> Result<int64_t, nsresult> { if (!directoryPaddingGetResult) { // Fail to read padding size from the dir padding file, so try to // restore.
// Not delete the temporary padding file here, because we're going // to overwrite it below anyway.
QM_TRY(MOZ_TO_RESULT(
DirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE)));
// We don't need to add the aIncreaseSize or aDecreaseSize here, // because it's already encompassed within the database.
QM_TRY_RETURN(db::FindOverallPaddingSize(aConn));
}
if (shouldRevise) { // If somehow runing into this condition, the tracking padding size is // incorrect. // Delete padding file to indicate the padding size is incorrect for // avoiding error happening in the following lines.
QM_TRY(MOZ_TO_RESULT(
DirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE)));
// XXXtt: we should have an easy way to update (increase or // recalulate) padding size in the QM. For now, only correct the // padding size in padding file and make QM be able to get the correct // size in the next QM initialization. We still want to catch this in // the debug build.
MOZ_ASSERT(false, "The padding size is unsync with QM");
}
Result<int64_t, nsresult> DirectoryPaddingRestore(nsIFile& aBaseDir,
mozIStorageConnection& aConn, constbool aMustRestore) { // The content of padding file is untrusted, so remove it here.
QM_TRY(MOZ_TO_RESULT(
DirectoryPaddingDeleteFile(aBaseDir, DirPaddingFile::FILE)));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.