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 56 : virtual ~nsPrefixSetReporter() {};
76 :
77 : NS_DECL_ISUPPORTS
78 : NS_DECL_NSIMEMORYREPORTER
79 :
80 : private:
81 : nsCString mPath;
82 : nsUrlClassifierPrefixSet * mParent;
83 : };
84 :
85 56 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter)
86 :
87 0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StoragePrefixSetMallocSizeOf,
88 : "storage/prefixset")
89 :
90 14 : nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent,
91 : const nsACString & aName)
92 14 : : mParent(aParent)
93 : {
94 14 : mPath.Assign(NS_LITERAL_CSTRING("explicit/storage/prefixset"));
95 14 : if (!aName.IsEmpty()) {
96 14 : mPath.Append("/");
97 14 : mPath.Append(aName);
98 : }
99 14 : }
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 134 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet)
145 :
146 14 : nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
147 : : mPrefixSetLock("mPrefixSetLock"),
148 : mSetIsReady(mPrefixSetLock, "mSetIsReady"),
149 : mHasPrefixes(false),
150 14 : mRandomKey(0)
151 : {
152 : #if defined(PR_LOGGING)
153 14 : if (!gUrlClassifierPrefixSetLog)
154 8 : gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet");
155 : #endif
156 :
157 14 : nsresult rv = InitKey();
158 14 : if (NS_FAILED(rv)) {
159 0 : LOG(("Failed to initialize PrefixSet"));
160 : }
161 :
162 28 : mReporter = new nsPrefixSetReporter(this, NS_LITERAL_CSTRING("all"));
163 14 : NS_RegisterMemoryReporter(mReporter);
164 14 : }
165 :
166 42 : nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet()
167 : {
168 14 : NS_UnregisterMemoryReporter(mReporter);
169 56 : }
170 :
171 : nsresult
172 14 : nsUrlClassifierPrefixSet::InitKey()
173 : {
174 : nsCOMPtr<nsIRandomGenerator> rg =
175 28 : do_GetService("@mozilla.org/security/random-generator;1");
176 14 : NS_ENSURE_STATE(rg);
177 :
178 : PRUint8 *temp;
179 14 : nsresult rv = rg->GenerateRandomBytes(sizeof(mRandomKey), &temp);
180 14 : NS_ENSURE_SUCCESS(rv, rv);
181 14 : memcpy(&mRandomKey, temp, sizeof(mRandomKey));
182 14 : NS_Free(temp);
183 :
184 14 : LOG(("Initialized PrefixSet, key = %X", mRandomKey));
185 :
186 14 : return NS_OK;
187 : }
188 :
189 : NS_IMETHODIMP
190 203 : nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength)
191 : {
192 203 : if (aLength <= 0) {
193 112 : MutexAutoLock lock(mPrefixSetLock);
194 56 : if (mHasPrefixes) {
195 52 : LOG(("Clearing PrefixSet"));
196 52 : mDeltas.Clear();
197 52 : mIndexPrefixes.Clear();
198 52 : mIndexStarts.Clear();
199 52 : mHasPrefixes = false;
200 : }
201 : } else {
202 147 : return MakePrefixSet(aArray, aLength);
203 : }
204 :
205 56 : return NS_OK;
206 : }
207 :
208 : nsresult
209 147 : nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLength)
210 : {
211 147 : if (aLength == 0) {
212 0 : return NS_OK;
213 : }
214 :
215 : #ifdef DEBUG
216 1293 : for (PRUint32 i = 1; i < aLength; i++) {
217 1146 : MOZ_ASSERT(prefixes[i] >= prefixes[i-1]);
218 : }
219 : #endif
220 :
221 294 : FallibleTArray<PRUint32> newIndexPrefixes;
222 294 : FallibleTArray<PRUint32> newIndexStarts;
223 294 : FallibleTArray<PRUint16> newDeltas;
224 :
225 147 : if (!newIndexPrefixes.AppendElement(prefixes[0])) {
226 0 : return NS_ERROR_OUT_OF_MEMORY;
227 : }
228 147 : if (!newIndexStarts.AppendElement(newDeltas.Length())) {
229 0 : return NS_ERROR_OUT_OF_MEMORY;
230 : }
231 :
232 147 : PRUint32 numOfDeltas = 0;
233 147 : PRUint32 currentItem = prefixes[0];
234 1293 : for (PRUint32 i = 1; i < aLength; i++) {
235 2292 : if ((numOfDeltas >= DELTAS_LIMIT) ||
236 1146 : (prefixes[i] - currentItem >= MAX_INDEX_DIFF)) {
237 1088 : if (!newIndexStarts.AppendElement(newDeltas.Length())) {
238 0 : return NS_ERROR_OUT_OF_MEMORY;
239 : }
240 1088 : if (!newIndexPrefixes.AppendElement(prefixes[i])) {
241 0 : return NS_ERROR_OUT_OF_MEMORY;
242 : }
243 1088 : numOfDeltas = 0;
244 : } else {
245 58 : PRUint16 delta = prefixes[i] - currentItem;
246 58 : if (!newDeltas.AppendElement(delta)) {
247 0 : return NS_ERROR_OUT_OF_MEMORY;
248 : }
249 58 : numOfDeltas++;
250 : }
251 1146 : currentItem = prefixes[i];
252 : }
253 :
254 147 : newIndexPrefixes.Compact();
255 147 : newIndexStarts.Compact();
256 147 : newDeltas.Compact();
257 :
258 147 : LOG(("Total number of indices: %d", newIndexPrefixes.Length()));
259 147 : LOG(("Total number of deltas: %d", newDeltas.Length()));
260 :
261 294 : MutexAutoLock lock(mPrefixSetLock);
262 :
263 : // This just swaps some pointers
264 147 : mIndexPrefixes.SwapElements(newIndexPrefixes);
265 147 : mIndexStarts.SwapElements(newIndexStarts);
266 147 : mDeltas.SwapElements(newDeltas);
267 :
268 147 : mHasPrefixes = true;
269 147 : mSetIsReady.NotifyAll();
270 :
271 147 : return NS_OK;
272 : }
273 :
274 2155 : PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start,
275 : PRUint32 end,
276 : PRUint32 target)
277 : {
278 21418 : while (start != end && end >= start) {
279 17623 : PRUint32 i = start + ((end - start) >> 1);
280 17623 : PRUint32 value = mIndexPrefixes[i];
281 17623 : if (value < target) {
282 8750 : start = i + 1;
283 8873 : } else if (value > target) {
284 8358 : end = i - 1;
285 : } else {
286 515 : return i;
287 : }
288 : }
289 1640 : return end;
290 : }
291 :
292 : nsresult
293 2176 : nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool * aFound)
294 : {
295 2176 : mPrefixSetLock.AssertCurrentThreadOwns();
296 :
297 2176 : *aFound = false;
298 :
299 2176 : if (!mHasPrefixes) {
300 0 : return NS_OK;
301 : }
302 :
303 2176 : PRUint32 target = aPrefix;
304 :
305 : // We want to do a "Price is Right" binary search, that is, we want to find
306 : // the index of the value either equal to the target or the closest value
307 : // that is less than the target.
308 : //
309 2176 : if (target < mIndexPrefixes[0]) {
310 21 : return NS_OK;
311 : }
312 :
313 : // |binsearch| does not necessarily return the correct index (when the
314 : // target is not found) but rather it returns an index at least one away
315 : // from the correct index.
316 : // Because of this, we need to check if the target lies before the beginning
317 : // of the indices.
318 :
319 2155 : PRUint32 i = BinSearch(0, mIndexPrefixes.Length() - 1, target);
320 2155 : if (mIndexPrefixes[i] > target && i > 0) {
321 482 : i--;
322 : }
323 :
324 : // Now search through the deltas for the target.
325 2155 : PRUint32 diff = target - mIndexPrefixes[i];
326 2155 : PRUint32 deltaIndex = mIndexStarts[i];
327 2155 : PRUint32 deltaSize = mDeltas.Length();
328 4175 : PRUint32 end = (i + 1 < mIndexStarts.Length()) ? mIndexStarts[i+1]
329 6330 : : deltaSize;
330 :
331 : // Sanity check the read values
332 2155 : if (end > deltaSize) {
333 0 : return NS_ERROR_FILE_CORRUPTED;
334 : }
335 :
336 4444 : while (diff > 0 && deltaIndex < end) {
337 134 : diff -= mDeltas[deltaIndex];
338 134 : deltaIndex++;
339 : }
340 :
341 2155 : if (diff == 0) {
342 1024 : *aFound = true;
343 : }
344 :
345 2155 : return NS_OK;
346 : }
347 :
348 : size_t
349 0 : nsUrlClassifierPrefixSet::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
350 : {
351 0 : MutexAutoLock lock(mPrefixSetLock);
352 0 : size_t n = 0;
353 0 : n += aMallocSizeOf(this);
354 0 : n += mDeltas.SizeOfExcludingThis(aMallocSizeOf);
355 0 : n += mIndexPrefixes.SizeOfExcludingThis(aMallocSizeOf);
356 0 : n += mIndexStarts.SizeOfExcludingThis(aMallocSizeOf);
357 0 : return n;
358 : }
359 :
360 : NS_IMETHODIMP
361 141 : nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty)
362 : {
363 282 : MutexAutoLock lock(mPrefixSetLock);
364 141 : *aEmpty = !mHasPrefixes;
365 141 : return NS_OK;
366 : }
367 :
368 : NS_IMETHODIMP
369 2296 : nsUrlClassifierPrefixSet::GetKey(PRUint32 * aKey)
370 : {
371 4592 : MutexAutoLock lock(mPrefixSetLock);
372 2296 : *aKey = mRandomKey;
373 2296 : return NS_OK;
374 : }
375 :
376 : NS_IMETHODIMP
377 2177 : nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRUint32 aKey,
378 : bool* aReady, bool* aFound)
379 : {
380 4354 : MutexAutoLock lock(mPrefixSetLock);
381 :
382 2177 : *aFound = false;
383 :
384 : // We might have raced here with a LoadPrefixSet call,
385 : // loading a saved PrefixSet with another key than the one used to probe us.
386 : // This must occur exactly between the GetKey call and the Probe call.
387 : // This could cause a false negative immediately after browser start.
388 : // Claim we are still busy loading instead.
389 2177 : if (aKey != mRandomKey) {
390 0 : LOG(("Potential race condition detected, avoiding"));
391 0 : *aReady = false;
392 0 : return NS_OK;
393 : }
394 :
395 : // check whether we are opportunistically probing or should wait
396 2177 : if (*aReady) {
397 : // we should block until we are ready
398 0 : while (!mHasPrefixes) {
399 0 : LOG(("Set is empty, probe must wait"));
400 0 : mSetIsReady.Wait();
401 : }
402 : } else {
403 : // opportunistic probe -> check if set is loaded
404 2177 : if (mHasPrefixes) {
405 2176 : *aReady = true;
406 : } else {
407 1 : return NS_OK;
408 : }
409 : }
410 :
411 2176 : nsresult rv = Contains(aPrefix, aFound);
412 2176 : NS_ENSURE_SUCCESS(rv, rv);
413 :
414 2176 : return NS_OK;
415 : }
416 :
417 : nsresult
418 0 : nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd)
419 : {
420 : PRUint32 magic;
421 : PRInt32 read;
422 :
423 0 : read = PR_Read(fileFd, &magic, sizeof(PRUint32));
424 0 : NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FAILURE);
425 :
426 0 : if (magic == PREFIXSET_VERSION_MAGIC) {
427 : PRUint32 indexSize;
428 : PRUint32 deltaSize;
429 :
430 0 : read = PR_Read(fileFd, &mRandomKey, sizeof(PRUint32));
431 0 : NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
432 0 : read = PR_Read(fileFd, &indexSize, sizeof(PRUint32));
433 0 : NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
434 0 : read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32));
435 0 : NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
436 :
437 0 : if (indexSize == 0) {
438 0 : LOG(("stored PrefixSet is empty!"));
439 0 : return NS_ERROR_FAILURE;
440 : }
441 :
442 0 : if (deltaSize > (indexSize * DELTAS_LIMIT)) {
443 0 : return NS_ERROR_FILE_CORRUPTED;
444 : }
445 :
446 0 : nsTArray<PRUint32> mNewIndexPrefixes;
447 0 : nsTArray<PRUint32> mNewIndexStarts;
448 0 : nsTArray<PRUint16> mNewDeltas;
449 :
450 0 : mNewIndexStarts.SetLength(indexSize);
451 0 : mNewIndexPrefixes.SetLength(indexSize);
452 0 : mNewDeltas.SetLength(deltaSize);
453 :
454 0 : PRInt32 toRead = indexSize*sizeof(PRUint32);
455 0 : read = PR_Read(fileFd, mNewIndexPrefixes.Elements(), toRead);
456 0 : NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
457 0 : read = PR_Read(fileFd, mNewIndexStarts.Elements(), toRead);
458 0 : NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
459 0 : if (deltaSize > 0) {
460 0 : toRead = deltaSize*sizeof(PRUint16);
461 0 : read = PR_Read(fileFd, mNewDeltas.Elements(), toRead);
462 0 : NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
463 : }
464 :
465 0 : MutexAutoLock lock(mPrefixSetLock);
466 :
467 0 : mIndexPrefixes.SwapElements(mNewIndexPrefixes);
468 0 : mIndexStarts.SwapElements(mNewIndexStarts);
469 0 : mDeltas.SwapElements(mNewDeltas);
470 :
471 0 : mHasPrefixes = true;
472 0 : mSetIsReady.NotifyAll();
473 : } else {
474 0 : LOG(("Version magic mismatch, not loading"));
475 0 : return NS_ERROR_FAILURE;
476 : }
477 :
478 0 : LOG(("Loading PrefixSet successful"));
479 :
480 0 : return NS_OK;
481 : }
482 :
483 : NS_IMETHODIMP
484 0 : nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile)
485 : {
486 : nsresult rv;
487 0 : nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
488 0 : NS_ENSURE_SUCCESS(rv, rv);
489 :
490 0 : AutoFDClose fileFd;
491 0 : rv = file->OpenNSPRFileDesc(PR_RDONLY | nsILocalFile::OS_READAHEAD, 0, &fileFd);
492 0 : NS_ENSURE_SUCCESS(rv, rv);
493 :
494 0 : return LoadFromFd(fileFd);
495 : }
496 :
497 : nsresult
498 140 : nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd)
499 : {
500 : {
501 280 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer;
502 140 : PRInt64 size = 4 * sizeof(PRUint32);
503 140 : size += 2 * mIndexStarts.Length() * sizeof(PRUint32);
504 140 : size += mDeltas.Length() * sizeof(PRUint16);
505 :
506 140 : mozilla::fallocate(fileFd, size);
507 : }
508 :
509 : PRInt32 written;
510 140 : PRUint32 magic = PREFIXSET_VERSION_MAGIC;
511 140 : written = PR_Write(fileFd, &magic, sizeof(PRUint32));
512 140 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
513 :
514 140 : written = PR_Write(fileFd, &mRandomKey, sizeof(PRUint32));
515 140 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
516 :
517 140 : PRUint32 indexSize = mIndexStarts.Length();
518 140 : PRUint32 deltaSize = mDeltas.Length();
519 140 : written = PR_Write(fileFd, &indexSize, sizeof(PRUint32));
520 140 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
521 140 : written = PR_Write(fileFd, &deltaSize, sizeof(PRUint32));
522 140 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
523 :
524 140 : written = PR_Write(fileFd, mIndexPrefixes.Elements(), indexSize * sizeof(PRUint32));
525 140 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
526 140 : written = PR_Write(fileFd, mIndexStarts.Elements(), indexSize * sizeof(PRUint32));
527 140 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
528 140 : if (deltaSize > 0) {
529 4 : written = PR_Write(fileFd, mDeltas.Elements(), deltaSize * sizeof(PRUint16));
530 4 : NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
531 : }
532 :
533 140 : LOG(("Saving PrefixSet successful\n"));
534 :
535 140 : return NS_OK;
536 : }
537 :
538 : NS_IMETHODIMP
539 140 : nsUrlClassifierPrefixSet::StoreToFile(nsIFile * aFile)
540 : {
541 140 : if (!mHasPrefixes) {
542 0 : LOG(("Attempt to serialize empty PrefixSet"));
543 0 : return NS_ERROR_FAILURE;
544 : }
545 :
546 : nsresult rv;
547 280 : nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
548 140 : NS_ENSURE_SUCCESS(rv, rv);
549 :
550 280 : AutoFDClose fileFd;
551 140 : rv = file->OpenNSPRFileDesc(PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE,
552 140 : 0644, &fileFd);
553 140 : NS_ENSURE_SUCCESS(rv, rv);
554 :
555 280 : MutexAutoLock lock(mPrefixSetLock);
556 :
557 140 : return StoreToFd(fileFd);
558 : }
|