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