Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/cache/FileUtils.h"
8 :
9 : #include "mozilla/dom/quota/FileStreams.h"
10 : #include "mozilla/dom/quota/QuotaManager.h"
11 : #include "mozilla/SnappyCompressOutputStream.h"
12 : #include "mozilla/Unused.h"
13 : #include "nsIFile.h"
14 : #include "nsIUUIDGenerator.h"
15 : #include "nsNetCID.h"
16 : #include "nsISimpleEnumerator.h"
17 : #include "nsServiceManagerUtils.h"
18 : #include "nsString.h"
19 : #include "nsThreadUtils.h"
20 :
21 : namespace mozilla {
22 : namespace dom {
23 : namespace cache {
24 :
25 : using mozilla::dom::quota::FileInputStream;
26 : using mozilla::dom::quota::FileOutputStream;
27 : using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
28 : using mozilla::dom::quota::QuotaManager;
29 :
30 : namespace {
31 :
32 : enum BodyFileType
33 : {
34 : BODY_FILE_FINAL,
35 : BODY_FILE_TMP
36 : };
37 :
38 : nsresult
39 : BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
40 : nsIFile** aBodyFileOut);
41 :
42 : } // namespace
43 :
44 : // static
45 : nsresult
46 0 : BodyCreateDir(nsIFile* aBaseDir)
47 : {
48 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
49 :
50 0 : nsCOMPtr<nsIFile> aBodyDir;
51 0 : nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir));
52 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
53 :
54 0 : rv = aBodyDir->Append(NS_LITERAL_STRING("morgue"));
55 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
56 :
57 0 : rv = aBodyDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
58 0 : if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
59 0 : return NS_OK;
60 : }
61 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
62 :
63 0 : return rv;
64 : }
65 :
66 : // static
67 : nsresult
68 0 : BodyDeleteDir(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir)
69 : {
70 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
71 :
72 0 : nsCOMPtr<nsIFile> aBodyDir;
73 0 : nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir));
74 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
75 :
76 0 : rv = aBodyDir->Append(NS_LITERAL_STRING("morgue"));
77 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
78 :
79 0 : rv = RemoveNsIFileRecursively(aQuotaInfo, aBodyDir);
80 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
81 :
82 0 : return rv;
83 : }
84 :
85 : // static
86 : nsresult
87 0 : BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut)
88 : {
89 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
90 0 : MOZ_DIAGNOSTIC_ASSERT(aCacheDirOut);
91 :
92 0 : *aCacheDirOut = nullptr;
93 :
94 0 : nsresult rv = aBaseDir->Clone(aCacheDirOut);
95 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
96 0 : MOZ_DIAGNOSTIC_ASSERT(*aCacheDirOut);
97 :
98 0 : rv = (*aCacheDirOut)->Append(NS_LITERAL_STRING("morgue"));
99 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
100 :
101 : // Some file systems have poor performance when there are too many files
102 : // in a single directory. Mitigate this issue by spreading the body
103 : // files out into sub-directories. We use the last byte of the ID for
104 : // the name of the sub-directory.
105 0 : nsAutoString subDirName;
106 0 : subDirName.AppendInt(aId.m3[7]);
107 0 : rv = (*aCacheDirOut)->Append(subDirName);
108 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
109 :
110 0 : rv = (*aCacheDirOut)->Create(nsIFile::DIRECTORY_TYPE, 0755);
111 0 : if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
112 0 : return NS_OK;
113 : }
114 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
115 :
116 0 : return rv;
117 : }
118 :
119 : // static
120 : nsresult
121 0 : BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
122 : nsIFile* aBaseDir, nsIInputStream* aSource,
123 : void* aClosure,
124 : nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
125 : nsISupports** aCopyContextOut)
126 : {
127 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
128 0 : MOZ_DIAGNOSTIC_ASSERT(aSource);
129 0 : MOZ_DIAGNOSTIC_ASSERT(aClosure);
130 0 : MOZ_DIAGNOSTIC_ASSERT(aCallback);
131 0 : MOZ_DIAGNOSTIC_ASSERT(aIdOut);
132 0 : MOZ_DIAGNOSTIC_ASSERT(aCopyContextOut);
133 :
134 : nsresult rv;
135 : nsCOMPtr<nsIUUIDGenerator> idGen =
136 0 : do_GetService("@mozilla.org/uuid-generator;1", &rv);
137 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
138 :
139 0 : rv = idGen->GenerateUUIDInPlace(aIdOut);
140 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
141 :
142 0 : nsCOMPtr<nsIFile> finalFile;
143 0 : rv = BodyIdToFile(aBaseDir, *aIdOut, BODY_FILE_FINAL,
144 0 : getter_AddRefs(finalFile));
145 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
146 :
147 : bool exists;
148 0 : rv = finalFile->Exists(&exists);
149 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
150 0 : if (NS_WARN_IF(exists)) { return NS_ERROR_FILE_ALREADY_EXISTS; }
151 :
152 0 : nsCOMPtr<nsIFile> tmpFile;
153 0 : rv = BodyIdToFile(aBaseDir, *aIdOut, BODY_FILE_TMP, getter_AddRefs(tmpFile));
154 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
155 :
156 0 : rv = tmpFile->Exists(&exists);
157 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
158 0 : if (NS_WARN_IF(exists)) { return NS_ERROR_FILE_ALREADY_EXISTS; }
159 :
160 : nsCOMPtr<nsIOutputStream> fileStream =
161 0 : FileOutputStream::Create(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
162 0 : aQuotaInfo.mOrigin, tmpFile);
163 0 : if (NS_WARN_IF(!fileStream)) { return NS_ERROR_UNEXPECTED; }
164 :
165 : RefPtr<SnappyCompressOutputStream> compressed =
166 0 : new SnappyCompressOutputStream(fileStream);
167 :
168 : nsCOMPtr<nsIEventTarget> target =
169 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
170 :
171 0 : rv = NS_AsyncCopy(aSource, compressed, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS,
172 0 : compressed->BlockSize(), aCallback, aClosure,
173 : true, true, // close streams
174 0 : aCopyContextOut);
175 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
176 :
177 0 : return rv;
178 : }
179 :
180 : // static
181 : void
182 0 : BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
183 : {
184 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
185 0 : MOZ_DIAGNOSTIC_ASSERT(aCopyContext);
186 :
187 0 : nsresult rv = NS_CancelAsyncCopy(aCopyContext, NS_ERROR_ABORT);
188 0 : Unused << NS_WARN_IF(NS_FAILED(rv));
189 :
190 : // The partially written file must be cleaned up after the async copy
191 : // makes its callback.
192 0 : }
193 :
194 : // static
195 : nsresult
196 0 : BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
197 : {
198 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
199 :
200 0 : nsCOMPtr<nsIFile> tmpFile;
201 0 : nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP, getter_AddRefs(tmpFile));
202 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
203 :
204 0 : nsCOMPtr<nsIFile> finalFile;
205 0 : rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL, getter_AddRefs(finalFile));
206 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
207 :
208 0 : nsAutoString finalFileName;
209 0 : rv = finalFile->GetLeafName(finalFileName);
210 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
211 :
212 : // It's fine to not notify the QuotaManager that the path has been changed,
213 : // because its path will be updated and its size will be recalculated when
214 : // opening file next time.
215 0 : rv = tmpFile->RenameTo(nullptr, finalFileName);
216 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
217 :
218 0 : return rv;
219 : }
220 :
221 : // static
222 : nsresult
223 0 : BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
224 : nsIInputStream** aStreamOut)
225 : {
226 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
227 0 : MOZ_DIAGNOSTIC_ASSERT(aStreamOut);
228 :
229 0 : nsCOMPtr<nsIFile> finalFile;
230 0 : nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL,
231 0 : getter_AddRefs(finalFile));
232 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
233 :
234 : bool exists;
235 0 : rv = finalFile->Exists(&exists);
236 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
237 0 : if (NS_WARN_IF(!exists)) { return NS_ERROR_FILE_NOT_FOUND; }
238 :
239 : nsCOMPtr<nsIInputStream> fileStream =
240 0 : FileInputStream::Create(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
241 0 : aQuotaInfo.mOrigin, finalFile);
242 0 : if (NS_WARN_IF(!fileStream)) { return NS_ERROR_UNEXPECTED; }
243 :
244 0 : fileStream.forget(aStreamOut);
245 :
246 0 : return rv;
247 : }
248 :
249 : // static
250 : nsresult
251 0 : BodyDeleteFiles(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
252 : const nsTArray<nsID>& aIdList)
253 : {
254 0 : nsresult rv = NS_OK;
255 :
256 0 : for (uint32_t i = 0; i < aIdList.Length(); ++i) {
257 0 : nsCOMPtr<nsIFile> tmpFile;
258 0 : rv = BodyIdToFile(aBaseDir, aIdList[i], BODY_FILE_TMP,
259 0 : getter_AddRefs(tmpFile));
260 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
261 :
262 0 : rv = RemoveNsIFile(aQuotaInfo, tmpFile);
263 : // Only treat file deletion as a hard failure in DEBUG builds. Users
264 : // can unfortunately hit this on windows if anti-virus is scanning files,
265 : // etc.
266 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
267 :
268 0 : nsCOMPtr<nsIFile> finalFile;
269 0 : rv = BodyIdToFile(aBaseDir, aIdList[i], BODY_FILE_FINAL,
270 0 : getter_AddRefs(finalFile));
271 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
272 :
273 0 : rv = RemoveNsIFile(aQuotaInfo, finalFile);
274 : // Again, only treat removal as hard failure in debug build.
275 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
276 : }
277 :
278 0 : return NS_OK;
279 : }
280 :
281 : namespace {
282 :
283 : nsresult
284 0 : BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
285 : nsIFile** aBodyFileOut)
286 : {
287 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
288 0 : MOZ_DIAGNOSTIC_ASSERT(aBodyFileOut);
289 :
290 0 : *aBodyFileOut = nullptr;
291 :
292 0 : nsresult rv = BodyGetCacheDir(aBaseDir, aId, aBodyFileOut);
293 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
294 0 : MOZ_DIAGNOSTIC_ASSERT(*aBodyFileOut);
295 :
296 : char idString[NSID_LENGTH];
297 0 : aId.ToProvidedString(idString);
298 :
299 0 : NS_ConvertASCIItoUTF16 fileName(idString);
300 :
301 0 : if (aType == BODY_FILE_FINAL) {
302 0 : fileName.AppendLiteral(".final");
303 : } else {
304 0 : fileName.AppendLiteral(".tmp");
305 : }
306 :
307 0 : rv = (*aBodyFileOut)->Append(fileName);
308 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
309 :
310 0 : return rv;
311 : }
312 :
313 : } // namespace
314 :
315 : nsresult
316 0 : BodyDeleteOrphanedFiles(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
317 : nsTArray<nsID>& aKnownBodyIdList)
318 : {
319 0 : MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
320 :
321 : // body files are stored in a directory structure like:
322 : //
323 : // /morgue/01/{01fdddb2-884d-4c3d-95ba-0c8062f6c325}.final
324 : // /morgue/02/{02fdddb2-884d-4c3d-95ba-0c8062f6c325}.tmp
325 :
326 0 : nsCOMPtr<nsIFile> dir;
327 0 : nsresult rv = aBaseDir->Clone(getter_AddRefs(dir));
328 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
329 :
330 : // Add the root morgue directory
331 0 : rv = dir->Append(NS_LITERAL_STRING("morgue"));
332 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
333 :
334 0 : nsCOMPtr<nsISimpleEnumerator> entries;
335 0 : rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
336 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
337 :
338 : // Iterate over all the intermediate morgue subdirs
339 0 : bool hasMore = false;
340 0 : while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
341 0 : nsCOMPtr<nsISupports> entry;
342 0 : rv = entries->GetNext(getter_AddRefs(entry));
343 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
344 :
345 0 : nsCOMPtr<nsIFile> subdir = do_QueryInterface(entry);
346 :
347 0 : bool isDir = false;
348 0 : rv = subdir->IsDirectory(&isDir);
349 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
350 :
351 : // If a file got in here somehow, try to remove it and move on
352 0 : if (NS_WARN_IF(!isDir)) {
353 0 : rv = RemoveNsIFile(aQuotaInfo, subdir);
354 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
355 0 : continue;
356 : }
357 :
358 0 : nsCOMPtr<nsISimpleEnumerator> subEntries;
359 0 : rv = subdir->GetDirectoryEntries(getter_AddRefs(subEntries));
360 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
361 :
362 : // Now iterate over all the files in the subdir
363 0 : bool subHasMore = false;
364 0 : while(NS_SUCCEEDED(rv = subEntries->HasMoreElements(&subHasMore)) &&
365 : subHasMore) {
366 0 : nsCOMPtr<nsISupports> subEntry;
367 0 : rv = subEntries->GetNext(getter_AddRefs(subEntry));
368 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
369 :
370 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(subEntry);
371 :
372 0 : nsAutoCString leafName;
373 0 : rv = file->GetNativeLeafName(leafName);
374 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
375 :
376 : // Delete all tmp files regardless of known bodies. These are
377 : // all considered orphans.
378 0 : if (StringEndsWith(leafName, NS_LITERAL_CSTRING(".tmp"))) {
379 : // remove recursively in case its somehow a directory
380 0 : rv = RemoveNsIFileRecursively(aQuotaInfo, file);
381 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
382 0 : continue;
383 : }
384 :
385 0 : nsCString suffix(NS_LITERAL_CSTRING(".final"));
386 :
387 : // Otherwise, it must be a .final file. If its not, then just
388 : // skip it.
389 0 : if (NS_WARN_IF(!StringEndsWith(leafName, suffix) ||
390 : leafName.Length() != NSID_LENGTH - 1 + suffix.Length())) {
391 0 : continue;
392 : }
393 :
394 : // Finally, parse the uuid out of the name. If its fails to parse,
395 : // the ignore the file.
396 : nsID id;
397 0 : if (NS_WARN_IF(!id.Parse(leafName.BeginReading()))) {
398 0 : continue;
399 : }
400 :
401 0 : if (!aKnownBodyIdList.Contains(id)) {
402 : // remove recursively in case its somehow a directory
403 0 : rv = RemoveNsIFileRecursively(aQuotaInfo, file);
404 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
405 : }
406 : }
407 : }
408 :
409 0 : return rv;
410 : }
411 :
412 : namespace {
413 :
414 : nsresult
415 0 : GetMarkerFileHandle(const QuotaInfo& aQuotaInfo, nsIFile** aFileOut)
416 : {
417 0 : MOZ_DIAGNOSTIC_ASSERT(aFileOut);
418 :
419 0 : nsCOMPtr<nsIFile> marker;
420 0 : nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(marker));
421 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
422 :
423 0 : rv = marker->Append(NS_LITERAL_STRING("cache"));
424 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
425 :
426 0 : rv = marker->Append(NS_LITERAL_STRING("context_open.marker"));
427 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
428 :
429 0 : marker.forget(aFileOut);
430 :
431 0 : return rv;
432 : }
433 :
434 : } // namespace
435 :
436 : nsresult
437 0 : CreateMarkerFile(const QuotaInfo& aQuotaInfo)
438 : {
439 0 : nsCOMPtr<nsIFile> marker;
440 0 : nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker));
441 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
442 :
443 0 : rv = marker->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
444 0 : if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
445 0 : rv = NS_OK;
446 : }
447 :
448 : // Note, we don't need to fsync here. We only care about actually
449 : // writing the marker if later modifications to the Cache are
450 : // actually flushed to the disk. If the OS crashes before the marker
451 : // is written then we are ensured no other changes to the Cache were
452 : // flushed either.
453 :
454 0 : return rv;
455 : }
456 :
457 : nsresult
458 0 : DeleteMarkerFile(const QuotaInfo& aQuotaInfo)
459 : {
460 0 : nsCOMPtr<nsIFile> marker;
461 0 : nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker));
462 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
463 :
464 0 : rv = RemoveNsIFile(aQuotaInfo, marker);
465 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
466 :
467 : // Again, no fsync is necessary. If the OS crashes before the file
468 : // removal is flushed, then the Cache will search for stale data on
469 : // startup. This will cause the next Cache access to be a bit slow, but
470 : // it seems appropriate after an OS crash.
471 :
472 0 : return NS_OK;
473 : }
474 :
475 : bool
476 0 : MarkerFileExists(const QuotaInfo& aQuotaInfo)
477 : {
478 0 : nsCOMPtr<nsIFile> marker;
479 0 : nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker));
480 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
481 :
482 0 : bool exists = false;
483 0 : rv = marker->Exists(&exists);
484 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
485 :
486 0 : return exists;
487 : }
488 :
489 : // static
490 : nsresult
491 0 : RemoveNsIFileRecursively(const QuotaInfo& aQuotaInfo, nsIFile* aFile)
492 : {
493 0 : MOZ_DIAGNOSTIC_ASSERT(aFile);
494 :
495 0 : bool isDirectory = false;
496 0 : nsresult rv = aFile->IsDirectory(&isDirectory);
497 0 : if (rv == NS_ERROR_FILE_NOT_FOUND ||
498 : rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
499 0 : return NS_OK;
500 : }
501 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
502 :
503 0 : if (!isDirectory) {
504 0 : return RemoveNsIFile(aQuotaInfo, aFile);
505 : }
506 :
507 : // Unfortunately, we need to traverse all the entries and delete files one by
508 : // one to update their usages to the QuotaManager.
509 0 : nsCOMPtr<nsISimpleEnumerator> entries;
510 0 : rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
511 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
512 :
513 0 : bool hasMore = false;
514 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
515 0 : nsCOMPtr<nsISupports> entry;
516 0 : rv = entries->GetNext(getter_AddRefs(entry));
517 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
518 :
519 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
520 0 : MOZ_ASSERT(file);
521 :
522 0 : rv = RemoveNsIFileRecursively(aQuotaInfo, file);
523 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
524 : }
525 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
526 :
527 : // In the end, remove the folder
528 0 : rv = aFile->Remove(/* recursive */ false);
529 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
530 :
531 0 : return rv;
532 : }
533 :
534 : // static
535 : nsresult
536 0 : RemoveNsIFile(const QuotaInfo& aQuotaInfo, nsIFile* aFile)
537 : {
538 0 : MOZ_DIAGNOSTIC_ASSERT(aFile);
539 :
540 0 : int64_t fileSize = 0;
541 0 : nsresult rv = aFile->GetFileSize(&fileSize);
542 0 : if (rv == NS_ERROR_FILE_NOT_FOUND ||
543 : rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
544 0 : return NS_OK;
545 : }
546 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
547 :
548 0 : rv = aFile->Remove( /* recursive */ false);
549 0 : if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
550 :
551 0 : QuotaManager* quotaManager = QuotaManager::Get();
552 0 : MOZ_DIAGNOSTIC_ASSERT(quotaManager);
553 :
554 0 : quotaManager->DecreaseUsageForOrigin(PERSISTENCE_TYPE_DEFAULT,
555 : aQuotaInfo.mGroup, aQuotaInfo.mOrigin,
556 0 : fileSize);
557 :
558 0 : return rv;
559 : }
560 :
561 : } // namespace cache
562 : } // namespace dom
563 : } // namespace mozilla
|