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

Generated by: LCOV version 1.13