ncm: improve meta-db accuracy

This commit is contained in:
Michael Scire 2020-03-06 09:22:04 -08:00
parent dea664be3a
commit a9953fc805
4 changed files with 108 additions and 37 deletions

View File

@ -22,14 +22,16 @@ namespace ams::ncm {
R_TRY(this->EnsureEnabled()); R_TRY(this->EnsureEnabled());
/* Find the meta key. */ /* Find the meta key. */
ContentMetaKeyValueStore::Entry *entry = nullptr; const auto it = this->kvs->lower_bound(key);
R_TRY(this->FindContentMetaKeyValue(std::addressof(entry), key)); R_UNLESS(it != this->kvs->end(), ncm::ResultContentMetaNotFound());
const auto stored_key = entry->GetKey(); R_UNLESS(it->GetKey().id == key.id, ncm::ResultContentMetaNotFound());
const auto found_key = it->GetKey();
/* Create a reader for this content meta. */ /* Create a reader for this content meta. */
const void *meta; const void *meta;
size_t meta_size; size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, stored_key)); R_TRY(this->GetContentMetaPointer(&meta, &meta_size, found_key));
ContentMetaReader reader(meta, meta_size); ContentMetaReader reader(meta, meta_size);
@ -79,12 +81,25 @@ namespace ams::ncm {
Result ContentMetaDatabaseImpl::Get(sf::Out<u64> out_size, const ContentMetaKey &key, sf::OutBuffer out_value) { Result ContentMetaDatabaseImpl::Get(sf::Out<u64> out_size, const ContentMetaKey &key, sf::OutBuffer out_value) {
R_TRY(this->EnsureEnabled()); R_TRY(this->EnsureEnabled());
return this->kvs->Get(out_size.GetPointer(), out_value.GetPointer(), out_value.GetSize(), key);
/* Get the entry from our key-value store. */
size_t size;
R_TRY_CATCH(this->kvs->Get(std::addressof(size), out_value.GetPointer(), out_value.GetSize(), key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())
} R_END_TRY_CATCH;
out_size.SetValue(size);
return ResultSuccess();
} }
Result ContentMetaDatabaseImpl::Remove(const ContentMetaKey &key) { Result ContentMetaDatabaseImpl::Remove(const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled()); R_TRY(this->EnsureEnabled());
return this->kvs->Remove(key);
R_TRY_CATCH(this->kvs->Remove(key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())
} R_END_TRY_CATCH;
return ResultSuccess();
} }
Result ContentMetaDatabaseImpl::GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type) { Result ContentMetaDatabaseImpl::GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type) {
@ -121,7 +136,7 @@ namespace ams::ncm {
/* Iterate over all entries. */ /* Iterate over all entries. */
for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) { for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) {
ContentMetaKey key = entry->GetKey(); const ContentMetaKey key = entry->GetKey();
/* Check if this entry matches the given filters. */ /* Check if this entry matches the given filters. */
if (!((meta_type == ContentMetaType::Unknown || key.type == meta_type) && (min <= key.id && key.id <= max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) { if (!((meta_type == ContentMetaType::Unknown || key.type == meta_type) && (min <= key.id && key.id <= max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) {
@ -129,7 +144,7 @@ namespace ams::ncm {
} }
/* If application id is present, check if it matches the filter. */ /* If application id is present, check if it matches the filter. */
if (application_id != InvalidProgramId) { if (application_id != InvalidApplicationId) {
/* Obtain the content meta for the key. */ /* Obtain the content meta for the key. */
const void *meta; const void *meta;
size_t meta_size; size_t meta_size;
@ -169,10 +184,10 @@ namespace ams::ncm {
/* Iterate over all entries. */ /* Iterate over all entries. */
for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) { for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) {
ContentMetaKey key = entry->GetKey(); const ContentMetaKey key = entry->GetKey();
/* Check if this entry matches the given filters. */ /* Check if this entry matches the given filters. */
if (!((type == ContentMetaType::Unknown || key.type == type))) { if (!(type == ContentMetaType::Unknown || key.type == type)) {
continue; continue;
} }
@ -250,10 +265,14 @@ namespace ams::ncm {
ContentMetaReader reader(meta, meta_size); ContentMetaReader reader(meta, meta_size);
/* Obtain the required system version. */ /* Obtain the required system version. */
if (key.type == ContentMetaType::Application) { switch (key.type) {
out_version.SetValue(reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_system_version); case ContentMetaType::Application:
} else { out_version.SetValue(reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_system_version);
out_version.SetValue(reader.GetExtendedHeader<PatchMetaExtendedHeader>()->required_system_version); break;
case ContentMetaType::Patch:
out_version.SetValue(reader.GetExtendedHeader<PatchMetaExtendedHeader>()->required_system_version);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
} }
return ResultSuccess(); return ResultSuccess();
@ -333,14 +352,15 @@ namespace ams::ncm {
/* Create a reader. */ /* Create a reader. */
ContentMetaReader reader(meta, meta_size); ContentMetaReader reader(meta, meta_size);
/* Optimistically suppose that we will find the content. */
out.SetValue(true);
/* Check if any content infos contain a matching id. */ /* Check if any content infos contain a matching id. */
for (size_t i = 0; i < reader.GetContentCount(); i++) { for (size_t i = 0; i < reader.GetContentCount(); i++) {
if (content_id == reader.GetContentInfo(i)->GetId()) { R_SUCCEED_IF(content_id == reader.GetContentInfo(i)->GetId());
out.SetValue(true);
return ResultSuccess();
}
} }
/* We didn't find a content info. */
out.SetValue(false); out.SetValue(false);
return ResultSuccess(); return ResultSuccess();
} }
@ -395,13 +415,16 @@ namespace ams::ncm {
/* Get the required version. */ /* Get the required version. */
u32 required_version; u32 required_version;
if (key.type == ContentMetaType::Application && hos::GetVersion() >= hos::Version_900) { switch (key.type) {
/* As of 9.0.0, applications can be dependent on a specific base application version. */ case ContentMetaType::AddOnContent:
required_version = reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_application_version; required_version = reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->required_application_version;
} else if (key.type == ContentMetaType::AddOnContent) { break;
required_version = reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->required_application_version; case ContentMetaType::Application:
} else { /* As of 9.0.0, applications can be dependent on a specific base application version. */
return ncm::ResultInvalidContentMetaKey(); AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_900);
required_version = reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_application_version;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
} }
out_version.SetValue(required_version); out_version.SetValue(required_version);

View File

@ -25,7 +25,7 @@ namespace ams::ncm {
using ContentMetaKeyValueStore = ams::kvdb::MemoryKeyValueStore<ContentMetaKey>; using ContentMetaKeyValueStore = ams::kvdb::MemoryKeyValueStore<ContentMetaKey>;
protected: protected:
ContentMetaKeyValueStore *kvs; ContentMetaKeyValueStore *kvs;
char mount_name[16]; char mount_name[fs::MountNameLengthMax + 1];
bool disabled; bool disabled;
protected: protected:
ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs) : kvs(kvs), disabled(false) { /* ... */ } ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs) : kvs(kvs), disabled(false) { /* ... */ }
@ -40,14 +40,6 @@ namespace ams::ncm {
return ResultSuccess(); return ResultSuccess();
} }
Result FindContentMetaKeyValue(ContentMetaKeyValueStore::Entry **out_entry, const ContentMetaKey &key) const {
const auto it = this->kvs->lower_bound(key);
R_UNLESS(it != this->kvs->end(), ncm::ResultContentMetaNotFound());
R_UNLESS(it->GetKey().id == key.id, ncm::ResultContentMetaNotFound());
*out_entry = it;
return ResultSuccess();
}
Result GetContentMetaSize(size_t *out, const ContentMetaKey &key) const { Result GetContentMetaSize(size_t *out, const ContentMetaKey &key) const {
R_TRY_CATCH(this->kvs->GetValueSize(out, key)) { R_TRY_CATCH(this->kvs->GetValueSize(out, key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound()) R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())

View File

@ -18,26 +18,80 @@
namespace ams::ncm { namespace ams::ncm {
Result OnMemoryContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) { Result ContentMetaDatabaseImpl::GetLatestKeyImpl(ContentMetaKey *out_key, u64 id) const {
R_TRY(this->EnsureEnabled()); R_TRY(this->EnsureEnabled());
std::optional<ContentMetaKey> found_key; std::optional<ContentMetaKey> found_key = std::nullopt;
/* Find the last key with the desired program id. */ /* Find the last key with the desired program id. */
for (auto entry = this->kvs->lower_bound(ContentMetaKey::Make(id, 0, ContentMetaType::Unknown)); entry != this->kvs->end(); entry++) { for (auto entry = this->kvs->lower_bound(ContentMetaKey::MakeUnknownType(id, 0)); entry != this->kvs->end(); entry++) {
/* No further entries will match the program id, discontinue. */ /* No further entries will match the program id, discontinue. */
if (entry->GetKey().id != id) { if (entry->GetKey().id != id) {
break; break;
} }
/* On memory content database is interested in all keys. */
found_key = entry->GetKey(); found_key = entry->GetKey();
} }
/* Check if the key is absent. */
R_UNLESS(found_key, ncm::ResultContentMetaNotFound()); R_UNLESS(found_key, ncm::ResultContentMetaNotFound());
*out_key = *found_key; *out_key = *found_key;
return ResultSuccess(); return ResultSuccess();
} }
Result OnMemoryContentMetaDatabaseImpl::List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) {
/* NOTE: This function is *almost* identical to the ContentMetaDatabaseImpl equivalent. */
/* The only difference is that the min max comparison is exclusive for OnMemoryContentMetaDatabaseImpl, */
/* but inclusive for ContentMetaDatabaseImpl. This may or may not be a Nintendo bug? */
R_TRY(this->EnsureEnabled());
size_t entries_total = 0;
size_t entries_written = 0;
/* Iterate over all entries. */
for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) {
const ContentMetaKey key = entry->GetKey();
/* Check if this entry matches the given filters. */
if (!((meta_type == ContentMetaType::Unknown || key.type == meta_type) && (min < key.id && key.id < max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) {
continue;
}
/* If application id is present, check if it matches the filter. */
if (application_id != InvalidApplicationId) {
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Ensure application id matches, if present. */
if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id && application_id != *entry_application_id) {
continue;
}
}
/* Write the entry to the output buffer. */
if (entries_written < out_info.GetSize()) {
out_info[entries_written++] = key;
}
entries_total++;
}
out_entries_total.SetValue(entries_total);
out_entries_written.SetValue(entries_written);
return ResultSuccess();
}
Result OnMemoryContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) {
R_TRY(this->EnsureEnabled());
return this->GetLatestKeyImpl(out_key.GetPointer(), id);
}
Result OnMemoryContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) { Result OnMemoryContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) {
return ResultInvalidContentMetaDatabase(); return ResultInvalidContentMetaDatabase();
} }

View File

@ -22,8 +22,10 @@ namespace ams::ncm {
class OnMemoryContentMetaDatabaseImpl : public ContentMetaDatabaseImpl { class OnMemoryContentMetaDatabaseImpl : public ContentMetaDatabaseImpl {
public: public:
OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore<ContentMetaKey> *kvs) : ContentMetaDatabaseImpl(kvs) { /* ... */ } OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore<ContentMetaKey> *kvs) : ContentMetaDatabaseImpl(kvs) { /* ... */ }
Result GetLatestKeyImpl(ContentMetaKey *out_key, u64 id) const;
public: public:
/* Actual commands. */ /* Actual commands. */
virtual Result List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) override;
virtual Result GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) override; virtual Result GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) override;
virtual Result LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) override; virtual Result LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) override;
virtual Result Commit() override; virtual Result Commit() override;