LCOV - code coverage report
Current view: top level - toolkit/components/url-classifier - HashStore.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 561 0.2 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : // Originally based on Chrome sources:
       3             : // Copyright (c) 2010 The Chromium Authors. All rights reserved.
       4             : //
       5             : // Redistribution and use in source and binary forms, with or without
       6             : // modification, are permitted provided that the following conditions are
       7             : // met:
       8             : //
       9             : //    * Redistributions of source code must retain the above copyright
      10             : // notice, this list of conditions and the following disclaimer.
      11             : //    * Redistributions in binary form must reproduce the above
      12             : // copyright notice, this list of conditions and the following disclaimer
      13             : // in the documentation and/or other materials provided with the
      14             : // distribution.
      15             : //    * Neither the name of Google Inc. nor the names of its
      16             : // contributors may be used to endorse or promote products derived from
      17             : // this software without specific prior written permission.
      18             : //
      19             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      20             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      21             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      22             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      23             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      24             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      25             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      26             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      27             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      28             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      29             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      30             : 
      31             : 
      32             : #include "HashStore.h"
      33             : #include "nsICryptoHash.h"
      34             : #include "nsISeekableStream.h"
      35             : #include "nsIStreamConverterService.h"
      36             : #include "nsNetUtil.h"
      37             : #include "nsCheckSummedOutputStream.h"
      38             : #include "prio.h"
      39             : #include "mozilla/Logging.h"
      40             : #include "mozilla/IntegerPrintfMacros.h"
      41             : #include "zlib.h"
      42             : #include "Classifier.h"
      43             : #include "nsUrlClassifierDBService.h"
      44             : #include "mozilla/Telemetry.h"
      45             : 
      46             : // Main store for SafeBrowsing protocol data. We store
      47             : // known add/sub chunks, prefixes and completions in memory
      48             : // during an update, and serialize to disk.
      49             : // We do not store the add prefixes, those are retrieved by
      50             : // decompressing the PrefixSet cache whenever we need to apply
      51             : // an update.
      52             : //
      53             : // byte slicing: Many of the 4-byte values stored here are strongly
      54             : // correlated in the upper bytes, and uncorrelated in the lower
      55             : // bytes. Because zlib/DEFLATE requires match lengths of at least
      56             : // 3 to achieve good compression, and we don't get those if only
      57             : // the upper 16-bits are correlated, it is worthwhile to slice 32-bit
      58             : // values into 4 1-byte slices and compress the slices individually.
      59             : // The slices corresponding to MSBs will compress very well, and the
      60             : // slice corresponding to LSB almost nothing. Because of this, we
      61             : // only apply DEFLATE to the 3 most significant bytes, and store the
      62             : // LSB uncompressed.
      63             : //
      64             : // byte sliced (numValues) data format:
      65             : //    uint32_t compressed-size
      66             : //    compressed-size bytes    zlib DEFLATE data
      67             : //        0...numValues        byte MSB of 4-byte numValues data
      68             : //    uint32_t compressed-size
      69             : //    compressed-size bytes    zlib DEFLATE data
      70             : //        0...numValues        byte 2nd byte of 4-byte numValues data
      71             : //    uint32_t compressed-size
      72             : //    compressed-size bytes    zlib DEFLATE data
      73             : //        0...numValues        byte 3rd byte of 4-byte numValues data
      74             : //    0...numValues            byte LSB of 4-byte numValues data
      75             : //
      76             : // Store data format:
      77             : //    uint32_t magic
      78             : //    uint32_t version
      79             : //    uint32_t numAddChunks
      80             : //    uint32_t numSubChunks
      81             : //    uint32_t numAddPrefixes
      82             : //    uint32_t numSubPrefixes
      83             : //    uint32_t numAddCompletes
      84             : //    uint32_t numSubCompletes
      85             : //    0...numAddChunks               uint32_t addChunk
      86             : //    0...numSubChunks               uint32_t subChunk
      87             : //    byte sliced (numAddPrefixes)   uint32_t add chunk of AddPrefixes
      88             : //    byte sliced (numSubPrefixes)   uint32_t add chunk of SubPrefixes
      89             : //    byte sliced (numSubPrefixes)   uint32_t sub chunk of SubPrefixes
      90             : //    byte sliced (numSubPrefixes)   uint32_t SubPrefixes
      91             : //    0...numAddCompletes            32-byte Completions + uint32_t addChunk
      92             : //    0...numSubCompletes            32-byte Completions + uint32_t addChunk
      93             : //                                                       + uint32_t subChunk
      94             : //    16-byte MD5 of all preceding data
      95             : 
      96             : // Name of the SafeBrowsing store
      97             : #define STORE_SUFFIX ".sbstore"
      98             : 
      99             : // MOZ_LOG=UrlClassifierDbService:5
     100             : extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
     101             : #define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
     102             : #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
     103             : 
     104             : namespace mozilla {
     105             : namespace safebrowsing {
     106             : 
     107             : const uint32_t STORE_MAGIC = 0x1231af3b;
     108             : const uint32_t CURRENT_VERSION = 3;
     109             : 
     110             : nsresult
     111           0 : TableUpdateV2::NewAddPrefix(uint32_t aAddChunk, const Prefix& aHash)
     112             : {
     113           0 :   AddPrefix *add = mAddPrefixes.AppendElement(fallible);
     114           0 :   if (!add) return NS_ERROR_OUT_OF_MEMORY;
     115           0 :   add->addChunk = aAddChunk;
     116           0 :   add->prefix = aHash;
     117           0 :   return NS_OK;
     118             : }
     119             : 
     120             : nsresult
     121           0 : TableUpdateV2::NewSubPrefix(uint32_t aAddChunk, const Prefix& aHash, uint32_t aSubChunk)
     122             : {
     123           0 :   SubPrefix *sub = mSubPrefixes.AppendElement(fallible);
     124           0 :   if (!sub) return NS_ERROR_OUT_OF_MEMORY;
     125           0 :   sub->addChunk = aAddChunk;
     126           0 :   sub->prefix = aHash;
     127           0 :   sub->subChunk = aSubChunk;
     128           0 :   return NS_OK;
     129             : }
     130             : 
     131             : nsresult
     132           0 : TableUpdateV2::NewAddComplete(uint32_t aAddChunk, const Completion& aHash)
     133             : {
     134           0 :   AddComplete *add = mAddCompletes.AppendElement(fallible);
     135           0 :   if (!add) return NS_ERROR_OUT_OF_MEMORY;
     136           0 :   add->addChunk = aAddChunk;
     137           0 :   add->complete = aHash;
     138           0 :   return NS_OK;
     139             : }
     140             : 
     141             : nsresult
     142           0 : TableUpdateV2::NewSubComplete(uint32_t aAddChunk, const Completion& aHash, uint32_t aSubChunk)
     143             : {
     144           0 :   SubComplete *sub = mSubCompletes.AppendElement(fallible);
     145           0 :   if (!sub) return NS_ERROR_OUT_OF_MEMORY;
     146           0 :   sub->addChunk = aAddChunk;
     147           0 :   sub->complete = aHash;
     148           0 :   sub->subChunk = aSubChunk;
     149           0 :   return NS_OK;
     150             : }
     151             : 
     152             : nsresult
     153           0 : TableUpdateV2::NewMissPrefix(const Prefix& aPrefix)
     154             : {
     155           0 :   Prefix *prefix = mMissPrefixes.AppendElement(aPrefix, fallible);
     156           0 :   if (!prefix) return NS_ERROR_OUT_OF_MEMORY;
     157           0 :   return NS_OK;
     158             : }
     159             : 
     160             : void
     161           0 : TableUpdateV4::NewPrefixes(int32_t aSize, const nsACString& aPrefixes)
     162             : {
     163           0 :   NS_ENSURE_TRUE_VOID(aSize >= 4 && aSize <= COMPLETE_SIZE);
     164           0 :   NS_ENSURE_TRUE_VOID(aPrefixes.Length() % aSize == 0);
     165           0 :   NS_ENSURE_TRUE_VOID(!mPrefixesMap.Get(aSize));
     166             : 
     167           0 :   int numOfPrefixes = aPrefixes.Length() / aSize;
     168             : 
     169           0 :   if (aSize > 4) {
     170             :     // TODO Bug 1364043 we may have a better API to record multiple samples into
     171             :     // histograms with one call
     172             : #ifdef NIGHTLY_BUILD
     173           0 :     for (int i = 0; i < std::min(20, numOfPrefixes); i++) {
     174           0 :       Telemetry::Accumulate(Telemetry::URLCLASSIFIER_VLPS_LONG_PREFIXES, aSize);
     175             :     }
     176             : #endif
     177           0 :   } else if (LOG_ENABLED()) {
     178             :     const uint32_t* p =
     179           0 :       reinterpret_cast<const uint32_t*>(ToNewCString(aPrefixes));
     180             : 
     181             :     // Dump the first/last 10 fixed-length prefixes for debugging.
     182           0 :     LOG(("* The first 10 (maximum) fixed-length prefixes: "));
     183           0 :     for (int i = 0; i < std::min(10, numOfPrefixes); i++) {
     184           0 :       const uint8_t* c = reinterpret_cast<const uint8_t*>(&p[i]);
     185           0 :       LOG(("%.2X%.2X%.2X%.2X", c[0], c[1], c[2], c[3]));
     186             :     }
     187             : 
     188           0 :     LOG(("* The last 10 (maximum) fixed-length prefixes: "));
     189           0 :     for (int i = std::max(0, numOfPrefixes - 10); i < numOfPrefixes; i++) {
     190           0 :       const uint8_t* c = reinterpret_cast<const uint8_t*>(&p[i]);
     191           0 :       LOG(("%.2X%.2X%.2X%.2X", c[0], c[1], c[2], c[3]));
     192             :     }
     193             : 
     194           0 :     LOG(("---- %u fixed-length prefixes in total.", aPrefixes.Length() / aSize));
     195             :   }
     196             : 
     197           0 :   mPrefixesMap.Put(aSize, new nsCString(aPrefixes));
     198             : }
     199             : 
     200             : nsresult
     201           0 : TableUpdateV4::NewRemovalIndices(const uint32_t* aIndices, size_t aNumOfIndices)
     202             : {
     203           0 :   MOZ_ASSERT(mRemovalIndiceArray.IsEmpty(), "mRemovalIndiceArray must be empty");
     204             : 
     205           0 :   if (!mRemovalIndiceArray.SetCapacity(aNumOfIndices, fallible)) {
     206             :     return NS_ERROR_OUT_OF_MEMORY;
     207             :   }
     208             : 
     209           0 :   for (size_t i = 0; i < aNumOfIndices; i++) {
     210           0 :     mRemovalIndiceArray.AppendElement(aIndices[i]);
     211             :   }
     212             :   return NS_OK;
     213             : }
     214             : 
     215             : void
     216           0 : TableUpdateV4::NewChecksum(const std::string& aChecksum)
     217             : {
     218           0 :   mChecksum.Assign(aChecksum.data(), aChecksum.size());
     219           0 : }
     220             : 
     221             : nsresult
     222           0 : TableUpdateV4::NewFullHashResponse(const Prefix& aPrefix,
     223             :                                    const CachedFullHashResponse& aResponse)
     224             : {
     225             :   CachedFullHashResponse* response =
     226           0 :     mFullHashResponseMap.LookupOrAdd(aPrefix.ToUint32());
     227           0 :   if (!response) {
     228             :     return NS_ERROR_OUT_OF_MEMORY;
     229             :   }
     230           0 :   *response = aResponse;
     231           0 :   return NS_OK;
     232             : }
     233             : 
     234           0 : HashStore::HashStore(const nsACString& aTableName,
     235             :                      const nsACString& aProvider,
     236           0 :                      nsIFile* aRootStoreDir)
     237             :   : mTableName(aTableName)
     238             :   , mInUpdate(false)
     239           0 :   , mFileSize(0)
     240             : {
     241           0 :   nsresult rv = Classifier::GetPrivateStoreDirectory(aRootStoreDir,
     242             :                                                      aTableName,
     243             :                                                      aProvider,
     244           0 :                                                      getter_AddRefs(mStoreDirectory));
     245           0 :   if (NS_FAILED(rv)) {
     246           0 :     LOG(("Failed to get private store directory for %s", mTableName.get()));
     247           0 :     mStoreDirectory = aRootStoreDir;
     248             :   }
     249           0 : }
     250             : 
     251             : HashStore::~HashStore()
     252             : = default;
     253             : 
     254             : nsresult
     255           0 : HashStore::Reset()
     256             : {
     257           0 :   LOG(("HashStore resetting"));
     258             : 
     259           0 :   nsCOMPtr<nsIFile> storeFile;
     260           0 :   nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
     261           0 :   NS_ENSURE_SUCCESS(rv, rv);
     262             : 
     263           0 :   rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(STORE_SUFFIX));
     264           0 :   NS_ENSURE_SUCCESS(rv, rv);
     265             : 
     266           0 :   rv = storeFile->Remove(false);
     267           0 :   NS_ENSURE_SUCCESS(rv, rv);
     268             : 
     269           0 :   mFileSize = 0;
     270             : 
     271           0 :   return NS_OK;
     272             : }
     273             : 
     274             : nsresult
     275           0 : HashStore::CheckChecksum(uint32_t aFileSize)
     276             : {
     277           0 :   if (!mInputStream) {
     278             :     return NS_OK;
     279             :   }
     280             : 
     281             :   // Check for file corruption by
     282             :   // comparing the stored checksum to actual checksum of data
     283           0 :   nsAutoCString hash;
     284           0 :   nsAutoCString compareHash;
     285             :   char *data;
     286             :   uint32_t read;
     287             : 
     288           0 :   nsresult rv = CalculateChecksum(hash, aFileSize, true);
     289           0 :   NS_ENSURE_SUCCESS(rv, rv);
     290             : 
     291           0 :   compareHash.GetMutableData(&data, hash.Length());
     292             : 
     293           0 :   if (hash.Length() > aFileSize) {
     294           0 :     NS_WARNING("SafeBrowing file not long enough to store its hash");
     295           0 :     return NS_ERROR_FAILURE;
     296             :   }
     297           0 :   nsCOMPtr<nsISeekableStream> seekIn = do_QueryInterface(mInputStream);
     298           0 :   rv = seekIn->Seek(nsISeekableStream::NS_SEEK_SET, aFileSize - hash.Length());
     299           0 :   NS_ENSURE_SUCCESS(rv, rv);
     300             : 
     301           0 :   rv = mInputStream->Read(data, hash.Length(), &read);
     302           0 :   NS_ENSURE_SUCCESS(rv, rv);
     303           0 :   NS_ASSERTION(read == hash.Length(), "Could not read hash bytes");
     304             : 
     305           0 :   if (!hash.Equals(compareHash)) {
     306           0 :     NS_WARNING("Safebrowing file failed checksum.");
     307           0 :     return NS_ERROR_FAILURE;
     308             :   }
     309             : 
     310             :   return NS_OK;
     311             : }
     312             : 
     313             : nsresult
     314           0 : HashStore::Open()
     315             : {
     316           0 :   nsCOMPtr<nsIFile> storeFile;
     317           0 :   nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
     318           0 :   NS_ENSURE_SUCCESS(rv, rv);
     319             : 
     320           0 :   rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
     321           0 :   NS_ENSURE_SUCCESS(rv, rv);
     322             : 
     323           0 :   nsCOMPtr<nsIInputStream> origStream;
     324           0 :   rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile,
     325           0 :                                   PR_RDONLY | nsIFile::OS_READAHEAD);
     326             : 
     327           0 :   if (rv == NS_ERROR_FILE_NOT_FOUND) {
     328           0 :     UpdateHeader();
     329           0 :     return NS_OK;
     330             :   }
     331           0 :   NS_ENSURE_SUCCESS(rv, rv);
     332             : 
     333             :   int64_t fileSize;
     334           0 :   rv = storeFile->GetFileSize(&fileSize);
     335           0 :   NS_ENSURE_SUCCESS(rv, rv);
     336             : 
     337           0 :   if (fileSize < 0 || fileSize > UINT32_MAX) {
     338             :     return NS_ERROR_FAILURE;
     339             :   }
     340             : 
     341           0 :   mFileSize = static_cast<uint32_t>(fileSize);
     342           0 :   rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
     343           0 :                                  origStream.forget(), mFileSize);
     344           0 :   NS_ENSURE_SUCCESS(rv, rv);
     345             : 
     346           0 :   rv = ReadHeader();
     347           0 :   NS_ENSURE_SUCCESS(rv, rv);
     348             : 
     349           0 :   rv = SanityCheck();
     350           0 :   NS_ENSURE_SUCCESS(rv, rv);
     351             : 
     352             :   return NS_OK;
     353             : }
     354             : 
     355             : nsresult
     356           0 : HashStore::ReadHeader()
     357             : {
     358           0 :   if (!mInputStream) {
     359           0 :     UpdateHeader();
     360           0 :     return NS_OK;
     361             :   }
     362             : 
     363           0 :   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
     364           0 :   nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     365           0 :   NS_ENSURE_SUCCESS(rv, rv);
     366             : 
     367           0 :   void *buffer = &mHeader;
     368           0 :   rv = NS_ReadInputStreamToBuffer(mInputStream,
     369             :                                   &buffer,
     370           0 :                                   sizeof(Header));
     371           0 :   NS_ENSURE_SUCCESS(rv, rv);
     372             : 
     373             :   return NS_OK;
     374             : }
     375             : 
     376             : nsresult
     377           0 : HashStore::SanityCheck() const
     378             : {
     379           0 :   if (mHeader.magic != STORE_MAGIC || mHeader.version != CURRENT_VERSION) {
     380           0 :     NS_WARNING("Unexpected header data in the store.");
     381           0 :     return NS_ERROR_FAILURE;
     382             :   }
     383             : 
     384             :   return NS_OK;
     385             : }
     386             : 
     387             : nsresult
     388           0 : HashStore::CalculateChecksum(nsAutoCString& aChecksum,
     389             :                              uint32_t aFileSize,
     390             :                              bool aChecksumPresent)
     391             : {
     392           0 :   aChecksum.Truncate();
     393             : 
     394             :   // Reset mInputStream to start
     395           0 :   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
     396           0 :   nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     397             : 
     398           0 :   nsCOMPtr<nsICryptoHash> hash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
     399           0 :   NS_ENSURE_SUCCESS(rv, rv);
     400             : 
     401             :   // Size of MD5 hash in bytes
     402           0 :   const uint32_t CHECKSUM_SIZE = 16;
     403             : 
     404             :   // MD5 is not a secure hash function, but since this is a filesystem integrity
     405             :   // check, this usage is ok.
     406           0 :   rv = hash->Init(nsICryptoHash::MD5);
     407           0 :   NS_ENSURE_SUCCESS(rv, rv);
     408             : 
     409           0 :   if (!aChecksumPresent) {
     410             :     // Hash entire file
     411           0 :     rv = hash->UpdateFromStream(mInputStream, UINT32_MAX);
     412             :   } else {
     413             :     // Hash everything but last checksum bytes
     414           0 :     if (aFileSize < CHECKSUM_SIZE) {
     415           0 :       NS_WARNING("SafeBrowsing file isn't long enough to store its checksum");
     416           0 :       return NS_ERROR_FAILURE;
     417             :     }
     418           0 :     rv = hash->UpdateFromStream(mInputStream, aFileSize - CHECKSUM_SIZE);
     419             :   }
     420           0 :   NS_ENSURE_SUCCESS(rv, rv);
     421             : 
     422           0 :   rv = hash->Finish(false, aChecksum);
     423           0 :   NS_ENSURE_SUCCESS(rv, rv);
     424             : 
     425             :   return NS_OK;
     426             : }
     427             : 
     428             : void
     429           0 : HashStore::UpdateHeader()
     430             : {
     431           0 :   mHeader.magic = STORE_MAGIC;
     432           0 :   mHeader.version = CURRENT_VERSION;
     433             : 
     434           0 :   mHeader.numAddChunks = mAddChunks.Length();
     435           0 :   mHeader.numSubChunks = mSubChunks.Length();
     436           0 :   mHeader.numAddPrefixes = mAddPrefixes.Length();
     437           0 :   mHeader.numSubPrefixes = mSubPrefixes.Length();
     438           0 :   mHeader.numAddCompletes = mAddCompletes.Length();
     439           0 :   mHeader.numSubCompletes = mSubCompletes.Length();
     440           0 : }
     441             : 
     442             : nsresult
     443           0 : HashStore::ReadChunkNumbers()
     444             : {
     445           0 :   if (!mInputStream || AlreadyReadChunkNumbers()) {
     446             :     return NS_OK;
     447             :   }
     448             : 
     449           0 :   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
     450           0 :   nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
     451           0 :                                sizeof(Header));
     452           0 :   NS_ENSURE_SUCCESS(rv, rv);
     453             : 
     454           0 :   rv = mAddChunks.Read(mInputStream, mHeader.numAddChunks);
     455           0 :   NS_ENSURE_SUCCESS(rv, rv);
     456           0 :   NS_ASSERTION(mAddChunks.Length() == mHeader.numAddChunks, "Read the right amount of add chunks.");
     457             : 
     458           0 :   rv = mSubChunks.Read(mInputStream, mHeader.numSubChunks);
     459           0 :   NS_ENSURE_SUCCESS(rv, rv);
     460           0 :   NS_ASSERTION(mSubChunks.Length() == mHeader.numSubChunks, "Read the right amount of sub chunks.");
     461             : 
     462             :   return NS_OK;
     463             : }
     464             : 
     465             : nsresult
     466           0 : HashStore::ReadHashes()
     467             : {
     468           0 :   if (!mInputStream) {
     469             :     // BeginUpdate has been called but Open hasn't initialized mInputStream,
     470             :     // because the existing HashStore is empty.
     471             :     return NS_OK;
     472             :   }
     473             : 
     474           0 :   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
     475             : 
     476           0 :   uint32_t offset = sizeof(Header);
     477           0 :   offset += (mHeader.numAddChunks + mHeader.numSubChunks) * sizeof(uint32_t);
     478           0 :   nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
     479           0 :   NS_ENSURE_SUCCESS(rv, rv);
     480             : 
     481           0 :   rv = ReadAddPrefixes();
     482           0 :   NS_ENSURE_SUCCESS(rv, rv);
     483             : 
     484           0 :   rv = ReadSubPrefixes();
     485           0 :   NS_ENSURE_SUCCESS(rv, rv);
     486             : 
     487             :   // If completions was read before, then we are done here.
     488           0 :   if (AlreadyReadCompletions()) {
     489             :     return NS_OK;
     490             :   }
     491             : 
     492           0 :   rv = ReadTArray(mInputStream, &mAddCompletes, mHeader.numAddCompletes);
     493           0 :   NS_ENSURE_SUCCESS(rv, rv);
     494             : 
     495           0 :   rv = ReadTArray(mInputStream, &mSubCompletes, mHeader.numSubCompletes);
     496           0 :   NS_ENSURE_SUCCESS(rv, rv);
     497             : 
     498             :   return NS_OK;
     499             : }
     500             : 
     501             : 
     502             : nsresult
     503           0 : HashStore::ReadCompletions()
     504             : {
     505           0 :   if (!mInputStream || AlreadyReadCompletions()) {
     506             :     return NS_OK;
     507             :   }
     508             : 
     509           0 :   nsCOMPtr<nsIFile> storeFile;
     510           0 :   nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
     511           0 :   NS_ENSURE_SUCCESS(rv, rv);
     512             : 
     513           0 :   rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(STORE_SUFFIX));
     514           0 :   NS_ENSURE_SUCCESS(rv, rv);
     515             : 
     516           0 :   uint32_t offset = mFileSize -
     517           0 :                     sizeof(struct AddComplete) * mHeader.numAddCompletes -
     518           0 :                     sizeof(struct SubComplete) * mHeader.numSubCompletes -
     519           0 :                     nsCheckSummedOutputStream::CHECKSUM_SIZE;
     520             : 
     521           0 :   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
     522             : 
     523           0 :   rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
     524           0 :   NS_ENSURE_SUCCESS(rv, rv);
     525             : 
     526           0 :   rv = ReadTArray(mInputStream, &mAddCompletes, mHeader.numAddCompletes);
     527           0 :   NS_ENSURE_SUCCESS(rv, rv);
     528             : 
     529           0 :   rv = ReadTArray(mInputStream, &mSubCompletes, mHeader.numSubCompletes);
     530           0 :   NS_ENSURE_SUCCESS(rv, rv);
     531             : 
     532             :   return NS_OK;
     533             : }
     534             : 
     535             : nsresult
     536           0 : HashStore::PrepareForUpdate()
     537             : {
     538           0 :   nsresult rv = CheckChecksum(mFileSize);
     539           0 :   NS_ENSURE_SUCCESS(rv, rv);
     540             : 
     541           0 :   rv = ReadChunkNumbers();
     542           0 :   NS_ENSURE_SUCCESS(rv, rv);
     543             : 
     544           0 :   rv = ReadHashes();
     545           0 :   NS_ENSURE_SUCCESS(rv, rv);
     546             : 
     547             :   return NS_OK;
     548             : }
     549             : 
     550             : nsresult
     551           0 : HashStore::BeginUpdate()
     552             : {
     553             :   // Check wether the file is corrupted and read the rest of the store
     554             :   // in memory.
     555           0 :   nsresult rv = PrepareForUpdate();
     556           0 :   NS_ENSURE_SUCCESS(rv, rv);
     557             : 
     558             :   // Close input stream, won't be needed any more and
     559             :   // we will rewrite ourselves.
     560           0 :   if (mInputStream) {
     561           0 :     rv = mInputStream->Close();
     562           0 :     NS_ENSURE_SUCCESS(rv, rv);
     563             :   }
     564             : 
     565           0 :   mInUpdate = true;
     566             : 
     567           0 :   return NS_OK;
     568             : }
     569             : 
     570             : template<class T>
     571             : static nsresult
     572           0 : Merge(ChunkSet* aStoreChunks,
     573             :       FallibleTArray<T>* aStorePrefixes,
     574             :       const ChunkSet& aUpdateChunks,
     575             :       FallibleTArray<T>& aUpdatePrefixes,
     576             :       bool aAllowMerging = false)
     577             : {
     578           0 :   EntrySort(aUpdatePrefixes);
     579             : 
     580           0 :   auto storeIter = aStorePrefixes->begin();
     581           0 :   auto storeEnd = aStorePrefixes->end();
     582             : 
     583             :   // use a separate array so we can keep the iterators valid
     584             :   // if the nsTArray grows
     585           0 :   nsTArray<T> adds;
     586             : 
     587           0 :   for (const auto& updatePrefix : aUpdatePrefixes) {
     588             :     // skip this chunk if we already have it, unless we're
     589             :     // merging completions, in which case we'll always already
     590             :     // have the chunk from the original prefix
     591           0 :     if (aStoreChunks->Has(updatePrefix.Chunk()))
     592           0 :       if (!aAllowMerging)
     593             :         continue;
     594             :     // XXX: binary search for insertion point might be faster in common
     595             :     // case?
     596           0 :     while (storeIter < storeEnd && (storeIter->Compare(updatePrefix) < 0)) {
     597             :       // skip forward to matching element (or not...)
     598           0 :       storeIter++;
     599             :     }
     600             :     // no match, add
     601           0 :     if (storeIter == storeEnd
     602           0 :         || storeIter->Compare(updatePrefix) != 0) {
     603           0 :       if (!adds.AppendElement(updatePrefix, fallible)) {
     604           0 :         return NS_ERROR_OUT_OF_MEMORY;
     605             :       }
     606             :     }
     607             :   }
     608             : 
     609             :   // Chunks can be empty, but we should still report we have them
     610             :   // to make the chunkranges continuous.
     611           0 :   aStoreChunks->Merge(aUpdateChunks);
     612             : 
     613           0 :   if (!aStorePrefixes->AppendElements(adds, fallible))
     614             :     return NS_ERROR_OUT_OF_MEMORY;
     615             : 
     616           0 :   EntrySort(*aStorePrefixes);
     617             : 
     618           0 :   return NS_OK;
     619             : }
     620             : 
     621             : nsresult
     622           0 : HashStore::ApplyUpdate(RefPtr<TableUpdateV2> aUpdate)
     623             : {
     624           0 :   MOZ_ASSERT(mTableName.Equals(aUpdate->TableName()));
     625             : 
     626           0 :   nsresult rv = mAddExpirations.Merge(aUpdate->AddExpirations());
     627           0 :   NS_ENSURE_SUCCESS(rv, rv);
     628             : 
     629           0 :   rv = mSubExpirations.Merge(aUpdate->SubExpirations());
     630           0 :   NS_ENSURE_SUCCESS(rv, rv);
     631             : 
     632           0 :   rv = Expire();
     633           0 :   NS_ENSURE_SUCCESS(rv, rv);
     634             : 
     635           0 :   rv = Merge(&mAddChunks, &mAddPrefixes,
     636           0 :              aUpdate->AddChunks(), aUpdate->AddPrefixes());
     637           0 :   NS_ENSURE_SUCCESS(rv, rv);
     638             : 
     639           0 :   rv = Merge(&mAddChunks, &mAddCompletes,
     640           0 :              aUpdate->AddChunks(), aUpdate->AddCompletes(), true);
     641           0 :   NS_ENSURE_SUCCESS(rv, rv);
     642             : 
     643           0 :   rv = Merge(&mSubChunks, &mSubPrefixes,
     644           0 :              aUpdate->SubChunks(), aUpdate->SubPrefixes());
     645           0 :   NS_ENSURE_SUCCESS(rv, rv);
     646             : 
     647           0 :   rv = Merge(&mSubChunks, &mSubCompletes,
     648           0 :              aUpdate->SubChunks(), aUpdate->SubCompletes(), true);
     649           0 :   NS_ENSURE_SUCCESS(rv, rv);
     650             : 
     651             :   return NS_OK;
     652             : }
     653             : 
     654             : nsresult
     655           0 : HashStore::Rebuild()
     656             : {
     657           0 :   NS_ASSERTION(mInUpdate, "Must be in update to rebuild.");
     658             : 
     659           0 :   nsresult rv = ProcessSubs();
     660           0 :   NS_ENSURE_SUCCESS(rv, rv);
     661             : 
     662           0 :   UpdateHeader();
     663             : 
     664           0 :   return NS_OK;
     665             : }
     666             : 
     667             : void
     668           0 : HashStore::ClearCompletes()
     669             : {
     670           0 :   NS_ASSERTION(mInUpdate, "Must be in update to clear completes.");
     671             : 
     672           0 :   mAddCompletes.Clear();
     673           0 :   mSubCompletes.Clear();
     674             : 
     675           0 :   UpdateHeader();
     676           0 : }
     677             : 
     678             : template<class T>
     679             : static void
     680           0 : ExpireEntries(FallibleTArray<T>* aEntries, ChunkSet& aExpirations)
     681             : {
     682           0 :   auto addIter = aEntries->begin();
     683             : 
     684           0 :   for (const auto& entry : *aEntries) {
     685           0 :     if (!aExpirations.Has(entry.Chunk())) {
     686           0 :       *addIter = entry;
     687           0 :       addIter++;
     688             :     }
     689             :   }
     690             : 
     691           0 :   aEntries->TruncateLength(addIter - aEntries->begin());
     692           0 : }
     693             : 
     694             : nsresult
     695           0 : HashStore::Expire()
     696             : {
     697           0 :   ExpireEntries(&mAddPrefixes, mAddExpirations);
     698           0 :   ExpireEntries(&mAddCompletes, mAddExpirations);
     699           0 :   ExpireEntries(&mSubPrefixes, mSubExpirations);
     700           0 :   ExpireEntries(&mSubCompletes, mSubExpirations);
     701             : 
     702           0 :   mAddChunks.Remove(mAddExpirations);
     703           0 :   mSubChunks.Remove(mSubExpirations);
     704             : 
     705           0 :   mAddExpirations.Clear();
     706           0 :   mSubExpirations.Clear();
     707             : 
     708           0 :   return NS_OK;
     709             : }
     710             : 
     711             : template<class T>
     712           0 : nsresult DeflateWriteTArray(nsIOutputStream* aStream, nsTArray<T>& aIn)
     713             : {
     714           0 :   uLongf insize = aIn.Length() * sizeof(T);
     715           0 :   uLongf outsize = compressBound(insize);
     716           0 :   FallibleTArray<char> outBuff;
     717           0 :   if (!outBuff.SetLength(outsize, fallible)) {
     718             :     return NS_ERROR_OUT_OF_MEMORY;
     719             :   }
     720             : 
     721           0 :   int zerr = compress(reinterpret_cast<Bytef*>(outBuff.Elements()),
     722             :                       &outsize,
     723           0 :                       reinterpret_cast<const Bytef*>(aIn.Elements()),
     724           0 :                       insize);
     725           0 :   if (zerr != Z_OK) {
     726             :     return NS_ERROR_FAILURE;
     727             :   }
     728           0 :   LOG(("DeflateWriteTArray: %lu in %lu out", insize, outsize));
     729             : 
     730           0 :   outBuff.TruncateLength(outsize);
     731             : 
     732             :   // Length of compressed data stream
     733           0 :   uint32_t dataLen = outBuff.Length();
     734             :   uint32_t written;
     735           0 :   nsresult rv = aStream->Write(reinterpret_cast<char*>(&dataLen), sizeof(dataLen), &written);
     736           0 :   NS_ENSURE_SUCCESS(rv, rv);
     737             : 
     738           0 :   NS_ASSERTION(written == sizeof(dataLen), "Error writing deflate length");
     739             : 
     740             :   // Store to stream
     741           0 :   rv = WriteTArray(aStream, outBuff);
     742           0 :   NS_ENSURE_SUCCESS(rv, rv);
     743             : 
     744             :   return NS_OK;
     745             : }
     746             : 
     747             : template<class T>
     748           0 : nsresult InflateReadTArray(nsIInputStream* aStream, FallibleTArray<T>* aOut,
     749             :                            uint32_t aExpectedSize)
     750             : {
     751             : 
     752             :   uint32_t inLen;
     753             :   uint32_t read;
     754           0 :   nsresult rv = aStream->Read(reinterpret_cast<char*>(&inLen), sizeof(inLen), &read);
     755           0 :   NS_ENSURE_SUCCESS(rv, rv);
     756             : 
     757           0 :   NS_ASSERTION(read == sizeof(inLen), "Error reading inflate length");
     758             : 
     759           0 :   FallibleTArray<char> inBuff;
     760           0 :   if (!inBuff.SetLength(inLen, fallible)) {
     761             :     return NS_ERROR_OUT_OF_MEMORY;
     762             :   }
     763             : 
     764           0 :   rv = ReadTArray(aStream, &inBuff, inLen);
     765           0 :   NS_ENSURE_SUCCESS(rv, rv);
     766             : 
     767           0 :   uLongf insize = inLen;
     768           0 :   uLongf outsize = aExpectedSize * sizeof(T);
     769           0 :   if (!aOut->SetLength(aExpectedSize, fallible)) {
     770             :     return NS_ERROR_OUT_OF_MEMORY;
     771             :   }
     772             : 
     773           0 :   int zerr = uncompress(reinterpret_cast<Bytef*>(aOut->Elements()),
     774             :                         &outsize,
     775           0 :                         reinterpret_cast<const Bytef*>(inBuff.Elements()),
     776           0 :                         insize);
     777           0 :   if (zerr != Z_OK) {
     778             :     return NS_ERROR_FAILURE;
     779             :   }
     780           0 :   LOG(("InflateReadTArray: %lu in %lu out", insize, outsize));
     781             : 
     782           0 :   NS_ASSERTION(outsize == aExpectedSize * sizeof(T), "Decompression size mismatch");
     783             : 
     784             :   return NS_OK;
     785             : }
     786             : 
     787             : static nsresult
     788           0 : ByteSliceWrite(nsIOutputStream* aOut, nsTArray<uint32_t>& aData)
     789             : {
     790           0 :   nsTArray<uint8_t> slice;
     791           0 :   uint32_t count = aData.Length();
     792             : 
     793             :   // Only process one slice at a time to avoid using too much memory.
     794           0 :   if (!slice.SetLength(count, fallible)) {
     795             :     return NS_ERROR_OUT_OF_MEMORY;
     796             :   }
     797             : 
     798             :   // Process slice 1.
     799           0 :   for (uint32_t i = 0; i < count; i++) {
     800           0 :     slice[i] = (aData[i] >> 24);
     801             :   }
     802             : 
     803           0 :   nsresult rv = DeflateWriteTArray(aOut, slice);
     804           0 :   NS_ENSURE_SUCCESS(rv, rv);
     805             : 
     806             :   // Process slice 2.
     807           0 :   for (uint32_t i = 0; i < count; i++) {
     808           0 :     slice[i] = ((aData[i] >> 16) & 0xFF);
     809             :   }
     810             : 
     811           0 :   rv = DeflateWriteTArray(aOut, slice);
     812           0 :   NS_ENSURE_SUCCESS(rv, rv);
     813             : 
     814             :   // Process slice 3.
     815           0 :   for (uint32_t i = 0; i < count; i++) {
     816           0 :     slice[i] = ((aData[i] >> 8) & 0xFF);
     817             :   }
     818             : 
     819           0 :   rv = DeflateWriteTArray(aOut, slice);
     820           0 :   NS_ENSURE_SUCCESS(rv, rv);
     821             : 
     822             :   // Process slice 4.
     823           0 :   for (uint32_t i = 0; i < count; i++) {
     824           0 :     slice[i] = (aData[i] & 0xFF);
     825             :   }
     826             : 
     827             :   // The LSB slice is generally uncompressible, don't bother
     828             :   // compressing it.
     829           0 :   rv = WriteTArray(aOut, slice);
     830           0 :   NS_ENSURE_SUCCESS(rv, rv);
     831             : 
     832             :   return NS_OK;
     833             : }
     834             : 
     835             : static nsresult
     836           0 : ByteSliceRead(nsIInputStream* aInStream, FallibleTArray<uint32_t>* aData, uint32_t count)
     837             : {
     838           0 :   FallibleTArray<uint8_t> slice1;
     839           0 :   FallibleTArray<uint8_t> slice2;
     840           0 :   FallibleTArray<uint8_t> slice3;
     841           0 :   FallibleTArray<uint8_t> slice4;
     842             : 
     843           0 :   nsresult rv = InflateReadTArray(aInStream, &slice1, count);
     844           0 :   NS_ENSURE_SUCCESS(rv, rv);
     845             : 
     846           0 :   rv = InflateReadTArray(aInStream, &slice2, count);
     847           0 :   NS_ENSURE_SUCCESS(rv, rv);
     848             : 
     849           0 :   rv = InflateReadTArray(aInStream, &slice3, count);
     850           0 :   NS_ENSURE_SUCCESS(rv, rv);
     851             : 
     852           0 :   rv = ReadTArray(aInStream, &slice4, count);
     853           0 :   NS_ENSURE_SUCCESS(rv, rv);
     854             : 
     855           0 :   if (!aData->SetCapacity(count, fallible)) {
     856             :     return NS_ERROR_OUT_OF_MEMORY;
     857             :   }
     858             : 
     859           0 :   for (uint32_t i = 0; i < count; i++) {
     860           0 :     aData->AppendElement((slice1[i] << 24) |
     861           0 :                            (slice2[i] << 16) |
     862           0 :                            (slice3[i] << 8) |
     863           0 :                            (slice4[i]),
     864           0 :                          fallible);
     865             :   }
     866             : 
     867             :   return NS_OK;
     868             : }
     869             : 
     870             : nsresult
     871           0 : HashStore::ReadAddPrefixes()
     872             : {
     873           0 :   FallibleTArray<uint32_t> chunks;
     874           0 :   uint32_t count = mHeader.numAddPrefixes;
     875             : 
     876           0 :   nsresult rv = ByteSliceRead(mInputStream, &chunks, count);
     877           0 :   NS_ENSURE_SUCCESS(rv, rv);
     878             : 
     879           0 :   if (!mAddPrefixes.SetCapacity(count, fallible)) {
     880             :     return NS_ERROR_OUT_OF_MEMORY;
     881             :   }
     882           0 :   for (uint32_t i = 0; i < count; i++) {
     883           0 :     AddPrefix *add = mAddPrefixes.AppendElement(fallible);
     884           0 :     add->prefix.FromUint32(0);
     885           0 :     add->addChunk = chunks[i];
     886             :   }
     887             : 
     888             :   return NS_OK;
     889             : }
     890             : 
     891             : nsresult
     892           0 : HashStore::ReadSubPrefixes()
     893             : {
     894           0 :   FallibleTArray<uint32_t> addchunks;
     895           0 :   FallibleTArray<uint32_t> subchunks;
     896           0 :   FallibleTArray<uint32_t> prefixes;
     897           0 :   uint32_t count = mHeader.numSubPrefixes;
     898             : 
     899           0 :   nsresult rv = ByteSliceRead(mInputStream, &addchunks, count);
     900           0 :   NS_ENSURE_SUCCESS(rv, rv);
     901             : 
     902           0 :   rv = ByteSliceRead(mInputStream, &subchunks, count);
     903           0 :   NS_ENSURE_SUCCESS(rv, rv);
     904             : 
     905           0 :   rv = ByteSliceRead(mInputStream, &prefixes, count);
     906           0 :   NS_ENSURE_SUCCESS(rv, rv);
     907             : 
     908           0 :   if (!mSubPrefixes.SetCapacity(count, fallible)) {
     909             :     return NS_ERROR_OUT_OF_MEMORY;
     910             :   }
     911           0 :   for (uint32_t i = 0; i < count; i++) {
     912           0 :     SubPrefix *sub = mSubPrefixes.AppendElement(fallible);
     913           0 :     sub->addChunk = addchunks[i];
     914           0 :     sub->prefix.FromUint32(prefixes[i]);
     915           0 :     sub->subChunk = subchunks[i];
     916             :   }
     917             : 
     918             :   return NS_OK;
     919             : }
     920             : 
     921             : // Split up PrefixArray back into the constituents
     922             : nsresult
     923           0 : HashStore::WriteAddPrefixes(nsIOutputStream* aOut)
     924             : {
     925           0 :   nsTArray<uint32_t> chunks;
     926           0 :   uint32_t count = mAddPrefixes.Length();
     927           0 :   if (!chunks.SetCapacity(count, fallible)) {
     928             :     return NS_ERROR_OUT_OF_MEMORY;
     929             :   }
     930             : 
     931           0 :   for (uint32_t i = 0; i < count; i++) {
     932           0 :     chunks.AppendElement(mAddPrefixes[i].Chunk());
     933             :   }
     934             : 
     935           0 :   nsresult rv = ByteSliceWrite(aOut, chunks);
     936           0 :   NS_ENSURE_SUCCESS(rv, rv);
     937             : 
     938             :   return NS_OK;
     939             : }
     940             : 
     941             : nsresult
     942           0 : HashStore::WriteSubPrefixes(nsIOutputStream* aOut)
     943             : {
     944           0 :   nsTArray<uint32_t> addchunks;
     945           0 :   nsTArray<uint32_t> subchunks;
     946           0 :   nsTArray<uint32_t> prefixes;
     947           0 :   uint32_t count = mSubPrefixes.Length();
     948           0 :   if (!addchunks.SetCapacity(count, fallible) ||
     949           0 :       !subchunks.SetCapacity(count, fallible) ||
     950           0 :       !prefixes.SetCapacity(count, fallible)) {
     951             :     return NS_ERROR_OUT_OF_MEMORY;
     952             :   }
     953             : 
     954           0 :   for (uint32_t i = 0; i < count; i++) {
     955           0 :     addchunks.AppendElement(mSubPrefixes[i].AddChunk());
     956           0 :     prefixes.AppendElement(mSubPrefixes[i].PrefixHash().ToUint32());
     957           0 :     subchunks.AppendElement(mSubPrefixes[i].Chunk());
     958             :   }
     959             : 
     960           0 :   nsresult rv = ByteSliceWrite(aOut, addchunks);
     961           0 :   NS_ENSURE_SUCCESS(rv, rv);
     962             : 
     963           0 :   rv = ByteSliceWrite(aOut, subchunks);
     964           0 :   NS_ENSURE_SUCCESS(rv, rv);
     965             : 
     966           0 :   rv = ByteSliceWrite(aOut, prefixes);
     967           0 :   NS_ENSURE_SUCCESS(rv, rv);
     968             : 
     969             :   return NS_OK;
     970             : }
     971             : 
     972             : nsresult
     973           0 : HashStore::WriteFile()
     974             : {
     975           0 :   NS_ASSERTION(mInUpdate, "Must be in update to write database.");
     976           0 :   if (nsUrlClassifierDBService::ShutdownHasStarted()) {
     977             :     return NS_ERROR_ABORT;
     978             :   }
     979             : 
     980           0 :   nsCOMPtr<nsIFile> storeFile;
     981           0 :   nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
     982           0 :   NS_ENSURE_SUCCESS(rv, rv);
     983           0 :   rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
     984           0 :   NS_ENSURE_SUCCESS(rv, rv);
     985             : 
     986           0 :   nsCOMPtr<nsIOutputStream> out;
     987           0 :   rv = NS_NewCheckSummedOutputStream(getter_AddRefs(out), storeFile);
     988           0 :   NS_ENSURE_SUCCESS(rv, rv);
     989             : 
     990             :   uint32_t written;
     991           0 :   rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
     992           0 :   NS_ENSURE_SUCCESS(rv, rv);
     993             : 
     994             :   // Write chunk numbers.
     995           0 :   rv = mAddChunks.Write(out);
     996           0 :   NS_ENSURE_SUCCESS(rv, rv);
     997             : 
     998           0 :   rv = mSubChunks.Write(out);
     999           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1000             : 
    1001             :   // Write hashes.
    1002           0 :   rv = WriteAddPrefixes(out);
    1003           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1004             : 
    1005           0 :   rv = WriteSubPrefixes(out);
    1006           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1007             : 
    1008           0 :   rv = WriteTArray(out, mAddCompletes);
    1009           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1010             : 
    1011           0 :   rv = WriteTArray(out, mSubCompletes);
    1012           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1013             : 
    1014           0 :   nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out, &rv);
    1015           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1016             : 
    1017           0 :   rv = safeOut->Finish();
    1018           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1019             : 
    1020             :   return NS_OK;
    1021             : }
    1022             : 
    1023             : template <class T>
    1024             : static void
    1025           0 : Erase(FallibleTArray<T>* array,
    1026             :       typename nsTArray<T>::iterator& iterStart,
    1027             :       typename nsTArray<T>::iterator& iterEnd)
    1028             : {
    1029           0 :   uint32_t start = iterStart - array->begin();
    1030           0 :   uint32_t count = iterEnd - iterStart;
    1031             : 
    1032           0 :   if (count > 0) {
    1033           0 :     array->RemoveElementsAt(start, count);
    1034             :   }
    1035           0 : }
    1036             : 
    1037             : // Find items matching between |subs| and |adds|, and remove them,
    1038             : // recording the item from |adds| in |adds_removed|.  To minimize
    1039             : // copies, the inputs are processing in parallel, so |subs| and |adds|
    1040             : // should be compatibly ordered (either by SBAddPrefixLess or
    1041             : // SBAddPrefixHashLess).
    1042             : //
    1043             : // |predAS| provides add < sub, |predSA| provides sub < add, for the
    1044             : // tightest compare appropriate (see calls in SBProcessSubs).
    1045             : template<class TSub, class TAdd>
    1046             : static void
    1047           0 : KnockoutSubs(FallibleTArray<TSub>* aSubs, FallibleTArray<TAdd>* aAdds)
    1048             : {
    1049             :   // Keep a pair of output iterators for writing kept items.  Due to
    1050             :   // deletions, these may lag the main iterators.  Using erase() on
    1051             :   // individual items would result in O(N^2) copies.  Using a list
    1052             :   // would work around that, at double or triple the memory cost.
    1053           0 :   auto addOut = aAdds->begin();
    1054           0 :   auto addIter = aAdds->begin();
    1055             : 
    1056           0 :   auto subOut = aSubs->begin();
    1057           0 :   auto subIter = aSubs->begin();
    1058             : 
    1059           0 :   auto addEnd = aAdds->end();
    1060           0 :   auto subEnd = aSubs->end();
    1061             : 
    1062           0 :   while (addIter != addEnd && subIter != subEnd) {
    1063             :     // additer compare, so it compares on add chunk
    1064           0 :     int32_t cmp = addIter->Compare(*subIter);
    1065           0 :     if (cmp > 0) {
    1066             :       // If |*sub_iter| < |*add_iter|, retain the sub.
    1067           0 :       *subOut = *subIter;
    1068           0 :       ++subOut;
    1069             :       ++subIter;
    1070           0 :     } else if (cmp < 0) {
    1071             :       // If |*add_iter| < |*sub_iter|, retain the add.
    1072           0 :       *addOut = *addIter;
    1073           0 :       ++addOut;
    1074             :       ++addIter;
    1075             :     } else {
    1076             :       // Drop equal items
    1077           0 :       ++addIter;
    1078             :       ++subIter;
    1079             :     }
    1080             :   }
    1081             : 
    1082           0 :   Erase(aAdds, addOut, addIter);
    1083           0 :   Erase(aSubs, subOut, subIter);
    1084           0 : }
    1085             : 
    1086             : // Remove items in |removes| from |fullHashes|.  |fullHashes| and
    1087             : // |removes| should be ordered by SBAddPrefix component.
    1088             : template <class T>
    1089             : static void
    1090           0 : RemoveMatchingPrefixes(const SubPrefixArray& aSubs, FallibleTArray<T>* aFullHashes)
    1091             : {
    1092             :   // Where to store kept items.
    1093           0 :   auto out = aFullHashes->begin();
    1094           0 :   auto hashIter = aFullHashes->begin();
    1095           0 :   auto hashEnd = aFullHashes->end();
    1096             : 
    1097           0 :   auto removeIter = aSubs.begin();
    1098           0 :   auto removeEnd = aSubs.end();
    1099             : 
    1100           0 :   while (hashIter != hashEnd && removeIter != removeEnd) {
    1101           0 :     int32_t cmp = removeIter->CompareAlt(*hashIter);
    1102           0 :     if (cmp > 0) {
    1103             :       // Keep items less than |*removeIter|.
    1104           0 :       *out = *hashIter;
    1105           0 :       ++out;
    1106             :       ++hashIter;
    1107           0 :     } else if (cmp < 0) {
    1108             :       // No hit for |*removeIter|, bump it forward.
    1109             :       ++removeIter;
    1110             :     } else {
    1111             :       // Drop equal items, there may be multiple hits.
    1112           0 :       do {
    1113           0 :         ++hashIter;
    1114           0 :       } while (hashIter != hashEnd &&
    1115           0 :                !(removeIter->CompareAlt(*hashIter) < 0));
    1116             :       ++removeIter;
    1117             :     }
    1118             :   }
    1119           0 :   Erase(aFullHashes, out, hashIter);
    1120           0 : }
    1121             : 
    1122             : static void
    1123           0 : RemoveDeadSubPrefixes(SubPrefixArray& aSubs, ChunkSet& aAddChunks)
    1124             : {
    1125           0 :   auto subIter = aSubs.begin();
    1126             : 
    1127           0 :   for (const auto& sub : aSubs) {
    1128           0 :     bool hasChunk = aAddChunks.Has(sub.AddChunk());
    1129             :     // Keep the subprefix if the chunk it refers to is one
    1130             :     // we haven't seen it yet.
    1131           0 :     if (!hasChunk) {
    1132           0 :       *subIter = sub;
    1133           0 :       subIter++;
    1134             :     }
    1135             :   }
    1136             : 
    1137           0 :   LOG(("Removed %" PRId64 " dead SubPrefix entries.",
    1138             :        static_cast<int64_t>(aSubs.end() - subIter)));
    1139           0 :   aSubs.TruncateLength(subIter - aSubs.begin());
    1140           0 : }
    1141             : 
    1142             : #ifdef DEBUG
    1143             : template <class T>
    1144           0 : static void EnsureSorted(FallibleTArray<T>* aArray)
    1145             : {
    1146           0 :   auto start = aArray->begin();
    1147           0 :   auto end = aArray->end();
    1148           0 :   auto iter = start;
    1149             :   auto previous = start;
    1150             : 
    1151           0 :   while (iter != end) {
    1152           0 :     previous = iter;
    1153           0 :     ++iter;
    1154           0 :     if (iter != end) {
    1155           0 :       MOZ_ASSERT(iter->Compare(*previous) >= 0);
    1156             :     }
    1157             :   }
    1158             : 
    1159           0 :   return;
    1160             : }
    1161             : #endif
    1162             : 
    1163             : nsresult
    1164           0 : HashStore::ProcessSubs()
    1165             : {
    1166             : #ifdef DEBUG
    1167           0 :   EnsureSorted(&mAddPrefixes);
    1168           0 :   EnsureSorted(&mSubPrefixes);
    1169           0 :   EnsureSorted(&mAddCompletes);
    1170           0 :   EnsureSorted(&mSubCompletes);
    1171           0 :   LOG(("All databases seem to have a consistent sort order."));
    1172             : #endif
    1173             : 
    1174           0 :   RemoveMatchingPrefixes(mSubPrefixes, &mAddCompletes);
    1175           0 :   RemoveMatchingPrefixes(mSubPrefixes, &mSubCompletes);
    1176             : 
    1177             :   // Remove any remaining subbed prefixes from both addprefixes
    1178             :   // and addcompletes.
    1179           0 :   KnockoutSubs(&mSubPrefixes, &mAddPrefixes);
    1180           0 :   KnockoutSubs(&mSubCompletes, &mAddCompletes);
    1181             : 
    1182             :   // Remove any remaining subprefixes referring to addchunks that
    1183             :   // we have (and hence have been processed above).
    1184           0 :   RemoveDeadSubPrefixes(mSubPrefixes, mAddChunks);
    1185             : 
    1186             : #ifdef DEBUG
    1187           0 :   EnsureSorted(&mAddPrefixes);
    1188           0 :   EnsureSorted(&mSubPrefixes);
    1189           0 :   EnsureSorted(&mAddCompletes);
    1190           0 :   EnsureSorted(&mSubCompletes);
    1191           0 :   LOG(("All databases seem to have a consistent sort order."));
    1192             : #endif
    1193             : 
    1194           0 :   return NS_OK;
    1195             : }
    1196             : 
    1197             : nsresult
    1198           0 : HashStore::AugmentAdds(const nsTArray<uint32_t>& aPrefixes)
    1199             : {
    1200           0 :   uint32_t cnt = aPrefixes.Length();
    1201           0 :   if (cnt != mAddPrefixes.Length()) {
    1202           0 :     LOG(("Amount of prefixes in cache not consistent with store (%zu vs %zu)",
    1203             :          aPrefixes.Length(), mAddPrefixes.Length()));
    1204             :     return NS_ERROR_FAILURE;
    1205             :   }
    1206           0 :   for (uint32_t i = 0; i < cnt; i++) {
    1207           0 :     mAddPrefixes[i].prefix.FromUint32(aPrefixes[i]);
    1208             :   }
    1209             :   return NS_OK;
    1210             : }
    1211             : 
    1212             : ChunkSet&
    1213           0 : HashStore::AddChunks()
    1214             : {
    1215           0 :   ReadChunkNumbers();
    1216             : 
    1217           0 :   return mAddChunks;
    1218             : }
    1219             : 
    1220             : ChunkSet&
    1221           0 : HashStore::SubChunks()
    1222             : {
    1223           0 :   ReadChunkNumbers();
    1224             : 
    1225           0 :   return mSubChunks;
    1226             : }
    1227             : 
    1228             : AddCompleteArray&
    1229           0 : HashStore::AddCompletes()
    1230             : {
    1231           0 :   ReadCompletions();
    1232             : 
    1233           0 :   return mAddCompletes;
    1234             : }
    1235             : 
    1236             : SubCompleteArray&
    1237           0 : HashStore::SubCompletes()
    1238             : {
    1239           0 :   ReadCompletions();
    1240             : 
    1241           0 :   return mSubCompletes;
    1242             : }
    1243             : 
    1244             : bool
    1245           0 : HashStore::AlreadyReadChunkNumbers() const
    1246             : {
    1247             :   // If there are chunks but chunk set not yet contains any data
    1248             :   // Then we haven't read chunk numbers.
    1249           0 :   if ((mHeader.numAddChunks != 0 && mAddChunks.Length() == 0) ||
    1250           0 :       (mHeader.numSubChunks != 0 && mSubChunks.Length() == 0)) {
    1251             :     return false;
    1252             :   }
    1253           0 :   return true;
    1254             : }
    1255             : 
    1256             : bool
    1257           0 : HashStore::AlreadyReadCompletions() const
    1258             : {
    1259             :   // If there are completions but completion set not yet contains any data
    1260             :   // Then we haven't read completions.
    1261           0 :   if ((mHeader.numAddCompletes != 0 && mAddCompletes.Length() == 0) ||
    1262           0 :       (mHeader.numSubCompletes != 0 && mSubCompletes.Length() == 0)) {
    1263             :     return false;
    1264             :   }
    1265           0 :   return true;
    1266             : }
    1267             : 
    1268             : } // namespace safebrowsing
    1269           3 : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952