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 }