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());
/* Find the meta key. */
ContentMetaKeyValueStore::Entry *entry = nullptr;
R_TRY(this->FindContentMetaKeyValue(std::addressof(entry), key));
const auto stored_key = entry->GetKey();
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());
const auto found_key = it->GetKey();
/* Create a reader for this content meta. */
const void *meta;
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);
@ -79,12 +81,25 @@ namespace ams::ncm {
Result ContentMetaDatabaseImpl::Get(sf::Out<u64> out_size, const ContentMetaKey &key, sf::OutBuffer out_value) {
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) {
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) {
@ -121,7 +136,7 @@ namespace ams::ncm {
/* Iterate over all entries. */
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. */
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 != InvalidProgramId) {
if (application_id != InvalidApplicationId) {
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
@ -169,10 +184,10 @@ namespace ams::ncm {
/* Iterate over all entries. */
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. */
if (!((type == ContentMetaType::Unknown || key.type == type))) {
if (!(type == ContentMetaType::Unknown || key.type == type)) {
continue;
}
@ -250,10 +265,14 @@ namespace ams::ncm {
ContentMetaReader reader(meta, meta_size);
/* Obtain the required system version. */
if (key.type == ContentMetaType::Application) {
out_version.SetValue(reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_system_version);
} else {
out_version.SetValue(reader.GetExtendedHeader<PatchMetaExtendedHeader>()->required_system_version);
switch (key.type) {
case ContentMetaType::Application:
out_version.SetValue(reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_system_version);
break;
case ContentMetaType::Patch:
out_version.SetValue(reader.GetExtendedHeader<PatchMetaExtendedHeader>()->required_system_version);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
return ResultSuccess();
@ -333,14 +352,15 @@ namespace ams::ncm {
/* Create a reader. */
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. */
for (size_t i = 0; i < reader.GetContentCount(); i++) {
if (content_id == reader.GetContentInfo(i)->GetId()) {
out.SetValue(true);
return ResultSuccess();
}
R_SUCCEED_IF(content_id == reader.GetContentInfo(i)->GetId());
}
/* We didn't find a content info. */
out.SetValue(false);
return ResultSuccess();
}
@ -395,13 +415,16 @@ namespace ams::ncm {
/* Get the required version. */
u32 required_version;
if (key.type == ContentMetaType::Application && hos::GetVersion() >= hos::Version_900) {
/* As of 9.0.0, applications can be dependent on a specific base application version. */
required_version = reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_application_version;
} else if (key.type == ContentMetaType::AddOnContent) {
required_version = reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->required_application_version;
} else {
return ncm::ResultInvalidContentMetaKey();
switch (key.type) {
case ContentMetaType::AddOnContent:
required_version = reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->required_application_version;
break;
case ContentMetaType::Application:
/* As of 9.0.0, applications can be dependent on a specific base application version. */
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);

View file

@ -25,7 +25,7 @@ namespace ams::ncm {
using ContentMetaKeyValueStore = ams::kvdb::MemoryKeyValueStore<ContentMetaKey>;
protected:
ContentMetaKeyValueStore *kvs;
char mount_name[16];
char mount_name[fs::MountNameLengthMax + 1];
bool disabled;
protected:
ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs) : kvs(kvs), disabled(false) { /* ... */ }
@ -40,14 +40,6 @@ namespace ams::ncm {
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 {
R_TRY_CATCH(this->kvs->GetValueSize(out, key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())

View file

@ -18,26 +18,80 @@
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());
std::optional<ContentMetaKey> found_key;
std::optional<ContentMetaKey> found_key = std::nullopt;
/* 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. */
if (entry->GetKey().id != id) {
break;
}
/* On memory content database is interested in all keys. */
found_key = entry->GetKey();
}
/* Check if the key is absent. */
R_UNLESS(found_key, ncm::ResultContentMetaNotFound());
*out_key = *found_key;
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) {
return ResultInvalidContentMetaDatabase();
}

View file

@ -22,8 +22,10 @@ namespace ams::ncm {
class OnMemoryContentMetaDatabaseImpl : public ContentMetaDatabaseImpl {
public:
OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore<ContentMetaKey> *kvs) : ContentMetaDatabaseImpl(kvs) { /* ... */ }
Result GetLatestKeyImpl(ContentMetaKey *out_key, u64 id) const;
public:
/* 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 LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) override;
virtual Result Commit() override;