зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1812243 - Rescan usages at QuotaClient initialization after crash. r=dom-storage-reviewers,janv
Differential Revision: https://phabricator.services.mozilla.com/D162584
This commit is contained in:
Родитель
94dabce14e
Коммит
030abfe7c4
|
@ -40,6 +40,8 @@ void HandleFailedStatus(nsresult aError, const RefPtr<Promise>& aPromise) {
|
|||
case NS_ERROR_FILE_ACCESS_DENIED:
|
||||
aPromise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||
break;
|
||||
case NS_ERROR_FILE_NOT_FOUND:
|
||||
[[fallthrough]];
|
||||
case NS_ERROR_DOM_NOT_FOUND_ERR:
|
||||
aPromise->MaybeRejectWithNotFoundError("Entry not found");
|
||||
break;
|
||||
|
|
|
@ -168,10 +168,10 @@ bool FileSystemAccessHandle::IsInactive() const {
|
|||
|
||||
RefPtr<FileSystemAccessHandle::InitPromise>
|
||||
FileSystemAccessHandle::BeginInit() {
|
||||
if (!mDataManager->LockExclusive(mEntryId)) {
|
||||
return InitPromise::CreateAndReject(
|
||||
NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR, __func__);
|
||||
}
|
||||
QM_TRY(MOZ_TO_RESULT(mDataManager->LockExclusive(mEntryId)),
|
||||
[](const nsresult aRv) {
|
||||
return InitPromise::CreateAndReject(aRv, __func__);
|
||||
});
|
||||
|
||||
mLocked = true;
|
||||
|
||||
|
|
|
@ -186,10 +186,8 @@ mozilla::ipc::IPCResult FileSystemManagerParent::RecvGetWritable(
|
|||
AssertIsOnIOTarget();
|
||||
MOZ_ASSERT(mDataManager);
|
||||
|
||||
if (!mDataManager->LockShared(aRequest.entryId())) {
|
||||
aResolver(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
|
||||
return IPC_OK();
|
||||
}
|
||||
QM_TRY(MOZ_TO_RESULT(mDataManager->LockShared(aRequest.entryId())), IPC_OK(),
|
||||
([aResolver](const nsresult& aRv) { aResolver(aRv); }));
|
||||
|
||||
auto autoUnlock =
|
||||
MakeScopeExit([self = RefPtr<FileSystemManagerParent>(this), aRequest] {
|
||||
|
|
|
@ -127,6 +127,9 @@ Result<quota::UsageInfo, nsresult> QuotaClient::InitOrigin(
|
|||
QM_TRY_INSPECT(const ResultConnection& conn,
|
||||
GetStorageConnection(origin).mapErr(toNSResult));
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(
|
||||
data::FileSystemDatabaseManager::RescanUsages(conn, origin)));
|
||||
|
||||
return data::FileSystemDatabaseManager::GetUsage(conn, origin)
|
||||
.mapErr(toNSResult);
|
||||
}
|
||||
|
|
|
@ -348,15 +348,21 @@ bool FileSystemDataManager::IsLocked(const EntryId& aEntryId) const {
|
|||
return mExclusiveLocks.Contains(aEntryId);
|
||||
}
|
||||
|
||||
bool FileSystemDataManager::LockExclusive(const EntryId& aEntryId) {
|
||||
nsresult FileSystemDataManager::LockExclusive(const EntryId& aEntryId) {
|
||||
if (IsLocked(aEntryId)) {
|
||||
return false;
|
||||
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
// If the file has been removed, we should get a file not found error.
|
||||
// Otherwise, if usage tracking cannot be started because file size is not
|
||||
// known and attempts to read it are failing, lock is denied to freeze the
|
||||
// quota usage until the (external) blocker is gone or the file is removed.
|
||||
QM_TRY(MOZ_TO_RESULT(mDatabaseManager->BeginUsageTracking(aEntryId)));
|
||||
|
||||
LOG_VERBOSE(("ExclusiveLock"));
|
||||
mExclusiveLocks.Insert(aEntryId);
|
||||
|
||||
return true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void FileSystemDataManager::UnlockExclusive(const EntryId& aEntryId) {
|
||||
|
@ -365,10 +371,13 @@ void FileSystemDataManager::UnlockExclusive(const EntryId& aEntryId) {
|
|||
LOG_VERBOSE(("ExclusiveUnlock"));
|
||||
mExclusiveLocks.Remove(aEntryId);
|
||||
|
||||
QM_WARNONLY_TRY(MOZ_TO_RESULT(mDatabaseManager->UpdateUsage(aEntryId)));
|
||||
// On error, usage tracking remains on to prevent writes until usage is
|
||||
// updated successfully.
|
||||
QM_TRY(MOZ_TO_RESULT(mDatabaseManager->UpdateUsage(aEntryId)), QM_VOID);
|
||||
QM_TRY(MOZ_TO_RESULT(mDatabaseManager->EndUsageTracking(aEntryId)), QM_VOID);
|
||||
}
|
||||
|
||||
bool FileSystemDataManager::LockShared(const EntryId& aEntryId) {
|
||||
nsresult FileSystemDataManager::LockShared(const EntryId& aEntryId) {
|
||||
return LockExclusive(aEntryId);
|
||||
}
|
||||
|
||||
|
|
|
@ -119,11 +119,11 @@ class FileSystemDataManager
|
|||
|
||||
bool IsLocked(const EntryId& aEntryId) const;
|
||||
|
||||
bool LockExclusive(const EntryId& aEntryId);
|
||||
nsresult LockExclusive(const EntryId& aEntryId);
|
||||
|
||||
void UnlockExclusive(const EntryId& aEntryId);
|
||||
|
||||
bool LockShared(const EntryId& aEntryId);
|
||||
nsresult LockShared(const EntryId& aEntryId);
|
||||
|
||||
void UnlockShared(const EntryId& aEntryId);
|
||||
|
||||
|
|
|
@ -18,6 +18,29 @@
|
|||
|
||||
namespace mozilla::dom::fs::data {
|
||||
|
||||
/* static */
|
||||
nsresult FileSystemDatabaseManager::RescanUsages(
|
||||
const ResultConnection& aConnection, const Origin& aOrigin) {
|
||||
DatabaseVersion version = 0;
|
||||
QM_TRY(MOZ_TO_RESULT(aConnection->GetSchemaVersion(&version)));
|
||||
|
||||
switch (version) {
|
||||
case 0: {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
return FileSystemDatabaseManagerVersion001::RescanTrackedUsages(
|
||||
aConnection, aOrigin);
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/* static */
|
||||
Result<quota::UsageInfo, QMResult> FileSystemDatabaseManager::GetUsage(
|
||||
const ResultConnection& aConnection, const Origin& aOrigin) {
|
||||
|
|
|
@ -34,6 +34,14 @@ namespace data {
|
|||
|
||||
class FileSystemDatabaseManager {
|
||||
public:
|
||||
/**
|
||||
* @brief Updates stored usage data for all tracked files.
|
||||
*
|
||||
* @return nsresult error code
|
||||
*/
|
||||
static nsresult RescanUsages(const ResultConnection& aConnection,
|
||||
const Origin& aOrigin);
|
||||
|
||||
/**
|
||||
* @brief Obtains the current total usage for origin and connection.
|
||||
*
|
||||
|
@ -141,6 +149,16 @@ class FileSystemDatabaseManager {
|
|||
*/
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* @brief Start tracking file's usage.
|
||||
*/
|
||||
virtual nsresult BeginUsageTracking(const EntryId& aEntryId) = 0;
|
||||
|
||||
/**
|
||||
* @brief Stop tracking file's usage.
|
||||
*/
|
||||
virtual nsresult EndUsageTracking(const EntryId& aEntryId) = 0;
|
||||
|
||||
virtual ~FileSystemDatabaseManager() = default;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,16 +6,22 @@
|
|||
|
||||
#include "FileSystemDatabaseManagerVersion001.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "FileSystemDataManager.h"
|
||||
#include "FileSystemFileManager.h"
|
||||
#include "ResultStatement.h"
|
||||
#include "mozStorageHelper.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/FileSystemDataManager.h"
|
||||
#include "mozilla/dom/FileSystemHandle.h"
|
||||
#include "mozilla/dom/FileSystemLog.h"
|
||||
#include "mozilla/dom/FileSystemTypes.h"
|
||||
#include "mozilla/dom/PFileSystemManager.h"
|
||||
#include "mozilla/dom/quota/Client.h"
|
||||
#include "mozilla/dom/quota/QuotaCommon.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/quota/QuotaObject.h"
|
||||
#include "mozilla/dom/quota/ResultExtensions.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
@ -26,6 +32,8 @@ namespace fs::data {
|
|||
|
||||
namespace {
|
||||
|
||||
auto toNSResult = [](const auto& aRv) { return ToNSResult(aRv); };
|
||||
|
||||
Result<bool, QMResult> ApplyEntryExistsQuery(
|
||||
const FileSystemConnection& aConnection, const nsACString& aQuery,
|
||||
const FileSystemChildMetadata& aHandle) {
|
||||
|
@ -47,7 +55,7 @@ Result<bool, QMResult> ApplyEntryExistsQuery(
|
|||
return stmt.YesOrNoQuery();
|
||||
}
|
||||
|
||||
Result<bool, QMResult> IsDirectoryEmpty(FileSystemConnection& mConnection,
|
||||
Result<bool, QMResult> IsDirectoryEmpty(const FileSystemConnection& mConnection,
|
||||
const EntryId& aEntryId) {
|
||||
const nsLiteralCString isDirEmptyQuery =
|
||||
"SELECT EXISTS ("
|
||||
|
@ -359,8 +367,6 @@ nsresult PerformRename(const FileSystemConnection& aConnection,
|
|||
return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
|
||||
}
|
||||
|
||||
auto toNSResult = [](const auto& aRv) { return ToNSResult(aRv); };
|
||||
|
||||
// TODO: This should fail when handle doesn't exist - the
|
||||
// explicit file or directory existence queries are redundant
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
|
@ -432,8 +438,315 @@ Result<nsTArray<EntryId>, QMResult> FindDescendants(
|
|||
return descendants;
|
||||
}
|
||||
|
||||
nsresult SetUsageTracking(const FileSystemConnection& aConnection,
|
||||
const EntryId& aEntryId, bool aTracked) {
|
||||
const nsLiteralCString setTrackedQuery =
|
||||
"INSERT INTO Usages "
|
||||
"( handle, tracked ) "
|
||||
"VALUES "
|
||||
"( :handle, :tracked ) "
|
||||
"ON CONFLICT(handle) DO "
|
||||
"UPDATE SET tracked = excluded.tracked "
|
||||
";"_ns;
|
||||
|
||||
const nsresult onMissingFile = aTracked ? NS_ERROR_DOM_NOT_FOUND_ERR : NS_OK;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, setTrackedQuery));
|
||||
QM_TRY(MOZ_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, aEntryId)));
|
||||
QM_TRY(MOZ_TO_RESULT(stmt.BindBooleanByName("tracked"_ns, aTracked)));
|
||||
QM_TRY(MOZ_TO_RESULT(stmt.Execute()), onMissingFile,
|
||||
([&aConnection, &aEntryId](const auto& aRv) {
|
||||
// Usages constrains entryId to be present in Files
|
||||
MOZ_ASSERT(NS_ERROR_STORAGE_CONSTRAINT == ToNSResult(aRv));
|
||||
|
||||
// The query *should* fail if and only if file does not exist
|
||||
QM_TRY_UNWRAP(DebugOnly<bool> fileExists,
|
||||
DoesFileExist(aConnection, aEntryId), QM_VOID);
|
||||
MOZ_ASSERT(!fileExists);
|
||||
}));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Result<nsTArray<EntryId>, QMResult> GetTrackedFiles(
|
||||
const FileSystemConnection& aConnection) {
|
||||
static const nsLiteralCString getTrackedFilesQuery =
|
||||
"SELECT handle FROM Usages WHERE tracked = TRUE;"_ns;
|
||||
|
||||
nsTArray<EntryId> trackedFiles;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, getTrackedFilesQuery));
|
||||
QM_TRY_UNWRAP(bool moreResults, stmt.ExecuteStep());
|
||||
|
||||
while (moreResults) {
|
||||
QM_TRY_UNWRAP(EntryId entryId, stmt.GetEntryIdByColumn(/* Column */ 0u));
|
||||
|
||||
trackedFiles.AppendElement(entryId);
|
||||
|
||||
QM_TRY_UNWRAP(moreResults, stmt.ExecuteStep());
|
||||
}
|
||||
|
||||
return trackedFiles;
|
||||
}
|
||||
|
||||
/** This handles the file not found error by assigning 0 usage to the dangling
|
||||
* handle and puts the handle to a non-tracked state. Otherwise, when the
|
||||
* file or database cannot be reached, the file remains in the tracked state.
|
||||
*/
|
||||
template <class QuotaCacheUpdate>
|
||||
nsresult UpdateUsageForFileEntry(const FileSystemConnection& aConnection,
|
||||
const FileSystemFileManager& aFileManager,
|
||||
const EntryId& aEntryId,
|
||||
const nsLiteralCString& aUpdateQuery,
|
||||
QuotaCacheUpdate&& aUpdateCache) {
|
||||
QM_TRY_INSPECT(const auto& fileHandle, aFileManager.GetFile(aEntryId));
|
||||
|
||||
// A file could have changed in a way which doesn't allow to read its size.
|
||||
QM_TRY_UNWRAP(
|
||||
const int64_t fileSize,
|
||||
QM_OR_ELSE_WARN_IF(
|
||||
// Expression.
|
||||
MOZ_TO_RESULT_INVOKE_MEMBER(fileHandle, GetFileSize),
|
||||
// Predicate.
|
||||
([](const nsresult rv) { return rv == NS_ERROR_FILE_NOT_FOUND; }),
|
||||
// Fallback. If the file does no longer exist, treat it as 0-sized.
|
||||
ErrToDefaultOk<int64_t>));
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(aUpdateCache(fileSize)));
|
||||
|
||||
// No transaction as one statement succeeds or fails atomically
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, aUpdateQuery));
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, aEntryId)));
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(stmt.BindUsageByName("usage"_ns, fileSize)));
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(stmt.Execute()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult UpdateUsageUnsetTracked(const FileSystemConnection& aConnection,
|
||||
const FileSystemFileManager& aFileManager,
|
||||
const EntryId& aEntryId) {
|
||||
static const nsLiteralCString updateUsagesUnsetTrackedQuery =
|
||||
"UPDATE Usages SET usage = :usage, tracked = FALSE "
|
||||
"WHERE handle = :handle;"_ns;
|
||||
|
||||
auto noCacheUpdateNeeded = [](auto) { return NS_OK; };
|
||||
|
||||
return UpdateUsageForFileEntry(aConnection, aFileManager, aEntryId,
|
||||
updateUsagesUnsetTrackedQuery,
|
||||
std::move(noCacheUpdateNeeded));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the sum of usages for all file descendants of a directory entry.
|
||||
* We obtain the value with one query, which is presumably better than having a
|
||||
* separate query for each individual descendant.
|
||||
* TODO: Check if this is true
|
||||
*
|
||||
* Please see GetFileUsage documentation for why we use the latest recorded
|
||||
* value from the database instead of the file size property from the disk.
|
||||
*/
|
||||
Result<Usage, QMResult> GetUsagesOfDescendants(
|
||||
const FileSystemConnection& aConnection, const EntryId& aEntryId) {
|
||||
const nsLiteralCString descendantUsagesQuery =
|
||||
"WITH RECURSIVE traceChildren(handle, parent) AS ( "
|
||||
"SELECT handle, parent "
|
||||
"FROM Entries "
|
||||
"WHERE handle=:handle "
|
||||
"UNION "
|
||||
"SELECT Entries.handle, Entries.parent FROM traceChildren, Entries "
|
||||
"WHERE traceChildren.handle=Entries.parent ) "
|
||||
"SELECT sum(Usages.usage) "
|
||||
"FROM traceChildren INNER JOIN Usages "
|
||||
"USING(handle) "
|
||||
";"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, descendantUsagesQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, aEntryId)));
|
||||
QM_TRY_UNWRAP(const bool moreResults, stmt.ExecuteStep());
|
||||
if (!moreResults) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QM_TRY_RETURN(stmt.GetUsageByColumn(/* Column */ 0u));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get recorded usage or zero if nothing was ever written to the file.
|
||||
* Removing files is only allowed when there is no lock on the file, and their
|
||||
* usage is either correctly recorded in the database during unlock, or nothing,
|
||||
* or they remain in tracked state and the quota manager assumes their usage to
|
||||
* be equal to the latest recorded value. In all cases, the latest recorded
|
||||
* value (or nothing) is the correct amount of quota to be released.
|
||||
*/
|
||||
Result<Usage, QMResult> GetKnownUsage(const FileSystemConnection& aConnection,
|
||||
const EntryId& aEntryId) {
|
||||
const nsLiteralCString trackedUsageQuery =
|
||||
"SELECT usage FROM Usages WHERE handle = :handle ;"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, trackedUsageQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, aEntryId)));
|
||||
|
||||
QM_TRY_UNWRAP(const bool moreResults, stmt.ExecuteStep());
|
||||
if (!moreResults) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QM_TRY_RETURN(stmt.GetUsageByColumn(/* Column */ 0u));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the recorded usage only if the file is in tracked state.
|
||||
* During origin initialization, if the usage on disk is unreadable, the latest
|
||||
* recorded usage is reported to the quota manager for the tracked files.
|
||||
* To allow writing, we attempt to update the real usage with one database and
|
||||
* one file size query.
|
||||
*/
|
||||
Result<Maybe<Usage>, QMResult> GetMaybeTrackedUsage(
|
||||
const FileSystemConnection& aConnection, const EntryId& aEntryId) {
|
||||
const nsLiteralCString trackedUsageQuery =
|
||||
"SELECT usage FROM Usages WHERE tracked = TRUE AND handle = :handle "
|
||||
");"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, trackedUsageQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, aEntryId)));
|
||||
|
||||
QM_TRY_UNWRAP(const bool moreResults, stmt.ExecuteStep());
|
||||
if (!moreResults) {
|
||||
return Maybe<Usage>(Nothing());
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(Usage trackedUsage, stmt.GetUsageByColumn(/* Column */ 0u));
|
||||
|
||||
return Some(trackedUsage);
|
||||
}
|
||||
|
||||
Result<bool, nsresult> ScanTrackedFiles(
|
||||
const FileSystemConnection& aConnection,
|
||||
const FileSystemFileManager& aFileManager) {
|
||||
QM_TRY_INSPECT(const nsTArray<EntryId>& trackedFiles,
|
||||
GetTrackedFiles(aConnection).mapErr(toNSResult));
|
||||
|
||||
bool ok = true;
|
||||
for (const auto& entryId : trackedFiles) {
|
||||
// On success, tracked is set to false, otherwise its value is kept (= true)
|
||||
QM_WARNONLY_TRY(MOZ_TO_RESULT(UpdateUsageUnsetTracked(
|
||||
aConnection, aFileManager, entryId)),
|
||||
[&ok](const auto& /*aRv*/) { ok = false; });
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
Result<Ok, QMResult> DeleteEntry(const FileSystemConnection& aConnection,
|
||||
const EntryId& aEntryId) {
|
||||
// If it's a directory, deleting the handle will cascade
|
||||
const nsLiteralCString deleteEntryQuery =
|
||||
"DELETE FROM Entries "
|
||||
"WHERE handle = :handle "
|
||||
";"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, deleteEntryQuery));
|
||||
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, aEntryId)));
|
||||
|
||||
QM_TRY(QM_TO_RESULT(stmt.Execute()));
|
||||
|
||||
return Ok{};
|
||||
}
|
||||
|
||||
nsresult IncreaseCachedQuotaUsage(const FileSystemConnection& aConnection,
|
||||
int64_t aDelta) {
|
||||
RefPtr<quota::QuotaObject> quotaObject;
|
||||
{
|
||||
RefPtr<quota::QuotaObject> dummy;
|
||||
QM_TRY(MOZ_TO_RESULT(aConnection->GetQuotaObjects(
|
||||
getter_AddRefs(quotaObject), getter_AddRefs(dummy))));
|
||||
}
|
||||
MOZ_ASSERT(quotaObject);
|
||||
|
||||
QM_TRY(OkIf(quotaObject->IncreaseSize(aDelta)),
|
||||
NS_ERROR_FILE_NO_DEVICE_SPACE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Result<int32_t, QMResult> GetTrackedFilesCount(
|
||||
const FileSystemConnection& aConnection) {
|
||||
// TODO: We could query the count directly
|
||||
QM_TRY_INSPECT(const auto& trackedFiles, GetTrackedFiles(aConnection));
|
||||
|
||||
CheckedInt32 checkedFileCount = trackedFiles.Length();
|
||||
QM_TRY(OkIf(checkedFileCount.isValid()),
|
||||
Err(QMResult(NS_ERROR_ILLEGAL_VALUE)));
|
||||
|
||||
return checkedFileCount.value();
|
||||
}
|
||||
|
||||
void LogWithFilename(const FileSystemFileManager& aFileManager,
|
||||
const char* aFormat, const EntryId& aEntryId) {
|
||||
if (!LOG_ENABLED()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QM_TRY_INSPECT(const auto& localFile, aFileManager.GetFile(aEntryId),
|
||||
QM_VOID);
|
||||
|
||||
nsAutoString localPath;
|
||||
QM_TRY(MOZ_TO_RESULT(localFile->GetPath(localPath)), QM_VOID);
|
||||
LOG((aFormat, NS_ConvertUTF16toUTF8(localPath).get()));
|
||||
}
|
||||
|
||||
// TODO: Implement idle maintenance
|
||||
void TryRemoveDuringIdleMaintenance(
|
||||
const nsTArray<EntryId>& /* aItemToRemove */) {
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FileSystemDatabaseManagerVersion001::FileSystemDatabaseManagerVersion001(
|
||||
FileSystemDataManager* aDataManager, FileSystemConnection&& aConnection,
|
||||
UniquePtr<FileSystemFileManager>&& aFileManager, const EntryId& aRootEntry)
|
||||
: mDataManager(aDataManager),
|
||||
mConnection(aConnection),
|
||||
mFileManager(std::move(aFileManager)),
|
||||
mRootEntry(aRootEntry),
|
||||
mClientMetadata(aDataManager->OriginMetadataRef(),
|
||||
quota::Client::FILESYSTEM),
|
||||
mFilesOfUnknownUsage(-1) {}
|
||||
|
||||
/* static */
|
||||
nsresult FileSystemDatabaseManagerVersion001::RescanTrackedUsages(
|
||||
const FileSystemConnection& aConnection, const Origin& aOrigin) {
|
||||
QM_TRY_UNWRAP(
|
||||
FileSystemFileManager fileManager,
|
||||
data::FileSystemFileManager::CreateFileSystemFileManager(aOrigin));
|
||||
|
||||
QM_TRY_UNWRAP(bool ok, ScanTrackedFiles(aConnection, fileManager));
|
||||
if (ok) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Retry once without explicit delay
|
||||
QM_TRY_UNWRAP(ok, ScanTrackedFiles(aConnection, fileManager));
|
||||
if (!ok) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
Result<Usage, QMResult> FileSystemDatabaseManagerVersion001::GetFileUsage(
|
||||
const FileSystemConnection& aConnection) {
|
||||
|
@ -665,6 +978,12 @@ nsresult FileSystemDatabaseManagerVersion001::GetFile(
|
|||
nsCOMPtr<nsIFile>& aFile) const {
|
||||
MOZ_ASSERT(!aEntryId.IsEmpty());
|
||||
|
||||
const FileSystemEntryPair endPoints(mRootEntry, aEntryId);
|
||||
QM_TRY_UNWRAP(aPath, ResolveReversedPath(mConnection, endPoints));
|
||||
if (aPath.IsEmpty()) {
|
||||
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(aFile, mFileManager->GetOrCreateFile(aEntryId));
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(GetFileAttributes(mConnection, aEntryId, aType)));
|
||||
|
@ -673,11 +992,6 @@ nsresult FileSystemDatabaseManagerVersion001::GetFile(
|
|||
QM_TRY(MOZ_TO_RESULT(aFile->GetLastModifiedTime(&lastModTime)));
|
||||
lastModifiedMilliSeconds = static_cast<TimeStamp>(lastModTime);
|
||||
|
||||
FileSystemEntryPair endPoints(mRootEntry, aEntryId);
|
||||
QM_TRY_UNWRAP(aPath, ResolveReversedPath(mConnection, endPoints));
|
||||
if (aPath.IsEmpty()) {
|
||||
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
||||
}
|
||||
aPath.Reverse();
|
||||
|
||||
return NS_OK;
|
||||
|
@ -685,8 +999,6 @@ nsresult FileSystemDatabaseManagerVersion001::GetFile(
|
|||
|
||||
nsresult FileSystemDatabaseManagerVersion001::UpdateUsage(
|
||||
const EntryId& aEntry) {
|
||||
auto toNSResult = [](const auto& aRv) { return ToNSResult(aRv); };
|
||||
|
||||
// We don't track directories or non-existent files.
|
||||
QM_TRY_UNWRAP(bool fileExists,
|
||||
DoesFileExist(mConnection, aEntry).mapErr(toNSResult));
|
||||
|
@ -712,6 +1024,80 @@ nsresult FileSystemDatabaseManagerVersion001::UpdateUsage(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult FileSystemDatabaseManagerVersion001::UpdateCachedQuotaUsage(
|
||||
int64_t aDelta) {
|
||||
if (0 == aDelta) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (0 < aDelta) {
|
||||
return IncreaseCachedQuotaUsage(mConnection, aDelta);
|
||||
}
|
||||
|
||||
DecreaseCachedQuotaUsage(-aDelta); // Minus aDelta > 0 of quota free'd
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Result<Ok, QMResult> FileSystemDatabaseManagerVersion001::EnsureUsageIsKnown(
|
||||
const EntryId& aEntryId) {
|
||||
if (mFilesOfUnknownUsage < 0) { // Lazy initialization
|
||||
QM_TRY_UNWRAP(mFilesOfUnknownUsage, GetTrackedFilesCount(mConnection));
|
||||
}
|
||||
|
||||
if (mFilesOfUnknownUsage == 0) {
|
||||
return Ok{};
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(Maybe<Usage> oldUsage,
|
||||
GetMaybeTrackedUsage(mConnection, aEntryId));
|
||||
if (oldUsage.isNothing()) {
|
||||
return Ok{}; // Usage is 0 or it was successfully recorded at unlocking.
|
||||
}
|
||||
|
||||
auto quotaCacheUpdate = [this, oldSize = oldUsage.value()](int64_t aNewSize) {
|
||||
return UpdateCachedQuotaUsage(aNewSize - oldSize);
|
||||
};
|
||||
|
||||
static const nsLiteralCString updateUsagesKeepTrackedQuery =
|
||||
"UPDATE Usages SET usage = :usage WHERE handle = :handle;"_ns;
|
||||
|
||||
// If usage update fails, we log an error and keep things the way they were.
|
||||
QM_TRY(QM_TO_RESULT(UpdateUsageForFileEntry(
|
||||
mConnection, *mFileManager, aEntryId, updateUsagesKeepTrackedQuery,
|
||||
std::move(quotaCacheUpdate))),
|
||||
Err(QMResult(NS_ERROR_DOM_FILE_NOT_READABLE_ERR)),
|
||||
([this, &aEntryId](const auto& /*aRv*/) {
|
||||
LogWithFilename(*mFileManager, "Could not read the size of file %s",
|
||||
aEntryId);
|
||||
}));
|
||||
|
||||
// We read and updated the quota usage successfully.
|
||||
--mFilesOfUnknownUsage;
|
||||
MOZ_ASSERT(mFilesOfUnknownUsage >= 0);
|
||||
|
||||
return Ok{};
|
||||
}
|
||||
|
||||
nsresult FileSystemDatabaseManagerVersion001::BeginUsageTracking(
|
||||
const EntryId& aEntryId) {
|
||||
MOZ_ASSERT(!aEntryId.IsEmpty());
|
||||
|
||||
// If file is already tracked but we cannot read its size, error.
|
||||
// If file does not exist, this will succeed because usage is zero.
|
||||
QM_TRY(EnsureUsageIsKnown(aEntryId));
|
||||
|
||||
// If file does not exist, set usage tracking to true fails with
|
||||
// file not found error.
|
||||
return SetUsageTracking(mConnection, aEntryId, true);
|
||||
}
|
||||
|
||||
nsresult FileSystemDatabaseManagerVersion001::EndUsageTracking(
|
||||
const EntryId& aEntryId) {
|
||||
// This is expected to fail only if database is unreachable.
|
||||
return SetUsageTracking(mConnection, aEntryId, false);
|
||||
}
|
||||
|
||||
Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveDirectory(
|
||||
const FileSystemChildMetadata& aHandle, bool aRecursive) {
|
||||
MOZ_ASSERT(!aHandle.parentId().IsEmpty());
|
||||
|
@ -726,6 +1112,7 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveDirectory(
|
|||
if (aHandle.childName().IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugOnly<Name> name = aHandle.childName();
|
||||
MOZ_ASSERT(!name.inspect().IsVoid());
|
||||
|
||||
|
@ -741,57 +1128,34 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveDirectory(
|
|||
|
||||
QM_TRY_UNWRAP(bool isEmpty, IsDirectoryEmpty(mConnection, entryId));
|
||||
|
||||
if (!aRecursive && !isEmpty) {
|
||||
QM_TRY_INSPECT(const nsTArray<EntryId>& descendants,
|
||||
FindDescendants(mConnection, entryId));
|
||||
|
||||
// TODO: This is only done to return the right error for web-compat reasons.
|
||||
// The spec does not say when the locks need to be checked but wpt tests do.
|
||||
QM_TRY(OkIf(!isAnyDescendantLocked(descendants)),
|
||||
Err(QMResult(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR)));
|
||||
|
||||
if (!aRecursive && !isEmpty) {
|
||||
return Err(QMResult(NS_ERROR_DOM_INVALID_MODIFICATION_ERR));
|
||||
}
|
||||
// If it's empty or we can delete recursively, deleting the handle will
|
||||
// cascade
|
||||
|
||||
const nsLiteralCString deleteEntryQuery =
|
||||
"DELETE FROM Entries "
|
||||
"WHERE handle = :handle "
|
||||
";"_ns;
|
||||
QM_TRY_UNWRAP(Usage usage, GetUsagesOfDescendants(mConnection, entryId));
|
||||
|
||||
mozStorageTransaction transaction(
|
||||
mConnection.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
nsTArray<EntryId> removeFails;
|
||||
QM_TRY_UNWRAP(DebugOnly<Usage> removedUsage,
|
||||
mFileManager->RemoveFiles(descendants, removeFails));
|
||||
|
||||
QM_TRY_INSPECT(const nsTArray<EntryId>& descendants,
|
||||
FindDescendants(mConnection, entryId));
|
||||
// We only check the most common case. This can fail spuriously if an external
|
||||
// application writes to the file, or OS reports zero size due to corruption.
|
||||
MOZ_ASSERT_IF(removeFails.IsEmpty() && (0 == mFilesOfUnknownUsage),
|
||||
usage == removedUsage);
|
||||
|
||||
QM_TRY(OkIf(!isAnyDescendantLocked(descendants)),
|
||||
Err(QMResult(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR)));
|
||||
TryRemoveDuringIdleMaintenance(removeFails);
|
||||
|
||||
{
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(mConnection, deleteEntryQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, entryId)));
|
||||
QM_TRY(QM_TO_RESULT(stmt.Execute()));
|
||||
if (usage > 0) { // Performance!
|
||||
DecreaseCachedQuotaUsage(usage);
|
||||
}
|
||||
|
||||
QM_TRY(QM_TO_RESULT(transaction.Commit()));
|
||||
|
||||
for (const auto& child : descendants) {
|
||||
QM_WARNONLY_TRY_UNWRAP(const auto maybeFileSize,
|
||||
mFileManager->RemoveFile(child));
|
||||
|
||||
if (maybeFileSize) {
|
||||
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
quotaManager->DecreaseUsageForClient(
|
||||
quota::ClientMetadata{mDataManager->OriginMetadataRef(),
|
||||
quota::Client::FILESYSTEM},
|
||||
*maybeFileSize);
|
||||
}
|
||||
}
|
||||
QM_TRY(DeleteEntry(mConnection, entryId));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -803,6 +1167,7 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveFile(
|
|||
if (aHandle.childName().IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugOnly<Name> name = aHandle.childName();
|
||||
MOZ_ASSERT(!name.inspect().IsVoid());
|
||||
|
||||
|
@ -812,6 +1177,7 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveFile(
|
|||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, entry exists and is a file
|
||||
QM_TRY_UNWRAP(EntryId entryId, FindEntryId(mConnection, aHandle, true));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
@ -825,35 +1191,24 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveFile(
|
|||
return Err(QMResult(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR));
|
||||
}
|
||||
|
||||
const nsLiteralCString deleteEntryQuery =
|
||||
"DELETE FROM Entries "
|
||||
"WHERE handle = :handle "
|
||||
";"_ns;
|
||||
|
||||
mozStorageTransaction transaction(
|
||||
mConnection.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
{
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(mConnection, deleteEntryQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, entryId)));
|
||||
QM_TRY(QM_TO_RESULT(stmt.Execute()));
|
||||
}
|
||||
|
||||
QM_TRY(QM_TO_RESULT(transaction.Commit()));
|
||||
|
||||
QM_WARNONLY_TRY_UNWRAP(const auto maybeFileSize,
|
||||
QM_TRY_UNWRAP(Usage usage, GetKnownUsage(mConnection, entryId));
|
||||
QM_WARNONLY_TRY_UNWRAP(Maybe<Usage> removedUsage,
|
||||
mFileManager->RemoveFile(entryId));
|
||||
|
||||
if (maybeFileSize) {
|
||||
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
quotaManager->DecreaseUsageForClient(
|
||||
quota::ClientMetadata{mDataManager->OriginMetadataRef(),
|
||||
quota::Client::FILESYSTEM},
|
||||
*maybeFileSize);
|
||||
// We only check the most common case. This can fail spuriously if an external
|
||||
// application writes to the file, or OS reports zero size due to corruption.
|
||||
MOZ_ASSERT_IF(removedUsage && (0 == mFilesOfUnknownUsage),
|
||||
usage == removedUsage.value());
|
||||
if (!removedUsage) {
|
||||
TryRemoveDuringIdleMaintenance({entryId});
|
||||
}
|
||||
|
||||
if (usage > 0) { // Performance!
|
||||
DecreaseCachedQuotaUsage(usage);
|
||||
}
|
||||
|
||||
QM_TRY(DeleteEntry(mConnection, entryId));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1031,6 +1386,14 @@ Result<Path, QMResult> FileSystemDatabaseManagerVersion001::Resolve(
|
|||
|
||||
void FileSystemDatabaseManagerVersion001::Close() { mConnection->Close(); }
|
||||
|
||||
void FileSystemDatabaseManagerVersion001::DecreaseCachedQuotaUsage(
|
||||
int64_t aDelta) {
|
||||
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
quotaManager->DecreaseUsageForClient(mClientMetadata, aDelta);
|
||||
}
|
||||
|
||||
} // namespace fs::data
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATABASEMANAGERVERSION001_H_
|
||||
|
||||
#include "FileSystemDatabaseManager.h"
|
||||
#include "mozilla/dom/quota/CommonMetadata.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla::dom::fs::data {
|
||||
|
@ -39,12 +40,13 @@ class FileSystemDatabaseManagerVersion001 : public FileSystemDatabaseManager {
|
|||
FileSystemDatabaseManagerVersion001(
|
||||
FileSystemDataManager* aDataManager, FileSystemConnection&& aConnection,
|
||||
UniquePtr<FileSystemFileManager>&& aFileManager,
|
||||
const EntryId& aRootEntry)
|
||||
: mDataManager(aDataManager),
|
||||
mConnection(aConnection),
|
||||
mFileManager(std::move(aFileManager)),
|
||||
mRootEntry(aRootEntry) {}
|
||||
const EntryId& aRootEntry);
|
||||
|
||||
/* Static to allow use by quota client without instantiation */
|
||||
static nsresult RescanTrackedUsages(const FileSystemConnection& aConnection,
|
||||
const Origin& aOrigin);
|
||||
|
||||
/* Static to allow use by quota client without instantiation */
|
||||
static Result<Usage, QMResult> GetFileUsage(
|
||||
const FileSystemConnection& aConnection);
|
||||
|
||||
|
@ -81,11 +83,21 @@ class FileSystemDatabaseManagerVersion001 : public FileSystemDatabaseManager {
|
|||
|
||||
virtual void Close() override;
|
||||
|
||||
virtual nsresult BeginUsageTracking(const EntryId& aEntryId) override;
|
||||
|
||||
virtual nsresult EndUsageTracking(const EntryId& aEntryId) override;
|
||||
|
||||
virtual ~FileSystemDatabaseManagerVersion001() = default;
|
||||
|
||||
private:
|
||||
nsresult UpdateUsageInDatabase(const EntryId& aEntry, int64_t aNewDiskUsage);
|
||||
|
||||
Result<Ok, QMResult> EnsureUsageIsKnown(const EntryId& aEntryId);
|
||||
|
||||
void DecreaseCachedQuotaUsage(int64_t aDelta);
|
||||
|
||||
nsresult UpdateCachedQuotaUsage(int64_t aDelta);
|
||||
|
||||
// This is a raw pointer since we're owned by the FileSystemDataManager.
|
||||
FileSystemDataManager* MOZ_NON_OWNING_REF mDataManager;
|
||||
|
||||
|
@ -94,6 +106,10 @@ class FileSystemDatabaseManagerVersion001 : public FileSystemDatabaseManager {
|
|||
UniquePtr<FileSystemFileManager> mFileManager;
|
||||
|
||||
const EntryId mRootEntry;
|
||||
|
||||
const quota::ClientMetadata mClientMetadata;
|
||||
|
||||
int32_t mFilesOfUnknownUsage;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom::fs::data
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/quota/ResultExtensions.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileProtocolHandler.h"
|
||||
#include "nsIFileURL.h"
|
||||
#include "nsIURIMutator.h"
|
||||
#include "nsTHashMap.h"
|
||||
#include "nsXPCOM.h"
|
||||
|
||||
namespace mozilla::dom::fs::data {
|
||||
|
@ -109,6 +111,46 @@ Result<nsCOMPtr<nsIFile>, QMResult> GetOrCreateFile(
|
|||
return result;
|
||||
}
|
||||
|
||||
nsresult RemoveFileObject(const nsCOMPtr<nsIFile>& aFilePtr) {
|
||||
// If we cannot tell whether the object is file or directory, or it is a
|
||||
// directory, it is abandoned as an unknown object. If an attempt is made to
|
||||
// create a new object with the same path on disk, we regenerate the entryId
|
||||
// until the collision is resolved.
|
||||
|
||||
bool isFile = false;
|
||||
QM_TRY(MOZ_TO_RESULT(aFilePtr->IsFile(&isFile)));
|
||||
|
||||
QM_TRY(OkIf(isFile), NS_ERROR_FILE_IS_DIRECTORY);
|
||||
|
||||
QM_TRY(QM_TO_RESULT(aFilePtr->Remove(/* recursive */ false)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Unused in release builds
|
||||
Result<Usage, QMResult> GetFileSize(const nsCOMPtr<nsIFile>& aFileObject) {
|
||||
bool exists = false;
|
||||
QM_TRY(QM_TO_RESULT(aFileObject->Exists(&exists)));
|
||||
|
||||
if (!exists) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isFile = false;
|
||||
QM_TRY(QM_TO_RESULT(aFileObject->IsFile(&isFile)));
|
||||
|
||||
// We never create directories with this path: this is an unknown object
|
||||
// and the file does not exist
|
||||
QM_TRY(OkIf(isFile), 0);
|
||||
|
||||
QM_TRY_UNWRAP(Usage fileSize,
|
||||
QM_TO_RESULT_INVOKE_MEMBER(aFileObject, GetFileSize));
|
||||
|
||||
return fileSize;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
Result<nsCOMPtr<nsIFile>, QMResult> GetFileSystemDirectory(
|
||||
|
@ -236,7 +278,7 @@ Result<nsCOMPtr<nsIFile>, QMResult> FileSystemFileManager::GetOrCreateFile(
|
|||
return data::GetOrCreateFile(mTopDirectory, aEntryId);
|
||||
}
|
||||
|
||||
Result<int64_t, QMResult> FileSystemFileManager::RemoveFile(
|
||||
Result<Usage, QMResult> FileSystemFileManager::RemoveFile(
|
||||
const EntryId& aEntryId) {
|
||||
MOZ_ASSERT(!aEntryId.IsEmpty());
|
||||
QM_TRY_UNWRAP(nsCOMPtr<nsIFile> pathObject,
|
||||
|
@ -252,16 +294,58 @@ Result<int64_t, QMResult> FileSystemFileManager::RemoveFile(
|
|||
bool isFile = false;
|
||||
QM_TRY(QM_TO_RESULT(pathObject->IsFile(&isFile)));
|
||||
|
||||
// We could handle this also as a nonexistent file.
|
||||
if (!isFile) {
|
||||
return Err(QMResult(NS_ERROR_FILE_IS_DIRECTORY));
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(int64_t fileSize,
|
||||
Usage totalUsage = 0;
|
||||
#ifdef DEBUG
|
||||
QM_TRY_UNWRAP(totalUsage,
|
||||
QM_TO_RESULT_INVOKE_MEMBER(pathObject, GetFileSize));
|
||||
#endif
|
||||
|
||||
QM_TRY(QM_TO_RESULT(pathObject->Remove(/* recursive */ false)));
|
||||
|
||||
return fileSize;
|
||||
return totalUsage;
|
||||
}
|
||||
|
||||
Result<DebugOnly<Usage>, QMResult> FileSystemFileManager::RemoveFiles(
|
||||
const nsTArray<EntryId>& aEntryIds, nsTArray<EntryId>& aRemoveFails) {
|
||||
if (aEntryIds.IsEmpty()) {
|
||||
return DebugOnly<Usage>(0);
|
||||
}
|
||||
|
||||
CheckedInt64 totalUsage = 0;
|
||||
for (const auto& entryId : aEntryIds) {
|
||||
QM_WARNONLY_TRY_UNWRAP(Maybe<nsCOMPtr<nsIFile>> maybeFile,
|
||||
GetFileDestination(mTopDirectory, entryId));
|
||||
if (!maybeFile) {
|
||||
aRemoveFails.AppendElement(entryId);
|
||||
continue;
|
||||
}
|
||||
nsCOMPtr<nsIFile> fileObject = maybeFile.value();
|
||||
|
||||
// Size recorded at close is checked to be equal to the sum of sizes on disk
|
||||
#ifdef DEBUG
|
||||
QM_WARNONLY_TRY_UNWRAP(Maybe<Usage> fileSize, GetFileSize(fileObject));
|
||||
if (!fileSize) {
|
||||
aRemoveFails.AppendElement(entryId);
|
||||
continue;
|
||||
}
|
||||
totalUsage += fileSize.value();
|
||||
#endif
|
||||
|
||||
QM_WARNONLY_TRY_UNWRAP(Maybe<Ok> ok,
|
||||
MOZ_TO_RESULT(RemoveFileObject(fileObject)));
|
||||
if (!ok) {
|
||||
aRemoveFails.AppendElement(entryId);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(totalUsage.isValid());
|
||||
|
||||
return DebugOnly<Usage>(totalUsage.value());
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom::fs::data
|
||||
|
|
|
@ -129,11 +129,21 @@ class FileSystemFileManager {
|
|||
|
||||
/**
|
||||
* @brief Remove the disk-backed file object for a specified entry id.
|
||||
* Note: The returned value is 0 in release builds.
|
||||
*
|
||||
* @param aEntryId Specified id of a file system entry
|
||||
* @return Result<int64_t, QMResult> Error or file size
|
||||
* @return Result<Usage, QMResult> Error or file size
|
||||
*/
|
||||
Result<int64_t, QMResult> RemoveFile(const EntryId& aEntryId);
|
||||
Result<Usage, QMResult> RemoveFile(const EntryId& aEntryId);
|
||||
|
||||
/**
|
||||
* @brief This method can be used to try to delete a group of files from the
|
||||
* disk. In debug builds, the sum of the usages is provided ad return value,
|
||||
* in release builds the sum is not calculated.
|
||||
* The method attempts to remove all the files requested.
|
||||
*/
|
||||
Result<DebugOnly<Usage>, QMResult> RemoveFiles(
|
||||
const nsTArray<EntryId>& aEntryIds, nsTArray<EntryId>& aRemoveFails);
|
||||
|
||||
private:
|
||||
explicit FileSystemFileManager(nsCOMPtr<nsIFile>&& aTopDirectory);
|
||||
|
|
|
@ -383,8 +383,7 @@ TEST_F(TestFileSystemQuotaClient, WritesToFilesShouldIncreaseUsage) {
|
|||
PerformOnBackgroundThread(std::move(backgroundTask));
|
||||
}
|
||||
|
||||
TEST_F(TestFileSystemQuotaClient,
|
||||
DISABLED_TrackedFilesOnInitOriginShouldCauseRescan) {
|
||||
TEST_F(TestFileSystemQuotaClient, TrackedFilesOnInitOriginShouldCauseRescan) {
|
||||
auto backgroundTask = []() {
|
||||
mozilla::Atomic<bool> isCanceled{false};
|
||||
EntryId* testFileId = new EntryId();
|
||||
|
@ -450,7 +449,7 @@ TEST_F(TestFileSystemQuotaClient,
|
|||
rdm->MutableDatabaseManagerPtr());
|
||||
|
||||
// This should force a rescan
|
||||
ASSERT_TRUE(rdm->LockExclusive(*testFileId));
|
||||
ASSERT_NSEQ(NS_OK, rdm->LockExclusive(*testFileId));
|
||||
PerformOnIOThread(std::move(writingToFile), std::move(quotaClient),
|
||||
rdm->MutableDatabaseManagerPtr());
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче