1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/indexedDB/FileManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,433 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "FileManager.h" 1.11 + 1.12 +#include "mozIStorageConnection.h" 1.13 +#include "mozIStorageStatement.h" 1.14 +#include "nsIInputStream.h" 1.15 +#include "nsISimpleEnumerator.h" 1.16 + 1.17 +#include "mozilla/dom/quota/Utilities.h" 1.18 +#include "mozStorageCID.h" 1.19 +#include "mozStorageHelper.h" 1.20 + 1.21 +#include "Client.h" 1.22 +#include "FileInfo.h" 1.23 +#include "IndexedDatabaseManager.h" 1.24 +#include "OpenDatabaseHelper.h" 1.25 + 1.26 +#include "IndexedDatabaseInlines.h" 1.27 +#include <algorithm> 1.28 + 1.29 +USING_INDEXEDDB_NAMESPACE 1.30 +using mozilla::dom::quota::AssertIsOnIOThread; 1.31 + 1.32 +namespace { 1.33 + 1.34 +PLDHashOperator 1.35 +EnumerateToTArray(const uint64_t& aKey, 1.36 + FileInfo* aValue, 1.37 + void* aUserArg) 1.38 +{ 1.39 + NS_ASSERTION(aValue, "Null pointer!"); 1.40 + NS_ASSERTION(aUserArg, "Null pointer!"); 1.41 + 1.42 + nsTArray<FileInfo*>* array = 1.43 + static_cast<nsTArray<FileInfo*>*>(aUserArg); 1.44 + 1.45 + array->AppendElement(aValue); 1.46 + 1.47 + return PL_DHASH_NEXT; 1.48 +} 1.49 + 1.50 +already_AddRefed<nsIFile> 1.51 +GetDirectoryFor(const nsAString& aDirectoryPath) 1.52 +{ 1.53 + nsCOMPtr<nsIFile> directory = 1.54 + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); 1.55 + NS_ENSURE_TRUE(directory, nullptr); 1.56 + 1.57 + nsresult rv = directory->InitWithPath(aDirectoryPath); 1.58 + NS_ENSURE_SUCCESS(rv, nullptr); 1.59 + 1.60 + return directory.forget(); 1.61 +} 1.62 + 1.63 +} // anonymous namespace 1.64 + 1.65 +nsresult 1.66 +FileManager::Init(nsIFile* aDirectory, 1.67 + mozIStorageConnection* aConnection) 1.68 +{ 1.69 + AssertIsOnIOThread(); 1.70 + NS_ASSERTION(aDirectory, "Null directory!"); 1.71 + NS_ASSERTION(aConnection, "Null connection!"); 1.72 + 1.73 + bool exists; 1.74 + nsresult rv = aDirectory->Exists(&exists); 1.75 + NS_ENSURE_SUCCESS(rv, rv); 1.76 + 1.77 + if (exists) { 1.78 + bool isDirectory; 1.79 + rv = aDirectory->IsDirectory(&isDirectory); 1.80 + NS_ENSURE_SUCCESS(rv, rv); 1.81 + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); 1.82 + } 1.83 + else { 1.84 + rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); 1.85 + NS_ENSURE_SUCCESS(rv, rv); 1.86 + } 1.87 + 1.88 + rv = aDirectory->GetPath(mDirectoryPath); 1.89 + NS_ENSURE_SUCCESS(rv, rv); 1.90 + 1.91 + nsCOMPtr<nsIFile> journalDirectory; 1.92 + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); 1.93 + NS_ENSURE_SUCCESS(rv, rv); 1.94 + 1.95 + rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); 1.96 + NS_ENSURE_SUCCESS(rv, rv); 1.97 + 1.98 + rv = journalDirectory->Exists(&exists); 1.99 + NS_ENSURE_SUCCESS(rv, rv); 1.100 + 1.101 + if (exists) { 1.102 + bool isDirectory; 1.103 + rv = journalDirectory->IsDirectory(&isDirectory); 1.104 + NS_ENSURE_SUCCESS(rv, rv); 1.105 + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); 1.106 + } 1.107 + 1.108 + rv = journalDirectory->GetPath(mJournalDirectoryPath); 1.109 + NS_ENSURE_SUCCESS(rv, rv); 1.110 + 1.111 + nsCOMPtr<mozIStorageStatement> stmt; 1.112 + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( 1.113 + "SELECT id, refcount " 1.114 + "FROM file" 1.115 + ), getter_AddRefs(stmt)); 1.116 + NS_ENSURE_SUCCESS(rv, rv); 1.117 + 1.118 + bool hasResult; 1.119 + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 1.120 + int64_t id; 1.121 + rv = stmt->GetInt64(0, &id); 1.122 + NS_ENSURE_SUCCESS(rv, rv); 1.123 + 1.124 + int32_t refcount; 1.125 + rv = stmt->GetInt32(1, &refcount); 1.126 + NS_ENSURE_SUCCESS(rv, rv); 1.127 + 1.128 + NS_ASSERTION(refcount, "This shouldn't happen!"); 1.129 + 1.130 + nsRefPtr<FileInfo> fileInfo = FileInfo::Create(this, id); 1.131 + fileInfo->mDBRefCnt = refcount; 1.132 + 1.133 + mFileInfos.Put(id, fileInfo); 1.134 + 1.135 + mLastFileId = std::max(id, mLastFileId); 1.136 + } 1.137 + 1.138 + return NS_OK; 1.139 +} 1.140 + 1.141 +nsresult 1.142 +FileManager::Invalidate() 1.143 +{ 1.144 + if (IndexedDatabaseManager::IsClosed()) { 1.145 + NS_ERROR("Shouldn't be called after shutdown!"); 1.146 + return NS_ERROR_UNEXPECTED; 1.147 + } 1.148 + 1.149 + nsTArray<FileInfo*> fileInfos; 1.150 + { 1.151 + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); 1.152 + 1.153 + NS_ASSERTION(!mInvalidated, "Invalidate more than once?!"); 1.154 + mInvalidated = true; 1.155 + 1.156 + fileInfos.SetCapacity(mFileInfos.Count()); 1.157 + mFileInfos.EnumerateRead(EnumerateToTArray, &fileInfos); 1.158 + } 1.159 + 1.160 + for (uint32_t i = 0; i < fileInfos.Length(); i++) { 1.161 + FileInfo* fileInfo = fileInfos.ElementAt(i); 1.162 + fileInfo->ClearDBRefs(); 1.163 + } 1.164 + 1.165 + return NS_OK; 1.166 +} 1.167 + 1.168 +already_AddRefed<nsIFile> 1.169 +FileManager::GetDirectory() 1.170 +{ 1.171 + return GetDirectoryFor(mDirectoryPath); 1.172 +} 1.173 + 1.174 +already_AddRefed<nsIFile> 1.175 +FileManager::GetJournalDirectory() 1.176 +{ 1.177 + return GetDirectoryFor(mJournalDirectoryPath); 1.178 +} 1.179 + 1.180 +already_AddRefed<nsIFile> 1.181 +FileManager::EnsureJournalDirectory() 1.182 +{ 1.183 + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); 1.184 + 1.185 + nsCOMPtr<nsIFile> journalDirectory = GetDirectoryFor(mJournalDirectoryPath); 1.186 + NS_ENSURE_TRUE(journalDirectory, nullptr); 1.187 + 1.188 + bool exists; 1.189 + nsresult rv = journalDirectory->Exists(&exists); 1.190 + NS_ENSURE_SUCCESS(rv, nullptr); 1.191 + 1.192 + if (exists) { 1.193 + bool isDirectory; 1.194 + rv = journalDirectory->IsDirectory(&isDirectory); 1.195 + NS_ENSURE_SUCCESS(rv, nullptr); 1.196 + NS_ENSURE_TRUE(isDirectory, nullptr); 1.197 + } 1.198 + else { 1.199 + rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); 1.200 + NS_ENSURE_SUCCESS(rv, nullptr); 1.201 + } 1.202 + 1.203 + return journalDirectory.forget(); 1.204 +} 1.205 + 1.206 +already_AddRefed<FileInfo> 1.207 +FileManager::GetFileInfo(int64_t aId) 1.208 +{ 1.209 + if (IndexedDatabaseManager::IsClosed()) { 1.210 + NS_ERROR("Shouldn't be called after shutdown!"); 1.211 + return nullptr; 1.212 + } 1.213 + 1.214 + FileInfo* fileInfo = nullptr; 1.215 + { 1.216 + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); 1.217 + fileInfo = mFileInfos.Get(aId); 1.218 + } 1.219 + nsRefPtr<FileInfo> result = fileInfo; 1.220 + return result.forget(); 1.221 +} 1.222 + 1.223 +already_AddRefed<FileInfo> 1.224 +FileManager::GetNewFileInfo() 1.225 +{ 1.226 + if (IndexedDatabaseManager::IsClosed()) { 1.227 + NS_ERROR("Shouldn't be called after shutdown!"); 1.228 + return nullptr; 1.229 + } 1.230 + 1.231 + nsAutoPtr<FileInfo> fileInfo; 1.232 + 1.233 + { 1.234 + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); 1.235 + 1.236 + int64_t id = mLastFileId + 1; 1.237 + 1.238 + fileInfo = FileInfo::Create(this, id); 1.239 + 1.240 + mFileInfos.Put(id, fileInfo); 1.241 + 1.242 + mLastFileId = id; 1.243 + } 1.244 + 1.245 + nsRefPtr<FileInfo> result = fileInfo.forget(); 1.246 + return result.forget(); 1.247 +} 1.248 + 1.249 +// static 1.250 +already_AddRefed<nsIFile> 1.251 +FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId) 1.252 +{ 1.253 + NS_ASSERTION(aDirectory, "Null pointer!"); 1.254 + 1.255 + nsAutoString id; 1.256 + id.AppendInt(aId); 1.257 + 1.258 + nsCOMPtr<nsIFile> file; 1.259 + nsresult rv = aDirectory->Clone(getter_AddRefs(file)); 1.260 + NS_ENSURE_SUCCESS(rv, nullptr); 1.261 + 1.262 + rv = file->Append(id); 1.263 + NS_ENSURE_SUCCESS(rv, nullptr); 1.264 + 1.265 + return file.forget(); 1.266 +} 1.267 + 1.268 +// static 1.269 +nsresult 1.270 +FileManager::InitDirectory(nsIFile* aDirectory, 1.271 + nsIFile* aDatabaseFile, 1.272 + PersistenceType aPersistenceType, 1.273 + const nsACString& aGroup, 1.274 + const nsACString& aOrigin) 1.275 +{ 1.276 + AssertIsOnIOThread(); 1.277 + NS_ASSERTION(aDirectory, "Null directory!"); 1.278 + NS_ASSERTION(aDatabaseFile, "Null database file!"); 1.279 + 1.280 + bool exists; 1.281 + nsresult rv = aDirectory->Exists(&exists); 1.282 + NS_ENSURE_SUCCESS(rv, rv); 1.283 + 1.284 + if (!exists) { 1.285 + return NS_OK; 1.286 + } 1.287 + 1.288 + bool isDirectory; 1.289 + rv = aDirectory->IsDirectory(&isDirectory); 1.290 + NS_ENSURE_SUCCESS(rv, rv); 1.291 + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); 1.292 + 1.293 + nsCOMPtr<nsIFile> journalDirectory; 1.294 + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); 1.295 + NS_ENSURE_SUCCESS(rv, rv); 1.296 + 1.297 + rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); 1.298 + NS_ENSURE_SUCCESS(rv, rv); 1.299 + 1.300 + rv = journalDirectory->Exists(&exists); 1.301 + NS_ENSURE_SUCCESS(rv, rv); 1.302 + 1.303 + if (exists) { 1.304 + rv = journalDirectory->IsDirectory(&isDirectory); 1.305 + NS_ENSURE_SUCCESS(rv, rv); 1.306 + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); 1.307 + 1.308 + nsCOMPtr<nsISimpleEnumerator> entries; 1.309 + rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries)); 1.310 + NS_ENSURE_SUCCESS(rv, rv); 1.311 + 1.312 + bool hasElements; 1.313 + rv = entries->HasMoreElements(&hasElements); 1.314 + NS_ENSURE_SUCCESS(rv, rv); 1.315 + 1.316 + if (hasElements) { 1.317 + nsCOMPtr<mozIStorageConnection> connection; 1.318 + rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile, 1.319 + aDirectory, NullString(), aPersistenceType, aGroup, aOrigin, 1.320 + getter_AddRefs(connection)); 1.321 + NS_ENSURE_SUCCESS(rv, rv); 1.322 + 1.323 + mozStorageTransaction transaction(connection, false); 1.324 + 1.325 + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( 1.326 + "CREATE VIRTUAL TABLE fs USING filesystem;" 1.327 + )); 1.328 + NS_ENSURE_SUCCESS(rv, rv); 1.329 + 1.330 + nsCOMPtr<mozIStorageStatement> stmt; 1.331 + rv = connection->CreateStatement(NS_LITERAL_CSTRING( 1.332 + "SELECT name, (name IN (SELECT id FROM file)) FROM fs " 1.333 + "WHERE path = :path" 1.334 + ), getter_AddRefs(stmt)); 1.335 + NS_ENSURE_SUCCESS(rv, rv); 1.336 + 1.337 + nsString path; 1.338 + rv = journalDirectory->GetPath(path); 1.339 + NS_ENSURE_SUCCESS(rv, rv); 1.340 + 1.341 + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path); 1.342 + NS_ENSURE_SUCCESS(rv, rv); 1.343 + 1.344 + bool hasResult; 1.345 + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 1.346 + nsString name; 1.347 + rv = stmt->GetString(0, name); 1.348 + NS_ENSURE_SUCCESS(rv, rv); 1.349 + 1.350 + int32_t flag = stmt->AsInt32(1); 1.351 + 1.352 + if (!flag) { 1.353 + nsCOMPtr<nsIFile> file; 1.354 + rv = aDirectory->Clone(getter_AddRefs(file)); 1.355 + NS_ENSURE_SUCCESS(rv, rv); 1.356 + 1.357 + rv = file->Append(name); 1.358 + NS_ENSURE_SUCCESS(rv, rv); 1.359 + 1.360 + if (NS_FAILED(file->Remove(false))) { 1.361 + NS_WARNING("Failed to remove orphaned file!"); 1.362 + } 1.363 + } 1.364 + 1.365 + nsCOMPtr<nsIFile> journalFile; 1.366 + rv = journalDirectory->Clone(getter_AddRefs(journalFile)); 1.367 + NS_ENSURE_SUCCESS(rv, rv); 1.368 + 1.369 + rv = journalFile->Append(name); 1.370 + NS_ENSURE_SUCCESS(rv, rv); 1.371 + 1.372 + if (NS_FAILED(journalFile->Remove(false))) { 1.373 + NS_WARNING("Failed to remove journal file!"); 1.374 + } 1.375 + } 1.376 + 1.377 + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( 1.378 + "DROP TABLE fs;" 1.379 + )); 1.380 + NS_ENSURE_SUCCESS(rv, rv); 1.381 + 1.382 + transaction.Commit(); 1.383 + } 1.384 + } 1.385 + 1.386 + return NS_OK; 1.387 +} 1.388 + 1.389 +// static 1.390 +nsresult 1.391 +FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) 1.392 +{ 1.393 + AssertIsOnIOThread(); 1.394 + 1.395 + bool exists; 1.396 + nsresult rv = aDirectory->Exists(&exists); 1.397 + NS_ENSURE_SUCCESS(rv, rv); 1.398 + 1.399 + if (!exists) { 1.400 + *aUsage = 0; 1.401 + return NS_OK; 1.402 + } 1.403 + 1.404 + nsCOMPtr<nsISimpleEnumerator> entries; 1.405 + rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); 1.406 + NS_ENSURE_SUCCESS(rv, rv); 1.407 + 1.408 + uint64_t usage = 0; 1.409 + 1.410 + bool hasMore; 1.411 + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { 1.412 + nsCOMPtr<nsISupports> entry; 1.413 + rv = entries->GetNext(getter_AddRefs(entry)); 1.414 + NS_ENSURE_SUCCESS(rv, rv); 1.415 + 1.416 + nsCOMPtr<nsIFile> file = do_QueryInterface(entry); 1.417 + NS_ENSURE_TRUE(file, NS_NOINTERFACE); 1.418 + 1.419 + nsString leafName; 1.420 + rv = file->GetLeafName(leafName); 1.421 + NS_ENSURE_SUCCESS(rv, rv); 1.422 + 1.423 + if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { 1.424 + continue; 1.425 + } 1.426 + 1.427 + int64_t fileSize; 1.428 + rv = file->GetFileSize(&fileSize); 1.429 + NS_ENSURE_SUCCESS(rv, rv); 1.430 + 1.431 + quota::IncrementUsage(&usage, uint64_t(fileSize)); 1.432 + } 1.433 + 1.434 + *aUsage = usage; 1.435 + return NS_OK; 1.436 +}