dom/indexedDB/FileManager.cpp

changeset 0
6474c204b198
     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 +}

mercurial