winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

SQLiteStorageBase.cpp (7025B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Public/winget/SQLiteStorageBase.h"
      5 #include "Public/winget/SQLiteMetadataTable.h"
      6 #include "AppInstallerDateTime.h"
      7 
      8 namespace AppInstaller::SQLite
      9 {
     10     namespace
     11     {
     12         static char const* const GetOpenDispositionString(SQLiteStorageBase::OpenDisposition disposition)
     13         {
     14             switch (disposition)
     15             {
     16             case SQLiteStorageBase::OpenDisposition::Read:
     17                 return "Read";
     18             case SQLiteStorageBase::OpenDisposition::ReadWrite:
     19                 return "ReadWrite";
     20             case SQLiteStorageBase::OpenDisposition::Immutable:
     21                 return "ImmutableRead";
     22             default:
     23                 return "Unknown";
     24             }
     25         }
     26 
     27         std::filesystem::path AddSuffix(const std::filesystem::path& source, std::wstring_view suffix)
     28         {
     29             std::filesystem::path result{ source };
     30 
     31             if (!suffix.empty())
     32             {
     33                 std::wstring filename = result.filename().wstring();
     34                 filename += suffix;
     35                 result.replace_filename(std::move(filename));
     36             }
     37 
     38             return result;
     39         }
     40     }
     41 
     42     // One method for converting open disposition to proper open disposition
     43     // another method for obtaining the right flags
     44     void SQLiteStorageBase::SetLastWriteTime()
     45     {
     46         MetadataTable::SetNamedValue(m_dbconn, s_MetadataValueName_LastWriteTime, Utility::GetCurrentUnixEpoch());
     47     }
     48 
     49     // Recording last write time based on MSDN documentation stating that time returns a POSIX epoch time and thus
     50     // should be consistent across systems.
     51     std::chrono::system_clock::time_point SQLiteStorageBase::GetLastWriteTime() const
     52     {
     53         int64_t lastWriteTime = MetadataTable::GetNamedValue<int64_t>(m_dbconn, s_MetadataValueName_LastWriteTime);
     54         return Utility::ConvertUnixEpochToSystemClock(lastWriteTime);
     55     }
     56 
     57     std::string SQLiteStorageBase::GetDatabaseIdentifier() const
     58     {
     59         return MetadataTable::TryGetNamedValue<std::string>(m_dbconn, s_MetadataValueName_DatabaseIdentifier).value_or(std::string{});
     60     }
     61 
     62     void SQLiteStorageBase::RenameSQLiteDatabase(const std::filesystem::path& source, const std::filesystem::path& destination, bool overwrite)
     63     {
     64         auto fileSuffixes = { L"", L"-journal", L"-wal" };
     65 
     66         THROW_WIN32_IF(ERROR_FILE_NOT_FOUND, !std::filesystem::exists(source));
     67         THROW_WIN32_IF(ERROR_DIRECTORY, std::filesystem::is_directory(source));
     68 
     69         if (overwrite)
     70         {
     71             for (const auto& suffix : fileSuffixes)
     72             {
     73                 std::filesystem::path target = AddSuffix(destination, suffix);
     74 
     75                 if (std::filesystem::exists(target))
     76                 {
     77                     std::filesystem::remove_all(target);
     78                 }
     79             }
     80         }
     81 
     82         for (const auto& suffix : fileSuffixes)
     83         {
     84             std::filesystem::path target = AddSuffix(source, suffix);
     85 
     86             if (std::filesystem::exists(target))
     87             {
     88                 std::filesystem::rename(target, AddSuffix(destination, suffix));
     89             }
     90         }
     91     }
     92 
     93     SQLiteStorageBase::SQLiteStorageBase(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& file) :
     94         m_indexFile(std::move(file))
     95     {
     96         AICLI_LOG(Repo, Info, << "Opening database for " << GetOpenDispositionString(disposition) << " at '" << filePath << "'");
     97         switch (disposition)
     98         {
     99         case OpenDisposition::Read:
    100             m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::None);
    101             break;
    102         case OpenDisposition::ReadWrite:
    103             m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadWrite, SQLite::Connection::OpenFlags::None);
    104             break;
    105         case OpenDisposition::Immutable:
    106         {
    107             // Following the algorithm set forth at https://sqlite.org/uri.html [3.1] to convert to a URI path
    108             // The execution order builds out the string so that it shouldn't require any moves (other than growing)
    109             std::string target;
    110             // Add an 'arbitrary' growth size to prevent the majority of needing to grow (adding 'file:/' and '?immutable=1')
    111             target.reserve(filePath.size() + 20);
    112 
    113             target += "file:";
    114 
    115             bool wasLastCharSlash = false;
    116 
    117             if (filePath.size() >= 2 && filePath[1] == ':' &&
    118                 ((filePath[0] >= 'a' && filePath[0] <= 'z') ||
    119                     (filePath[0] >= 'A' && filePath[0] <= 'Z')))
    120             {
    121                 target += '/';
    122                 wasLastCharSlash = true;
    123             }
    124 
    125             for (char c : filePath)
    126             {
    127                 bool wasThisCharSlash = false;
    128                 switch (c)
    129                 {
    130                 case '?': target += "%3f"; break;
    131                 case '#': target += "%23"; break;
    132                 case '\\':
    133                 case '/':
    134                 {
    135                     wasThisCharSlash = true;
    136                     if (!wasLastCharSlash)
    137                     {
    138                         target += '/';
    139                     }
    140                     break;
    141                 }
    142                 default: target += c; break;
    143                 }
    144 
    145                 wasLastCharSlash = wasThisCharSlash;
    146             }
    147 
    148             target += "?immutable=1";
    149             m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::Uri);
    150             break;
    151         }
    152         default:
    153             THROW_HR(E_UNEXPECTED);
    154         }
    155 
    156         m_version = Version::GetSchemaVersion(m_dbconn);
    157     }
    158 
    159     SQLiteStorageBase::SQLiteStorageBase(const std::string& target, const Version& version) :
    160         m_dbconn(SQLite::Connection::Create(target, SQLite::Connection::OpenDisposition::Create))
    161     {
    162         m_version = version;
    163         MetadataTable::Create(m_dbconn);
    164 
    165         // Write a new identifier for this database
    166         GUID databaseIdentifier;
    167         THROW_IF_FAILED(CoCreateGuid(&databaseIdentifier));
    168         std::ostringstream stream;
    169         stream << databaseIdentifier;
    170         MetadataTable::SetNamedValue(m_dbconn, s_MetadataValueName_DatabaseIdentifier, stream.str());
    171     }
    172     
    173     SQLiteStorageBase::SQLiteStorageBase(const std::string& target, SQLiteStorageBase& source) :
    174         m_dbconn(SQLite::Connection::Create(target, SQLite::Connection::OpenDisposition::Create)),
    175         m_version(source.m_version)
    176     {
    177         std::string mainDatabase = "main";
    178         Backup backup = Backup::Create(m_dbconn, mainDatabase, source.m_dbconn, mainDatabase);
    179         backup.Step();
    180     }
    181 }