Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / indexedDB / FileManager.cpp
blobc0fc7ea9452536128321f8b22e9e850180fcb00b
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 /* 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/. */
7 #include "FileManager.h"
9 #include "mozIStorageConnection.h"
10 #include "mozIStorageStatement.h"
11 #include "nsIInputStream.h"
12 #include "nsISimpleEnumerator.h"
14 #include "mozilla/dom/quota/Utilities.h"
15 #include "mozStorageCID.h"
16 #include "mozStorageHelper.h"
18 #include "Client.h"
19 #include "FileInfo.h"
20 #include "IndexedDatabaseManager.h"
21 #include "OpenDatabaseHelper.h"
23 #include "IndexedDatabaseInlines.h"
24 #include <algorithm>
26 USING_INDEXEDDB_NAMESPACE
27 using mozilla::dom::quota::AssertIsOnIOThread;
29 namespace {
31 PLDHashOperator
32 EnumerateToTArray(const uint64_t& aKey,
33 FileInfo* aValue,
34 void* aUserArg)
36 NS_ASSERTION(aValue, "Null pointer!");
37 NS_ASSERTION(aUserArg, "Null pointer!");
39 nsTArray<FileInfo*>* array =
40 static_cast<nsTArray<FileInfo*>*>(aUserArg);
42 array->AppendElement(aValue);
44 return PL_DHASH_NEXT;
47 already_AddRefed<nsIFile>
48 GetDirectoryFor(const nsAString& aDirectoryPath)
50 nsCOMPtr<nsIFile> directory =
51 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
52 NS_ENSURE_TRUE(directory, nullptr);
54 nsresult rv = directory->InitWithPath(aDirectoryPath);
55 NS_ENSURE_SUCCESS(rv, nullptr);
57 return directory.forget();
60 } // anonymous namespace
62 nsresult
63 FileManager::Init(nsIFile* aDirectory,
64 mozIStorageConnection* aConnection)
66 AssertIsOnIOThread();
67 NS_ASSERTION(aDirectory, "Null directory!");
68 NS_ASSERTION(aConnection, "Null connection!");
70 bool exists;
71 nsresult rv = aDirectory->Exists(&exists);
72 NS_ENSURE_SUCCESS(rv, rv);
74 if (exists) {
75 bool isDirectory;
76 rv = aDirectory->IsDirectory(&isDirectory);
77 NS_ENSURE_SUCCESS(rv, rv);
78 NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE);
80 else {
81 rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
82 NS_ENSURE_SUCCESS(rv, rv);
85 rv = aDirectory->GetPath(mDirectoryPath);
86 NS_ENSURE_SUCCESS(rv, rv);
88 nsCOMPtr<nsIFile> journalDirectory;
89 rv = aDirectory->Clone(getter_AddRefs(journalDirectory));
90 NS_ENSURE_SUCCESS(rv, rv);
92 rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME));
93 NS_ENSURE_SUCCESS(rv, rv);
95 rv = journalDirectory->Exists(&exists);
96 NS_ENSURE_SUCCESS(rv, rv);
98 if (exists) {
99 bool isDirectory;
100 rv = journalDirectory->IsDirectory(&isDirectory);
101 NS_ENSURE_SUCCESS(rv, rv);
102 NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE);
105 rv = journalDirectory->GetPath(mJournalDirectoryPath);
106 NS_ENSURE_SUCCESS(rv, rv);
108 nsCOMPtr<mozIStorageStatement> stmt;
109 rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
110 "SELECT id, refcount "
111 "FROM file"
112 ), getter_AddRefs(stmt));
113 NS_ENSURE_SUCCESS(rv, rv);
115 bool hasResult;
116 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
117 int64_t id;
118 rv = stmt->GetInt64(0, &id);
119 NS_ENSURE_SUCCESS(rv, rv);
121 int32_t refcount;
122 rv = stmt->GetInt32(1, &refcount);
123 NS_ENSURE_SUCCESS(rv, rv);
125 NS_ASSERTION(refcount, "This shouldn't happen!");
127 nsRefPtr<FileInfo> fileInfo = FileInfo::Create(this, id);
128 fileInfo->mDBRefCnt = refcount;
130 mFileInfos.Put(id, fileInfo);
132 mLastFileId = std::max(id, mLastFileId);
135 return NS_OK;
138 nsresult
139 FileManager::Invalidate()
141 if (IndexedDatabaseManager::IsClosed()) {
142 NS_ERROR("Shouldn't be called after shutdown!");
143 return NS_ERROR_UNEXPECTED;
146 nsTArray<FileInfo*> fileInfos;
148 MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
150 NS_ASSERTION(!mInvalidated, "Invalidate more than once?!");
151 mInvalidated = true;
153 fileInfos.SetCapacity(mFileInfos.Count());
154 mFileInfos.EnumerateRead(EnumerateToTArray, &fileInfos);
157 for (uint32_t i = 0; i < fileInfos.Length(); i++) {
158 FileInfo* fileInfo = fileInfos.ElementAt(i);
159 fileInfo->ClearDBRefs();
162 return NS_OK;
165 already_AddRefed<nsIFile>
166 FileManager::GetDirectory()
168 return GetDirectoryFor(mDirectoryPath);
171 already_AddRefed<nsIFile>
172 FileManager::GetJournalDirectory()
174 return GetDirectoryFor(mJournalDirectoryPath);
177 already_AddRefed<nsIFile>
178 FileManager::EnsureJournalDirectory()
180 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
182 nsCOMPtr<nsIFile> journalDirectory = GetDirectoryFor(mJournalDirectoryPath);
183 NS_ENSURE_TRUE(journalDirectory, nullptr);
185 bool exists;
186 nsresult rv = journalDirectory->Exists(&exists);
187 NS_ENSURE_SUCCESS(rv, nullptr);
189 if (exists) {
190 bool isDirectory;
191 rv = journalDirectory->IsDirectory(&isDirectory);
192 NS_ENSURE_SUCCESS(rv, nullptr);
193 NS_ENSURE_TRUE(isDirectory, nullptr);
195 else {
196 rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
197 NS_ENSURE_SUCCESS(rv, nullptr);
200 return journalDirectory.forget();
203 already_AddRefed<FileInfo>
204 FileManager::GetFileInfo(int64_t aId)
206 if (IndexedDatabaseManager::IsClosed()) {
207 NS_ERROR("Shouldn't be called after shutdown!");
208 return nullptr;
211 FileInfo* fileInfo = nullptr;
213 MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
214 fileInfo = mFileInfos.Get(aId);
216 nsRefPtr<FileInfo> result = fileInfo;
217 return result.forget();
220 already_AddRefed<FileInfo>
221 FileManager::GetNewFileInfo()
223 if (IndexedDatabaseManager::IsClosed()) {
224 NS_ERROR("Shouldn't be called after shutdown!");
225 return nullptr;
228 nsAutoPtr<FileInfo> fileInfo;
231 MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
233 int64_t id = mLastFileId + 1;
235 fileInfo = FileInfo::Create(this, id);
237 mFileInfos.Put(id, fileInfo);
239 mLastFileId = id;
242 nsRefPtr<FileInfo> result = fileInfo.forget();
243 return result.forget();
246 // static
247 already_AddRefed<nsIFile>
248 FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId)
250 NS_ASSERTION(aDirectory, "Null pointer!");
252 nsAutoString id;
253 id.AppendInt(aId);
255 nsCOMPtr<nsIFile> file;
256 nsresult rv = aDirectory->Clone(getter_AddRefs(file));
257 NS_ENSURE_SUCCESS(rv, nullptr);
259 rv = file->Append(id);
260 NS_ENSURE_SUCCESS(rv, nullptr);
262 return file.forget();
265 // static
266 nsresult
267 FileManager::InitDirectory(nsIFile* aDirectory,
268 nsIFile* aDatabaseFile,
269 PersistenceType aPersistenceType,
270 const nsACString& aGroup,
271 const nsACString& aOrigin)
273 AssertIsOnIOThread();
274 NS_ASSERTION(aDirectory, "Null directory!");
275 NS_ASSERTION(aDatabaseFile, "Null database file!");
277 bool exists;
278 nsresult rv = aDirectory->Exists(&exists);
279 NS_ENSURE_SUCCESS(rv, rv);
281 if (!exists) {
282 return NS_OK;
285 bool isDirectory;
286 rv = aDirectory->IsDirectory(&isDirectory);
287 NS_ENSURE_SUCCESS(rv, rv);
288 NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE);
290 nsCOMPtr<nsIFile> journalDirectory;
291 rv = aDirectory->Clone(getter_AddRefs(journalDirectory));
292 NS_ENSURE_SUCCESS(rv, rv);
294 rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME));
295 NS_ENSURE_SUCCESS(rv, rv);
297 rv = journalDirectory->Exists(&exists);
298 NS_ENSURE_SUCCESS(rv, rv);
300 if (exists) {
301 rv = journalDirectory->IsDirectory(&isDirectory);
302 NS_ENSURE_SUCCESS(rv, rv);
303 NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE);
305 nsCOMPtr<nsISimpleEnumerator> entries;
306 rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries));
307 NS_ENSURE_SUCCESS(rv, rv);
309 bool hasElements;
310 rv = entries->HasMoreElements(&hasElements);
311 NS_ENSURE_SUCCESS(rv, rv);
313 if (hasElements) {
314 nsCOMPtr<mozIStorageConnection> connection;
315 rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile,
316 aDirectory, NullString(), aPersistenceType, aGroup, aOrigin,
317 getter_AddRefs(connection));
318 NS_ENSURE_SUCCESS(rv, rv);
320 mozStorageTransaction transaction(connection, false);
322 rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
323 "CREATE VIRTUAL TABLE fs USING filesystem;"
325 NS_ENSURE_SUCCESS(rv, rv);
327 nsCOMPtr<mozIStorageStatement> stmt;
328 rv = connection->CreateStatement(NS_LITERAL_CSTRING(
329 "SELECT name, (name IN (SELECT id FROM file)) FROM fs "
330 "WHERE path = :path"
331 ), getter_AddRefs(stmt));
332 NS_ENSURE_SUCCESS(rv, rv);
334 nsString path;
335 rv = journalDirectory->GetPath(path);
336 NS_ENSURE_SUCCESS(rv, rv);
338 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path);
339 NS_ENSURE_SUCCESS(rv, rv);
341 bool hasResult;
342 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
343 nsString name;
344 rv = stmt->GetString(0, name);
345 NS_ENSURE_SUCCESS(rv, rv);
347 int32_t flag = stmt->AsInt32(1);
349 if (!flag) {
350 nsCOMPtr<nsIFile> file;
351 rv = aDirectory->Clone(getter_AddRefs(file));
352 NS_ENSURE_SUCCESS(rv, rv);
354 rv = file->Append(name);
355 NS_ENSURE_SUCCESS(rv, rv);
357 if (NS_FAILED(file->Remove(false))) {
358 NS_WARNING("Failed to remove orphaned file!");
362 nsCOMPtr<nsIFile> journalFile;
363 rv = journalDirectory->Clone(getter_AddRefs(journalFile));
364 NS_ENSURE_SUCCESS(rv, rv);
366 rv = journalFile->Append(name);
367 NS_ENSURE_SUCCESS(rv, rv);
369 if (NS_FAILED(journalFile->Remove(false))) {
370 NS_WARNING("Failed to remove journal file!");
374 rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
375 "DROP TABLE fs;"
377 NS_ENSURE_SUCCESS(rv, rv);
379 transaction.Commit();
383 return NS_OK;
386 // static
387 nsresult
388 FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage)
390 AssertIsOnIOThread();
392 bool exists;
393 nsresult rv = aDirectory->Exists(&exists);
394 NS_ENSURE_SUCCESS(rv, rv);
396 if (!exists) {
397 *aUsage = 0;
398 return NS_OK;
401 nsCOMPtr<nsISimpleEnumerator> entries;
402 rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
403 NS_ENSURE_SUCCESS(rv, rv);
405 uint64_t usage = 0;
407 bool hasMore;
408 while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
409 nsCOMPtr<nsISupports> entry;
410 rv = entries->GetNext(getter_AddRefs(entry));
411 NS_ENSURE_SUCCESS(rv, rv);
413 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
414 NS_ENSURE_TRUE(file, NS_NOINTERFACE);
416 nsString leafName;
417 rv = file->GetLeafName(leafName);
418 NS_ENSURE_SUCCESS(rv, rv);
420 if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
421 continue;
424 int64_t fileSize;
425 rv = file->GetFileSize(&fileSize);
426 NS_ENSURE_SUCCESS(rv, rv);
428 quota::IncrementUsage(&usage, uint64_t(fileSize));
431 *aUsage = usage;
432 return NS_OK;