tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CanonicalQuotaObject.cpp (10013B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CanonicalQuotaObject.h"
      8 
      9 #include "GroupInfo.h"
     10 #include "GroupInfoPair.h"
     11 #include "OriginInfo.h"
     12 #include "mozilla/dom/StorageActivityService.h"
     13 #include "mozilla/dom/quota/AssertionsImpl.h"
     14 #include "mozilla/dom/quota/NotifyUtils.h"
     15 #include "mozilla/dom/quota/OriginDirectoryLock.h"
     16 #include "mozilla/dom/quota/QuotaManager.h"
     17 #include "mozilla/ipc/BackgroundParent.h"
     18 
     19 namespace mozilla::dom::quota {
     20 
     21 NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::AddRef() {
     22  QuotaManager* quotaManager = QuotaManager::Get();
     23  if (!quotaManager) {
     24    NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
     25 
     26    return ++mRefCnt;
     27  }
     28 
     29  MutexAutoLock lock(quotaManager->mQuotaMutex);
     30 
     31  return ++mRefCnt;
     32 }
     33 
     34 NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::Release() {
     35  QuotaManager* quotaManager = QuotaManager::Get();
     36  if (!quotaManager) {
     37    NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
     38 
     39    nsrefcnt count = --mRefCnt;
     40    if (count == 0) {
     41      mRefCnt = 1;
     42      delete this;
     43      return 0;
     44    }
     45 
     46    return mRefCnt;
     47  }
     48 
     49  {
     50    MutexAutoLock lock(quotaManager->mQuotaMutex);
     51 
     52    --mRefCnt;
     53 
     54    if (mRefCnt > 0) {
     55      return mRefCnt;
     56    }
     57 
     58    if (mOriginInfo) {
     59      mOriginInfo->mCanonicalQuotaObjects.Remove(mPath);
     60    }
     61  }
     62 
     63  delete this;
     64  return 0;
     65 }
     66 
     67 bool CanonicalQuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate) {
     68  QuotaManager* quotaManager = QuotaManager::Get();
     69  MOZ_ASSERT(quotaManager);
     70 
     71  MutexAutoLock lock(quotaManager->mQuotaMutex);
     72 
     73  return LockedMaybeUpdateSize(aSize, aTruncate);
     74 }
     75 
     76 bool CanonicalQuotaObject::IncreaseSize(int64_t aDelta) {
     77  MOZ_ASSERT(aDelta >= 0);
     78 
     79  QuotaManager* quotaManager = QuotaManager::Get();
     80  MOZ_ASSERT(quotaManager);
     81 
     82  MutexAutoLock lock(quotaManager->mQuotaMutex);
     83 
     84  AssertNoOverflow(mSize, aDelta);
     85  int64_t size = mSize + aDelta;
     86 
     87  return LockedMaybeUpdateSize(size, /* aTruncate */ false);
     88 }
     89 
     90 void CanonicalQuotaObject::DisableQuotaCheck() {
     91  QuotaManager* quotaManager = QuotaManager::Get();
     92  MOZ_ASSERT(quotaManager);
     93 
     94  MutexAutoLock lock(quotaManager->mQuotaMutex);
     95 
     96  mQuotaCheckDisabled = true;
     97 }
     98 
     99 void CanonicalQuotaObject::EnableQuotaCheck() {
    100  QuotaManager* quotaManager = QuotaManager::Get();
    101  MOZ_ASSERT(quotaManager);
    102 
    103  MutexAutoLock lock(quotaManager->mQuotaMutex);
    104 
    105  mQuotaCheckDisabled = false;
    106 }
    107 
    108 bool CanonicalQuotaObject::LockedMaybeUpdateSize(int64_t aSize, bool aTruncate)
    109    MOZ_NO_THREAD_SAFETY_ANALYSIS {
    110  QuotaManager* quotaManager = QuotaManager::Get();
    111  MOZ_ASSERT(quotaManager);
    112 
    113  quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
    114 
    115  if (mWritingDone == false && mOriginInfo) {
    116    mWritingDone = true;
    117    StorageActivityService::SendActivity(mOriginInfo->mOrigin);
    118  }
    119 
    120  if (mQuotaCheckDisabled) {
    121    return true;
    122  }
    123 
    124  if (mSize == aSize) {
    125    return true;
    126  }
    127 
    128  if (!mOriginInfo) {
    129    mSize = aSize;
    130    return true;
    131  }
    132 
    133  GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
    134  MOZ_ASSERT(groupInfo);
    135 
    136  if (mSize > aSize) {
    137    if (aTruncate) {
    138      const int64_t delta = mSize - aSize;
    139 
    140      AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta);
    141      quotaManager->mTemporaryStorageUsage -= delta;
    142 
    143      if (!mOriginInfo->LockedPersisted()) {
    144        AssertNoUnderflow(groupInfo->mUsage, delta);
    145        groupInfo->mUsage -= delta;
    146      }
    147 
    148      AssertNoUnderflow(mOriginInfo->mUsage, delta);
    149      mOriginInfo->mUsage -= delta;
    150 
    151      MOZ_ASSERT(mOriginInfo->mClientUsages[mClientType].isSome());
    152      AssertNoUnderflow(mOriginInfo->mClientUsages[mClientType].value(), delta);
    153      mOriginInfo->mClientUsages[mClientType] =
    154          Some(mOriginInfo->mClientUsages[mClientType].value() - delta);
    155 
    156      mSize = aSize;
    157    }
    158    return true;
    159  }
    160 
    161  MOZ_ASSERT(mSize < aSize);
    162 
    163  const auto& complementaryPersistenceTypes =
    164      ComplementaryPersistenceTypes(groupInfo->mPersistenceType);
    165 
    166  uint64_t delta = aSize - mSize;
    167 
    168  AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
    169  uint64_t newClientUsage =
    170      mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
    171 
    172  AssertNoOverflow(mOriginInfo->mUsage, delta);
    173  uint64_t newUsage = mOriginInfo->mUsage + delta;
    174 
    175  // Temporary storage has no limit for origin usage (there's a group and the
    176  // global limit though).
    177 
    178  uint64_t newGroupUsage = groupInfo->mUsage;
    179  if (!mOriginInfo->LockedPersisted()) {
    180    AssertNoOverflow(groupInfo->mUsage, delta);
    181    newGroupUsage += delta;
    182 
    183    uint64_t groupUsage = groupInfo->mUsage;
    184    for (const auto& complementaryPersistenceType :
    185         complementaryPersistenceTypes) {
    186      const auto& complementaryGroupInfo =
    187          groupInfo->mGroupInfoPair->LockedGetGroupInfo(
    188              complementaryPersistenceType);
    189 
    190      if (complementaryGroupInfo) {
    191        AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
    192        groupUsage += complementaryGroupInfo->mUsage;
    193      }
    194    }
    195 
    196    // Temporary storage has a hard limit for group usage (20 % of the global
    197    // limit).
    198    AssertNoOverflow(groupUsage, delta);
    199    if (groupUsage + delta > quotaManager->GetGroupLimit()) {
    200      return false;
    201    }
    202  }
    203 
    204  AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
    205  uint64_t newTemporaryStorageUsage =
    206      quotaManager->mTemporaryStorageUsage + delta;
    207 
    208  if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) {
    209    // This will block the thread without holding the lock while waitting.
    210 
    211    AutoTArray<RefPtr<OriginDirectoryLock>, 10> locks;
    212    uint64_t sizeToBeFreed;
    213 
    214    if (::mozilla::ipc::IsOnBackgroundThread()) {
    215      MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
    216 
    217      sizeToBeFreed = quotaManager->CollectOriginsForEviction(delta, locks);
    218    } else {
    219      sizeToBeFreed =
    220          quotaManager->LockedCollectOriginsForEviction(delta, locks);
    221    }
    222 
    223    if (!sizeToBeFreed) {
    224      uint64_t usage = quotaManager->mTemporaryStorageUsage;
    225 
    226      MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
    227 
    228      NotifyStoragePressure(*quotaManager, usage);
    229 
    230      return false;
    231    }
    232 
    233    NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
    234 
    235    {
    236      MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
    237 
    238      for (const auto& lock : locks) {
    239        quotaManager->DeleteOriginDirectory(lock->OriginMetadata());
    240      }
    241    }
    242 
    243    // Relocked.
    244 
    245    NS_ASSERTION(mOriginInfo, "How come?!");
    246 
    247    for (const auto& lock : locks) {
    248      MOZ_ASSERT(!(lock->GetPersistenceType() == groupInfo->mPersistenceType &&
    249                   lock->Origin() == mOriginInfo->mOrigin),
    250                 "Deleted itself!");
    251 
    252      quotaManager->LockedRemoveQuotaForOrigin(lock->OriginMetadata());
    253    }
    254 
    255    // We unlocked and relocked several times so we need to recompute all the
    256    // essential variables and recheck the group limit.
    257 
    258    AssertNoUnderflow(aSize, mSize);
    259    delta = aSize - mSize;
    260 
    261    AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
    262    newClientUsage = mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
    263 
    264    AssertNoOverflow(mOriginInfo->mUsage, delta);
    265    newUsage = mOriginInfo->mUsage + delta;
    266 
    267    newGroupUsage = groupInfo->mUsage;
    268    if (!mOriginInfo->LockedPersisted()) {
    269      AssertNoOverflow(groupInfo->mUsage, delta);
    270      newGroupUsage += delta;
    271 
    272      uint64_t groupUsage = groupInfo->mUsage;
    273 
    274      for (const auto& complementaryPersistenceType :
    275           complementaryPersistenceTypes) {
    276        const auto& complementaryGroupInfo =
    277            groupInfo->mGroupInfoPair->LockedGetGroupInfo(
    278                complementaryPersistenceType);
    279 
    280        if (complementaryGroupInfo) {
    281          AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
    282          groupUsage += complementaryGroupInfo->mUsage;
    283        }
    284      }
    285 
    286      AssertNoOverflow(groupUsage, delta);
    287      if (groupUsage + delta > quotaManager->GetGroupLimit()) {
    288        // Unfortunately some other thread increased the group usage in the
    289        // meantime and we are not below the group limit anymore.
    290 
    291        // However, the origin eviction must be finalized in this case too.
    292        MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
    293 
    294        quotaManager->FinalizeOriginEviction(std::move(locks));
    295 
    296        return false;
    297      }
    298    }
    299 
    300    AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
    301    newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
    302 
    303    NS_ASSERTION(
    304        newTemporaryStorageUsage <= quotaManager->mTemporaryStorageLimit,
    305        "How come?!");
    306 
    307    // Ok, we successfully freed enough space and the operation can continue
    308    // without throwing the quota error.
    309    mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
    310 
    311    mOriginInfo->mUsage = newUsage;
    312    if (!mOriginInfo->LockedPersisted()) {
    313      groupInfo->mUsage = newGroupUsage;
    314    }
    315    quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
    316    ;
    317 
    318    // Some other thread could increase the size in the meantime, but no more
    319    // than this one.
    320    MOZ_ASSERT(mSize < aSize);
    321    mSize = aSize;
    322 
    323    // Finally, release IO thread only objects and allow next synchronized
    324    // ops for the evicted origins.
    325    MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
    326 
    327    quotaManager->FinalizeOriginEviction(std::move(locks));
    328 
    329    return true;
    330  }
    331 
    332  mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
    333 
    334  mOriginInfo->mUsage = newUsage;
    335  if (!mOriginInfo->LockedPersisted()) {
    336    groupInfo->mUsage = newGroupUsage;
    337  }
    338  quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
    339 
    340  mSize = aSize;
    341 
    342  return true;
    343 }
    344 
    345 }  // namespace mozilla::dom::quota