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