winget-cli

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

PinningIndex.cpp (7341B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "PinningIndex.h"
      5 #include <winget/SQLiteStorageBase.h>
      6 #include "Schema/Pinning_1_0/PinningIndexInterface.h"
      7 
      8 namespace AppInstaller::Repository::Microsoft
      9 {
     10 #ifndef AICLI_DISABLE_TEST_HOOKS
     11     std::optional<std::filesystem::path> s_PinningIndexOverride{};
     12     void TestHook_SetPinningIndex_Override(std::optional<std::filesystem::path>&& indexPath)
     13     {
     14         s_PinningIndexOverride = std::move(indexPath);
     15     }
     16 #endif
     17 
     18     namespace
     19     {
     20         std::filesystem::path GetPinningDatabasePath()
     21         {
     22             const auto DefaultPath = Runtime::GetPathTo(Runtime::PathName::LocalState) / "pinning.db";
     23 
     24             return
     25 #ifndef AICLI_DISABLE_TEST_HOOKS
     26                 s_PinningIndexOverride.has_value() ? s_PinningIndexOverride.value() :
     27 #endif
     28                 DefaultPath;
     29         }
     30 
     31         std::shared_ptr<PinningIndex> OpenDatabaseIfExists(const std::filesystem::path& path, SQLite::SQLiteStorageBase::OpenDisposition openDisposition)
     32         {
     33             AICLI_LOG(Repo, Info, << "Attempting to open pinning database: " << path);
     34 
     35             try
     36             {
     37                 if (std::filesystem::exists(path))
     38                 {
     39                     if (std::filesystem::is_regular_file(path))
     40                     {
     41                         try
     42                         {
     43                             AICLI_LOG(Repo, Info, << "... opening existing pinning database");
     44                             return std::make_shared<PinningIndex>(PinningIndex::Open(path.u8string(), openDisposition));
     45                         }
     46                         CATCH_LOG();
     47 
     48                         AICLI_LOG(Repo, Info, << "... deleting bad pinning database file");
     49                         std::filesystem::remove_all(path);
     50                     }
     51                     else
     52                     {
     53                         AICLI_LOG(Repo, Info, << "... deleting pinning database path that is a directory");
     54                         std::filesystem::remove_all(path);
     55                     }
     56                 }
     57             }
     58             CATCH_LOG();
     59 
     60             return {};
     61         }
     62     }
     63 
     64     PinningIndex PinningIndex::CreateNew(const std::string& filePath, SQLite::Version version)
     65     {
     66         AICLI_LOG(Repo, Info, << "Creating new Pinning Index with version [" << version << "] at '" << filePath << "'");
     67         PinningIndex result{ filePath, version };
     68 
     69         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "pinningindex_createnew");
     70 
     71         // Use calculated version, as incoming version could be 'latest'
     72         result.m_version.SetSchemaVersion(result.m_dbconn);
     73 
     74         result.m_interface->CreateTables(result.m_dbconn);
     75 
     76         result.SetLastWriteTime();
     77 
     78         savepoint.Commit();
     79 
     80         return result;
     81     }
     82 
     83     std::shared_ptr<PinningIndex> PinningIndex::OpenIfExists(OpenDisposition openDisposition)
     84     {
     85         return OpenDatabaseIfExists(GetPinningDatabasePath(), openDisposition);
     86     }
     87 
     88     std::shared_ptr<PinningIndex> PinningIndex::OpenOrCreateDefault(OpenDisposition openDisposition)
     89     {
     90         const auto databasePath = GetPinningDatabasePath();
     91 
     92         std::shared_ptr<PinningIndex> result = OpenDatabaseIfExists(databasePath, openDisposition);
     93 
     94         if (!result)
     95         {
     96             AICLI_LOG(Repo, Info, << "... creating pinning database");
     97 
     98             try
     99             {
    100                 result = std::make_shared<PinningIndex>(PinningIndex::CreateNew(databasePath.u8string()));
    101             }
    102             CATCH_LOG();
    103         }
    104 
    105         return result;
    106     }
    107 
    108     PinningIndex::IdType PinningIndex::AddPin(const Pinning::Pin& pin)
    109     {
    110         std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock };
    111         AICLI_LOG(Repo, Verbose, << "Adding Pin " << pin.ToString());
    112 
    113         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "pinningindex_addpin");
    114 
    115         IdType result = m_interface->AddPin(m_dbconn, pin);
    116 
    117         SetLastWriteTime();
    118 
    119         savepoint.Commit();
    120 
    121         return result;
    122     }
    123 
    124     bool PinningIndex::UpdatePin(const Pinning::Pin& pin)
    125     {
    126         std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock };
    127         AICLI_LOG(Repo, Verbose, << "Updating Pin " << pin.ToString());
    128 
    129         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "pinningindex_updatepin");
    130 
    131         bool result = m_interface->UpdatePin(m_dbconn, pin).first;
    132 
    133         if (result)
    134         {
    135             SetLastWriteTime();
    136             savepoint.Commit();
    137         }
    138 
    139         return result;
    140     }
    141 
    142     void PinningIndex::AddOrUpdatePin(const Pinning::Pin& pin)
    143     {
    144         auto existingPin = GetPin(pin.GetKey());
    145         if (existingPin.has_value())
    146         {
    147             UpdatePin(pin);
    148         }
    149         else
    150         {
    151             AddPin(pin);
    152         }
    153     }
    154 
    155     void PinningIndex::RemovePin(const Pinning::PinKey& pinKey)
    156     {
    157         std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock };
    158         AICLI_LOG(Repo, Verbose, << "Removing Pin " << pinKey.ToString());
    159 
    160         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "pinningIndex_removePin");
    161 
    162         m_interface->RemovePin(m_dbconn, pinKey);
    163 
    164         SetLastWriteTime();
    165 
    166         savepoint.Commit();
    167     }
    168 
    169     std::optional<Pinning::Pin> PinningIndex::GetPin(const Pinning::PinKey& pinKey)
    170     {
    171         std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock };
    172         return m_interface->GetPin(m_dbconn, pinKey);
    173     }
    174 
    175     std::vector<Pinning::Pin> PinningIndex::GetAllPins()
    176     {
    177         std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock };
    178         return m_interface->GetAllPins(m_dbconn);
    179     }
    180 
    181     bool PinningIndex::ResetAllPins(std::string_view sourceId)
    182     {
    183         std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock };
    184         return m_interface->ResetAllPins(m_dbconn, sourceId);
    185     }
    186 
    187     std::unique_ptr<Schema::IPinningIndex> PinningIndex::CreateIPinningIndex() const
    188     {
    189         if (m_version == SQLite::Version{ 1, 0 } ||
    190             m_version.MajorVersion == 1 ||
    191             m_version.IsLatest())
    192         {
    193             return std::make_unique<Schema::Pinning_V1_0::PinningIndexInterface>();
    194         }
    195 
    196         THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
    197     }
    198 
    199     PinningIndex::PinningIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) :
    200         SQLiteStorageBase(target, disposition, std::move(indexFile))
    201     {
    202         AICLI_LOG(Repo, Info, << "Opened Pinning Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]");
    203         m_interface = CreateIPinningIndex();
    204         THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion());
    205     }
    206 
    207     PinningIndex::PinningIndex(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version)
    208     {
    209         m_interface = CreateIPinningIndex();
    210         m_version = m_interface->GetVersion();
    211     }
    212 }