Bug 856032 - 'Quota management enabled even for origins with unlimited permission granted'. r=janv.

This commit is contained in:
Ben Turner 2013-03-30 11:30:16 -07:00
parent ec8daa4925
commit a47376de61
10 changed files with 124 additions and 48 deletions

View File

@ -15,6 +15,7 @@
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
#include "CheckQuotaHelper.h"
#include "nsContentUtils.h"
#include "nsDOMStorage.h"
#include "nsNetUtil.h"
@ -38,9 +39,10 @@
#define PERMISSION_DENIED nsIPermissionManager::DENY_ACTION
#define PERMISSION_PROMPT nsIPermissionManager::ALLOW_ACTION
using namespace mozilla;
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
using mozilla::dom::quota::CheckQuotaHelper;
using mozilla::Preferences;
namespace {
@ -111,14 +113,19 @@ CheckPermissionsHelper::Run()
// we cannot set the permission from the child).
if (permission != PERMISSION_PROMPT &&
IndexedDatabaseManager::IsMainProcess()) {
NS_ASSERTION(mWindow, "Null window!");
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
nsIPrincipal* windowPrincipal = sop->GetPrincipal();
NS_ASSERTION(windowPrincipal, "Null principal!");
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_STATE(permissionManager);
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
rv = permissionManager->AddFromPrincipal(sop->GetPrincipal(),
rv = permissionManager->AddFromPrincipal(windowPrincipal,
PERMISSION_INDEXEDDB, permission,
nsIPermissionManager::EXPIRE_NEVER,
0);
@ -141,6 +148,24 @@ CheckPermissionsHelper::Run()
window.swap(mWindow);
if (permission == PERMISSION_ALLOWED) {
// If we're running from a window then we should check the quota permission
// as well. If we don't have a window then we're opening a chrome database
// and the quota will be unlimited already.
if (window) {
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
nsIPrincipal* windowPrincipal = sop->GetPrincipal();
NS_ASSERTION(windowPrincipal, "Null principal!");
uint32_t quotaPermission =
CheckQuotaHelper::GetQuotaPermission(windowPrincipal);
if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) {
helper->SetUnlimitedQuotaAllowed();
}
}
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
NS_ASSERTION(quotaManager, "This should never be null!");

View File

@ -2691,22 +2691,25 @@ CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
char copyBuffer[FILE_COPY_BUFFER_SIZE];
uint32_t numRead;
rv = aInputStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
NS_ENSURE_SUCCESS(rv, rv);
rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (numRead <= 0) {
if (!numRead) {
break;
}
uint32_t numWrite;
rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
if (numWrite < numRead) {
// Must have hit the quota limit.
return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
}
} while (true);
rv = aOutputStream->Flush();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;
}
@ -2914,7 +2917,7 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = CopyData(inputStream, outputStream);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ENSURE_SUCCESS(rv, rv);
cloneFile.mFile->AddFileInfo(fileInfo);
}

View File

@ -72,6 +72,7 @@ LOCAL_INCLUDES = \
-I$(topsrcdir)/db/sqlite3/src \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/dom/src/storage \
-I$(topsrcdir)/dom/quota \
-I$(topsrcdir)/xpcom/build \
$(NULL)

View File

@ -1651,7 +1651,7 @@ OpenDatabaseHelper::DoDatabaseWork()
nsresult rv =
quotaManager->EnsureOriginIsInitialized(mASCIIOrigin,
mPrivilege,
mTrackingQuota,
getter_AddRefs(dbDirectory));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

View File

@ -6,12 +6,15 @@
#define mozilla_dom_indexeddb_opendatabasehelper_h__
#include "AsyncConnectionHelper.h"
#include "nsIRunnable.h"
#include "mozilla/dom/quota/StoragePrivilege.h"
#include "DatabaseInfo.h"
#include "IDBDatabase.h"
#include "IDBRequest.h"
#include "nsIRunnable.h"
class mozIStorageConnection;
namespace mozilla {
@ -22,8 +25,12 @@ class ContentParent;
BEGIN_INDEXEDDB_NAMESPACE
class CheckPermissionsHelper;
class OpenDatabaseHelper : public HelperBase
{
friend class CheckPermissionsHelper;
typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
public:
@ -39,7 +46,8 @@ public:
mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
mLoadDBMetadata(false)
mLoadDBMetadata(false),
mTrackingQuota(aPrivilege != mozilla::dom::quota::Chrome)
{
NS_ASSERTION(!aForDeletion || !aRequestedVersion,
"Can't be for deletion and request a version!");
@ -102,6 +110,12 @@ protected:
void DispatchErrorEvent();
virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
// Called by CheckPermissionsHelper on the main thread before dispatch.
void SetUnlimitedQuotaAllowed()
{
mTrackingQuota = false;
}
// Methods only called on the DB thread
nsresult DoDatabaseWork();
@ -140,6 +154,7 @@ protected:
nsRefPtr<DatabaseInfo> mDBInfo;
bool mLoadDBMetadata;
bool mTrackingQuota;
};
END_INDEXEDDB_NAMESPACE

View File

@ -35,29 +35,14 @@ namespace {
inline
uint32_t
GetQuotaPermissions(nsIDOMWindow* aWindow)
GetQuotaPermissionFromWindow(nsIDOMWindow* aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
if (nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) {
return nsIPermissionManager::ALLOW_ACTION;
}
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_TRUE(permissionManager, nsIPermissionManager::DENY_ACTION);
uint32_t permission;
nsresult rv =
permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(),
PERMISSION_INDEXEDDB_UNLIMITED,
&permission);
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
return permission;
return CheckQuotaHelper::GetQuotaPermission(sop->GetPrincipal());
}
} // anonymous namespace
@ -128,6 +113,30 @@ CheckQuotaHelper::Cancel()
}
}
// static
uint32_t
CheckQuotaHelper::GetQuotaPermission(nsIPrincipal* aPrincipal)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aPrincipal, "Null principal!");
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
return nsIPermissionManager::ALLOW_ACTION;
}
nsCOMPtr<nsIPermissionManager> pm =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
uint32_t permission;
nsresult rv = pm->TestPermissionFromPrincipal(aPrincipal,
PERMISSION_INDEXEDDB_UNLIMITED,
&permission);
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
return permission;
}
NS_IMPL_THREADSAFE_ISUPPORTS3(CheckQuotaHelper, nsIRunnable,
nsIInterfaceRequestor,
nsIObserver)
@ -141,7 +150,7 @@ CheckQuotaHelper::Run()
if (NS_SUCCEEDED(rv)) {
if (!mHasPrompted) {
mPromptResult = GetQuotaPermissions(mWindow);
mPromptResult = GetQuotaPermissionFromWindow(mWindow);
}
if (mHasPrompted) {

View File

@ -16,6 +16,7 @@
#include "mozilla/Mutex.h"
#include "mozilla/CondVar.h"
class nsIPrincipal;
class nsPIDOMWindow;
BEGIN_QUOTA_NAMESPACE
@ -37,6 +38,8 @@ public:
void Cancel();
static uint32_t GetQuotaPermission(nsIPrincipal* aPrincipal);
private:
nsPIDOMWindow* mWindow;

View File

@ -76,7 +76,10 @@ FileQuotaStreamWithWrite<FileStreamBase>::Write(const char* aBuf,
if (!FileQuotaStreamWithWrite::
mQuotaObject->MaybeAllocateMoreSpace(offset, aCount)) {
return NS_ERROR_FAILURE;
// A quota failure shouldn't be confused with a write failure. We don't
// attempt the write and inform the caller.
*_retval = 0;
return NS_OK;
}
}

View File

@ -481,10 +481,14 @@ QuotaManager::Init()
void
QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin,
int64_t aLimit,
int64_t aUsage)
int64_t aLimitBytes,
int64_t aUsageBytes)
{
OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage);
MOZ_ASSERT(aUsageBytes >= 0);
MOZ_ASSERT(aLimitBytes > 0);
MOZ_ASSERT(aUsageBytes <= aLimitBytes);
OriginInfo* info = new OriginInfo(aOrigin, aLimitBytes, aUsageBytes);
MutexAutoLock lock(mQuotaMutex);
@ -818,7 +822,7 @@ QuotaManager::GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
nsresult
QuotaManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
StoragePrivilege aPrivilege,
bool aTrackQuota,
nsIFile** aDirectory)
{
#ifdef DEBUG
@ -870,7 +874,7 @@ QuotaManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
// We need to initialize directories of all clients if they exists and also
// get the total usage to initialize the quota.
nsAutoPtr<UsageRunnable> runnable;
if (aPrivilege != Chrome) {
if (aTrackQuota) {
runnable = new UsageRunnable();
}
@ -915,12 +919,25 @@ QuotaManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
NS_ENSURE_SUCCESS(rv, rv);
}
if (aPrivilege != Chrome) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
if (aTrackQuota) {
uint64_t quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
uint64_t totalUsageBytes = runnable->TotalUsage();
quotaManager->InitQuotaForOrigin(aOrigin, GetStorageQuotaMB(),
runnable->TotalUsage());
if (totalUsageBytes > quotaMaxBytes) {
NS_WARNING("Origin is already using more storage than allowed by quota!");
return NS_ERROR_UNEXPECTED;
}
// XXX This signed/unsigned mismatch must be fixed.
int64_t limit = quotaMaxBytes >= uint64_t(INT64_MAX) ?
INT64_MAX :
int64_t(quotaMaxBytes);
int64_t usage = totalUsageBytes >= uint64_t(INT64_MAX) ?
INT64_MAX :
int64_t(totalUsageBytes);
InitQuotaForOrigin(aOrigin, limit, usage);
}
mInitializedOrigins.AppendElement(aOrigin);

View File

@ -82,8 +82,8 @@ public:
void
InitQuotaForOrigin(const nsACString& aOrigin,
int64_t aLimit,
int64_t aUsage);
int64_t aLimitBytes,
int64_t aUsageBytes);
void
DecreaseUsageForOrigin(const nsACString& aOrigin,
@ -192,7 +192,7 @@ public:
nsresult
EnsureOriginIsInitialized(const nsACString& aOrigin,
StoragePrivilege aPrivilege,
bool aTrackQuota,
nsIFile** aDirectory);
void