1 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Url Classifier code
17 : *
18 : * The Initial Developer of the Original Code is
19 : * the Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2011
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Gian-Carlo Pascutto <gpascutto@mozilla.com>
25 : * Mehdi Mulani <mars.martian+bugmail@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsAutoPtr.h"
42 : #include "nsCOMPtr.h"
43 : #include "nsDebug.h"
44 : #include "nsTArray.h"
45 : #include "nsString.h"
46 : #include "nsUrlClassifierPrefixSet.h"
47 : #include "nsIUrlClassifierPrefixSet.h"
48 : #include "nsIRandomGenerator.h"
49 : #include "nsIFile.h"
50 : #include "nsILocalFile.h"
51 : #include "nsToolkitCompsCID.h"
52 : #include "nsTArray.h"
53 : #include "nsThreadUtils.h"
54 : #include "mozilla/Mutex.h"
55 : #include "mozilla/Telemetry.h"
56 : #include "mozilla/FileUtils.h"
57 : #include "prlog.h"
58 :
59 : using namespace mozilla;
60 :
61 : // NSPR_LOG_MODULES=UrlClassifierPrefixSet:5
62 : #if defined(PR_LOGGING)
63 : static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull;
64 : #define LOG(args) PR_LOG(gUrlClassifierPrefixSetLog, PR_LOG_DEBUG, args)
65 : #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierPrefixSetLog, 4)
66 : #else
67 : #define LOG(args)
68 : #define LOG_ENABLED() (false)
69 : #endif
70 :
71 : class nsPrefixSetReporter : public nsIMemoryReporter
72 : {
73 : public:
74 : nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, const nsACString& aName);
75 192 : virtual ~nsPrefixSetReporter() {};
76 :
77 : NS_DECL_ISUPPORTS
78 : NS_DECL_NSIMEMORYREPORTER
79 :
80 : private:
81 : nsCString mPath;
82 : nsUrlClassifierPrefixSet* mParent;
83 : };
84 :
85 192 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter)
86 :
87 0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StoragePrefixSetMallocSizeOf,
88 : "storage/prefixset")
89 :
90 48 : nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent,
91 : const nsACString& aName)
92 48 : : mParent(aParent)
93 : {
94 48 : mPath.Assign(NS_LITERAL_CSTRING("explicit/storage/prefixset"));
95 48 : if (!aName.IsEmpty()) {
96 48 : mPath.Append("/");
97 48 : mPath.Append(aName);
98 : }
99 48 : }
100 :
101 : NS_IMETHODIMP
102 0 : nsPrefixSetReporter::GetProcess(nsACString& aProcess)
103 : {
104 0 : aProcess.Truncate();
105 0 : return NS_OK;
106 : }
107 :
108 : NS_IMETHODIMP
109 0 : nsPrefixSetReporter::GetPath(nsACString& aPath)
110 : {
111 0 : aPath.Assign(mPath);
112 0 : return NS_OK;
113 : }
114 :
115 : NS_IMETHODIMP
116 0 : nsPrefixSetReporter::GetKind(PRInt32* aKind)
117 : {
118 0 : *aKind = nsIMemoryReporter::KIND_HEAP;
119 0 : return NS_OK;
120 : }
121 :
122 : NS_IMETHODIMP
123 0 : nsPrefixSetReporter::GetUnits(PRInt32* aUnits)
124 : {
125 0 : *aUnits = nsIMemoryReporter::UNITS_BYTES;
126 0 : return NS_OK;
127 : }
128 :
129 : NS_IMETHODIMP
130 0 : nsPrefixSetReporter::GetAmount(PRInt64* aAmount)
131 : {
132 0 : *aAmount = mParent->SizeOfIncludingThis(StoragePrefixSetMallocSizeOf);
133 0 : return NS_OK;
134 : }
135 :
136 : NS_IMETHODIMP
137 0 : nsPrefixSetReporter::GetDescription(nsACString& aDescription)
138 : {
139 0 : aDescription.Assign(NS_LITERAL_CSTRING("Memory used by a PrefixSet for "
140 0 : "UrlClassifier, in bytes."));
141 0 : return NS_OK;
142 : }
143 :
144 192 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet)
145 :
146 51 : nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
147 : : mPrefixSetLock("mPrefixSetLock"),
148 : mSetIsReady(mPrefixSetLock, "mSetIsReady"),
149 51 : mHasPrefixes(false)
150 : {
151 : #if defined(PR_LOGGING)
152 51 : if (!gUrlClassifierPrefixSetLog)
153 5 : gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet");
154 : #endif
155 51 : }
156 :
157 : NS_IMETHODIMP
158 48 : nsUrlClassifierPrefixSet::Init(const nsACString& aName)
159 : {
160 48 : mReporter = new nsPrefixSetReporter(this, aName);
161 48 : NS_RegisterMemoryReporter(mReporter);
162 :
163 48 : return NS_OK;
164 : }
165 :
166 153 : nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet()
167 : {
168 51 : NS_UnregisterMemoryReporter(mReporter);
169 204 : }
170 :
171 : NS_IMETHODIMP
172 150 : nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32* aArray, PRUint32 aLength)
173 : {
174 150 : if (aLength <= 0) {
175 92 : MutexAutoLock lock(mPrefixSetLock);
176 46 : if (mHasPrefixes) {
177 1 : LOG(("Clearing PrefixSet"));
178 1 : mDeltas.Clear();
179 1 : mIndexPrefixes.Clear();
180 1 : mIndexStarts.Clear();
181 1 : mHasPrefixes = false;
182 : }
183 : } else {
184 104 : return MakePrefixSet(aArray, aLength);
185 : }
186 :
187 46 : return NS_OK;
188 : }
189 :
190 : nsresult
191 104 : nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLength)
192 : {
193 104 : if (aLength == 0) {
194 0 : return NS_OK;
195 : }
196 :
197 : #ifdef DEBUG
198 1208 : for (PRUint32 i = 1; i < aLength; i++) {
199 1104 : MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i-1]);
200 : }
201 : #endif
202 :
203 208 : FallibleTArray<PRUint32> newIndexPrefixes;
204 208 : FallibleTArray<PRUint32> newIndexStarts;
205 208 : FallibleTArray<PRUint16> newDeltas;
206 :
207 104 : if (!newIndexPrefixes.AppendElement(aPrefixes[0])) {
208 0 : return NS_ERROR_OUT_OF_MEMORY;
209 : }
210 104 : if (!newIndexStarts.AppendElement(newDeltas.Length())) {
211 0 : return NS_ERROR_OUT_OF_MEMORY;
212 : }
213 :
214 104 : PRUint32 numOfDeltas = 0;
215 104 : PRUint32 currentItem = aPrefixes[0];
216 1208 : for (PRUint32 i = 1; i < aLength; i++) {
217 2208 : if ((numOfDeltas >= DELTAS_LIMIT) ||
218 1104 : (aPrefixes[i] - currentItem >= MAX_INDEX_DIFF)) {
219 1055 : if (!newIndexStarts.AppendElement(newDeltas.Length())) {
220 0 : return NS_ERROR_OUT_OF_MEMORY;
221 : }
222 1055 : if (!newIndexPrefixes.AppendElement(aPrefixes[i])) {
223 0 : return NS_ERROR_OUT_OF_MEMORY;
224 : }
225 1055 : numOfDeltas = 0;
226 : } else {
227 49 : PRUint16 delta = aPrefixes[i] - currentItem;
228 49 : if (!newDeltas.AppendElement(delta)) {
229 0 : return NS_ERROR_OUT_OF_MEMORY;
230 : }
231 49 : numOfDeltas++;
232 : }
233 1104 : currentItem = aPrefixes[i];
234 : }
235 :
236 104 : newIndexPrefixes.Compact();
237 104 : newIndexStarts.Compact();
238 104 : newDeltas.Compact();
239 :
240 104 : LOG(("Total number of indices: %d", newIndexPrefixes.Length()));
241 104 : LOG(("Total number of deltas: %d", newDeltas.Length()));
242 :
243 208 : MutexAutoLock lock(mPrefixSetLock);
244 :
245 : // This just swaps some pointers
246 104 : mIndexPrefixes.SwapElements(newIndexPrefixes);
247 104 : mIndexStarts.SwapElements(newIndexStarts);
248 104 : mDeltas.SwapElements(newDeltas);
249 :
250 104 : mHasPrefixes = true;
251 104 : mSetIsReady.NotifyAll();
252 :
253 104 : return NS_OK;
254 : }
255 :
256 : NS_IMETHODIMP
257 59 : nsUrlClassifierPrefixSet::GetPrefixes(PRUint32* aCount,
258 : PRUint32** aPrefixes)
259 : {
260 59 : NS_ENSURE_ARG_POINTER(aCount);
261 59 : *aCount = 0;
262 59 : NS_ENSURE_ARG_POINTER(aPrefixes);
263 59 : *aPrefixes = nsnull;
264 :
265 118 : nsTArray<PRUint32> aArray;
266 59 : PRUint32 prefixLength = mIndexPrefixes.Length();
267 :
268 1142 : for (PRUint32 i = 0; i < prefixLength; i++) {
269 1083 : PRUint32 prefix = mIndexPrefixes[i];
270 1083 : PRUint32 start = mIndexStarts[i];
271 58 : PRUint32 end = (i == (prefixLength - 1)) ? mDeltas.Length()
272 1141 : : mIndexStarts[i + 1];
273 1083 : aArray.AppendElement(prefix);
274 1129 : for (PRUint32 j = start; j < end; j++) {
275 46 : prefix += mDeltas[j];
276 46 : aArray.AppendElement(prefix);
277 : }
278 : }
279 :
280 59 : NS_ASSERTION(mIndexStarts.Length() + mDeltas.Length() == aArray.Length(),
281 : "Lengths are inconsistent");
282 :
283 59 : PRUint32 itemCount = aArray.Length();
284 :
285 59 : if (itemCount == 1 && aArray[0] == 0) {
286 : /* sentinel for empty set */
287 28 : aArray.Clear();
288 28 : itemCount = 0;
289 : }
290 :
291 59 : PRUint32* retval = static_cast<PRUint32*>(nsMemory::Alloc(itemCount * sizeof(PRUint32)));
292 59 : NS_ENSURE_TRUE(retval, NS_ERROR_OUT_OF_MEMORY);
293 1160 : for (PRUint32 i = 0; i < itemCount; i++) {
294 1101 : retval[i] = aArray[i];
295 : }
296 :
297 59 : *aCount = itemCount;
298 59 : *aPrefixes = retval;
299 :
300 59 : return NS_OK;
301 : }
302 :
303 2392 : PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start,
304 : PRUint32 end,
305 : PRUint32 target)
306 : {
307 21935 : while (start != end && end >= start) {
308 17681 : PRUint32 i = start + ((end - start) >> 1);
309 17681 : PRUint32 value = mIndexPrefixes[i];
310 17681 : if (value < target) {
311 8771 : start = i + 1;
312 8910 : } else if (value > target) {
313 8380 : end = i - 1;
314 : } else {
315 530 : return i;
316 : }
317 : }
318 1862 : return end;
319 : }
320 :
321 : nsresult
322 2408 : nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool* aFound)
323 : {
324 2408 : mPrefixSetLock.AssertCurrentThreadOwns();
325 :
326 2408 : *aFound = false;
327 :
328 2408 : if (!mHasPrefixes) {
329 0 : return NS_OK;
330 : }
331 :
332 2408 : PRUint32 target = aPrefix;
333 :
334 : // We want to do a "Price is Right" binary search, that is, we want to find
335 : // the index of the value either equal to the target or the closest value
336 : // that is less than the target.
337 : //
338 2408 : if (target < mIndexPrefixes[0]) {
339 16 : return NS_OK;
340 : }
341 :
342 : // |binsearch| does not necessarily return the correct index (when the
343 : // target is not found) but rather it returns an index at least one away
344 : // from the correct index.
345 : // Because of this, we need to check if the target lies before the beginning
346 : // of the indices.
347 :
348 2392 : PRUint32 i = BinSearch(0, mIndexPrefixes.Length() - 1, target);
349 2392 : if (mIndexPrefixes[i] > target && i > 0) {
350 550 : i--;
351 : }
352 :
353 : // Now search through the deltas for the target.
354 2392 : PRUint32 diff = target - mIndexPrefixes[i];
355 2392 : PRUint32 deltaIndex = mIndexStarts[i];
356 2392 : PRUint32 deltaSize = mDeltas.Length();
357 4444 : PRUint32 end = (i + 1 < mIndexStarts.Length()) ? mIndexStarts[i+1]
358 6836 : : deltaSize;
359 :
360 : // Sanity check the read values
361 2392 : if (end > deltaSize) {
362 0 : return NS_ERROR_FILE_CORRUPTED;
363 : }
364 :
365 4916 : while (diff > 0 && deltaIndex < end) {
366 132 : diff -= mDeltas[deltaIndex];
367 132 : deltaIndex++;
368 : }
369 :
370 2392 : if (diff == 0) {
371 1068 : *aFound = true;
372 : }
373 :
374 2392 : return NS_OK;
375 : }
376 :
377 : size_t
378 97 : nsUrlClassifierPrefixSet::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
379 : {
380 194 : MutexAutoLock lock(mPrefixSetLock);
381 97 : size_t n = 0;
382 97 : n += aMallocSizeOf(this);
383 97 : n += mDeltas.SizeOfExcludingThis(aMallocSizeOf);
384 97 : n += mIndexPrefixes.SizeOfExcludingThis(aMallocSizeOf);
385 97 : n += mIndexStarts.SizeOfExcludingThis(aMallocSizeOf);
386 97 : return n;
387 : }
388 :
389 : NS_IMETHODIMP
390 0 : nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty)
391 : {
392 0 : MutexAutoLock lock(mPrefixSetLock);
393 0 : *aEmpty = !mHasPrefixes;
394 0 : return NS_OK;
395 : }
396 :
397 : NS_IMETHODIMP
398 2409 : nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix,
399 : bool* aReady, bool* aFound)
400 : {
401 4818 : MutexAutoLock lock(mPrefixSetLock);
402 :
403 2409 : *aFound = false;
404 :
405 : // check whether we are opportunistically probing or should wait
406 2409 : if (*aReady) {
407 : // we should block until we are ready
408 548 : while (!mHasPrefixes) {
409 0 : LOG(("Set is empty, probe must wait"));
410 0 : mSetIsReady.Wait();
411 : }
412 : } else {
413 : // opportunistic probe -> check if set is loaded
414 2135 : if (mHasPrefixes) {
415 2134 : *aReady = true;
416 : } else {
417 1 : return NS_OK;
418 : }
419 : }
420 :
421 2408 : nsresult rv = Contains(aPrefix, aFound);
422 2408 : NS_ENSURE_SUCCESS(rv, rv);
423 :
424 2408 : return NS_OK;
425 : }
426 :
427 : nsresult
428 0 : nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
429 : {
430 : PRUint32 magic;
431 : PRInt32 read;
432 :
433 0 : read = PR_Read(fileFd, &magic, sizeof(PRUint32));
434 0 : NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FAILURE);
435 :
436 0 : if (magic == PREFIXSET_VERSION_MAGIC) {
437 : PRUint32 indexSize;
438 : PRUint32 deltaSize;
439 :
440 0 : read = PR_Read(fileFd, &indexSize, sizeof(PRUint32));
441 0 : NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
442 0 : read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32));
443 0 : NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
444 :
445 0 : if (indexSize == 0) {
446 0 : LOG(("stored PrefixSet is empty!"));
447 0 : return NS_ERROR_FAILURE;
448 : }
449 :
450 0 : if (deltaSize > (indexSize * DELTAS_LIMIT)) {
451 0 : return NS_ERROR_FILE_CORRUPTED;
452 : }
453 :
454 0 : nsTArray<PRUint32> mNewIndexPrefixes;
455 0 : nsTArray<PRUint32> mNewIndexStarts;
456 0 : nsTArray<PRUint16> mNewDeltas;
457 :
458 0 : mNewIndexStarts.SetLength(indexSize);
459 0 : mNewIndexPrefixes.SetLength(indexSize);
460 0 : mNewDeltas.SetLength(deltaSize);
461 :
462 0 : PRInt32 toRead = indexSize*sizeof(PRUint32);
463 0 : read = PR_Read(fileFd, mNewIndexPrefixes.Elements(), toRead);
464 0 : NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
465 0 : read = PR_Read(fileFd, mNewIndexStarts.Elements(), toRead);
466 0 : NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
467 0 : if (deltaSize > 0) {
468 0 : toRead = deltaSize*sizeof(PRUint16);
469 0 : read = PR_Read(fileFd, mNewDeltas.Elements(), toRead);
470 0 : NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
471 : }
472 :
473 0 : MutexAutoLock lock(mPrefixSetLock);
474 :
475 0 : mIndexPrefixes.SwapElements(mNewIndexPrefixes);
476 0 : mIndexStarts.SwapElements(mNewIndexStarts);
477 0 : mDeltas.SwapElements(mNewDeltas);
478 :
479 0 : mHasPrefixes = true;
480 0 : mSetIsReady.NotifyAll();
481 : } else {
482 0 : LOG(("Version magic mismatch, not loading"));
483 0 : return NS_ERROR_FAILURE;
484 : }
485 :
486 0 : LOG(("Loading PrefixSet successful"));
487 :
488 0 : return NS_OK;
489 : }
490 :
491 : NS_IMETHODIMP
492 0 : nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile)
493 : {
494 0 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer;
495 :
496 : nsresult rv;
497 0 : nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
498 0 : NS_ENSURE_SUCCESS(rv, rv);
499 :
500 0 : AutoFDClose fileFd;
501 0 : rv = file->OpenNSPRFileDesc(PR_RDONLY | nsILocalFile::OS_READAHEAD, 0, &fileFd);
502 0 : NS_ENSURE_SUCCESS(rv, rv);
503 :
504 0 : return LoadFromFd(fileFd);
505 : }
506 :
507 : nsresult
508 97 : nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd)
509 : {
510 : {
511 194 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer;
512 97 : PRInt64 size = 4 * sizeof(PRUint32);
513 97 : size += 2 * mIndexStarts.Length() * sizeof(PRUint32);
514 97 : size += mDeltas.Length() * sizeof(PRUint16);
515 :
516 97 : mozilla::fallocate(fileFd, size);
517 : }
518 :
519 : PRInt32 written;
520 97 : PRUint32 magic = PREFIXSET_VERSION_MAGIC;
521 97 : written = PR_Write(fileFd, &magic, sizeof(PRUint32));
522 97 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
523 :
524 97 : PRUint32 indexSize = mIndexStarts.Length();
525 97 : PRUint32 deltaSize = mDeltas.Length();
526 97 : written = PR_Write(fileFd, &indexSize, sizeof(PRUint32));
527 97 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
528 97 : written = PR_Write(fileFd, &deltaSize, sizeof(PRUint32));
529 97 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
530 :
531 97 : written = PR_Write(fileFd, mIndexPrefixes.Elements(), indexSize * sizeof(PRUint32));
532 97 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
533 97 : written = PR_Write(fileFd, mIndexStarts.Elements(), indexSize * sizeof(PRUint32));
534 97 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
535 97 : if (deltaSize > 0) {
536 0 : written = PR_Write(fileFd, mDeltas.Elements(), deltaSize * sizeof(PRUint16));
537 0 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
538 : }
539 :
540 97 : LOG(("Saving PrefixSet successful\n"));
541 :
542 97 : return NS_OK;
543 : }
544 :
545 : NS_IMETHODIMP
546 97 : nsUrlClassifierPrefixSet::StoreToFile(nsIFile* aFile)
547 : {
548 97 : if (!mHasPrefixes) {
549 0 : LOG(("Attempt to serialize empty PrefixSet"));
550 0 : return NS_ERROR_FAILURE;
551 : }
552 :
553 : nsresult rv;
554 194 : nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
555 97 : NS_ENSURE_SUCCESS(rv, rv);
556 :
557 194 : AutoFDClose fileFd;
558 97 : rv = file->OpenNSPRFileDesc(PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE,
559 97 : 0644, &fileFd);
560 97 : NS_ENSURE_SUCCESS(rv, rv);
561 :
562 194 : MutexAutoLock lock(mPrefixSetLock);
563 :
564 97 : return StoreToFd(fileFd);
565 : }
|