diff --git a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.cpp b/stratosphere/ncm/source/impl/ncm_placeholder_accessor.cpp index f209052fc..ba3a8249f 100644 --- a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.cpp +++ b/stratosphere/ncm/source/impl/ncm_placeholder_accessor.cpp @@ -24,6 +24,87 @@ namespace sts::ncm::impl { + Result PlaceHolderAccessor::Open(FILE** out_handle, PlaceHolderId placeholder_id) { + if (this->LoadFromCache(out_handle, placeholder_id)) { + return ResultSuccess; + } + char placeholder_path[FS_MAX_PATH] = {0}; + this->MakePath(placeholder_path, placeholder_id); + + FILE* f = nullptr; + R_TRY(OpenFile(&f, placeholder_path, FS_OPEN_WRITE)); + + *out_handle = f; + return ResultSuccess; + } + + bool PlaceHolderAccessor::LoadFromCache(FILE** out_handle, PlaceHolderId placeholder_id) { + std::scoped_lock lk(this->cache_mutex); + CacheEntry *entry = this->FindInCache(placeholder_id); + if (!entry) { + return false; + } + *out_handle = entry->handle; + entry->id = InvalidUuid; + entry->handle = nullptr; + return true; + } + + PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::FindInCache(PlaceHolderId placeholder_id) { + for (size_t i = 0; i < MaxCaches; i++) { + if (placeholder_id == this->caches[i].id) { + return &this->caches[i]; + } + } + return nullptr; + } + + PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::GetFreeEntry() { + /* Try to find an already free entry. */ + CacheEntry* entry = this->FindInCache(InvalidUuid); + + if (entry) { + return entry; + } + + /* Get the oldest entry. */ + { + entry = &this->caches[0]; + for (size_t i = 1; i < MaxCaches; i++) { + if (entry->counter > this->caches[i].counter) { + entry = &this->caches[i]; + } + } + this->Invalidate(entry); + return entry; + } + } + + void PlaceHolderAccessor::StoreToCache(FILE* handle, PlaceHolderId placeholder_id) { + std::scoped_lock lk(this->cache_mutex); + CacheEntry *entry = this->GetFreeEntry(); + entry->id = placeholder_id; + entry->handle = handle; + entry->counter = this->cur_counter++; + } + + void PlaceHolderAccessor::Invalidate(CacheEntry *entry) { + if (entry != nullptr) { + if (entry->handle != nullptr) { + fflush(entry->handle); + fclose(entry->handle); + entry->handle = nullptr; + } + entry->id = InvalidUuid; + } + } + + void PlaceHolderAccessor::Initialize(char* root, MakePlaceHolderPathFunc path_func, bool delay_flush) { + this->root_path = root; + this->make_placeholder_path_func = path_func; + this->delay_flush = delay_flush; + } + unsigned int PlaceHolderAccessor::GetDirectoryDepth() { if (this->make_placeholder_path_func == static_cast(path::MakePlaceHolderPathFlat)) { return 1; @@ -34,37 +115,18 @@ namespace sts::ncm::impl { std::abort(); } - void PlaceHolderAccessor::GetPlaceHolderPathUncached(char* placeholder_path_out, PlaceHolderId placeholder_id) { - std::scoped_lock lock(this->cache_mutex); - - if (placeholder_id != InvalidUuid) { - CacheEntry* found_cache = NULL; - - for (size_t i = 0; i < PlaceHolderAccessor::MaxCaches; i++) { - CacheEntry* cache = &this->caches[i]; - - if (placeholder_id == cache->id) { - found_cache = cache; - break; - } - } - - if (found_cache) { - /* Flush and close */ - fflush(found_cache->handle); - fclose(found_cache->handle); - std::fill(found_cache->id.uuid, found_cache->id.uuid + sizeof(PlaceHolderId), 0); - } - } - - this->GetPlaceHolderPath(placeholder_path_out, placeholder_id); + void PlaceHolderAccessor::GetPath(char* placeholder_path_out, PlaceHolderId placeholder_id) { + std::scoped_lock lock(this->cache_mutex); + CacheEntry* entry = this->FindInCache(placeholder_id); + this->Invalidate(entry); + this->MakePath(placeholder_path_out, placeholder_id); } Result PlaceHolderAccessor::Create(PlaceHolderId placeholder_id, size_t size) { char placeholder_path[FS_MAX_PATH] = {0}; this->EnsureRecursively(placeholder_id); - this->GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->GetPath(placeholder_path, placeholder_id); debug::DebugLog("Creating %s\n", placeholder_path); R_TRY_CATCH(fsdevCreateFile(placeholder_path, size, FS_CREATE_BIG_FILE)) { @@ -79,7 +141,7 @@ namespace sts::ncm::impl { Result PlaceHolderAccessor::Delete(PlaceHolderId placeholder_id) { char placeholder_path[FS_MAX_PATH] = {0}; - this->GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->GetPath(placeholder_path, placeholder_id); debug::DebugLog("Deleting %s\n", placeholder_path); if (std::remove(placeholder_path) != 0) { @@ -93,24 +155,26 @@ namespace sts::ncm::impl { return ResultSuccess; } - Result PlaceHolderAccessor::Open(FILE** out_handle, PlaceHolderId placeholder_id) { - if (this->LoadFromCache(out_handle, placeholder_id)) { - return ResultSuccess; - } - char placeholder_path[FS_MAX_PATH] = {0}; - - this->GetPlaceHolderPath(placeholder_path, placeholder_id); - + Result PlaceHolderAccessor::Write(PlaceHolderId placeholder_id, size_t offset, const void* buffer, size_t size) { FILE* f = nullptr; - R_TRY(OpenFile(&f, placeholder_path, FS_OPEN_WRITE)); - *out_handle = f; + R_TRY_CATCH(this->Open(&f, placeholder_id)) { + R_CATCH(ResultFsPathNotFound) { + return ResultNcmPlaceHolderNotFound; + } + } R_END_TRY_CATCH; + + ON_SCOPE_EXIT { + this->StoreToCache(f, placeholder_id); + }; + + R_TRY(WriteFile(f, offset, buffer, size, !this->delay_flush)); return ResultSuccess; } Result PlaceHolderAccessor::SetSize(PlaceHolderId placeholder_id, size_t size) { char placeholder_path[FS_MAX_PATH] = {0}; - this->GetPlaceHolderPath(placeholder_path, placeholder_id); + this->MakePath(placeholder_path, placeholder_id); if (truncate(placeholder_path, size) == -1) { R_TRY_CATCH(fsdevGetLastResult()) { R_CATCH(ResultFsPathNotFound) { @@ -127,7 +191,7 @@ namespace sts::ncm::impl { /* Set the scope for the scoped_lock. */ { - std::scoped_lock lock(this->cache_mutex); + std::scoped_lock lock(this->cache_mutex); if (placeholder_id == InvalidUuid) { *found_in_cache = false; @@ -162,73 +226,15 @@ namespace sts::ncm::impl { Result PlaceHolderAccessor::EnsureRecursively(PlaceHolderId placeholder_id) { char placeholder_path[FS_MAX_PATH] = {0}; - this->GetPlaceHolderPath(placeholder_path, placeholder_id); + this->MakePath(placeholder_path, placeholder_id); R_TRY(EnsureParentDirectoryRecursively(placeholder_path)); return ResultSuccess; } - bool PlaceHolderAccessor::LoadFromCache(FILE** out_handle, PlaceHolderId placeholder_id) { - std::scoped_lock lk(this->cache_mutex); - CacheEntry *entry = this->FindInCache(placeholder_id); - if (entry == nullptr) { - return false; - } - entry->id = InvalidUuid; - *out_handle = entry->handle; - return true; - } - - PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::FindInCache(PlaceHolderId placeholder_id) { - if (placeholder_id == InvalidUuid) { - return nullptr; - } - for (size_t i = 0; i < MaxCaches; i++) { - if (placeholder_id == this->caches[i].id) { - return &this->caches[i]; - } - } - return nullptr; - } - - void PlaceHolderAccessor::StoreToCache(FILE* handle, PlaceHolderId placeholder_id) { - std::scoped_lock lk(this->cache_mutex); - CacheEntry* cache = nullptr; - - /* Find an empty cache */ - for (size_t i = 0; i < MaxCaches; i++) { - if (placeholder_id == InvalidUuid) { - cache = &this->caches[i]; - break; - } - } - - /* No empty caches found. Let's clear cache 0. */ - if (cache == nullptr) { - cache = &this->caches[0]; - - /* Flush and close any handles. */ - if (cache->handle) { - fflush(cache->handle); - fclose(cache->handle); - } - cache->id = InvalidUuid; - } - - /* Cache the new placeholder id and its file handle. */ - cache->id = placeholder_id; - cache->handle = handle; - cache->counter = this->cur_counter; - this->cur_counter++; - } - - void PlaceHolderAccessor::ClearAllCaches() { - for (size_t i = 0; i < MaxCaches; i++) { - CacheEntry* cache = &this->caches[i]; - - if (cache->id != InvalidUuid) { - fflush(cache->handle); - fclose(cache->handle); - cache->id = InvalidUuid; + void PlaceHolderAccessor::InvalidateAll() { + for (auto &entry : this->caches) { + if (entry.id != InvalidUuid) { + this->Invalidate(&entry); } } } diff --git a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.hpp b/stratosphere/ncm/source/impl/ncm_placeholder_accessor.hpp index 07ece0506..acaed14ce 100644 --- a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.hpp +++ b/stratosphere/ncm/source/impl/ncm_placeholder_accessor.hpp @@ -24,53 +24,56 @@ namespace sts::ncm::impl { class PlaceHolderAccessor { - public: + private: class CacheEntry { public: PlaceHolderId id; FILE* handle; u64 counter; }; - - public: + private: static constexpr size_t MaxCaches = 0x2; - CacheEntry caches[MaxCaches]; + std::array caches; char* root_path; u64 cur_counter; HosMutex cache_mutex; MakePlaceHolderPathFunc make_placeholder_path_func; bool delay_flush; - + private: + Result Open(FILE** out_handle, PlaceHolderId placeholder_id); + CacheEntry *FindInCache(PlaceHolderId placeholder_id); + bool LoadFromCache(FILE** out_handle, PlaceHolderId placeholder_id); + CacheEntry *GetFreeEntry(); + void StoreToCache(FILE* handle, PlaceHolderId placeholder_id); + void Invalidate(CacheEntry *entry); + public: PlaceHolderAccessor() : cur_counter(0), delay_flush(false) { for (size_t i = 0; i < MaxCaches; i++) { caches[i].id = InvalidUuid; } } - inline void GetPlaceHolderRootPath(char* out_placeholder_root) { + inline void MakeRootPath(char* out_placeholder_root) { path::GetPlaceHolderRootPath(out_placeholder_root, this->root_path); } - inline void GetPlaceHolderPath(char* out_placeholder_path, PlaceHolderId placeholder_id) { + inline void MakePath(char* out_placeholder_path, PlaceHolderId placeholder_id) { char placeholder_root_path[FS_MAX_PATH] = {0}; - this->GetPlaceHolderRootPath(placeholder_root_path); + this->MakeRootPath(placeholder_root_path); this->make_placeholder_path_func(out_placeholder_path, placeholder_id, placeholder_root_path); } + void Initialize(char* root, MakePlaceHolderPathFunc path_func, bool delay_flush); unsigned int GetDirectoryDepth(); - void GetPlaceHolderPathUncached(char* out_placeholder_path, PlaceHolderId placeholder_id); + void GetPath(char* out_placeholder_path, PlaceHolderId placeholder_id); Result Create(PlaceHolderId placeholder_id, size_t size); Result Delete(PlaceHolderId placeholder_id); - Result Open(FILE** out_handle, PlaceHolderId placeholder_id); + Result Write(PlaceHolderId placeholder_id, size_t offset, const void* buffer, size_t size); Result SetSize(PlaceHolderId placeholder_id, size_t size); Result GetSize(bool* found_in_cache, size_t* out_size, PlaceHolderId placeholder_id); Result EnsureRecursively(PlaceHolderId placeholder_id); - - CacheEntry *FindInCache(PlaceHolderId placeholder_id); - bool LoadFromCache(FILE** out_handle, PlaceHolderId placeholder_id); - void StoreToCache(FILE* handle, PlaceHolderId placeholder_id); - void ClearAllCaches(); + void InvalidateAll(); }; } \ No newline at end of file diff --git a/stratosphere/ncm/source/ncm_contentstorage.cpp b/stratosphere/ncm/source/ncm_contentstorage.cpp index 3a698a9ba..8df832ff2 100644 --- a/stratosphere/ncm/source/ncm_contentstorage.cpp +++ b/stratosphere/ncm/source/ncm_contentstorage.cpp @@ -39,15 +39,13 @@ namespace sts::ncm { strncpy(this->root_path, root_path, FS_MAX_PATH-2); this->make_content_path_func = *content_path_func; - this->placeholder_accessor.root_path = this->root_path; - this->placeholder_accessor.make_placeholder_path_func = *placeholder_path_func; - this->placeholder_accessor.delay_flush = delay_flush; + this->placeholder_accessor.Initialize(this->root_path, *placeholder_path_func, delay_flush); return ResultSuccess; } void ContentStorageInterface::Finalize() { this->ClearContentCache(); - this->placeholder_accessor.ClearAllCaches(); + this->placeholder_accessor.InvalidateAll(); } void ContentStorageInterface::ClearContentCache() { @@ -126,7 +124,7 @@ namespace sts::ncm { R_TRY(this->EnsureEnabled()); char placeholder_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.GetPlaceHolderPath(placeholder_path, placeholder_id); + this->placeholder_accessor.MakePath(placeholder_path, placeholder_id); bool has = false; R_TRY(HasFile(&has, placeholder_path)); @@ -144,21 +142,7 @@ namespace sts::ncm { } R_TRY(this->EnsureEnabled()); - - FILE* f = nullptr; - - R_TRY_CATCH(this->placeholder_accessor.Open(&f, placeholder_id)) { - R_CATCH(ResultFsPathNotFound) { - return ResultNcmPlaceHolderNotFound; - } - } R_END_TRY_CATCH; - - ON_SCOPE_EXIT { - this->placeholder_accessor.StoreToCache(f, placeholder_id); - }; - - R_TRY(WriteFile(f, offset, data.buffer, data.num_elements, !this->placeholder_accessor.delay_flush)); - + R_TRY(this->placeholder_accessor.Write(placeholder_id, offset, data.buffer, data.num_elements)); return ResultSuccess; R_DEBUG_END } @@ -171,7 +155,7 @@ namespace sts::ncm { char placeholder_path[FS_MAX_PATH] = {0}; char content_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->placeholder_accessor.GetPath(placeholder_path, placeholder_id); this->GetContentPath(content_path, content_id); if (rename(placeholder_path, content_path) != 0) { @@ -244,7 +228,7 @@ namespace sts::ncm { char placeholder_path[FS_MAX_PATH] = {0}; char common_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->placeholder_accessor.GetPath(placeholder_path, placeholder_id); R_TRY(ConvertToFsCommonPath(common_path, FS_MAX_PATH-1, placeholder_path)); *out.pointer = common_path; return ResultSuccess; @@ -256,8 +240,8 @@ namespace sts::ncm { R_TRY(this->EnsureEnabled()); char placeholder_root_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.ClearAllCaches(); - this->placeholder_accessor.GetPlaceHolderRootPath(placeholder_root_path); + this->placeholder_accessor.InvalidateAll(); + this->placeholder_accessor.MakeRootPath(placeholder_root_path); /* Nintendo uses CleanDirectoryRecursively which is 3.0.0+. We'll just delete the directory and recreate it to support all firmwares. */ @@ -276,7 +260,7 @@ namespace sts::ncm { R_TRY(this->EnsureEnabled()); char placeholder_root_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.GetPlaceHolderRootPath(placeholder_root_path); + this->placeholder_accessor.MakeRootPath(placeholder_root_path); const unsigned int dir_depth = this->placeholder_accessor.GetDirectoryDepth(); size_t entry_count = 0; @@ -403,7 +387,7 @@ namespace sts::ncm { R_DEBUG_START this->disabled = true; this->ClearContentCache(); - this->placeholder_accessor.ClearAllCaches(); + this->placeholder_accessor.InvalidateAll(); return ResultSuccess; R_DEBUG_END } @@ -423,7 +407,7 @@ namespace sts::ncm { R_TRY(EnsureParentDirectoryRecursively(new_content_path)); R_TRY(this->placeholder_accessor.EnsureRecursively(placeholder_id)); - this->placeholder_accessor.GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->placeholder_accessor.GetPath(placeholder_path, placeholder_id); if (rename(old_content_path, placeholder_path) != 0) { R_TRY_CATCH(fsdevGetLastResult()) { R_CATCH(ResultFsPathNotFound) { @@ -474,7 +458,7 @@ namespace sts::ncm { char placeholder_path[FS_MAX_PATH] = {0}; char common_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->placeholder_accessor.GetPath(placeholder_path, placeholder_id); R_TRY(ConvertToFsCommonPath(common_path, FS_MAX_PATH-1, placeholder_path)); R_TRY(fsGetRightsIdAndKeyGenerationByPath(common_path, &key_generation, &rights_id)); @@ -607,7 +591,7 @@ namespace sts::ncm { Result ContentStorageInterface::FlushPlaceHolder() { R_DEBUG_START - this->placeholder_accessor.ClearAllCaches(); + this->placeholder_accessor.InvalidateAll(); return ResultSuccess; R_DEBUG_END } @@ -629,7 +613,7 @@ namespace sts::ncm { char placeholder_path[FS_MAX_PATH] = {0}; struct stat st; - this->placeholder_accessor.GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->placeholder_accessor.GetPath(placeholder_path, placeholder_id); if (stat(placeholder_path, &st) == -1) { return fsdevGetLastResult(); } @@ -662,8 +646,8 @@ namespace sts::ncm { R_TRY(TraverseDirectory(content_root_path, dir_depth, fix_file_attributes)); char placeholder_root_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.ClearAllCaches(); - this->placeholder_accessor.GetPlaceHolderRootPath(placeholder_root_path); + this->placeholder_accessor.InvalidateAll(); + this->placeholder_accessor.MakeRootPath(placeholder_root_path); dir_depth = this->placeholder_accessor.GetDirectoryDepth(); R_TRY(TraverseDirectory(placeholder_root_path, dir_depth, fix_file_attributes)); @@ -699,7 +683,7 @@ namespace sts::ncm { u8 key_generation = 0; char placeholder_path[FS_MAX_PATH] = {0}; char common_path[FS_MAX_PATH] = {0}; - this->placeholder_accessor.GetPlaceHolderPathUncached(placeholder_path, placeholder_id); + this->placeholder_accessor.GetPath(placeholder_path, placeholder_id); R_TRY(ConvertToFsCommonPath(common_path, FS_MAX_PATH-1, placeholder_path)); R_TRY(fsGetRightsIdAndKeyGenerationByPath(common_path, &key_generation, &rights_id));