ncm: refactor rights cache

This commit is contained in:
Adubbz 2020-02-29 22:21:55 +11:00
parent f93a1a987c
commit 9b34fc7ce7
12 changed files with 276 additions and 195 deletions

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::fs {
union RightsId {
u8 data[0x10];
u64 data64[2];
};
static_assert(sizeof(RightsId) == 0x10);
static_assert(std::is_pod<RightsId>::value);
/* Rights ID API */
Result GetRightsId(RightsId *out, const char *path);
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path);
}

View file

@ -22,6 +22,8 @@
namespace ams::ncm {
class IContentStorage : public sf::IServiceObject {
NON_COPYABLE(IContentStorage);
NON_MOVEABLE(IContentStorage);
protected:
enum class CommandId {
GeneratePlaceHolderId = 0,
@ -53,15 +55,8 @@ namespace ams::ncm {
RepairInvalidFileAttribute = 26,
GetRightsIdFromPlaceHolderIdWithCache = 27,
};
protected:
char root_path[FS_MAX_PATH-1];
MakeContentPathFunc make_content_path_func;
bool disabled;
protected:
Result EnsureEnabled() {
R_UNLESS(!this->disabled, ncm::ResultInvalidContentStorage());
return ResultSuccess();
}
public:
IContentStorage() { /* ... */ }
public:
virtual Result GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) = 0;
virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, u64 size) = 0;
@ -82,15 +77,15 @@ namespace ams::ncm {
virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) = 0;
virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) = 0;
virtual Result ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, u64 offset) = 0;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id) = 0;
virtual Result GetRightsIdFromContentId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, ContentId content_id) = 0;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) = 0;
virtual Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) = 0;
virtual Result WriteContentForDebug(ContentId content_id, u64 offset, sf::InBuffer data) = 0;
virtual Result GetFreeSpaceSize(sf::Out<u64> out_size) = 0;
virtual Result GetTotalSpaceSize(sf::Out<u64> out_size) = 0;
virtual Result FlushPlaceHolder() = 0;
virtual Result GetSizeFromPlaceHolderId(sf::Out<u64> out, PlaceHolderId placeholder_id) = 0;
virtual Result RepairInvalidFileAttribute() = 0;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id, ContentId cache_content_id) = 0;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(GeneratePlaceHolderId),

View file

@ -16,6 +16,7 @@
#pragma once
#include <vapours.hpp>
#include <stratosphere/fs/fs_rights_id.hpp>
namespace ams::ncm {
@ -542,6 +543,14 @@ namespace ams::ncm {
static_assert(sizeof(ProgramLocation) == 0x10 && std::is_pod<ProgramLocation>::value, "ProgramLocation definition!");
static_assert(sizeof(ProgramLocation) == sizeof(::NcmProgramLocation) && alignof(ProgramLocation) == alignof(::NcmProgramLocation), "ProgramLocation Libnx Compatibility");
struct RightsId {
fs::RightsId id;
u8 key_generation;
u8 reserved[7];
};
static_assert(sizeof(RightsId) == 0x18);
static_assert(std::is_pod<RightsId>::value);
struct ContentMetaKey {
ProgramId id;
u32 version;

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include <stratosphere/fs/fs_rights_id.hpp>
namespace ams::fs {
Result GetRightsId(RightsId *out, const char *path) {
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
return fsGetRightsIdByPath(path, reinterpret_cast<::FsRightsId *>(out));
}
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path) {
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
return fsGetRightsIdAndKeyGenerationByPath(path, out_key_generation, reinterpret_cast<::FsRightsId *>(out));
}
}

View file

@ -20,10 +20,10 @@
#include <optional>
#include "../ncm_content_meta_database.hpp"
#include "../ncm_content_storage.hpp"
#include "../ncm_content_storage_impl.hpp"
#include "../ncm_fs.hpp"
#include "../ncm_make_path.hpp"
#include "../ncm_read_only_content_storage.hpp"
#include "../ncm_read_only_content_storage_impl.hpp"
#include "ncm_content_manager.hpp"
#include "ncm_rights_cache.hpp"
@ -427,14 +427,14 @@ namespace ams::ncm::impl {
auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_point); };
if (storage_id == StorageId::GameCard) {
auto content_storage = std::make_shared<ReadOnlyContentStorageInterface>();
auto content_storage = std::make_shared<ReadOnlyContentStorageImpl>();
R_TRY(content_storage->Initialize(root->path, path::MakeContentPathFlat));
root->content_storage = std::move(content_storage);
} else {
MakeContentPathFunc content_path_func = nullptr;
MakePlaceHolderPathFunc placeholder_path_func = nullptr;
bool delay_flush = false;
auto content_storage = std::make_shared<ContentStorageInterface>();
auto content_storage = std::make_shared<ContentStorageImpl>();
switch (storage_id) {
case StorageId::BuiltInSystem:

View file

@ -21,21 +21,22 @@
namespace ams::ncm::impl {
class RightsIdCache {
public:
NON_COPYABLE(RightsIdCache);
NON_MOVEABLE(RightsIdCache);
private:
static constexpr size_t MaxEntries = 0x80;
public:
private:
struct Entry {
public:
util::Uuid uuid;
FsRightsId rights_id;
u64 key_generation;
ncm::RightsId rights_id;
u64 last_accessed;
};
private:
Entry entries[MaxEntries];
u64 counter;
os::Mutex mutex;
public:
RightsIdCache() {
this->Invalidate();
}
@ -46,6 +47,45 @@ namespace ams::ncm::impl {
this->entries[i].last_accessed = 1;
}
}
void Store(ContentId content_id, ncm::RightsId rights_id) {
std::scoped_lock lk(this->mutex);
Entry *eviction_candidate = &this->entries[0];
/* Find a suitable existing entry to store our new one at. */
for (size_t i = 1; i < MaxEntries; i++) {
Entry *entry = &this->entries[i];
/* Change eviction candidates if the uuid already matches ours, or if the uuid doesn't already match and the last_accessed count is lower */
if (content_id == entry->uuid || (content_id != eviction_candidate->uuid && entry->last_accessed < eviction_candidate->last_accessed)) {
eviction_candidate = entry;
}
}
/* Update the cache. */
eviction_candidate->uuid = content_id.uuid;
eviction_candidate->rights_id = rights_id;
eviction_candidate->last_accessed = this->counter;
this->counter++;
}
bool Find(ncm::RightsId *out_rights_id, ContentId content_id) {
std::scoped_lock lk(this->mutex);
/* Attempt to locate the content id in the cache. */
for (size_t i = 0; i < MaxEntries; i++) {
Entry *entry = &this->entries[i];
if (entry->last_accessed != 1 && content_id == entry->uuid) {
entry->last_accessed = this->counter;
this->counter++;
*out_rights_id = entry->rights_id;
return true;
}
}
return false;
}
};
}

View file

@ -14,18 +14,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ncm_content_storage.hpp"
#include "ncm_content_storage_impl.hpp"
#include "ncm_fs.hpp"
#include "ncm_make_path.hpp"
#include "ncm_utils.hpp"
namespace ams::ncm {
ContentStorageInterface::~ContentStorageInterface() {
ContentStorageImpl::~ContentStorageImpl() {
this->Finalize();
}
Result ContentStorageInterface::Initialize(const char *root_path, MakeContentPathFunc content_path_func, MakePlaceHolderPathFunc placeholder_path_func, bool delay_flush, impl::RightsIdCache *rights_id_cache) {
Result ContentStorageImpl::Initialize(const char *root_path, MakeContentPathFunc content_path_func, MakePlaceHolderPathFunc placeholder_path_func, bool delay_flush, impl::RightsIdCache *rights_id_cache) {
R_TRY(this->EnsureEnabled());
R_TRY(fs::CheckContentStorageDirectoriesExist(root_path));
const size_t root_path_len = strnlen(root_path, FS_MAX_PATH-1);
@ -39,19 +39,19 @@ namespace ams::ncm {
return ResultSuccess();
}
void ContentStorageInterface::Finalize() {
void ContentStorageImpl::Finalize() {
this->ClearContentCache();
this->placeholder_accessor.InvalidateAll();
}
void ContentStorageInterface::ClearContentCache() {
void ContentStorageImpl::ClearContentCache() {
if (this->cached_content_id != InvalidContentId) {
fclose(this->content_cache_file_handle);
this->cached_content_id = InvalidContentId;
}
}
unsigned int ContentStorageInterface::GetContentDirectoryDepth() {
unsigned int ContentStorageImpl::GetContentDirectoryDepth() {
if (this->make_content_path_func == static_cast<MakeContentPathFunc>(path::MakeContentPathFlat)) {
return 1;
} else if (this->make_content_path_func == static_cast<MakeContentPathFunc>(path::MakeContentPathHashByteLayered)) {
@ -65,7 +65,7 @@ namespace ams::ncm {
AMS_ABORT();
}
Result ContentStorageInterface::OpenCachedContentFile(ContentId content_id) {
Result ContentStorageImpl::OpenCachedContentFile(ContentId content_id) {
R_UNLESS(this->cached_content_id != content_id, ResultSuccess());
this->ClearContentCache();
@ -80,13 +80,13 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
Result ContentStorageImpl::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
R_TRY(this->EnsureEnabled());
out.SetValue({util::GenerateUuid()});
return ResultSuccess();
}
Result ContentStorageInterface::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, u64 size) {
Result ContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, u64 size) {
R_TRY(this->EnsureEnabled());
char content_path[FS_MAX_PATH] = {0};
@ -98,12 +98,12 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::DeletePlaceHolder(PlaceHolderId placeholder_id) {
Result ContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
return this->placeholder_accessor.Delete(placeholder_id);
}
Result ContentStorageInterface::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
Result ContentStorageImpl::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
char placeholder_path[FS_MAX_PATH] = {0};
@ -116,7 +116,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::WritePlaceHolder(PlaceHolderId placeholder_id, u64 offset, sf::InBuffer data) {
Result ContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, u64 offset, sf::InBuffer data) {
/* Offset is too large */
R_UNLESS(offset <= std::numeric_limits<s64>::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
@ -124,7 +124,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::Register(PlaceHolderId placeholder_id, ContentId content_id) {
Result ContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) {
this->ClearContentCache();
R_TRY(this->EnsureEnabled());
@ -144,7 +144,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::Delete(ContentId content_id) {
Result ContentStorageImpl::Delete(ContentId content_id) {
R_TRY(this->EnsureEnabled());
this->ClearContentCache();
@ -160,7 +160,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::Has(sf::Out<bool> out, ContentId content_id) {
Result ContentStorageImpl::Has(sf::Out<bool> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
char content_path[FS_MAX_PATH] = {0};
@ -173,7 +173,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GetPath(sf::Out<lr::Path> out, ContentId content_id) {
Result ContentStorageImpl::GetPath(sf::Out<lr::Path> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
char content_path[FS_MAX_PATH] = {0};
@ -184,7 +184,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GetPlaceHolderPath(sf::Out<lr::Path> out, PlaceHolderId placeholder_id) {
Result ContentStorageImpl::GetPlaceHolderPath(sf::Out<lr::Path> out, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
char placeholder_path[FS_MAX_PATH] = {0};
@ -195,7 +195,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::CleanupAllPlaceHolder() {
Result ContentStorageImpl::CleanupAllPlaceHolder() {
R_TRY(this->EnsureEnabled());
char placeholder_root_path[FS_MAX_PATH] = {0};
@ -209,7 +209,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::ListPlaceHolder(sf::Out<u32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
Result ContentStorageImpl::ListPlaceHolder(sf::Out<u32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
R_TRY(this->EnsureEnabled());
char placeholder_root_path[FS_MAX_PATH] = {0};
@ -236,7 +236,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GetContentCount(sf::Out<u32> out_count) {
Result ContentStorageImpl::GetContentCount(sf::Out<u32> out_count) {
R_TRY(this->EnsureEnabled());
char content_root_path[FS_MAX_PATH] = {0};
@ -259,7 +259,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::ListContentId(sf::Out<u32> out_count, const sf::OutArray<ContentId> &out_buf, u32 start_offset) {
Result ContentStorageImpl::ListContentId(sf::Out<u32> out_count, const sf::OutArray<ContentId> &out_buf, u32 start_offset) {
R_UNLESS(start_offset <= std::numeric_limits<s32>::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
@ -308,7 +308,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GetSizeFromContentId(sf::Out<u64> out_size, ContentId content_id) {
Result ContentStorageImpl::GetSizeFromContentId(sf::Out<u64> out_size, ContentId content_id) {
R_TRY(this->EnsureEnabled());
char content_path[FS_MAX_PATH] = {0};
@ -320,14 +320,14 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::DisableForcibly() {
Result ContentStorageImpl::DisableForcibly() {
this->disabled = true;
this->ClearContentCache();
this->placeholder_accessor.InvalidateAll();
return ResultSuccess();
}
Result ContentStorageInterface::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
Result ContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
R_TRY(this->EnsureEnabled());
char old_content_path[FS_MAX_PATH] = {0};
@ -352,13 +352,13 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) {
Result ContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) {
R_TRY(this->EnsureEnabled());
R_TRY(this->placeholder_accessor.SetSize(placeholder_id, size));
return ResultSuccess();
}
Result ContentStorageInterface::ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, u64 offset) {
Result ContentStorageImpl::ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, u64 offset) {
/* Offset is too large */
R_UNLESS(offset <= std::numeric_limits<s64>::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
@ -370,82 +370,43 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GetRightsIdFromPlaceHolderId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id) {
Result ContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
FsRightsId rights_id = {0};
u8 key_generation = 0;
char placeholder_path[FS_MAX_PATH] = {0};
char common_path[FS_MAX_PATH] = {0};
this->placeholder_accessor.GetPath(placeholder_path, placeholder_id);
R_TRY(fs::ConvertToFsCommonPath(common_path, FS_MAX_PATH-1, placeholder_path));
R_TRY(fsGetRightsIdAndKeyGenerationByPath(common_path, &key_generation, &rights_id));
ncm::RightsId rights_id;
R_TRY(GetRightsId(&rights_id, common_path));
out_rights_id.SetValue(rights_id);
out_key_generation.SetValue(static_cast<u64>(key_generation));
return ResultSuccess();
}
Result ContentStorageInterface::GetRightsIdFromContentId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, ContentId content_id) {
Result ContentStorageImpl::GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) {
R_TRY(this->EnsureEnabled());
{
std::scoped_lock lk(this->rights_id_cache->mutex);
/* Attempt to locate the content id in the cache. */
for (size_t i = 0; i < impl::RightsIdCache::MaxEntries; i++) {
impl::RightsIdCache::Entry *entry = &this->rights_id_cache->entries[i];
if (entry->last_accessed != 1 && content_id == entry->uuid) {
entry->last_accessed = this->rights_id_cache->counter;
this->rights_id_cache->counter++;
out_rights_id.SetValue(entry->rights_id);
out_key_generation.SetValue(entry->key_generation);
return ResultSuccess();
}
}
if (this->rights_id_cache->Find(out_rights_id.GetPointer(), content_id)) {
return ResultSuccess();
}
FsRightsId rights_id = {0};
u8 key_generation = 0;
char content_path[FS_MAX_PATH] = {0};
char common_path[FS_MAX_PATH] = {0};
this->GetContentPath(content_path, content_id);
R_TRY(fs::ConvertToFsCommonPath(common_path, FS_MAX_PATH-1, content_path));
R_TRY(fsGetRightsIdAndKeyGenerationByPath(common_path, &key_generation, &rights_id));
{
std::scoped_lock lk(this->rights_id_cache->mutex);
impl::RightsIdCache::Entry *eviction_candidate = &this->rights_id_cache->entries[0];
/* Find a suitable existing entry to store our new one at. */
for (size_t i = 1; i < impl::RightsIdCache::MaxEntries; i++) {
impl::RightsIdCache::Entry *entry = &this->rights_id_cache->entries[i];
/* Change eviction candidates if the uuid already matches ours, or if the uuid doesn't already match and the last_accessed count is lower */
if (content_id == entry->uuid || (content_id != eviction_candidate->uuid && entry->last_accessed < eviction_candidate->last_accessed)) {
eviction_candidate = entry;
}
}
/* Update the cache. */
eviction_candidate->uuid = content_id.uuid;
eviction_candidate->rights_id = rights_id;
eviction_candidate->key_generation = key_generation;
eviction_candidate->last_accessed = this->rights_id_cache->counter;
this->rights_id_cache->counter++;
/* Set output. */
out_rights_id.SetValue(rights_id);
out_key_generation.SetValue(key_generation);
}
ncm::RightsId rights_id;
R_TRY(GetRightsId(&rights_id, common_path));
this->rights_id_cache->Store(content_id, rights_id);
/* Set output. */
out_rights_id.SetValue(rights_id);
return ResultSuccess();
}
Result ContentStorageInterface::WriteContentForDebug(ContentId content_id, u64 offset, sf::InBuffer data) {
Result ContentStorageImpl::WriteContentForDebug(ContentId content_id, u64 offset, sf::InBuffer data) {
/* Offset is too large */
R_UNLESS(offset <= std::numeric_limits<s64>::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
@ -472,26 +433,26 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GetFreeSpaceSize(sf::Out<u64> out_size) {
Result ContentStorageImpl::GetFreeSpaceSize(sf::Out<u64> out_size) {
struct statvfs st = {0};
R_UNLESS(statvfs(this->root_path, &st) != -1, fsdevGetLastResult());
out_size.SetValue(st.f_bfree);
return ResultSuccess();
}
Result ContentStorageInterface::GetTotalSpaceSize(sf::Out<u64> out_size) {
Result ContentStorageImpl::GetTotalSpaceSize(sf::Out<u64> out_size) {
struct statvfs st = {0};
R_UNLESS(statvfs(this->root_path, &st) != -1, fsdevGetLastResult());
out_size.SetValue(st.f_blocks);
return ResultSuccess();
}
Result ContentStorageInterface::FlushPlaceHolder() {
Result ContentStorageImpl::FlushPlaceHolder() {
this->placeholder_accessor.InvalidateAll();
return ResultSuccess();
}
Result ContentStorageInterface::GetSizeFromPlaceHolderId(sf::Out<u64> out_size, PlaceHolderId placeholder_id) {
Result ContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out<u64> out_size, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
bool found_in_cache = false;
@ -514,7 +475,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::RepairInvalidFileAttribute() {
Result ContentStorageImpl::RepairInvalidFileAttribute() {
char content_root_path[FS_MAX_PATH] = {0};
this->GetContentRootPath(content_root_path);
unsigned int dir_depth = this->GetContentDirectoryDepth();
@ -545,59 +506,25 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ContentStorageInterface::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id, ContentId cache_content_id) {
Result ContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) {
R_TRY(this->EnsureEnabled());
{
std::scoped_lock lk(this->rights_id_cache->mutex);
/* Attempt to locate the content id in the cache. */
for (size_t i = 0; i < impl::RightsIdCache::MaxEntries; i++) {
impl::RightsIdCache::Entry *entry = &this->rights_id_cache->entries[i];
if (entry->last_accessed != 1 && cache_content_id == entry->uuid) {
entry->last_accessed = this->rights_id_cache->counter;
this->rights_id_cache->counter++;
out_rights_id.SetValue(entry->rights_id);
out_key_generation.SetValue(entry->key_generation);
return ResultSuccess();
}
}
if (this->rights_id_cache->Find(out_rights_id.GetPointer(), cache_content_id)) {
return ResultSuccess();
}
FsRightsId rights_id = {0};
u8 key_generation = 0;
char placeholder_path[FS_MAX_PATH] = {0};
char common_path[FS_MAX_PATH] = {0};
char placeholder_path[FS_MAX_PATH] = {0};
this->placeholder_accessor.GetPath(placeholder_path, placeholder_id);
R_TRY(fs::ConvertToFsCommonPath(common_path, FS_MAX_PATH-1, placeholder_path));
R_TRY(fsGetRightsIdAndKeyGenerationByPath(common_path, &key_generation, &rights_id));
{
std::scoped_lock lk(this->rights_id_cache->mutex);
impl::RightsIdCache::Entry *eviction_candidate = &this->rights_id_cache->entries[0];
ncm::RightsId rights_id;
R_TRY(GetRightsId(&rights_id, common_path));
this->rights_id_cache->Store(cache_content_id, rights_id);
/* Find a suitable existing entry to store our new one at. */
for (size_t i = 1; i < impl::RightsIdCache::MaxEntries; i++) {
impl::RightsIdCache::Entry *entry = &this->rights_id_cache->entries[i];
/* Change eviction candidates if the uuid already matches ours, or if the uuid doesn't already match and the last_accessed count is lower */
if (cache_content_id == entry->uuid || (cache_content_id != eviction_candidate->uuid && entry->last_accessed < eviction_candidate->last_accessed)) {
eviction_candidate = entry;
}
}
/* Update the cache. */
eviction_candidate->uuid = cache_content_id.uuid;
eviction_candidate->rights_id = rights_id;
eviction_candidate->key_generation = key_generation;
eviction_candidate->last_accessed = this->rights_id_cache->counter;
this->rights_id_cache->counter++;
/* Set output. */
out_rights_id.SetValue(rights_id);
out_key_generation.SetValue(key_generation);
}
/* Set output. */
out_rights_id.SetValue(rights_id);
return ResultSuccess();
}

View file

@ -20,18 +20,19 @@
#include "impl/ncm_placeholder_accessor.hpp"
#include "impl/ncm_rights_cache.hpp"
#include "ncm_content_storage_impl_base.hpp"
#include "ncm_path_utils.hpp"
namespace ams::ncm {
class ContentStorageInterface : public IContentStorage {
class ContentStorageImpl : public ContentStorageImplBase {
protected:
impl::PlaceHolderAccessor placeholder_accessor;
ContentId cached_content_id;
FILE *content_cache_file_handle;
impl::RightsIdCache *rights_id_cache;
public:
~ContentStorageInterface();
~ContentStorageImpl();
Result Initialize(const char *root_path, MakeContentPathFunc content_path_func, MakePlaceHolderPathFunc placeholder_path_func, bool delay_flush, impl::RightsIdCache *rights_id_cache);
void Finalize();
@ -70,15 +71,15 @@ namespace ams::ncm {
virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) override;
virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) override;
virtual Result ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, u64 offset) override;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromContentId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, ContentId content_id) override;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) override;
virtual Result WriteContentForDebug(ContentId content_id, u64 offset, sf::InBuffer data) override;
virtual Result GetFreeSpaceSize(sf::Out<u64> out_size) override;
virtual Result GetTotalSpaceSize(sf::Out<u64> out_size) override;
virtual Result FlushPlaceHolder() override;
virtual Result GetSizeFromPlaceHolderId(sf::Out<u64> out, PlaceHolderId placeholder_id) override;
virtual Result RepairInvalidFileAttribute() override;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id, ContentId cache_content_id) override;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) override;
};
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::ncm {
class ContentStorageImplBase : public IContentStorage {
NON_COPYABLE(ContentStorageImplBase);
NON_MOVEABLE(ContentStorageImplBase);
protected:
char root_path[FS_MAX_PATH-1];
MakeContentPathFunc make_content_path_func;
bool disabled;
protected:
ContentStorageImplBase() { /* ... */ }
protected:
Result EnsureEnabled() {
R_UNLESS(!this->disabled, ncm::ResultInvalidContentStorage());
return ResultSuccess();
}
static Result GetRightsId(ncm::RightsId *out_rights_id, const char *path) {
if (hos::GetVersion() >= hos::Version_300) {
R_TRY(ams::fs::GetRightsId(std::addressof(out_rights_id->id), std::addressof(out_rights_id->key_generation), path));
} else {
R_TRY(ams::fs::GetRightsId(std::addressof(out_rights_id->id), path));
out_rights_id->key_generation = 0;
}
return ResultSuccess();
}
};
}

View file

@ -44,7 +44,7 @@ namespace ams::ncm::fs {
return ResultSuccess();
}
Result WriteFile(FILE *f, size_t offset, const void *buffer, size_t size, u32 option) {
Result WriteFile(FILE *f, size_t offset, const void *buffer, size_t size, ams::fs::WriteOption option) {
R_UNLESS(fseek(f, 0, SEEK_END) == 0, fsdevGetLastResult());
size_t existing_size = ftell(f);
@ -52,7 +52,7 @@ namespace ams::ncm::fs {
R_UNLESS(fseek(f, offset, SEEK_SET) == 0, fsdevGetLastResult());
R_UNLESS(fwrite(buffer, 1, size, f) == size, fsdevGetLastResult());
if (option & FsWriteOption_Flush) {
if (option.HasFlushFlag()) {
fflush(f);
}

View file

@ -16,11 +16,11 @@
#include "ncm_fs.hpp"
#include "ncm_path_utils.hpp"
#include "ncm_read_only_content_storage.hpp"
#include "ncm_read_only_content_storage_impl.hpp"
namespace ams::ncm {
Result ReadOnlyContentStorageInterface::Initialize(const char *root_path, MakeContentPathFunc content_path_func) {
Result ReadOnlyContentStorageImpl::Initialize(const char *root_path, MakeContentPathFunc content_path_func) {
R_TRY(this->EnsureEnabled());
const size_t root_path_len = strnlen(root_path, FS_MAX_PATH-1);
@ -30,35 +30,35 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
Result ReadOnlyContentStorageImpl::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, u64 size) {
Result ReadOnlyContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, u64 size) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::DeletePlaceHolder(PlaceHolderId placeholder_id) {
Result ReadOnlyContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
Result ReadOnlyContentStorageImpl::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::WritePlaceHolder(PlaceHolderId placeholder_id, u64 offset, sf::InBuffer data) {
Result ReadOnlyContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, u64 offset, sf::InBuffer data) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::Register(PlaceHolderId placeholder_id, ContentId content_id) {
Result ReadOnlyContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::Delete(ContentId content_id) {
Result ReadOnlyContentStorageImpl::Delete(ContentId content_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::Has(sf::Out<bool> out, ContentId content_id) {
Result ReadOnlyContentStorageImpl::Has(sf::Out<bool> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
char content_path[FS_MAX_PATH] = {0};
@ -76,7 +76,7 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::GetPath(sf::Out<lr::Path> out, ContentId content_id) {
Result ReadOnlyContentStorageImpl::GetPath(sf::Out<lr::Path> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
char content_path[FS_MAX_PATH] = {0};
@ -96,27 +96,27 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::GetPlaceHolderPath(sf::Out<lr::Path> out, PlaceHolderId placeholder_id) {
Result ReadOnlyContentStorageImpl::GetPlaceHolderPath(sf::Out<lr::Path> out, PlaceHolderId placeholder_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::CleanupAllPlaceHolder() {
Result ReadOnlyContentStorageImpl::CleanupAllPlaceHolder() {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::ListPlaceHolder(sf::Out<u32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
Result ReadOnlyContentStorageImpl::ListPlaceHolder(sf::Out<u32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::GetContentCount(sf::Out<u32> out_count) {
Result ReadOnlyContentStorageImpl::GetContentCount(sf::Out<u32> out_count) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::ListContentId(sf::Out<u32> out_count, const sf::OutArray<ContentId> &out_buf, u32 start_offset) {
Result ReadOnlyContentStorageImpl::ListContentId(sf::Out<u32> out_count, const sf::OutArray<ContentId> &out_buf, u32 start_offset) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::GetSizeFromContentId(sf::Out<u64> out_size, ContentId content_id) {
Result ReadOnlyContentStorageImpl::GetSizeFromContentId(sf::Out<u64> out_size, ContentId content_id) {
R_TRY(this->EnsureEnabled());
char content_path[FS_MAX_PATH] = {0};
@ -135,20 +135,20 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::DisableForcibly() {
Result ReadOnlyContentStorageImpl::DisableForcibly() {
this->disabled = true;
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
Result ReadOnlyContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) {
Result ReadOnlyContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, u64 offset) {
Result ReadOnlyContentStorageImpl::ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, u64 offset) {
/* Offset is too large */
R_UNLESS(offset <= std::numeric_limits<s64>::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
@ -174,16 +174,13 @@ namespace ams::ncm {
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::GetRightsIdFromPlaceHolderId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id) {
Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::GetRightsIdFromContentId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, ContentId content_id) {
Result ReadOnlyContentStorageImpl::GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) {
R_TRY(this->EnsureEnabled());
FsRightsId rights_id = {0};
u8 key_generation = 0;
char content_path[FS_MAX_PATH] = {0};
char common_path[FS_MAX_PATH] = {0};
bool is_content_meta_file = false;
@ -196,41 +193,41 @@ namespace ams::ncm {
}
R_TRY(fs::ConvertToFsCommonPath(common_path, FS_MAX_PATH-1, content_path));
R_TRY(fsGetRightsIdAndKeyGenerationByPath(common_path, &key_generation, &rights_id));
ncm::RightsId rights_id;
R_TRY(GetRightsId(&rights_id, common_path));
out_rights_id.SetValue(rights_id);
out_key_generation.SetValue(static_cast<u64>(key_generation));
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::WriteContentForDebug(ContentId content_id, u64 offset, sf::InBuffer data) {
Result ReadOnlyContentStorageImpl::WriteContentForDebug(ContentId content_id, u64 offset, sf::InBuffer data) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::GetFreeSpaceSize(sf::Out<u64> out_size) {
Result ReadOnlyContentStorageImpl::GetFreeSpaceSize(sf::Out<u64> out_size) {
out_size.SetValue(0);
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::GetTotalSpaceSize(sf::Out<u64> out_size) {
Result ReadOnlyContentStorageImpl::GetTotalSpaceSize(sf::Out<u64> out_size) {
out_size.SetValue(0);
return ResultSuccess();
}
Result ReadOnlyContentStorageInterface::FlushPlaceHolder() {
Result ReadOnlyContentStorageImpl::FlushPlaceHolder() {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::GetSizeFromPlaceHolderId(sf::Out<u64> out, PlaceHolderId placeholder_id) {
Result ReadOnlyContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out<u64> out, PlaceHolderId placeholder_id) {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::RepairInvalidFileAttribute() {
Result ReadOnlyContentStorageImpl::RepairInvalidFileAttribute() {
return ResultInvalidContentStorageOperation();
}
Result ReadOnlyContentStorageInterface::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id, ContentId cache_content_id) {
Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) {
return ResultInvalidContentStorageOperation();
}

View file

@ -17,10 +17,11 @@
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "ncm_content_storage_impl_base.hpp"
namespace ams::ncm {
class ReadOnlyContentStorageInterface : public IContentStorage {
class ReadOnlyContentStorageImpl : public ContentStorageImplBase {
public:
Result Initialize(const char *root_path, MakeContentPathFunc content_path_func);
public:
@ -43,15 +44,15 @@ namespace ams::ncm {
virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) override;
virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) override;
virtual Result ReadContentIdFile(sf::OutBuffer buf, ContentId content_id, u64 offset) override;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromContentId(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, ContentId content_id) override;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) override;
virtual Result WriteContentForDebug(ContentId content_id, u64 offset, sf::InBuffer data) override;
virtual Result GetFreeSpaceSize(sf::Out<u64> out_size) override;
virtual Result GetTotalSpaceSize(sf::Out<u64> out_size) override;
virtual Result FlushPlaceHolder() override;
virtual Result GetSizeFromPlaceHolderId(sf::Out<u64> out, PlaceHolderId placeholder_id) override;
virtual Result RepairInvalidFileAttribute() override;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<FsRightsId> out_rights_id, sf::Out<u64> out_key_generation, PlaceHolderId placeholder_id, ContentId cache_content_id) override;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) override;
};
}