Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@
<ClInclude Include="Microsoft\PinningIndex.h" />
<ClInclude Include="Microsoft\PredefinedInstalledSourceFactory.h" />
<ClInclude Include="Microsoft\PredefinedWriteableSourceFactory.h" />
<ClInclude Include="Microsoft\PreIndexed\IndexForm.h" />
<ClInclude Include="Microsoft\PreIndexed\PackageStore.h" />
<ClInclude Include="Microsoft\PreIndexed\RemotePackage.h" />
<ClInclude Include="Microsoft\PreIndexedPackageSourceFactory.h" />
<ClInclude Include="Microsoft\Schema\1_0\ChannelTable.h" />
<ClInclude Include="Microsoft\Schema\1_0\CommandsTable.h" />
Expand Down Expand Up @@ -427,6 +430,11 @@
<ClCompile Include="Microsoft\PortableIndex.cpp" />
<ClCompile Include="Microsoft\PredefinedInstalledSourceFactory.cpp" />
<ClCompile Include="Microsoft\PredefinedWriteableSourceFactory.cpp" />
<ClCompile Include="Microsoft\PreIndexed\DeployedPackageStore.cpp" />
<ClCompile Include="Microsoft\PreIndexed\FullIndexForm.cpp" />
<ClCompile Include="Microsoft\PreIndexed\LocalFilePackageStore.cpp" />
<ClCompile Include="Microsoft\PreIndexed\PackageStore.cpp" />
<ClCompile Include="Microsoft\PreIndexed\RemotePackage.cpp" />
<ClCompile Include="Microsoft\PreIndexedPackageSourceFactory.cpp" />
<ClCompile Include="Microsoft\CheckpointDatabase.cpp" />
<ClCompile Include="Microsoft\Schema\1_0\Interface_1_0.cpp" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<Filter Include="Microsoft">
<UniqueIdentifier>{2d97761a-4967-4ece-b5fd-d96f346731ed}</UniqueIdentifier>
</Filter>
<Filter Include="Microsoft\PreIndexed">
<UniqueIdentifier>{5c051a95-47d9-4501-a17b-4f2320e69c2d}</UniqueIdentifier>
</Filter>
<Filter Include="Microsoft\Schema">
<UniqueIdentifier>{7996cf09-4e36-4009-b48d-53ffc3bfa956}</UniqueIdentifier>
</Filter>
Expand Down Expand Up @@ -180,6 +183,15 @@
<ClInclude Include="SourceFactory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Microsoft\PreIndexed\IndexForm.h">
<Filter>Microsoft\PreIndexed</Filter>
</ClInclude>
<ClInclude Include="Microsoft\PreIndexed\PackageStore.h">
<Filter>Microsoft\PreIndexed</Filter>
</ClInclude>
<ClInclude Include="Microsoft\PreIndexed\RemotePackage.h">
<Filter>Microsoft\PreIndexed</Filter>
</ClInclude>
<ClInclude Include="Microsoft\PreIndexedPackageSourceFactory.h">
<Filter>Microsoft</Filter>
</ClInclude>
Expand Down Expand Up @@ -560,6 +572,21 @@
<ClCompile Include="RepositorySource.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Microsoft\PreIndexed\DeployedPackageStore.cpp">
<Filter>Microsoft\PreIndexed</Filter>
</ClCompile>
<ClCompile Include="Microsoft\PreIndexed\FullIndexForm.cpp">
<Filter>Microsoft\PreIndexed</Filter>
</ClCompile>
<ClCompile Include="Microsoft\PreIndexed\LocalFilePackageStore.cpp">
<Filter>Microsoft\PreIndexed</Filter>
</ClCompile>
<ClCompile Include="Microsoft\PreIndexed\PackageStore.cpp">
<Filter>Microsoft\PreIndexed</Filter>
</ClCompile>
<ClCompile Include="Microsoft\PreIndexed\RemotePackage.cpp">
<Filter>Microsoft\PreIndexed</Filter>
</ClCompile>
<ClCompile Include="Microsoft\PreIndexedPackageSourceFactory.cpp">
<Filter>Microsoft</Filter>
</ClCompile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "Microsoft/PreIndexed/PackageStore.h"

#include <AppInstallerDeployment.h>
#include <AppInstallerMsixInfo.h>

namespace AppInstaller::Repository::Microsoft::PreIndexed
{
namespace anon
{
// Holds a source's packages by registering them with the platform.
struct DeployedPackageStore : public PackageStoreBase
{
using PackageStoreBase::PackageStoreBase;

void Persist(AcquiredPackage&& package, IProgressCallback& progress) override
{
Deployment::Options options{ IsTrusted() };

// The file lock is deliberately held across deployment, so that the package the
// platform reads is the one that was validated.
winrt::Windows::Foundation::Uri uri = winrt::Windows::Foundation::Uri(package.Path.c_str());
Deployment::AddPackage(
uri,
options,
progress);

// Nothing further needs the file; it is removed when the package goes out of scope.
}

std::optional<Msix::PackageVersion> GetVersion(const PackageKey& package) const override
{
auto extension = GetExtension(package);

if (!extension)
{
return std::nullopt;
}

auto version = extension->GetPackageVersion();
return Msix::PackageVersion{ version.Major, version.Minor, version.Build, version.Revision };
}

std::optional<ExtractedIndex> GetIndex(const PackageKey& package, IProgressCallback& progress) override
{
auto extension = GetExtension(package);

if (!extension)
{
AICLI_LOG(Repo, Info, << "Deployed " << GetSlotName(package.Slot) << " package not found: " << package.Identity);
return std::nullopt;
}

THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NEEDS_REMEDIATION), !extension->VerifyContentIntegrity(progress));

ExtractedIndex result;

// To work around an issue with accessing the public folder, we are temporarily
// constructing the location ourself. This was already the case for the local file
// mechanism, and we can fix both in the future. The only problem with this is that
// the directory in the extension *must* be Public, rather than one set by the
// creator.
result.Path = extension->GetPackagePath();
result.Path /= s_IndexFilePath;

return std::optional<ExtractedIndex>{ std::move(result) };
}

void Remove(const std::vector<PackageKey>& packages, IProgressCallback& progress) override
{
for (const auto& package : packages)
{
auto fullName = Msix::GetPackageFullNameFromFamilyName(package.Identity);

if (!fullName)
{
AICLI_LOG(Repo, Info, << "No full name found for family name: " << package.Identity);
}
else
{
AICLI_LOG(Repo, Info, << "Removing package: " << *fullName);
Deployment::RemovePackage(*fullName, winrt::Windows::Management::Deployment::RemovalOptions::None, progress);
}
}
}

// The deployed index is read in place, so a read can be attempted before taking the
// lock and retried under it only when that fails.
bool AllowsUnlockedRead() const override { return true; }

private:
std::optional<Deployment::Extension> GetExtension(const PackageKey& package) const
{
THROW_HR_IF(E_UNEXPECTED, package.Identity.empty());
Deployment::ExtensionCatalog catalog(Deployment::SourceExtensionName);
return catalog.FindByPackageFamilyAndId(package.Identity, Deployment::IndexDBId);
}
};
}

std::unique_ptr<IPackageStore> CreateDeployedPackageStore(const SourceDetails& details)
{
return std::make_unique<anon::DeployedPackageStore>(details);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "Microsoft/PreIndexed/IndexForm.h"
#include "Microsoft/PreIndexed/RemotePackage.h"

#include <AppInstallerDateTime.h>
#include <AppInstallerDownloader.h>

namespace AppInstaller::Repository::Microsoft::PreIndexed
{
namespace anon
{
// A source composed of a single package holding the complete index.
struct FullIndexForm : public IIndexForm
{
FullIndexForm(const SourceDetails& details) : m_details(details)
{
m_identity = details.Data;
}

std::optional<std::string> DiscoverIdentities(IProgressCallback&) override
{
if (!m_identity.empty())
{
return m_identity;
}

PreIndexedPackageInfo packageInfo(GetFullIndexPackageLocations(m_details), [](const std::string& packageLocation)
{
THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE, Utility::IsUrlRemote(packageLocation) && !Utility::IsUrlSecure(packageLocation));
});

AICLI_LOG(Repo, Info, << "Initializing source from: " << m_details.Name << " => " << packageInfo.PackageLocation());

THROW_HR_IF(APPINSTALLER_CLI_ERROR_PACKAGE_IS_BUNDLE, packageInfo.MsixInfo().GetIsBundle());

auto fullName = packageInfo.MsixInfo().GetPackageFullName();
AICLI_LOG(Repo, Info, << "Found package full name: " << m_details.Name << " => " << fullName);

m_identity = Msix::GetPackageFamilyNameFromFullName(fullName);

return m_identity;
}

std::vector<PackageKey> GetPackages() const override
{
return { GetKey() };
}

bool HasIdentities() const override
{
return !m_identity.empty();
}

bool IsHeld(const IPackageStore& store) const override
{
return GetHeldVersion(store).has_value();
}

std::optional<Msix::PackageVersion> GetHeldVersion(const IPackageStore& store) const override
{
return store.GetVersion(GetKey());
}

UpdateResult Update(IPackageStore& store, bool isBackground, IProgressCallback& progress, UpdateReport& report) override
{
std::optional<Msix::PackageVersion> currentVersion = GetHeldVersion(store);
PreIndexedPackageUpdateCheck updateCheck(GetFullIndexPackageLocations(m_details));

if (currentVersion)
{
report.PreviousIndexPublishedAt = Utility::GetTimePointFromVersion(currentVersion.value());
}

report.NewIndexPublishedAt = Utility::GetTimePointFromVersion(updateCheck.AvailableVersion());

if (currentVersion)
{
if (currentVersion.value() >= updateCheck.AvailableVersion())
{
AICLI_LOG(Repo, Verbose, << "Remote source data (" << updateCheck.AvailableVersion().ToString() <<
") was not newer than existing (" << currentVersion.value().ToString() << "), no update needed");
return UpdateResult::Success;
}
else
{
AICLI_LOG(Repo, Verbose, << "Remote source data (" << updateCheck.AvailableVersion().ToString() <<
") was newer than existing (" << currentVersion.value().ToString() << "), updating");
}
}

if (progress.IsCancelledBy(CancelReason::Any))
{
AICLI_LOG(Repo, Info, << "Cancelling update upon request");
return UpdateResult::Aborted;
}

auto lock = store.Lock(progress, isBackground);
if (!lock)
{
// The lock says nothing about whether this form would have worked.
return UpdateResult::Aborted;
}

report.Reportable = true;

return Acquire(store, updateCheck.PackageLocation(), progress, report);
}

// Acquires the index from a location that the caller has already determined, under a
// lock that the caller already holds.
UpdateResult Acquire(IPackageStore& store, const std::string& location, IProgressCallback& progress, UpdateReport& report)
{
auto acquired = store.Acquire(GetKey(), location, progress);
if (!acquired)
{
// Acquisition reports nothing only when it was cancelled.
return UpdateResult::Aborted;
}

report.DownloadedBytes = acquired->DownloadedBytes;

store.Persist(std::move(acquired.value()), progress);

return UpdateResult::Success;
}

SQLiteIndex Open(IPackageStore& store, IProgressCallback& progress) override
{
auto extracted = store.GetIndex(GetKey(), progress);

if (progress.IsCancelledBy(CancelReason::Any))
{
THROW_WIN32(ERROR_CANCELLED);
}

if (!extracted)
{
THROW_HR(APPINSTALLER_CLI_ERROR_SOURCE_DATA_MISSING);
}

return SQLiteIndex::Open(extracted->Path.u8string(), SQLiteIndex::OpenDisposition::Immutable, std::move(extracted->TemporaryFile));
}

private:
PackageKey GetKey() const
{
THROW_HR_IF(E_NOT_VALID_STATE, m_identity.empty());
return PackageKey{ PackageSlot::FullIndex, m_identity };
}

SourceDetails m_details;
std::string m_identity;
};
}

std::unique_ptr<IIndexForm> CreateFullIndexForm(const SourceDetails& details)
{
return std::make_unique<anon::FullIndexForm>(details);
}
}
Loading
Loading