dom/indexedDB/FileManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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)
    35 {
    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;
    45 }
    47 already_AddRefed<nsIFile>
    48 GetDirectoryFor(const nsAString& aDirectoryPath)
    49 {
    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();
    58 }
    60 } // anonymous namespace
    62 nsresult
    63 FileManager::Init(nsIFile* aDirectory,
    64                   mozIStorageConnection* aConnection)
    65 {
    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);
    79   }
    80   else {
    81     rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
    82     NS_ENSURE_SUCCESS(rv, rv);
    83   }
    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);
   103   }
   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);
   133   }
   135   return NS_OK;
   136 }
   138 nsresult
   139 FileManager::Invalidate()
   140 {
   141   if (IndexedDatabaseManager::IsClosed()) {
   142     NS_ERROR("Shouldn't be called after shutdown!");
   143     return NS_ERROR_UNEXPECTED;
   144   }
   146   nsTArray<FileInfo*> fileInfos;
   147   {
   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);
   155   }
   157   for (uint32_t i = 0; i < fileInfos.Length(); i++) {
   158     FileInfo* fileInfo = fileInfos.ElementAt(i);
   159     fileInfo->ClearDBRefs();
   160   }
   162   return NS_OK;
   163 }
   165 already_AddRefed<nsIFile>
   166 FileManager::GetDirectory()
   167 {
   168   return GetDirectoryFor(mDirectoryPath);
   169 }
   171 already_AddRefed<nsIFile>
   172 FileManager::GetJournalDirectory()
   173 {
   174   return GetDirectoryFor(mJournalDirectoryPath);
   175 }
   177 already_AddRefed<nsIFile>
   178 FileManager::EnsureJournalDirectory()
   179 {
   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);
   194   }
   195   else {
   196     rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
   197     NS_ENSURE_SUCCESS(rv, nullptr);
   198   }
   200   return journalDirectory.forget();
   201 }
   203 already_AddRefed<FileInfo>
   204 FileManager::GetFileInfo(int64_t aId)
   205 {
   206   if (IndexedDatabaseManager::IsClosed()) {
   207     NS_ERROR("Shouldn't be called after shutdown!");
   208     return nullptr;
   209   }
   211   FileInfo* fileInfo = nullptr;
   212   {
   213     MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
   214     fileInfo = mFileInfos.Get(aId);
   215   }
   216   nsRefPtr<FileInfo> result = fileInfo;
   217   return result.forget();
   218 }
   220 already_AddRefed<FileInfo>
   221 FileManager::GetNewFileInfo()
   222 {
   223   if (IndexedDatabaseManager::IsClosed()) {
   224     NS_ERROR("Shouldn't be called after shutdown!");
   225     return nullptr;
   226   }
   228   nsAutoPtr<FileInfo> fileInfo;
   230   {
   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;
   240   }
   242   nsRefPtr<FileInfo> result = fileInfo.forget();
   243   return result.forget();
   244 }
   246 // static
   247 already_AddRefed<nsIFile>
   248 FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId)
   249 {
   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();
   263 }
   265 // static
   266 nsresult
   267 FileManager::InitDirectory(nsIFile* aDirectory,
   268                            nsIFile* aDatabaseFile,
   269                            PersistenceType aPersistenceType,
   270                            const nsACString& aGroup,
   271                            const nsACString& aOrigin)
   272 {
   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;
   283   }
   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;"
   324       ));
   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!");
   359           }
   360         }
   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!");
   371         }
   372       }
   374       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   375         "DROP TABLE fs;"
   376       ));
   377       NS_ENSURE_SUCCESS(rv, rv);
   379       transaction.Commit();
   380     }
   381   }
   383   return NS_OK;
   384 }
   386 // static
   387 nsresult
   388 FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage)
   389 {
   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;
   399   }
   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;
   422     }
   424     int64_t fileSize;
   425     rv = file->GetFileSize(&fileSize);
   426     NS_ENSURE_SUCCESS(rv, rv);
   428     quota::IncrementUsage(&usage, uint64_t(fileSize));
   429   }
   431   *aUsage = usage;
   432   return NS_OK;
   433 }

mercurial