diff -r 000000000000 -r 6474c204b198 dom/indexedDB/FileManager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/indexedDB/FileManager.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,433 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileManager.h" + +#include "mozIStorageConnection.h" +#include "mozIStorageStatement.h" +#include "nsIInputStream.h" +#include "nsISimpleEnumerator.h" + +#include "mozilla/dom/quota/Utilities.h" +#include "mozStorageCID.h" +#include "mozStorageHelper.h" + +#include "Client.h" +#include "FileInfo.h" +#include "IndexedDatabaseManager.h" +#include "OpenDatabaseHelper.h" + +#include "IndexedDatabaseInlines.h" +#include + +USING_INDEXEDDB_NAMESPACE +using mozilla::dom::quota::AssertIsOnIOThread; + +namespace { + +PLDHashOperator +EnumerateToTArray(const uint64_t& aKey, + FileInfo* aValue, + void* aUserArg) +{ + NS_ASSERTION(aValue, "Null pointer!"); + NS_ASSERTION(aUserArg, "Null pointer!"); + + nsTArray* array = + static_cast*>(aUserArg); + + array->AppendElement(aValue); + + return PL_DHASH_NEXT; +} + +already_AddRefed +GetDirectoryFor(const nsAString& aDirectoryPath) +{ + nsCOMPtr directory = + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + NS_ENSURE_TRUE(directory, nullptr); + + nsresult rv = directory->InitWithPath(aDirectoryPath); + NS_ENSURE_SUCCESS(rv, nullptr); + + return directory.forget(); +} + +} // anonymous namespace + +nsresult +FileManager::Init(nsIFile* aDirectory, + mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(aDirectory, "Null directory!"); + NS_ASSERTION(aConnection, "Null connection!"); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + bool isDirectory; + rv = aDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + } + else { + rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = aDirectory->GetPath(mDirectoryPath); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr journalDirectory; + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + bool isDirectory; + rv = journalDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + } + + rv = journalDirectory->GetPath(mJournalDirectoryPath); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id, refcount " + "FROM file" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { + int64_t id; + rv = stmt->GetInt64(0, &id); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t refcount; + rv = stmt->GetInt32(1, &refcount); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(refcount, "This shouldn't happen!"); + + nsRefPtr fileInfo = FileInfo::Create(this, id); + fileInfo->mDBRefCnt = refcount; + + mFileInfos.Put(id, fileInfo); + + mLastFileId = std::max(id, mLastFileId); + } + + return NS_OK; +} + +nsresult +FileManager::Invalidate() +{ + if (IndexedDatabaseManager::IsClosed()) { + NS_ERROR("Shouldn't be called after shutdown!"); + return NS_ERROR_UNEXPECTED; + } + + nsTArray fileInfos; + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + + NS_ASSERTION(!mInvalidated, "Invalidate more than once?!"); + mInvalidated = true; + + fileInfos.SetCapacity(mFileInfos.Count()); + mFileInfos.EnumerateRead(EnumerateToTArray, &fileInfos); + } + + for (uint32_t i = 0; i < fileInfos.Length(); i++) { + FileInfo* fileInfo = fileInfos.ElementAt(i); + fileInfo->ClearDBRefs(); + } + + return NS_OK; +} + +already_AddRefed +FileManager::GetDirectory() +{ + return GetDirectoryFor(mDirectoryPath); +} + +already_AddRefed +FileManager::GetJournalDirectory() +{ + return GetDirectoryFor(mJournalDirectoryPath); +} + +already_AddRefed +FileManager::EnsureJournalDirectory() +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsCOMPtr journalDirectory = GetDirectoryFor(mJournalDirectoryPath); + NS_ENSURE_TRUE(journalDirectory, nullptr); + + bool exists; + nsresult rv = journalDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, nullptr); + + if (exists) { + bool isDirectory; + rv = journalDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, nullptr); + NS_ENSURE_TRUE(isDirectory, nullptr); + } + else { + rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + NS_ENSURE_SUCCESS(rv, nullptr); + } + + return journalDirectory.forget(); +} + +already_AddRefed +FileManager::GetFileInfo(int64_t aId) +{ + if (IndexedDatabaseManager::IsClosed()) { + NS_ERROR("Shouldn't be called after shutdown!"); + return nullptr; + } + + FileInfo* fileInfo = nullptr; + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + fileInfo = mFileInfos.Get(aId); + } + nsRefPtr result = fileInfo; + return result.forget(); +} + +already_AddRefed +FileManager::GetNewFileInfo() +{ + if (IndexedDatabaseManager::IsClosed()) { + NS_ERROR("Shouldn't be called after shutdown!"); + return nullptr; + } + + nsAutoPtr fileInfo; + + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + + int64_t id = mLastFileId + 1; + + fileInfo = FileInfo::Create(this, id); + + mFileInfos.Put(id, fileInfo); + + mLastFileId = id; + } + + nsRefPtr result = fileInfo.forget(); + return result.forget(); +} + +// static +already_AddRefed +FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId) +{ + NS_ASSERTION(aDirectory, "Null pointer!"); + + nsAutoString id; + id.AppendInt(aId); + + nsCOMPtr file; + nsresult rv = aDirectory->Clone(getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = file->Append(id); + NS_ENSURE_SUCCESS(rv, nullptr); + + return file.forget(); +} + +// static +nsresult +FileManager::InitDirectory(nsIFile* aDirectory, + nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin) +{ + AssertIsOnIOThread(); + NS_ASSERTION(aDirectory, "Null directory!"); + NS_ASSERTION(aDatabaseFile, "Null database file!"); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!exists) { + return NS_OK; + } + + bool isDirectory; + rv = aDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + + nsCOMPtr journalDirectory; + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + rv = journalDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + + nsCOMPtr entries; + rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasElements; + rv = entries->HasMoreElements(&hasElements); + NS_ENSURE_SUCCESS(rv, rv); + + if (hasElements) { + nsCOMPtr connection; + rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile, + aDirectory, NullString(), aPersistenceType, aGroup, aOrigin, + getter_AddRefs(connection)); + NS_ENSURE_SUCCESS(rv, rv); + + mozStorageTransaction transaction(connection, false); + + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE VIRTUAL TABLE fs USING filesystem;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr stmt; + rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, (name IN (SELECT id FROM file)) FROM fs " + "WHERE path = :path" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsString path; + rv = journalDirectory->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { + nsString name; + rv = stmt->GetString(0, name); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t flag = stmt->AsInt32(1); + + if (!flag) { + nsCOMPtr file; + rv = aDirectory->Clone(getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = file->Append(name); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(file->Remove(false))) { + NS_WARNING("Failed to remove orphaned file!"); + } + } + + nsCOMPtr journalFile; + rv = journalDirectory->Clone(getter_AddRefs(journalFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalFile->Append(name); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(journalFile->Remove(false))) { + NS_WARNING("Failed to remove journal file!"); + } + } + + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE fs;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + transaction.Commit(); + } + } + + return NS_OK; +} + +// static +nsresult +FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) +{ + AssertIsOnIOThread(); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!exists) { + *aUsage = 0; + return NS_OK; + } + + nsCOMPtr entries; + rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + uint64_t usage = 0; + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr file = do_QueryInterface(entry); + NS_ENSURE_TRUE(file, NS_NOINTERFACE); + + nsString leafName; + rv = file->GetLeafName(leafName); + NS_ENSURE_SUCCESS(rv, rv); + + if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { + continue; + } + + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + quota::IncrementUsage(&usage, uint64_t(fileSize)); + } + + *aUsage = usage; + return NS_OK; +}