From a973874d9bba6705136aaf6c1968364d0f211cc7 Mon Sep 17 00:00:00 2001 From: Tom Tung Date: Fri, 25 Oct 2019 12:02:10 +0000 Subject: [PATCH] Bug 1576593 - P2 - Change the way for tunneling quota information to SQLite; r=janv Differential Revision: https://phabricator.services.mozilla.com/D48183 --HG-- extra : moz-landing-system : lando --- dom/cache/Context.cpp | 4 ++ dom/cache/DBAction.cpp | 24 ++++---- dom/cache/DBAction.h | 4 +- dom/cache/Manager.cpp | 4 +- dom/cache/QuotaClient.cpp | 24 +++++++- dom/cache/Types.h | 3 + dom/indexedDB/ActorsParent.cpp | 96 ++++++++++++++++------------- dom/indexedDB/FileManager.h | 2 - dom/quota/ActorsParent.cpp | 109 ++++++++++++++++++++++++++++++--- dom/quota/QuotaManager.h | 19 +++++- storage/TelemetryVFS.cpp | 53 +++------------- 11 files changed, 226 insertions(+), 116 deletions(-) diff --git a/dom/cache/Context.cpp b/dom/cache/Context.cpp index 54fac9eb764d..6fa6dfdf5f7c 100644 --- a/dom/cache/Context.cpp +++ b/dom/cache/Context.cpp @@ -233,11 +233,15 @@ void Context::QuotaInitRunnable::OpenDirectory() { void Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock) { NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); + MOZ_DIAGNOSTIC_ASSERT(aLock); MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK); MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock); mDirectoryLock = aLock; + MOZ_DIAGNOSTIC_ASSERT(mDirectoryLock->GetId() >= 0); + mQuotaInfo.mDirectoryLockId = mDirectoryLock->GetId(); + if (mCanceled) { Complete(NS_ERROR_ABORT); return; diff --git a/dom/cache/DBAction.cpp b/dom/cache/DBAction.cpp index be2c8a344bcb..e0ae797fb6a1 100644 --- a/dom/cache/DBAction.cpp +++ b/dom/cache/DBAction.cpp @@ -27,6 +27,7 @@ namespace cache { using mozilla::dom::quota::AssertIsOnIOThread; using mozilla::dom::quota::Client; +using mozilla::dom::quota::IntCString; using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT; using mozilla::dom::quota::PersistenceType; @@ -123,6 +124,7 @@ void DBAction::RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, nsresult DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, mozIStorageConnection** aConnOut) { MOZ_ASSERT(!NS_IsMainThread()); + MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDirectoryLockId >= 0); MOZ_DIAGNOSTIC_ASSERT(aDBDir); MOZ_DIAGNOSTIC_ASSERT(aConnOut); @@ -168,6 +170,7 @@ void SyncDBAction::RunWithDBOnTarget(Resolver* aResolver, nsresult OpenDBConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, mozIStorageConnection** aConnOut) { MOZ_ASSERT(!NS_IsMainThread()); + MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDirectoryLockId >= -1); MOZ_DIAGNOSTIC_ASSERT(aDBDir); MOZ_DIAGNOSTIC_ASSERT(aConnOut); @@ -208,19 +211,16 @@ nsresult OpenDBConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, nsCOMPtr dbFileUrl; - nsAutoCString type; - PersistenceTypeToText(PERSISTENCE_TYPE_DEFAULT, type); + const nsCString directoryLockIdClause = + aQuotaInfo.mDirectoryLockId >= 0 + ? NS_LITERAL_CSTRING("&directoryLockId=") + + IntCString(aQuotaInfo.mDirectoryLockId) + : EmptyCString(); - nsAutoCString clientType; - Client::TypeToText(Client::DOMCACHE, clientType); - - rv = NS_MutateURI(mutator) - .SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + - NS_LITERAL_CSTRING("&group=") + aQuotaInfo.mGroup + - NS_LITERAL_CSTRING("&origin=") + aQuotaInfo.mOrigin + - NS_LITERAL_CSTRING("&clientType=") + clientType + - NS_LITERAL_CSTRING("&cache=private")) - .Finalize(dbFileUrl); + rv = + NS_MutateURI(mutator) + .SetQuery(NS_LITERAL_CSTRING("cache=private") + directoryLockIdClause) + .Finalize(dbFileUrl); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/dom/cache/DBAction.h b/dom/cache/DBAction.h index 106dab30c86a..2c7f3630d630 100644 --- a/dom/cache/DBAction.h +++ b/dom/cache/DBAction.h @@ -40,8 +40,8 @@ class DBAction : public Action { mozIStorageConnection* aConn) = 0; private: - virtual void RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, - Data* aOptionalData) override; + void RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, + Data* aOptionalData) override; nsresult OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aQuotaDir, mozIStorageConnection** aConnOut); diff --git a/dom/cache/Manager.cpp b/dom/cache/Manager.cpp index 682cfdf8eb31..f9ad882a720d 100644 --- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -148,8 +148,8 @@ class DeleteOrphanedBodyAction final : public Action { mDeletedBodyIdList.AppendElement(aBodyId); } - virtual void RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, - Data*) override { + void RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, + Data*) override { MOZ_DIAGNOSTIC_ASSERT(aResolver); MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDir); diff --git a/dom/cache/QuotaClient.cpp b/dom/cache/QuotaClient.cpp index e1059a71f8dc..0406183095d6 100644 --- a/dom/cache/QuotaClient.cpp +++ b/dom/cache/QuotaClient.cpp @@ -115,6 +115,11 @@ static nsresult LockedGetPaddingSizeFromDB(nsIFile* aDir, QuotaInfo quotaInfo; quotaInfo.mGroup = aGroup; quotaInfo.mOrigin = aOrigin; + // This should only be called during the temporary storage is initializing. + // And at that moment, we haven't constructed in-memory objects + // (e.g. QuotaObject) yet. So, passing a specific id to not get a QuotaObject + // on the TelemetryVFS. + MOZ_DIAGNOSTIC_ASSERT(quotaInfo.mDirectoryLockId == -1); nsresult rv = mozilla::dom::cache::OpenDBConnection(quotaInfo, aDir, getter_AddRefs(conn)); if (rv == NS_ERROR_FILE_NOT_FOUND || @@ -432,11 +437,24 @@ class CacheQuotaClient final : public Client { dir, DirPaddingFile::TMP_FILE) || NS_WARN_IF(NS_FAILED(mozilla::dom::cache::LockedDirectoryPaddingGet( dir, &paddingSize)))) { - rv = LockedGetPaddingSizeFromDB(dir, aGroup, aOrigin, &paddingSize); - if (NS_WARN_IF(NS_FAILED(rv))) { + if (aInitializing) { + rv = LockedGetPaddingSizeFromDB(dir, aGroup, aOrigin, &paddingSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + REPORT_TELEMETRY_ERR_IN_INIT(aInitializing, kQuotaInternalError, + Cache_GetPaddingSize); + return rv; + } + } else { + // XXXtt This should be handled in a better way. + // If there is no another action/operation for Cache on other threads, + // it means the previous action failed. And we should be safe to get + // the padding size from the database. + // However, if there is other actions access the files on Cache IO + // thread, then we shouldn't touch the file here. REPORT_TELEMETRY_ERR_IN_INIT(aInitializing, kQuotaInternalError, Cache_GetPaddingSize); - return rv; + + return NS_ERROR_ABORT; } } } diff --git a/dom/cache/Types.h b/dom/cache/Types.h index f6340e83d8ff..b9e5e8b08b82 100644 --- a/dom/cache/Types.h +++ b/dom/cache/Types.h @@ -33,6 +33,9 @@ struct QuotaInfo { nsCString mSuffix; nsCString mGroup; nsCString mOrigin; + int64_t mDirectoryLockId; + + QuotaInfo() : mDirectoryLockId(-1) {} }; typedef std::function&&)> InputStreamResolver; diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 88db6d309385..b6848bb0f95b 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -3898,9 +3898,7 @@ nsresult UpgradeSchemaFrom25_0To26_0(mozIStorageConnection* aConnection) { return NS_OK; } -nsresult GetDatabaseFileURL(nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, const nsACString& aOrigin, +nsresult GetDatabaseFileURL(nsIFile* aDatabaseFile, int64_t aDirectoryLockId, uint32_t aTelemetryId, nsIFileURL** aResult) { MOZ_ASSERT(aDatabaseFile); MOZ_ASSERT(aResult); @@ -3927,11 +3925,14 @@ nsresult GetDatabaseFileURL(nsIFile* aDatabaseFile, nsCOMPtr fileUrl; - nsAutoCString type; - PersistenceTypeToText(aPersistenceType, type); - - nsAutoCString clientType; - Client::TypeToText(Client::IDB, clientType); + // The aDirectoryLock should only be a nullptr when the temporary storage + // hasn't been initialized. At that time, we don't have in-memory objects + // (e.g. originInfo) so that it doesn't make sense to tunnel the + // directoryLockId to TelemetryVFS to get the quotaObject. + const nsCString directoryLockIdClause = + aDirectoryLockId >= 0 ? NS_LITERAL_CSTRING("&directoryLockId=") + + IntCString(aDirectoryLockId) + : EmptyCString(); nsAutoCString telemetryFilenameClause; if (aTelemetryId) { @@ -3941,12 +3942,8 @@ nsresult GetDatabaseFileURL(nsIFile* aDatabaseFile, } rv = NS_MutateURI(mutator) - .SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + - NS_LITERAL_CSTRING("&group=") + aGroup + - NS_LITERAL_CSTRING("&origin=") + aOrigin + - NS_LITERAL_CSTRING("&clientType=") + clientType + - NS_LITERAL_CSTRING("&cache=private") + - telemetryFilenameClause) + .SetQuery(NS_LITERAL_CSTRING("cache=private") + + directoryLockIdClause + telemetryFilenameClause) .Finalize(fileUrl); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -4167,14 +4164,14 @@ nsresult OpenDatabaseAndHandleBusy(mozIStorageService* aStorageService, nsresult CreateStorageConnection(nsIFile* aDBFile, nsIFile* aFMDirectory, const nsAString& aName, - PersistenceType aPersistenceType, - const nsACString& aGroup, const nsACString& aOrigin, + int64_t aDirectoryLockId, uint32_t aTelemetryId, mozIStorageConnection** aConnection) { AssertIsOnIOThread(); MOZ_ASSERT(aDBFile); MOZ_ASSERT(aFMDirectory); + MOZ_ASSERT(aDirectoryLockId >= -1); MOZ_ASSERT(aConnection); AUTO_PROFILER_LABEL("CreateStorageConnection", DOM); @@ -4183,8 +4180,8 @@ nsresult CreateStorageConnection(nsIFile* aDBFile, nsIFile* aFMDirectory, bool exists; nsCOMPtr dbFileUrl; - rv = GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin, - aTelemetryId, getter_AddRefs(dbFileUrl)); + rv = GetDatabaseFileURL(aDBFile, aDirectoryLockId, aTelemetryId, + getter_AddRefs(dbFileUrl)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -4629,14 +4626,13 @@ already_AddRefed GetFileForPath(const nsAString& aPath) { return file.forget(); } -nsresult GetStorageConnection(nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, uint32_t aTelemetryId, +nsresult GetStorageConnection(nsIFile* aDatabaseFile, int64_t aDirectoryLockId, + uint32_t aTelemetryId, mozIStorageConnection** aConnection) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!IsOnBackgroundThread()); MOZ_ASSERT(aDatabaseFile); + MOZ_ASSERT(aDirectoryLockId >= 0); MOZ_ASSERT(aConnection); AUTO_PROFILER_LABEL("GetStorageConnection", DOM); @@ -4653,8 +4649,8 @@ nsresult GetStorageConnection(nsIFile* aDatabaseFile, } nsCOMPtr dbFileUrl; - rv = GetDatabaseFileURL(aDatabaseFile, aPersistenceType, aGroup, aOrigin, - aTelemetryId, getter_AddRefs(dbFileUrl)); + rv = GetDatabaseFileURL(aDatabaseFile, aDirectoryLockId, aTelemetryId, + getter_AddRefs(dbFileUrl)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -4686,14 +4682,13 @@ nsresult GetStorageConnection(nsIFile* aDatabaseFile, } nsresult GetStorageConnection(const nsAString& aDatabaseFilePath, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, uint32_t aTelemetryId, + int64_t aDirectoryLockId, uint32_t aTelemetryId, mozIStorageConnection** aConnection) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!IsOnBackgroundThread()); MOZ_ASSERT(!aDatabaseFilePath.IsEmpty()); MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, kSQLiteSuffix)); + MOZ_ASSERT(aDirectoryLockId >= 0); MOZ_ASSERT(aConnection); nsCOMPtr dbFile = GetFileForPath(aDatabaseFilePath); @@ -4702,8 +4697,8 @@ nsresult GetStorageConnection(const nsAString& aDatabaseFilePath, return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - return GetStorageConnection(dbFile, aPersistenceType, aGroup, aOrigin, - aTelemetryId, aConnection); + return GetStorageConnection(dbFile, aDirectoryLockId, aTelemetryId, + aConnection); } /******************************************************************************* @@ -5796,6 +5791,7 @@ class Database final const nsCString mId; const nsString mFilePath; uint32_t mActiveMutableFileCount; + int64_t mDirectoryLockId; const uint32_t mTelemetryId; const PersistenceType mPersistenceType; const bool mFileHandleDisabled; @@ -5846,6 +5842,8 @@ class Database final const nsCString& Id() const { return mId; } + int64_t DirectoryLockId() const { return mDirectoryLockId; } + uint32_t TelemetryId() const { return mTelemetryId; } PersistenceType Type() const { return mPersistenceType; } @@ -6672,6 +6670,7 @@ class FactoryOp nsCString mOrigin; nsCString mDatabaseId; nsString mDatabaseFilePath; + int64_t mDirectoryLockId; State mState; bool mWaitingForPermissionRetry; bool mEnforcingQuota; @@ -8333,6 +8332,7 @@ class DatabaseMaintenance final : public Runnable { const nsCString mGroup; const nsCString mOrigin; const nsString mDatabasePath; + int64_t mDirectoryLockId; nsCOMPtr mCompleteCallback; const PersistenceType mPersistenceType; @@ -8346,7 +8346,12 @@ class DatabaseMaintenance final : public Runnable { mGroup(aGroup), mOrigin(aOrigin), mDatabasePath(aDatabasePath), - mPersistenceType(aPersistenceType) {} + mPersistenceType(aPersistenceType) { + MOZ_ASSERT(aDirectoryLock); + + MOZ_ASSERT(mDirectoryLock->GetId() >= 0); + mDirectoryLockId = mDirectoryLock->GetId(); + } const nsString& DatabasePath() const { return mDatabasePath; } @@ -10910,10 +10915,9 @@ nsresult ConnectionPool::GetOrCreateConnection( MOZ_ASSERT(!dbInfo->mDEBUGConnectionThread); nsCOMPtr storageConnection; - nsresult rv = GetStorageConnection(aDatabase->FilePath(), aDatabase->Type(), - aDatabase->Group(), aDatabase->Origin(), - aDatabase->TelemetryId(), - getter_AddRefs(storageConnection)); + nsresult rv = GetStorageConnection( + aDatabase->FilePath(), aDatabase->DirectoryLockId(), + aDatabase->TelemetryId(), getter_AddRefs(storageConnection)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -12598,6 +12602,10 @@ Database::Database(Factory* aFactory, const PrincipalInfo& aPrincipalInfo, MOZ_ASSERT(aFileManager); MOZ_ASSERT_IF(aChromeWriteAccessAllowed, aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo); + + MOZ_ASSERT(mDirectoryLock); + MOZ_ASSERT(mDirectoryLock->GetId() >= 0); + mDirectoryLockId = mDirectoryLock->GetId(); } void Database::Invalidate() { @@ -15614,8 +15622,6 @@ already_AddRefed FileManager::GetCheckedFileForId(nsIFile* aDirectory, // static nsresult FileManager::InitDirectory(nsIFile* aDirectory, nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, const nsACString& aOrigin, uint32_t aTelemetryId) { AssertIsOnIOThread(); @@ -15683,7 +15689,7 @@ nsresult FileManager::InitDirectory(nsIFile* aDirectory, nsIFile* aDatabaseFile, if (hasElements) { nsCOMPtr connection; rv = CreateStorageConnection(aDatabaseFile, aDirectory, VoidString(), - aPersistenceType, aGroup, aOrigin, + aOrigin, /* aDirectoryLockId */ -1, aTelemetryId, getter_AddRefs(connection)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -16224,8 +16230,7 @@ nsresult QuotaClient::InitOrigin(PersistenceType aPersistenceType, } } - rv = FileManager::InitDirectory(fmDirectory, databaseFile, aPersistenceType, - aGroup, aOrigin, + rv = FileManager::InitDirectory(fmDirectory, databaseFile, aOrigin, TelemetryIdForFile(databaseFile)); if (NS_WARN_IF(NS_FAILED(rv))) { REPORT_TELEMETRY_INIT_ERR(kQuotaInternalError, IDB_InitDirectory); @@ -17612,8 +17617,8 @@ void DatabaseMaintenance::PerformMaintenanceOnDatabase() { MOZ_ASSERT(databaseFile); nsCOMPtr connection; - nsresult rv = GetStorageConnection(databaseFile, mPersistenceType, mGroup, - mOrigin, TelemetryIdForFile(databaseFile), + nsresult rv = GetStorageConnection(databaseFile, mDirectoryLockId, + TelemetryIdForFile(databaseFile), getter_AddRefs(connection)); if (NS_WARN_IF(NS_FAILED(rv))) { return; @@ -19294,6 +19299,7 @@ FactoryOp::FactoryOp(Factory* aFactory, mFactory(aFactory), mContentParent(std::move(aContentParent)), mCommonParams(aCommonParams), + mDirectoryLockId(-1), mState(State::Initial), mWaitingForPermissionRetry(false), mEnforcingQuota(true), @@ -20042,11 +20048,15 @@ FactoryOp::Run() { void FactoryOp::DirectoryLockAcquired(DirectoryLock* aLock) { AssertIsOnOwningThread(); + MOZ_ASSERT(aLock); MOZ_ASSERT(mState == State::DirectoryOpenPending); MOZ_ASSERT(!mDirectoryLock); mDirectoryLock = aLock; + MOZ_ASSERT(mDirectoryLock->GetId() >= 0); + mDirectoryLockId = mDirectoryLock->GetId(); + nsresult rv = DirectoryOpen(); if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_SUCCEEDED(mResultCode)) { @@ -20267,8 +20277,8 @@ nsresult OpenDatabaseOp::DoDatabaseWork() { } nsCOMPtr connection; - rv = CreateStorageConnection(dbFile, fmDirectory, databaseName, - persistenceType, mGroup, mOrigin, mTelemetryId, + rv = CreateStorageConnection(dbFile, fmDirectory, databaseName, mOrigin, + mDirectoryLockId, mTelemetryId, getter_AddRefs(connection)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/dom/indexedDB/FileManager.h b/dom/indexedDB/FileManager.h index 104010e69963..ac71c976e646 100644 --- a/dom/indexedDB/FileManager.h +++ b/dom/indexedDB/FileManager.h @@ -52,8 +52,6 @@ class FileManager final { int64_t aId); static nsresult InitDirectory(nsIFile* aDirectory, nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, const nsACString& aOrigin, uint32_t aTelemetryId); diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index be7c2b8513fc..f619dc2f0231 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -221,6 +221,8 @@ const char kProfileDoChangeTopic[] = "profile-do-change"; const int32_t kCacheVersion = 1; +const int64_t kBypassDirectoryLockIdTableId = -1; + /****************************************************************************** * SQLite functions ******************************************************************************/ @@ -671,6 +673,8 @@ class DirectoryLockImpl final : public DirectoryLock { nsTArray mBlocking; nsTArray mBlockedOn; + const int64_t mId; + const bool mExclusive; // Internal quota manager operations use this flag to prevent directory lock @@ -685,7 +689,8 @@ class DirectoryLockImpl final : public DirectoryLock { const Nullable& aPersistenceType, const nsACString& aGroup, const OriginScope& aOriginScope, const Nullable& aClientType, bool aExclusive, - bool aInternal, OpenDirectoryListener* aOpenListener); + bool aInternal, OpenDirectoryListener* aOpenListener, + const int64_t aDirectoryLockId); void AssertIsOnOwningThread() const #ifdef DEBUG @@ -703,10 +708,26 @@ class DirectoryLockImpl final : public DirectoryLock { const OriginScope& GetOriginScope() const { return mOriginScope; } + const nsACString& GetOrigin() const { + MOZ_DIAGNOSTIC_ASSERT(GetOriginScope().IsOrigin()); + return mOriginScope.GetOrigin(); + } + const Nullable& GetClientType() const { return mClientType; } + int64_t GetId() const { return mId; } + bool IsInternal() const { return mInternal; } + // To evict inactive oirigins, we need to register directorylocks for that. + // However, since accessing the QuotaMnaager::mDirectoryLockIdTable + // require to acquire the mQuotaMutex and so does the accessing the + // originInfo. Therefore, we decide not to access the + // QuotaManager::mDirectoryLockIdTable for the internal DirectoryLock. + bool ShouldUpdateLockIdTable() const { + return mId != kBypassDirectoryLockIdTableId; + } + void SetRegistered(bool aRegistered) { mRegistered = aRegistered; } bool ShouldUpdateLockTable() { @@ -2684,6 +2705,14 @@ bool RecvShutdownQuotaManager() { * Directory lock ******************************************************************************/ +const nsACString& DirectoryLock::GetOrigin() const { + return GetDirectoryLockImpl(this)->GetOrigin(); +} + +int64_t DirectoryLock::GetId() const { + return GetDirectoryLockImpl(this)->GetId(); +} + already_AddRefed DirectoryLock::Specialize( PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, Client::Type aClientType) const { @@ -2697,13 +2726,15 @@ DirectoryLockImpl::DirectoryLockImpl( QuotaManager* aQuotaManager, const Nullable& aPersistenceType, const nsACString& aGroup, const OriginScope& aOriginScope, const Nullable& aClientType, - bool aExclusive, bool aInternal, OpenDirectoryListener* aOpenListener) + bool aExclusive, bool aInternal, OpenDirectoryListener* aOpenListener, + const int64_t aId) : mQuotaManager(aQuotaManager), mPersistenceType(aPersistenceType), mGroup(aGroup), mOriginScope(aOriginScope), mClientType(aClientType), mOpenListener(aOpenListener), + mId(aId), mExclusive(aExclusive), mInternal(aInternal), mRegistered(false), @@ -2811,6 +2842,7 @@ already_AddRefed DirectoryLockImpl::Specialize( MOZ_ASSERT(aClientType < Client::TypeMax()); MOZ_ASSERT(!mOpenListener); MOZ_ASSERT(mBlockedOn.IsEmpty()); + MOZ_ASSERT(mQuotaManager); if (NS_WARN_IF(mExclusive)) { return nullptr; @@ -2819,7 +2851,8 @@ already_AddRefed DirectoryLockImpl::Specialize( RefPtr lock = new DirectoryLockImpl( mQuotaManager, Nullable(aPersistenceType), aGroup, OriginScope::FromOrigin(aOrigin), Nullable(aClientType), - /* aExclusive */ false, mInternal, /* aOpenListener */ nullptr); + /* aExclusive */ false, mInternal, /* aOpenListener */ nullptr, + mQuotaManager->GenerateDirectoryLockId()); if (NS_WARN_IF(!Overlaps(*lock))) { return nullptr; @@ -3425,6 +3458,7 @@ QuotaManager::QuotaManager() : mQuotaMutex("QuotaManager.mQuotaMutex"), mTemporaryStorageLimit(0), mTemporaryStorageUsage(0), + mNextDirectoryLockId(0), mTemporaryStorageInitialized(false), mCacheUsable(false) { AssertIsOnOwningThread(); @@ -3532,9 +3566,9 @@ auto QuotaManager::CreateDirectoryLock( MOZ_ASSERT_IF(!aInternal, aClientType.Value() < Client::TypeMax()); MOZ_ASSERT_IF(!aInternal, aOpenListener); - RefPtr lock = - new DirectoryLockImpl(this, aPersistenceType, aGroup, aOriginScope, - aClientType, aExclusive, aInternal, aOpenListener); + RefPtr lock = new DirectoryLockImpl( + this, aPersistenceType, aGroup, aOriginScope, aClientType, aExclusive, + aInternal, aOpenListener, GenerateDirectoryLockId()); mPendingDirectoryLocks.AppendElement(lock); @@ -3570,7 +3604,8 @@ auto QuotaManager::CreateDirectoryLockForEviction( this, Nullable(aPersistenceType), aGroup, OriginScope::FromOrigin(aOrigin), Nullable(), /* aExclusive */ true, - /* aInternal */ true, nullptr); + /* aInternal */ true, nullptr, /* aDirectoryLockId */ + kBypassDirectoryLockIdTableId); #ifdef DEBUG for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) { @@ -3590,6 +3625,12 @@ void QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock) { mDirectoryLocks.AppendElement(aLock); + if (aLock->ShouldUpdateLockIdTable()) { + MutexAutoLock lock(mQuotaMutex); + + mDirectoryLockIdTable.Put(aLock->GetId(), aLock); + } + if (aLock->ShouldUpdateLockTable()) { const Nullable& persistenceType = aLock->GetPersistenceType(); @@ -3621,9 +3662,17 @@ void QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock) { void QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl* aLock) { AssertIsOnOwningThread(); + MOZ_ASSERT(aLock); MOZ_ALWAYS_TRUE(mDirectoryLocks.RemoveElement(aLock)); + if (aLock->ShouldUpdateLockIdTable()) { + MutexAutoLock lock(mQuotaMutex); + + MOZ_DIAGNOSTIC_ASSERT(mDirectoryLockIdTable.Get(aLock->GetId())); + mDirectoryLockIdTable.Remove(aLock->GetId()); + } + if (aLock->ShouldUpdateLockTable()) { const Nullable& persistenceType = aLock->GetPersistenceType(); @@ -4646,6 +4695,36 @@ already_AddRefed QuotaManager::GetQuotaObject( aFileSize, aFileSizeOut); } +already_AddRefed QuotaManager::GetQuotaObject( + const int64_t aDirectoryLockId, const nsAString& aPath) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + MOZ_DIAGNOSTIC_ASSERT(aDirectoryLockId != kBypassDirectoryLockIdTableId); + + Maybe lock; + if (!IsOnBackgroundThread()) { + lock.emplace(mQuotaMutex); + } + + DirectoryLockImpl* directoryLock; + if (!mDirectoryLockIdTable.Get(aDirectoryLockId, &directoryLock)) { + MOZ_CRASH("Getting quota object for an unregistered directory lock?"); + } + + MOZ_DIAGNOSTIC_ASSERT(directoryLock); + MOZ_DIAGNOSTIC_ASSERT(!directoryLock->GetPersistenceType().IsNull()); + PersistenceType persistenceType = directoryLock->GetPersistenceType().Value(); + + nsCString group(directoryLock->GetGroup()); + nsCString origin(directoryLock->GetOrigin()); + + MOZ_DIAGNOSTIC_ASSERT(!directoryLock->GetClientType().IsNull()); + Client::Type clientType = directoryLock->GetClientType().Value(); + + lock.reset(); + + return GetQuotaObject(persistenceType, group, origin, clientType, aPath); +} + Nullable QuotaManager::OriginPersisted(const nsACString& aGroup, const nsACString& aOrigin) { AssertIsOnIOThread(); @@ -7600,6 +7679,22 @@ bool QuotaManager::IsSanitizedOriginValid(const nsACString& aSanitizedOrigin) { return valid; } +int64_t QuotaManager::GenerateDirectoryLockId() { + const int64_t directroylockId = mNextDirectoryLockId; + + CheckedInt64 result = CheckedInt64(mNextDirectoryLockId) + 1; + if (result.isValid()) { + mNextDirectoryLockId = result.value(); + } else { + // When this happens, it means we have run out of ids for directory locks. + // To avoid something unexpected happen and assuming there shouldn't have + // INT64_MAX directorylock exist at the same time, we reset the id here. + mNextDirectoryLockId = 0; + } + + return directroylockId; +} + /******************************************************************************* * Local class implementations ******************************************************************************/ diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index 671f7e81c09f..4a8614115785 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -63,6 +63,10 @@ class DirectoryLock : public RefCountedObject { friend class DirectoryLockImpl; public: + const nsACString& GetOrigin() const; + + int64_t GetId() const; + already_AddRefed Specialize(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, @@ -232,6 +236,9 @@ class QuotaManager final : public BackgroundThreadObject { int64_t aFileSize = -1, int64_t* aFileSizeOut = nullptr); + already_AddRefed GetQuotaObject(const int64_t aDirectoryLockId, + const nsAString& aPath); + Nullable OriginPersisted(const nsACString& aGroup, const nsACString& aOrigin); @@ -545,6 +552,8 @@ class QuotaManager final : public BackgroundThreadObject { bool IsSanitizedOriginValid(const nsACString& aSanitizedOrigin); + int64_t GenerateDirectoryLockId(); + static void ShutdownTimerCallback(nsITimer* aTimer, void* aClosure); // Thread on which IO is performed. @@ -562,9 +571,16 @@ class QuotaManager final : public BackgroundThreadObject { // Maintains a list of directory locks that are queued. nsTArray> mPendingDirectoryLocks; - // Maintains a list of directory locks that are acquired or queued. + // Maintains a list of directory locks that are acquired or queued. It can be + // accessed on the owning (PBackground) thread only. nsTArray mDirectoryLocks; + // Only modifed on the owning thread, but read on multiple threads. Therefore + // all modifications (including those on the owning thread) and all reads off + // the owning thread must be protected by mQuotaMutex. In other words, only + // reads on the owning thread don't have to be protected by mQuotaMutex. + nsDataHashtable mDirectoryLockIdTable; + // Directory lock tables that are used to update origin access time. DirectoryLockTable mTemporaryDirectoryLockTable; DirectoryLockTable mDefaultDirectoryLockTable; @@ -594,6 +610,7 @@ class QuotaManager final : public BackgroundThreadObject { uint64_t mTemporaryStorageLimit; uint64_t mTemporaryStorageUsage; + int64_t mNextDirectoryLockId; bool mTemporaryStorageInitialized; bool mCacheUsable; }; diff --git a/storage/TelemetryVFS.cpp b/storage/TelemetryVFS.cpp index 24e89670ae86..5b259e6775ad 100644 --- a/storage/TelemetryVFS.cpp +++ b/storage/TelemetryVFS.cpp @@ -299,57 +299,22 @@ already_AddRefed GetQuotaObjectFromNameAndParameters( MOZ_ASSERT(zName); MOZ_ASSERT(zURIParameterKey); - const char* persistenceType = - sqlite3_uri_parameter(zURIParameterKey, "persistenceType"); - if (!persistenceType) { + const char* directoryLockIdParam = + sqlite3_uri_parameter(zURIParameterKey, "directoryLockId"); + if (!directoryLockIdParam) { return nullptr; } - const char* group = sqlite3_uri_parameter(zURIParameterKey, "group"); - if (!group) { - NS_WARNING("SQLite URI had 'persistenceType' but not 'group'?!"); - return nullptr; - } - - const char* origin = sqlite3_uri_parameter(zURIParameterKey, "origin"); - if (!origin) { - NS_WARNING( - "SQLite URI had 'persistenceType' and 'group' but not " - "'origin'?!"); - return nullptr; - } - - const char* clientType = - sqlite3_uri_parameter(zURIParameterKey, "clientType"); - if (!clientType) { - NS_WARNING( - "SQLite URI had 'persistenceType', 'group' and 'origin' but not " - "'clientType'?!"); - return nullptr; - } - - // Re-escape group and origin to make sure we get the right quota group and - // origin. - nsAutoCString escGroup; - nsresult rv = - NS_EscapeURL(nsDependentCString(group), esc_Query, escGroup, fallible); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - nsAutoCString escOrigin; - rv = NS_EscapeURL(nsDependentCString(origin), esc_Query, escOrigin, fallible); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } + nsresult rv; + const int64_t directoryLockId = + nsDependentCString(directoryLockIdParam).ToInteger64(&rv); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); QuotaManager* quotaManager = QuotaManager::Get(); MOZ_ASSERT(quotaManager); - return quotaManager->GetQuotaObject( - PersistenceTypeFromText(nsDependentCString(persistenceType)), escGroup, - escOrigin, Client::TypeFromText(nsDependentCString(clientType)), - NS_ConvertUTF8toUTF16(zName)); + return quotaManager->GetQuotaObject(directoryLockId, + NS_ConvertUTF8toUTF16(zName)); } void MaybeEstablishQuotaControl(const char* zName, telemetry_file* pFile,