Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/url-classifier/LookupCacheV4.cpp
Line
Count
Source (jump to first uncovered line)
1
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "LookupCacheV4.h"
7
#include "HashStore.h"
8
#include "mozilla/Unused.h"
9
#include <string>
10
11
// MOZ_LOG=UrlClassifierDbService:5
12
extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
13
0
#define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
14
0
#define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
15
16
0
#define METADATA_SUFFIX NS_LITERAL_CSTRING(".metadata")
17
18
namespace mozilla {
19
namespace safebrowsing {
20
21
const int LookupCacheV4::VER = 4;
22
const uint32_t LookupCacheV4::MAX_METADATA_VALUE_LENGTH = 256;
23
24
// Prefixes coming from updates and VLPrefixSet are both stored in the HashTable
25
// where the (key, value) pair is a prefix size and a lexicographic-sorted string.
26
// The difference is prefixes from updates use std:string(to avoid additional copies)
27
// and prefixes from VLPrefixSet use nsCString.
28
// This class provides a common interface for the partial update algorithm to make it
29
// easier to operate on two different kind prefix string map..
30
class VLPrefixSet
31
{
32
public:
33
  explicit VLPrefixSet(const PrefixStringMap& aMap);
34
35
  // This function will merge the prefix map in VLPrefixSet to aPrefixMap.
36
  void Merge(PrefixStringMap& aPrefixMap);
37
38
  // Find the smallest string from the map in VLPrefixSet.
39
  bool GetSmallestPrefix(nsACString& aOutString) const;
40
41
  // Return the number of prefixes in the map
42
0
  uint32_t Count() const { return mCount; }
43
44
private:
45
  // PrefixString structure contains a lexicographic-sorted string with
46
  // a |pos| variable to indicate which substring we are pointing to right now.
47
  // |pos| increases each time GetSmallestPrefix finds the smallest string.
48
  struct PrefixString {
49
    PrefixString(const nsACString& aStr, uint32_t aSize)
50
      : data(aStr)
51
      , pos(0)
52
      , size(aSize)
53
0
    {
54
0
      MOZ_ASSERT(data.Length() % size == 0,
55
0
                 "PrefixString length must be a multiple of the prefix size.");
56
0
    }
57
58
0
    void getRemainingString(nsACString& out) {
59
0
      MOZ_ASSERT(out.IsEmpty());
60
0
      if (remaining() > 0) {
61
0
        out = Substring(data, pos);
62
0
      }
63
0
    }
64
0
    void getPrefix(nsACString& out) {
65
0
      MOZ_ASSERT(out.IsEmpty());
66
0
      if (remaining() >= size) {
67
0
        out = Substring(data, pos, size);
68
0
      } else {
69
0
        MOZ_ASSERT(remaining() == 0,
70
0
                   "Remaining bytes but not enough for a (size)-byte prefix.");
71
0
      }
72
0
    }
73
0
    void next() {
74
0
      pos += size;
75
0
      MOZ_ASSERT(pos <= data.Length());
76
0
    }
77
0
    uint32_t remaining() {
78
0
      return data.Length() - pos;
79
0
      MOZ_ASSERT(pos <= data.Length());
80
0
    }
81
82
    nsCString data;
83
    uint32_t pos;
84
    uint32_t size;
85
  };
86
87
  nsClassHashtable<nsUint32HashKey, PrefixString> mMap;
88
  uint32_t mCount;
89
};
90
91
nsresult
92
LookupCacheV4::Init()
93
0
{
94
0
  mVLPrefixSet = new VariableLengthPrefixSet();
95
0
  nsresult rv = mVLPrefixSet->Init(mTableName);
96
0
  NS_ENSURE_SUCCESS(rv, rv);
97
0
98
0
  return NS_OK;
99
0
}
100
101
nsresult
102
LookupCacheV4::Has(const Completion& aCompletion,
103
                   bool* aHas,
104
                   uint32_t* aMatchLength,
105
                   bool* aConfirmed)
106
0
{
107
0
  *aHas = *aConfirmed = false;
108
0
  *aMatchLength = 0;
109
0
110
0
  uint32_t length = 0;
111
0
  nsDependentCSubstring fullhash;
112
0
  fullhash.Rebind((const char *)aCompletion.buf, COMPLETE_SIZE);
113
0
114
0
  nsresult rv = mVLPrefixSet->Matches(fullhash, &length);
115
0
  NS_ENSURE_SUCCESS(rv, rv);
116
0
117
0
  MOZ_ASSERT(length == 0 || (length >= PREFIX_SIZE && length <= COMPLETE_SIZE));
118
0
119
0
  *aHas = length >= PREFIX_SIZE;
120
0
  *aMatchLength = length;
121
0
122
0
  if (LOG_ENABLED()) {
123
0
    uint32_t prefix = aCompletion.ToUint32();
124
0
    LOG(("Probe in V4 %s: %X, found %d, complete %d", mTableName.get(),
125
0
          prefix, *aHas, length == COMPLETE_SIZE));
126
0
  }
127
0
128
0
  // Check if fullhash match any prefix in the local database
129
0
  if (!(*aHas)) {
130
0
    return NS_OK;
131
0
  }
132
0
133
0
  // Even though V4 supports variable-length prefix, we always send 4-bytes for
134
0
  // completion (Bug 1323953). This means cached prefix length is also 4-bytes.
135
0
  return CheckCache(aCompletion, aHas, aConfirmed);
136
0
}
137
138
bool
139
LookupCacheV4::IsEmpty() const
140
0
{
141
0
  bool isEmpty;
142
0
  mVLPrefixSet->IsEmpty(&isEmpty);
143
0
  return isEmpty;
144
0
}
145
146
nsresult
147
LookupCacheV4::Build(PrefixStringMap& aPrefixMap)
148
0
{
149
0
  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_CONSTRUCT_TIME> timer;
150
0
151
0
  nsresult rv = mVLPrefixSet->SetPrefixes(aPrefixMap);
152
0
  NS_ENSURE_SUCCESS(rv, rv);
153
0
  mPrimed = true;
154
0
155
0
  return rv;
156
0
}
157
158
nsresult
159
LookupCacheV4::GetPrefixes(PrefixStringMap& aPrefixMap)
160
0
{
161
0
  if (!mPrimed) {
162
0
    // This can happen if its a new table, so no error.
163
0
    LOG(("GetPrefixes from empty LookupCache"));
164
0
    return NS_OK;
165
0
  }
166
0
  return mVLPrefixSet->GetPrefixes(aPrefixMap);
167
0
}
168
169
nsresult
170
LookupCacheV4::GetFixedLengthPrefixes(FallibleTArray<uint32_t>& aPrefixes)
171
0
{
172
0
  return mVLPrefixSet->GetFixedLengthPrefixes(aPrefixes);
173
0
}
174
175
nsresult
176
LookupCacheV4::ClearPrefixes()
177
0
{
178
0
  // Clear by seting a empty map
179
0
  PrefixStringMap map;
180
0
  return mVLPrefixSet->SetPrefixes(map);
181
0
}
182
183
nsresult
184
LookupCacheV4::StoreToFile(nsCOMPtr<nsIFile>& aFile)
185
0
{
186
0
  return mVLPrefixSet->StoreToFile(aFile);
187
0
}
188
189
nsresult
190
LookupCacheV4::LoadFromFile(nsCOMPtr<nsIFile>& aFile)
191
0
{
192
0
  nsresult rv = mVLPrefixSet->LoadFromFile(aFile);
193
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
194
0
    return rv;
195
0
  }
196
0
197
0
  nsCString state, checksum;
198
0
  rv = LoadMetadata(state, checksum);
199
0
  Telemetry::Accumulate(Telemetry::URLCLASSIFIER_VLPS_METADATA_CORRUPT,
200
0
                        rv == NS_ERROR_FILE_CORRUPTED);
201
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
202
0
    return rv;
203
0
  }
204
0
205
0
  rv = VerifyChecksum(checksum);
206
0
  Telemetry::Accumulate(Telemetry::URLCLASSIFIER_VLPS_LOAD_CORRUPT,
207
0
                        rv == NS_ERROR_FILE_CORRUPTED);
208
0
  Unused << NS_WARN_IF(NS_FAILED(rv));
209
0
  return rv;
210
0
}
211
212
size_t
213
LookupCacheV4::SizeOfPrefixSet() const
214
0
{
215
0
  return mVLPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
216
0
}
217
218
static nsresult
219
AppendPrefixToMap(PrefixStringMap& prefixes, const nsACString& prefix)
220
0
{
221
0
  uint32_t len = prefix.Length();
222
0
  MOZ_ASSERT(len >= PREFIX_SIZE && len <= COMPLETE_SIZE);
223
0
  if (!len) {
224
0
    return NS_OK;
225
0
  }
226
0
227
0
  nsCString* prefixString = prefixes.LookupOrAdd(len);
228
0
  if (!prefixString->Append(prefix, fallible)) {
229
0
    return NS_ERROR_OUT_OF_MEMORY;
230
0
  }
231
0
232
0
  return NS_OK;
233
0
}
234
235
static nsresult
236
InitCrypto(nsCOMPtr<nsICryptoHash>& aCrypto)
237
0
{
238
0
  nsresult rv;
239
0
  aCrypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
240
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
241
0
    return rv;
242
0
  }
243
0
244
0
  rv = aCrypto->Init(nsICryptoHash::SHA256);
245
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "InitCrypto failed");
246
0
247
0
  return rv;
248
0
}
249
250
// Read prefix into a buffer and also update the hash which
251
// keeps track of the checksum
252
static void
253
UpdateChecksum(nsICryptoHash* aCrypto, const nsACString& aPrefix)
254
0
{
255
0
  MOZ_ASSERT(aCrypto);
256
0
  aCrypto->Update(reinterpret_cast<uint8_t*>(const_cast<char*>(
257
0
                  aPrefix.BeginReading())),
258
0
                  aPrefix.Length());
259
0
}
260
261
// Please see https://bug1287058.bmoattachments.org/attachment.cgi?id=8795366
262
// for detail about partial update algorithm.
263
nsresult
264
LookupCacheV4::ApplyUpdate(RefPtr<TableUpdateV4> aTableUpdate,
265
                           PrefixStringMap& aInputMap,
266
                           PrefixStringMap& aOutputMap)
267
0
{
268
0
  MOZ_ASSERT(aOutputMap.IsEmpty());
269
0
270
0
  nsCOMPtr<nsICryptoHash> crypto;
271
0
  nsresult rv = InitCrypto(crypto);
272
0
  if (NS_FAILED(rv)) {
273
0
    return rv;
274
0
  }
275
0
276
0
  // oldPSet contains prefixes we already have or we just merged last round.
277
0
  // addPSet contains prefixes stored in tableUpdate which should be merged with oldPSet.
278
0
  VLPrefixSet oldPSet(aInputMap);
279
0
  VLPrefixSet addPSet(aTableUpdate->Prefixes());
280
0
281
0
  // RemovalIndiceArray is a sorted integer array indicating the index of prefix we should
282
0
  // remove from the old prefix set(according to lexigraphic order).
283
0
  // |removalIndex| is the current index of RemovalIndiceArray.
284
0
  // |numOldPrefixPicked| is used to record how many prefixes we picked from the old map.
285
0
  const TableUpdateV4::RemovalIndiceArray& removalArray = aTableUpdate->RemovalIndices();
286
0
  uint32_t removalIndex = 0;
287
0
  int32_t numOldPrefixPicked = -1;
288
0
289
0
  nsAutoCString smallestOldPrefix;
290
0
  nsAutoCString smallestAddPrefix;
291
0
292
0
  bool isOldMapEmpty = false, isAddMapEmpty = false;
293
0
294
0
  // This is used to avoid infinite loop for partial update algorithm.
295
0
  // The maximum loops will be the number of old prefixes plus the number of add prefixes.
296
0
  int32_t index = oldPSet.Count() + addPSet.Count() + 1;
297
0
  for(;index > 0; index--) {
298
0
    // Get smallest prefix from the old prefix set if we don't have one
299
0
    if (smallestOldPrefix.IsEmpty() && !isOldMapEmpty) {
300
0
      isOldMapEmpty = !oldPSet.GetSmallestPrefix(smallestOldPrefix);
301
0
    }
302
0
303
0
    // Get smallest prefix from add prefix set if we don't have one
304
0
    if (smallestAddPrefix.IsEmpty() && !isAddMapEmpty) {
305
0
      isAddMapEmpty = !addPSet.GetSmallestPrefix(smallestAddPrefix);
306
0
    }
307
0
308
0
    bool pickOld;
309
0
310
0
    // If both prefix sets are not empty, then compare to find the smaller one.
311
0
    if (!isOldMapEmpty && !isAddMapEmpty) {
312
0
      if (smallestOldPrefix == smallestAddPrefix) {
313
0
        LOG(("Add prefix should not exist in the original prefix set."));
314
0
        return NS_ERROR_UC_UPDATE_DUPLICATE_PREFIX;
315
0
      }
316
0
317
0
      // Compare the smallest string in old prefix set and add prefix set,
318
0
      // merge the smaller one into new map to ensure merged string still
319
0
      // follows lexigraphic order.
320
0
      pickOld = smallestOldPrefix < smallestAddPrefix;
321
0
    } else if (!isOldMapEmpty && isAddMapEmpty) {
322
0
      pickOld = true;
323
0
    } else if (isOldMapEmpty && !isAddMapEmpty) {
324
0
      pickOld = false;
325
0
    // If both maps are empty, then partial update is complete.
326
0
    } else {
327
0
      break;
328
0
    }
329
0
330
0
    if (pickOld) {
331
0
      numOldPrefixPicked++;
332
0
333
0
      // If the number of picks from old map matches the removalIndex, then this prefix
334
0
      // will be removed by not merging it to new map.
335
0
      if (removalIndex < removalArray.Length() &&
336
0
          numOldPrefixPicked == removalArray[removalIndex]) {
337
0
        removalIndex++;
338
0
      } else {
339
0
        rv = AppendPrefixToMap(aOutputMap, smallestOldPrefix);
340
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
341
0
          return rv;
342
0
        }
343
0
344
0
        UpdateChecksum(crypto, smallestOldPrefix);
345
0
      }
346
0
      smallestOldPrefix.SetLength(0);
347
0
    } else {
348
0
      rv = AppendPrefixToMap(aOutputMap, smallestAddPrefix);
349
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
350
0
        return rv;
351
0
      }
352
0
353
0
      UpdateChecksum(crypto, smallestAddPrefix);
354
0
      smallestAddPrefix.SetLength(0);
355
0
    }
356
0
  }
357
0
358
0
  // We expect index will be greater to 0 because max number of runs will be
359
0
  // the number of original prefix plus add prefix.
360
0
  if (index <= 0) {
361
0
    LOG(("There are still prefixes remaining after reaching maximum runs."));
362
0
    return NS_ERROR_UC_UPDATE_INFINITE_LOOP;
363
0
  }
364
0
365
0
  if (removalIndex < removalArray.Length()) {
366
0
    LOG(("There are still prefixes to remove after exhausting the old PrefixSet."));
367
0
    return NS_ERROR_UC_UPDATE_WRONG_REMOVAL_INDICES;
368
0
  }
369
0
370
0
  nsAutoCString checksum;
371
0
  crypto->Finish(false, checksum);
372
0
  if (aTableUpdate->Checksum().IsEmpty()) {
373
0
    LOG(("Update checksum missing."));
374
0
    Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UPDATE_ERROR, mProvider,
375
0
        NS_ERROR_GET_CODE(NS_ERROR_UC_UPDATE_MISSING_CHECKSUM));
376
0
377
0
    // Generate our own checksum to tableUpdate to ensure there is always
378
0
    // checksum in .metadata
379
0
    std::string stdChecksum(checksum.BeginReading(), checksum.Length());
380
0
    aTableUpdate->NewChecksum(stdChecksum);
381
0
  } else if (aTableUpdate->Checksum() != checksum){
382
0
    LOG(("Checksum mismatch after applying partial update"));
383
0
    return NS_ERROR_UC_UPDATE_CHECKSUM_MISMATCH;
384
0
  }
385
0
386
0
  return NS_OK;
387
0
}
388
389
nsresult
390
LookupCacheV4::AddFullHashResponseToCache(const FullHashResponseMap& aResponseMap)
391
0
{
392
0
  CopyClassHashTable<FullHashResponseMap>(aResponseMap, mFullHashCache);
393
0
394
0
  return NS_OK;
395
0
}
396
397
nsresult
398
LookupCacheV4::VerifyChecksum(const nsACString& aChecksum)
399
0
{
400
0
  nsCOMPtr<nsICryptoHash> crypto;
401
0
  nsresult rv = InitCrypto(crypto);
402
0
  if (NS_FAILED(rv)) {
403
0
    return rv;
404
0
  }
405
0
406
0
  PrefixStringMap map;
407
0
  mVLPrefixSet->GetPrefixes(map);
408
0
409
0
  VLPrefixSet loadPSet(map);
410
0
  uint32_t index = loadPSet.Count() + 1;
411
0
  for(;index > 0; index--) {
412
0
    nsAutoCString prefix;
413
0
    if (!loadPSet.GetSmallestPrefix(prefix)) {
414
0
      break;
415
0
    }
416
0
    UpdateChecksum(crypto, prefix);
417
0
  }
418
0
419
0
  nsAutoCString checksum;
420
0
  crypto->Finish(false, checksum);
421
0
422
0
  if (checksum != aChecksum) {
423
0
    LOG(("Checksum mismatch when loading prefixes from file."));
424
0
    return NS_ERROR_FILE_CORRUPTED;
425
0
  }
426
0
427
0
  return NS_OK;
428
0
}
429
430
//////////////////////////////////////////////////////////////////////////
431
// A set of lightweight functions for reading/writing value from/to file.
432
433
namespace {
434
435
template<typename T>
436
struct ValueTraits
437
{
438
  static_assert(sizeof(T) <= LookupCacheV4::MAX_METADATA_VALUE_LENGTH,
439
                "LookupCacheV4::MAX_METADATA_VALUE_LENGTH is too small.");
440
0
  static uint32_t Length(const T& aValue) { return sizeof(T); }
441
0
  static char* WritePtr(T& aValue, uint32_t aLength) { return (char*)&aValue; }
442
0
  static const char* ReadPtr(const T& aValue) { return (char*)&aValue; }
443
0
  static bool IsFixedLength() { return true; }
444
};
445
446
template<>
447
struct ValueTraits<nsACString>
448
{
449
0
  static bool IsFixedLength() { return false; }
450
451
  static uint32_t Length(const nsACString& aValue)
452
0
  {
453
0
    return aValue.Length();
454
0
  }
455
456
  static char* WritePtr(nsACString& aValue, uint32_t aLength)
457
0
  {
458
0
    aValue.SetLength(aLength);
459
0
    return aValue.BeginWriting();
460
0
  }
461
462
  static const char* ReadPtr(const nsACString& aValue)
463
0
  {
464
0
    return aValue.BeginReading();
465
0
  }
466
};
467
468
template<typename T> static nsresult
469
WriteValue(nsIOutputStream *aOutputStream, const T& aValue)
470
0
{
471
0
  uint32_t writeLength = ValueTraits<T>::Length(aValue);
472
0
  MOZ_ASSERT(writeLength <= LookupCacheV4::MAX_METADATA_VALUE_LENGTH,
473
0
             "LookupCacheV4::MAX_METADATA_VALUE_LENGTH is too small.");
474
0
  if (!ValueTraits<T>::IsFixedLength()) {
475
0
    // We need to write out the variable value length.
476
0
    nsresult rv = WriteValue(aOutputStream, writeLength);
477
0
    NS_ENSURE_SUCCESS(rv, rv);
478
0
  }
479
0
480
0
  // Write out the value.
481
0
  auto valueReadPtr = ValueTraits<T>::ReadPtr(aValue);
482
0
  uint32_t written;
483
0
  nsresult rv = aOutputStream->Write(valueReadPtr, writeLength, &written);
484
0
  NS_ENSURE_SUCCESS(rv, rv);
485
0
  if (NS_WARN_IF(written != writeLength)) {
486
0
    return NS_ERROR_FAILURE;
487
0
  }
488
0
489
0
  return rv;
490
0
}
Unexecuted instantiation: Unified_cpp_url-classifier0.cpp:nsresult mozilla::safebrowsing::(anonymous namespace)::WriteValue<nsTSubstring<char> >(nsIOutputStream*, nsTSubstring<char> const&)
Unexecuted instantiation: Unified_cpp_url-classifier0.cpp:nsresult mozilla::safebrowsing::(anonymous namespace)::WriteValue<unsigned int>(nsIOutputStream*, unsigned int const&)
491
492
template<typename T> static nsresult
493
ReadValue(nsIInputStream* aInputStream, T& aValue)
494
0
{
495
0
  nsresult rv;
496
0
497
0
  uint32_t readLength;
498
0
  if (ValueTraits<T>::IsFixedLength()) {
499
0
    readLength = ValueTraits<T>::Length(aValue);
500
0
  } else {
501
0
    // Read the variable value length from file.
502
0
    nsresult rv = ReadValue(aInputStream, readLength);
503
0
    NS_ENSURE_SUCCESS(rv, rv);
504
0
  }
505
0
506
0
  // Sanity-check the readLength in case of disk corruption
507
0
  // (see bug 1433636).
508
0
  if (readLength > LookupCacheV4::MAX_METADATA_VALUE_LENGTH) {
509
0
    return NS_ERROR_FILE_CORRUPTED;
510
0
  }
511
0
512
0
  // Read the value.
513
0
  uint32_t read;
514
0
  auto valueWritePtr = ValueTraits<T>::WritePtr(aValue, readLength);
515
0
  rv = aInputStream->Read(valueWritePtr, readLength, &read);
516
0
  if (NS_FAILED(rv) || read != readLength) {
517
0
    LOG(("Failed to read the value."));
518
0
    return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
519
0
  }
520
0
521
0
  return rv;
522
0
}
Unexecuted instantiation: Unified_cpp_url-classifier0.cpp:nsresult mozilla::safebrowsing::(anonymous namespace)::ReadValue<nsTSubstring<char> >(nsIInputStream*, nsTSubstring<char>&)
Unexecuted instantiation: Unified_cpp_url-classifier0.cpp:nsresult mozilla::safebrowsing::(anonymous namespace)::ReadValue<unsigned int>(nsIInputStream*, unsigned int&)
523
524
} // end of unnamed namespace.
525
////////////////////////////////////////////////////////////////////////
526
527
nsresult
528
LookupCacheV4::WriteMetadata(RefPtr<const TableUpdateV4> aTableUpdate)
529
0
{
530
0
  NS_ENSURE_ARG_POINTER(aTableUpdate);
531
0
  if (nsUrlClassifierDBService::ShutdownHasStarted()) {
532
0
    return NS_ERROR_ABORT;
533
0
  }
534
0
535
0
  nsCOMPtr<nsIFile> metaFile;
536
0
  nsresult rv = mStoreDirectory->Clone(getter_AddRefs(metaFile));
537
0
  NS_ENSURE_SUCCESS(rv, rv);
538
0
539
0
  rv = metaFile->AppendNative(mTableName + METADATA_SUFFIX);
540
0
  NS_ENSURE_SUCCESS(rv, rv);
541
0
542
0
  nsCOMPtr<nsIOutputStream> outputStream;
543
0
  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), metaFile,
544
0
                                   PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
545
0
  NS_ENSURE_SUCCESS(rv, rv);
546
0
547
0
  // Write the state.
548
0
  rv = WriteValue(outputStream, aTableUpdate->ClientState());
549
0
  NS_ENSURE_SUCCESS(rv, rv);
550
0
551
0
  // Write the checksum.
552
0
  rv = WriteValue(outputStream, aTableUpdate->Checksum());
553
0
  NS_ENSURE_SUCCESS(rv, rv);
554
0
555
0
  return rv;
556
0
}
557
558
nsresult
559
LookupCacheV4::LoadMetadata(nsACString& aState, nsACString& aChecksum)
560
0
{
561
0
  nsCOMPtr<nsIFile> metaFile;
562
0
  nsresult rv = mStoreDirectory->Clone(getter_AddRefs(metaFile));
563
0
  NS_ENSURE_SUCCESS(rv, rv);
564
0
565
0
  rv = metaFile->AppendNative(mTableName + METADATA_SUFFIX);
566
0
  NS_ENSURE_SUCCESS(rv, rv);
567
0
568
0
  nsCOMPtr<nsIInputStream> localInFile;
569
0
  rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), metaFile,
570
0
                                  PR_RDONLY | nsIFile::OS_READAHEAD);
571
0
  if (NS_FAILED(rv)) {
572
0
    LOG(("Unable to open metadata file."));
573
0
    return rv;
574
0
  }
575
0
576
0
  // Read the list state.
577
0
  rv = ReadValue(localInFile, aState);
578
0
  if (NS_FAILED(rv)) {
579
0
    LOG(("Failed to read state."));
580
0
    return rv;
581
0
  }
582
0
583
0
  // Read the checksum.
584
0
  rv = ReadValue(localInFile, aChecksum);
585
0
  if (NS_FAILED(rv)) {
586
0
    LOG(("Failed to read checksum."));
587
0
    return rv;
588
0
  }
589
0
590
0
  return rv;
591
0
}
592
593
VLPrefixSet::VLPrefixSet(const PrefixStringMap& aMap)
594
  : mCount(0)
595
0
{
596
0
  for (auto iter = aMap.ConstIter(); !iter.Done(); iter.Next()) {
597
0
    uint32_t size = iter.Key();
598
0
    MOZ_ASSERT(iter.Data()->Length() % size == 0,
599
0
               "PrefixString must be a multiple of the prefix size.");
600
0
    mMap.Put(size, new PrefixString(*iter.Data(), size));
601
0
    mCount += iter.Data()->Length() / size;
602
0
  }
603
0
}
604
605
void
606
0
VLPrefixSet::Merge(PrefixStringMap& aPrefixMap) {
607
0
  for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
608
0
    nsCString* prefixString = aPrefixMap.LookupOrAdd(iter.Key());
609
0
    PrefixString* str = iter.Data();
610
0
611
0
    nsAutoCString remainingString;
612
0
    str->getRemainingString(remainingString);
613
0
    if (!remainingString.IsEmpty()) {
614
0
      MOZ_ASSERT(remainingString.Length() == str->remaining());
615
0
      prefixString->Append(remainingString);
616
0
    }
617
0
  }
618
0
}
619
620
bool
621
0
VLPrefixSet::GetSmallestPrefix(nsACString& aOutString) const {
622
0
  PrefixString* pick = nullptr;
623
0
  for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
624
0
    PrefixString* str = iter.Data();
625
0
626
0
    if (str->remaining() <= 0) {
627
0
      continue;
628
0
    }
629
0
630
0
    if (aOutString.IsEmpty()) {
631
0
      str->getPrefix(aOutString);
632
0
      MOZ_ASSERT(aOutString.Length() == iter.Key());
633
0
      pick = str;
634
0
      continue;
635
0
    }
636
0
637
0
    nsAutoCString cur;
638
0
    str->getPrefix(cur);
639
0
    if (!cur.IsEmpty() && cur < aOutString) {
640
0
      aOutString.Assign(cur);
641
0
      MOZ_ASSERT(aOutString.Length() == iter.Key());
642
0
      pick = str;
643
0
    }
644
0
  }
645
0
646
0
  if (pick) {
647
0
    pick->next();
648
0
  }
649
0
650
0
  return pick != nullptr;
651
0
}
652
653
} // namespace safebrowsing
654
} // namespace mozilla