tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SchemaVersion001.cpp (5963B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=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/. */
      6 
      7 #include "SchemaVersion001.h"
      8 
      9 #include "FileSystemHashSource.h"
     10 #include "ResultStatement.h"
     11 #include "StartedTransaction.h"
     12 #include "fs/FileSystemConstants.h"
     13 #include "mozStorageHelper.h"
     14 #include "mozilla/dom/quota/QuotaCommon.h"
     15 #include "mozilla/dom/quota/ResultExtensions.h"
     16 
     17 namespace mozilla::dom::fs {
     18 
     19 namespace {
     20 
     21 nsresult CreateEntries(ResultConnection& aConn) {
     22  return aConn->ExecuteSimpleSQL(
     23      "CREATE TABLE IF NOT EXISTS Entries ( "
     24      "handle BLOB PRIMARY KEY, "  // Generated from parent + name, unique
     25      "parent BLOB, "              // Not null due to constraint
     26      "CONSTRAINT parent_is_a_directory "
     27      "FOREIGN KEY (parent) "
     28      "REFERENCES Directories (handle) "
     29      "ON DELETE CASCADE ) "
     30      ";"_ns);
     31 }
     32 
     33 nsresult CreateDirectories(ResultConnection& aConn) {
     34  return aConn->ExecuteSimpleSQL(
     35      "CREATE TABLE IF NOT EXISTS Directories ( "
     36      "handle BLOB PRIMARY KEY, "
     37      "name BLOB NOT NULL, "
     38      "CONSTRAINT directories_are_entries "
     39      "FOREIGN KEY (handle) "
     40      "REFERENCES Entries (handle) "
     41      "ON DELETE CASCADE ) "
     42      ";"_ns);
     43 }
     44 
     45 nsresult CreateFiles(ResultConnection& aConn) {
     46  return aConn->ExecuteSimpleSQL(
     47      "CREATE TABLE IF NOT EXISTS Files ( "
     48      "handle BLOB PRIMARY KEY, "
     49      "type TEXT, "
     50      "name BLOB NOT NULL, "
     51      "CONSTRAINT files_are_entries "
     52      "FOREIGN KEY (handle) "
     53      "REFERENCES Entries (handle) "
     54      "ON DELETE CASCADE ) "
     55      ";"_ns);
     56 }
     57 
     58 nsresult CreateUsages(ResultConnection& aConn) {
     59  return aConn->ExecuteSimpleSQL(
     60      "CREATE TABLE IF NOT EXISTS Usages ( "
     61      "handle BLOB PRIMARY KEY, "
     62      "usage INTEGER NOT NULL DEFAULT 0, "
     63      "tracked BOOLEAN NOT NULL DEFAULT 0 CHECK (tracked IN (0, 1)), "
     64      "CONSTRAINT handles_are_files "
     65      "FOREIGN KEY (handle) "
     66      "REFERENCES Files (handle) "
     67      "ON DELETE CASCADE ) "
     68      ";"_ns);
     69 }
     70 
     71 class KeepForeignKeysOffUntilScopeExit final {
     72 public:
     73  explicit KeepForeignKeysOffUntilScopeExit(const ResultConnection& aConn)
     74      : mConn(aConn) {}
     75 
     76  static Result<KeepForeignKeysOffUntilScopeExit, QMResult> Create(
     77      const ResultConnection& aConn) {
     78    QM_TRY(
     79        QM_TO_RESULT(aConn->ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns)));
     80    KeepForeignKeysOffUntilScopeExit result(aConn);
     81    return result;
     82  }
     83 
     84  ~KeepForeignKeysOffUntilScopeExit() {
     85    auto maskResult = [this]() -> Result<Ok, nsresult> {
     86      QM_TRY(MOZ_TO_RESULT(
     87          mConn->ExecuteSimpleSQL("PRAGMA foreign_keys = ON;"_ns)));
     88 
     89      return Ok{};
     90    };
     91    QM_WARNONLY_TRY(maskResult());
     92  }
     93 
     94 private:
     95  ResultConnection mConn;
     96 };
     97 
     98 nsresult CreateRootEntry(ResultConnection& aConn, const Origin& aOrigin) {
     99  KeepForeignKeysOffUntilScopeExit foreignKeysGuard(aConn);
    100 
    101  const nsLiteralCString createRootQuery =
    102      "INSERT OR IGNORE INTO Entries "
    103      "( handle, parent ) "
    104      "VALUES ( :handle, NULL );"_ns;
    105 
    106  const nsLiteralCString flagRootAsDirectoryQuery =
    107      "INSERT OR IGNORE INTO Directories "
    108      "( handle, name ) "
    109      "VALUES ( :handle, :name );"_ns;
    110 
    111  QM_TRY_UNWRAP(EntryId rootId,
    112                data::FileSystemHashSource::GenerateHash(aOrigin, kRootString));
    113 
    114  QM_TRY_UNWRAP(auto transaction, StartedTransaction::Create(aConn));
    115 
    116  {
    117    QM_TRY_UNWRAP(ResultStatement stmt,
    118                  ResultStatement::Create(aConn, createRootQuery));
    119    QM_TRY(MOZ_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, rootId)));
    120    QM_TRY(MOZ_TO_RESULT(stmt.Execute()));
    121  }
    122 
    123  {
    124    QM_TRY_UNWRAP(ResultStatement stmt,
    125                  ResultStatement::Create(aConn, flagRootAsDirectoryQuery));
    126    QM_TRY(MOZ_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, rootId)));
    127    QM_TRY(MOZ_TO_RESULT(stmt.BindNameByName("name"_ns, kRootString)));
    128    QM_TRY(MOZ_TO_RESULT(stmt.Execute()));
    129  }
    130 
    131  return transaction.Commit();
    132 }
    133 
    134 }  // namespace
    135 
    136 nsresult SetEncoding(ResultConnection& aConn) {
    137  return aConn->ExecuteSimpleSQL(R"(PRAGMA encoding = "UTF-16";)"_ns);
    138 }
    139 
    140 Result<bool, QMResult> CheckIfEmpty(ResultConnection& aConn) {
    141  const nsLiteralCString areThereTablesQuery =
    142      "SELECT EXISTS ("
    143      "SELECT 1 FROM sqlite_master "
    144      ");"_ns;
    145 
    146  QM_TRY_UNWRAP(ResultStatement stmt,
    147                ResultStatement::Create(aConn, areThereTablesQuery));
    148 
    149  QM_TRY_UNWRAP(bool tablesExist, stmt.YesOrNoQuery());
    150 
    151  return !tablesExist;
    152 };
    153 
    154 nsresult SchemaVersion001::CreateTables(ResultConnection& aConn,
    155                                        const Origin& aOrigin) {
    156  QM_TRY(MOZ_TO_RESULT(CreateEntries(aConn)));
    157  QM_TRY(MOZ_TO_RESULT(CreateDirectories(aConn)));
    158  QM_TRY(MOZ_TO_RESULT(CreateFiles(aConn)));
    159  QM_TRY(MOZ_TO_RESULT(CreateUsages(aConn)));
    160  QM_TRY(MOZ_TO_RESULT(CreateRootEntry(aConn, aOrigin)));
    161 
    162  return NS_OK;
    163 }
    164 
    165 Result<DatabaseVersion, QMResult> SchemaVersion001::InitializeConnection(
    166    ResultConnection& aConn, const Origin& aOrigin) {
    167  QM_TRY_UNWRAP(bool isEmpty, CheckIfEmpty(aConn));
    168 
    169  DatabaseVersion currentVersion = 0;
    170 
    171  if (isEmpty) {
    172    QM_TRY(QM_TO_RESULT(SetEncoding(aConn)));
    173  } else {
    174    QM_TRY(QM_TO_RESULT(aConn->GetSchemaVersion(&currentVersion)));
    175  }
    176 
    177  if (currentVersion < sVersion) {
    178    QM_TRY_UNWRAP(auto transaction, StartedTransaction::Create(aConn));
    179 
    180    QM_TRY(QM_TO_RESULT(SchemaVersion001::CreateTables(aConn, aOrigin)));
    181    QM_TRY(QM_TO_RESULT(aConn->SetSchemaVersion(sVersion)));
    182 
    183    QM_TRY(QM_TO_RESULT(transaction.Commit()));
    184  }
    185 
    186  QM_TRY(QM_TO_RESULT(aConn->ExecuteSimpleSQL("PRAGMA foreign_keys = ON;"_ns)));
    187 
    188  QM_TRY(QM_TO_RESULT(aConn->GetSchemaVersion(&currentVersion)));
    189 
    190  return currentVersion;
    191 }
    192 
    193 }  // namespace mozilla::dom::fs