diff --git a/.gitignore b/.gitignore index 66c6f9308..6b4b5309f 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ dkms.conf # Distribution files *.tgz *.zip +*.bz2 # IDA binaries *.id0 diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index 1ed062124..fe2fc2aca 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_filesystem_utils.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_filesystem_utils.hpp new file mode 100644 index 000000000..5460deade --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_filesystem_utils.hpp @@ -0,0 +1,29 @@ +/* + * 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 . + */ +#pragma once +#include "fs_common.hpp" +#include "fs_filesystem.hpp" + +namespace ams::fs { + + /* Common utilities. */ + Result EnsureDirectoryRecursively(const char *path); + Result EnsureParentDirectoryRecursively(const char *path); + + Result HasFile(bool *out, const char *path); + Result HasDirectory(bool *out, const char *path); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp index ce71ebc95..2e9a8b48c 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp @@ -142,7 +142,8 @@ namespace ams::fssystem { }; /* Other utility. */ - Result EnsureDirectoryExistsRecursively(fs::fsa::IFileSystem *fs, const char *path); + Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); + Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); template NX_INLINE Result RetryFinitelyForTargetLocked(F f) { diff --git a/libraries/libstratosphere/source/fs/fs_filesystem_utils.cpp b/libraries/libstratosphere/source/fs/fs_filesystem_utils.cpp new file mode 100644 index 000000000..04b60e123 --- /dev/null +++ b/libraries/libstratosphere/source/fs/fs_filesystem_utils.cpp @@ -0,0 +1,70 @@ +/* + * 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 . + */ +#include +#include "fsa/fs_mount_utils.hpp" +#include "fsa/fs_filesystem_accessor.hpp" + +namespace ams::fs { + + namespace { + + Result HasEntry(bool *out, const char *path, fs::DirectoryEntryType type) { + /* Set out to false initially. */ + *out = false; + + /* Try to get the entry type. */ + fs::DirectoryEntryType entry_type; + R_TRY_CATCH(fs::GetEntryType(std::addressof(entry_type), path)) { + /* If the path doesn't exist, nothing has gone wrong. */ + R_CONVERT(fs::ResultPathNotFound, ResultSuccess()); + } R_END_TRY_CATCH; + + /* We succeeded. */ + *out = entry_type == type; + return ResultSuccess(); + } + + } + + Result EnsureDirectoryRecursively(const char *path) { + /* Get the filesystem accessor and sub path. */ + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + /* Use the system implementation. */ + return fssystem::EnsureDirectoryRecursively(accessor->GetRawFileSystemUnsafe(), sub_path); + } + + Result EnsureParentDirectoryRecursively(const char *path) { + /* Get the filesystem accessor and sub path. */ + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + /* Use the system implementation. */ + return fssystem::EnsureParentDirectoryRecursively(accessor->GetRawFileSystemUnsafe(), sub_path); + } + + Result HasFile(bool *out, const char *path) { + return HasEntry(out, path, fs::DirectoryEntryType_File); + } + + Result HasDirectory(bool *out, const char *path) { + return HasEntry(out, path, fs::DirectoryEntryType_Directory); + } + +} diff --git a/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp index 958d8cd78..5302af4a3 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_filesystem_accessor.hpp @@ -86,6 +86,10 @@ namespace ams::fs::impl { } std::shared_ptr GetMultiCommitTarget(); + + fsa::IFileSystem *GetRawFileSystemUnsafe() { + return this->impl.get(); + } private: void NotifyCloseFile(FileAccessor *f); void NotifyCloseDirectory(DirectoryAccessor *d); diff --git a/libraries/libstratosphere/source/fssystem/fssystem_utility.cpp b/libraries/libstratosphere/source/fssystem/fssystem_utility.cpp index da2dde88a..3612875f0 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_utility.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_utility.cpp @@ -19,7 +19,7 @@ namespace ams::fssystem { namespace { - inline Result EnsureDirectoryExists(fs::fsa::IFileSystem *fs, const char *path) { + inline Result EnsureDirectory(fs::fsa::IFileSystem *fs, const char *path) { R_TRY_CATCH(fs->CreateDirectory(path)) { R_CATCH(fs::ResultPathAlreadyExists) { /* If path already exists, there's no problem. */ } } R_END_TRY_CATCH; @@ -27,6 +27,30 @@ namespace ams::fssystem { return ResultSuccess(); } + Result EnsureDirectoryRecursivelyImpl(fs::fsa::IFileSystem *fs, const char *path, bool create_last) { + /* Normalize the path. */ + char normalized_path[fs::EntryNameLengthMax + 1]; + size_t normalized_path_len; + R_TRY(PathTool::Normalize(normalized_path, &normalized_path_len, path, sizeof(normalized_path))); + + /* Repeatedly call CreateDirectory on each directory leading to the target. */ + for (size_t i = 1; i < normalized_path_len; i++) { + /* If we detect a separator, create the directory. */ + if (PathTool::IsSeparator(normalized_path[i])) { + normalized_path[i] = StringTraits::NullTerminator; + R_TRY(EnsureDirectory(fs, normalized_path)); + normalized_path[i] = StringTraits::DirectorySeparator; + } + } + + /* Create the last directory if requested. */ + if (create_last) { + R_TRY(EnsureDirectory(fs, normalized_path)); + } + + return ResultSuccess(); + } + } Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *entry, void *work_buf, size_t work_buf_size) { @@ -93,26 +117,12 @@ namespace ams::fssystem { ); } - Result EnsureDirectoryExistsRecursively(fs::fsa::IFileSystem *fs, const char *path) { - /* Normalize the path. */ - char normalized_path[fs::EntryNameLengthMax + 1]; - size_t normalized_path_len; - R_TRY(PathTool::Normalize(normalized_path, &normalized_path_len, path, sizeof(normalized_path))); + Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path) { + return EnsureDirectoryRecursivelyImpl(fs, path, true); + } - /* Repeatedly call CreateDirectory on each directory leading to the target. */ - for (size_t i = 1; i < normalized_path_len; i++) { - /* If we detect a separator, create the directory. */ - if (PathTool::IsSeparator(normalized_path[i])) { - normalized_path[i] = StringTraits::NullTerminator; - R_TRY(EnsureDirectoryExists(fs, normalized_path)); - normalized_path[i] = StringTraits::DirectorySeparator; - } - } - - /* Call CreateDirectory on the final path. */ - R_TRY(EnsureDirectoryExists(fs, normalized_path)); - - return ResultSuccess(); + Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path) { + return EnsureDirectoryRecursivelyImpl(fs, path, false); } } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp index d9d35d7a7..d6ab4eaed 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp @@ -260,7 +260,7 @@ namespace ams::ncm { ON_SCOPE_EXIT { fs::Unmount(root->mount_name); }; /* Ensure the path exists for us to import to. */ - R_TRY(impl::EnsureDirectoryRecursively(root->path)); + R_TRY(fs::EnsureDirectoryRecursively(root->path)); /* Copy the file from bis to our save. */ R_TRY(impl::CopyFile(savedata_db_path, bis_db_path)); @@ -389,7 +389,7 @@ namespace ams::ncm { ON_SCOPE_EXIT { fs::Unmount(root->mount_name); }; /* Ensure the content storage root's path exists. */ - R_TRY(impl::EnsureDirectoryRecursively(root->path)); + R_TRY(fs::EnsureDirectoryRecursively(root->path)); /* Initialize content and placeholder directories for the root. */ return ContentStorageImpl::InitializeBase(root->path); @@ -409,7 +409,7 @@ namespace ams::ncm { ON_SCOPE_EXIT { fs::Unmount(root->mount_name); }; /* Ensure the content meta database root's path exists. */ - R_TRY(impl::EnsureDirectoryRecursively(root->path)); + R_TRY(fs::EnsureDirectoryRecursively(root->path)); /* Commit our changes. */ return fs::CommitSaveData(root->mount_name); @@ -454,7 +454,7 @@ namespace ams::ncm { /* Ensure the root path exists. */ bool has_dir = false; - R_TRY(impl::HasDirectory(&has_dir, root->path)); + R_TRY(fs::HasDirectory(&has_dir, root->path)); R_UNLESS(has_dir, ncm::ResultInvalidContentMetaDatabase()); return ResultSuccess(); diff --git a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp index 8c4bfe113..8b820c2a2 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp @@ -36,7 +36,7 @@ namespace ams::ncm { Result EnsureContentDirectory(ContentId id, MakeContentPathFunction func, const char *root_path) { PathString path; MakeContentPath(std::addressof(path), id, func, root_path); - return impl::EnsureParentDirectoryRecursively(path); + return fs::EnsureParentDirectoryRecursively(path); } Result DeleteContentFile(ContentId id, MakeContentPathFunction func, const char *root_path) { @@ -175,11 +175,11 @@ namespace ams::ncm { /* Create the content directory. */ MakeBaseContentDirectoryPath(std::addressof(path), root_path); - R_TRY(impl::EnsureDirectoryRecursively(path)); + R_TRY(fs::EnsureDirectoryRecursively(path)); /* Create the placeholder directory. */ PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path); - return impl::EnsureDirectoryRecursively(path); + return fs::EnsureDirectoryRecursively(path); } Result ContentStorageImpl::CleanupBase(const char *root_path) { @@ -199,18 +199,18 @@ namespace ams::ncm { /* Check if root directory exists. */ bool has_dir; - R_TRY(impl::HasDirectory(std::addressof(has_dir), root_path)); + R_TRY(fs::HasDirectory(std::addressof(has_dir), root_path)); R_UNLESS(has_dir, ncm::ResultContentStorageBaseNotFound()); /* Check if content directory exists. */ bool has_registered; MakeBaseContentDirectoryPath(std::addressof(path), root_path); - R_TRY(impl::HasDirectory(std::addressof(has_registered), path)); + R_TRY(fs::HasDirectory(std::addressof(has_registered), path)); /* Check if placeholder directory exists. */ bool has_placeholder; PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path); - R_TRY(impl::HasDirectory(std::addressof(has_placeholder), path)); + R_TRY(fs::HasDirectory(std::addressof(has_placeholder), path)); /* Convert findings to results. */ R_UNLESS(has_registered || has_placeholder, ncm::ResultContentStorageBaseNotFound()); @@ -287,7 +287,7 @@ namespace ams::ncm { /* Check if placeholder file exists. */ bool has = false; - R_TRY(impl::HasFile(&has, placeholder_path)); + R_TRY(fs::HasFile(&has, placeholder_path)); out.SetValue(has); return ResultSuccess(); } @@ -335,7 +335,7 @@ namespace ams::ncm { /* Check if the content file exists. */ bool has = false; - R_TRY(impl::HasFile(&has, content_path)); + R_TRY(fs::HasFile(&has, content_path)); out.SetValue(has); return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp index 56881b558..f73698689 100644 --- a/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_fs_utils.cpp @@ -20,78 +20,10 @@ namespace ams::ncm::impl { namespace { - Result EnsureDirectory(const char *path) { - /* Create the path, and allow it to already exist. */ - R_TRY_CATCH(fs::CreateDirectory(path)) { - R_CONVERT(fs::ResultPathAlreadyExists, ResultSuccess()) - } R_END_TRY_CATCH; - - return ResultSuccess(); - } - - Result EnsureDirectoryRecursivelyImpl(const char *path, bool create_last) { - char work_buf[fs::EntryNameLengthMax]; - - /* Ensure the path is not too long. */ - const size_t len = std::strlen(path); - R_UNLESS(len + 1 <= sizeof(work_buf), ResultAllocationFailed()); - - /* Copy in the path. */ - std::strncpy(work_buf, path, sizeof(work_buf)); - - /* Create all but the last directory. */ - for (size_t i = 0; i < len; i++) { - if (i > 0 && fs::PathTool::IsSeparator(work_buf[i]) && !fs::PathTool::IsDriveSeparator(work_buf[i-1])) { - work_buf[i] = fs::StringTraits::NullTerminator; - R_TRY(EnsureDirectory(work_buf)); - work_buf[i] = fs::StringTraits::DirectorySeparator; - } - } - - /* Create the last directory if requested. */ - if (create_last) { - R_TRY(EnsureDirectory(path)); - } - - return ResultSuccess(); - } - - Result HasEntry(bool *out, const char *path, fs::DirectoryEntryType type) { - /* Set out to false initially. */ - *out = false; - - /* Try to get the entry type. */ - fs::DirectoryEntryType entry_type; - R_TRY_CATCH(fs::GetEntryType(std::addressof(entry_type), path)) { - /* If the path doesn't exist, nothing has gone wrong. */ - R_CONVERT(fs::ResultPathNotFound, ResultSuccess()); - } R_END_TRY_CATCH; - - /* We succeeded. */ - *out = entry_type == type; - return ResultSuccess(); - } - std::atomic g_mount_name_count; } - Result HasFile(bool *out, const char *path) { - return HasEntry(out, path, fs::DirectoryEntryType_File); - } - - Result HasDirectory(bool *out, const char *path) { - return HasEntry(out, path, fs::DirectoryEntryType_Directory); - } - - Result EnsureDirectoryRecursively(const char *path) { - return EnsureDirectoryRecursivelyImpl(path, true); - } - - Result EnsureParentDirectoryRecursively(const char *path) { - return EnsureDirectoryRecursivelyImpl(path, false); - } - bool PathView::HasPrefix(std::string_view prefix) const { return this->path.compare(0, prefix.length(), prefix) == 0; } diff --git a/libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp b/libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp index 438b2bde2..d6c4dbf2f 100644 --- a/libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_fs_utils.hpp @@ -18,12 +18,6 @@ namespace ams::ncm::impl { - Result HasFile(bool *out, const char *path); - Result HasDirectory(bool *out, const char *path); - - Result EnsureDirectoryRecursively(const char *path); - Result EnsureParentDirectoryRecursively(const char *path); - Result CopyFile(const char *dst_path, const char *src_path); class PathView { diff --git a/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp index e16771172..9ed264566 100644 --- a/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_placeholder_accessor.cpp @@ -59,7 +59,7 @@ namespace ams::ncm { Result PlaceHolderAccessor::EnsurePlaceHolderDirectory(PlaceHolderId placeholder_id) { PathString path; this->MakePath(std::addressof(path), placeholder_id); - return impl::EnsureParentDirectoryRecursively(path); + return fs::EnsureParentDirectoryRecursively(path); } Result PlaceHolderAccessor::GetPlaceHolderIdFromFileName(PlaceHolderId *out, const char *name) { diff --git a/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp index c54505409..11dbadf25 100644 --- a/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_read_only_content_storage_impl.cpp @@ -97,12 +97,12 @@ namespace ams::ncm { /* Check if the file exists. */ bool has; - R_TRY(impl::HasFile(std::addressof(has), content_path)); + R_TRY(fs::HasFile(std::addressof(has), content_path)); /* If the file is absent, make the path for game card content meta and check presence again. */ if (!has) { MakeGameCardContentMetaPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path); - R_TRY(impl::HasFile(std::addressof(has), content_path)); + R_TRY(fs::HasFile(std::addressof(has), content_path)); } out.SetValue(has); @@ -118,7 +118,7 @@ namespace ams::ncm { /* Check if the file exists. */ bool has_file; - R_TRY(impl::HasFile(std::addressof(has_file), content_path)); + R_TRY(fs::HasFile(std::addressof(has_file), content_path)); /* If the file is absent, make the path for regular content. */ if (!has_file) { diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index 66ca4fc2b..ec5b4727e 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -48,11 +48,8 @@ namespace ams::creport { } void TryCreateReportDirectories() { - mkdir("sdmc:/atmosphere", S_IRWXU); - mkdir("sdmc:/atmosphere/crash_reports", S_IRWXU); - mkdir("sdmc:/atmosphere/crash_reports/dumps", S_IRWXU); - mkdir("sdmc:/atmosphere/fatal_reports", S_IRWXU); - mkdir("sdmc:/atmosphere/fatal_reports/dumps", S_IRWXU); + fs::EnsureDirectoryRecursively("sdmc:/atmosphere/crash_reports/dumps"); + fs::EnsureDirectoryRecursively("sdmc:/atmosphere/fatal_reports/dumps"); } constexpr const char *GetDebugExceptionString(const svc::DebugException type) { @@ -281,90 +278,90 @@ namespace ams::creport { /* Save files. */ { - char file_path[FS_MAX_PATH]; + char file_path[fs::EntryNameLengthMax + 1]; /* Save crash report. */ std::snprintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/%011lu_%016lx.log", timestamp, this->process_info.program_id); - FILE *fp = fopen(file_path, "w"); - if (fp != nullptr) { - this->SaveToFile(fp); - fclose(fp); - fp = nullptr; + { + ScopedFile file(file_path); + if (file.IsOpen()) { + this->SaveToFile(file); + } } /* Dump threads. */ std::snprintf(file_path, sizeof(file_path), "sdmc:/atmosphere/crash_reports/dumps/%011lu_%016lx_thread_info.bin", timestamp, this->process_info.program_id); - fp = fopen(file_path, "wb"); - if (fp != nullptr) { - this->thread_list.DumpBinary(fp, this->crashed_thread.GetThreadId()); - fclose(fp); - fp = nullptr; + { + ScopedFile file(file_path); + if (file.IsOpen()) { + this->thread_list.DumpBinary(file, this->crashed_thread.GetThreadId()); + } } } } - void CrashReport::SaveToFile(FILE *f_report) { - fprintf(f_report, "Atmosphère Crash Report (v1.4):\n"); - fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->result.GetValue(), this->result.GetModule(), this->result.GetDescription()); + void CrashReport::SaveToFile(ScopedFile &file) { + file.WriteFormat("Atmosphère Crash Report (v1.5):\n"); + file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", this->result.GetValue(), this->result.GetModule(), this->result.GetDescription()); /* Process Info. */ char name_buf[0x10] = {}; static_assert(sizeof(name_buf) >= sizeof(this->process_info.name), "buffer overflow!"); std::memcpy(name_buf, this->process_info.name, sizeof(this->process_info.name)); - fprintf(f_report, "Process Info:\n"); - fprintf(f_report, " Process Name: %s\n", name_buf); - fprintf(f_report, " Program ID: %016lx\n", this->process_info.program_id); - fprintf(f_report, " Process ID: %016lx\n", this->process_info.process_id); - fprintf(f_report, " Process Flags: %08x\n", this->process_info.flags); + file.WriteFormat("Process Info:\n"); + file.WriteFormat(" Process Name: %s\n", name_buf); + file.WriteFormat(" Program ID: %016lx\n", this->process_info.program_id); + file.WriteFormat(" Process ID: %016lx\n", this->process_info.process_id); + file.WriteFormat(" Process Flags: %08x\n", this->process_info.flags); if (hos::GetVersion() >= hos::Version_500) { - fprintf(f_report, " User Exception Address: %s\n", this->module_list.GetFormattedAddressString(this->process_info.user_exception_context_address)); + file.WriteFormat(" User Exception Address: %s\n", this->module_list.GetFormattedAddressString(this->process_info.user_exception_context_address)); } /* Exception Info. */ - fprintf(f_report, "Exception Info:\n"); - fprintf(f_report, " Type: %s\n", GetDebugExceptionString(this->exception_info.type)); - fprintf(f_report, " Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.address)); + file.WriteFormat("Exception Info:\n"); + file.WriteFormat(" Type: %s\n", GetDebugExceptionString(this->exception_info.type)); + file.WriteFormat(" Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.address)); switch (this->exception_info.type) { case svc::DebugException_UndefinedInstruction: - fprintf(f_report, " Opcode: %08x\n", this->exception_info.specific.undefined_instruction.insn); + file.WriteFormat(" Opcode: %08x\n", this->exception_info.specific.undefined_instruction.insn); break; case svc::DebugException_DataAbort: case svc::DebugException_AlignmentFault: if (this->exception_info.specific.raw != this->exception_info.address) { - fprintf(f_report, " Fault Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.specific.raw)); + file.WriteFormat(" Fault Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.specific.raw)); } break; case svc::DebugException_UndefinedSystemCall: - fprintf(f_report, " Svc Id: 0x%02x\n", this->exception_info.specific.undefined_system_call.id); + file.WriteFormat(" Svc Id: 0x%02x\n", this->exception_info.specific.undefined_system_call.id); break; case svc::DebugException_UserBreak: - fprintf(f_report, " Break Reason: 0x%x\n", this->exception_info.specific.user_break.break_reason); - fprintf(f_report, " Break Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.specific.user_break.address)); - fprintf(f_report, " Break Size: 0x%lx\n", this->exception_info.specific.user_break.size); + file.WriteFormat(" Break Reason: 0x%x\n", this->exception_info.specific.user_break.break_reason); + file.WriteFormat(" Break Address: %s\n", this->module_list.GetFormattedAddressString(this->exception_info.specific.user_break.address)); + file.WriteFormat(" Break Size: 0x%lx\n", this->exception_info.specific.user_break.size); break; default: break; } /* Crashed Thread Info. */ - fprintf(f_report, "Crashed Thread Info:\n"); - this->crashed_thread.SaveToFile(f_report); + file.WriteFormat("Crashed Thread Info:\n"); + this->crashed_thread.SaveToFile(file); /* Dying Message. */ if (hos::GetVersion() >= hos::Version_500 && this->dying_message_size != 0) { - fprintf(f_report, "Dying Message Info:\n"); - fprintf(f_report, " Address: 0x%s\n", this->module_list.GetFormattedAddressString(this->dying_message_address)); - fprintf(f_report, " Size: 0x%016lx\n", this->dying_message_size); - DumpMemoryHexToFile(f_report, " Dying Message: ", this->dying_message, this->dying_message_size); + file.WriteFormat("Dying Message Info:\n"); + file.WriteFormat(" Address: 0x%s\n", this->module_list.GetFormattedAddressString(this->dying_message_address)); + file.WriteFormat(" Size: 0x%016lx\n", this->dying_message_size); + file.DumpMemory( " Dying Message: ", this->dying_message, this->dying_message_size); } /* Module Info. */ - fprintf(f_report, "Module Info:\n"); - this->module_list.SaveToFile(f_report); + file.WriteFormat("Module Info:\n"); + this->module_list.SaveToFile(file); /* Thread Info. */ - fprintf(f_report, "\nThread Report:\n"); - this->thread_list.SaveToFile(f_report); + file.WriteFormat("\nThread Report:\n"); + this->thread_list.SaveToFile(file); } } diff --git a/stratosphere/creport/source/creport_crash_report.hpp b/stratosphere/creport/source/creport_crash_report.hpp index ac2bbad35..8f444a0f5 100644 --- a/stratosphere/creport/source/creport_crash_report.hpp +++ b/stratosphere/creport/source/creport_crash_report.hpp @@ -90,7 +90,7 @@ namespace ams::creport { void HandleDebugEventInfoAttachThread(const svc::DebugEventInfo &d); void HandleDebugEventInfoException(const svc::DebugEventInfo &d); - void SaveToFile(FILE *f_report); + void SaveToFile(ScopedFile &file); }; } diff --git a/stratosphere/creport/source/creport_main.cpp b/stratosphere/creport/source/creport_main.cpp index 61d935512..127dc9dd5 100644 --- a/stratosphere/creport/source/creport_main.cpp +++ b/stratosphere/creport/source/creport_main.cpp @@ -75,12 +75,11 @@ void __appInit(void) { R_ABORT_UNLESS(fsInitialize()); }); - R_ABORT_UNLESS(fsdevMountSdmc()); + R_ABORT_UNLESS(fs::MountSdCard("sdmc")); } void __appExit(void) { /* Cleanup services. */ - fsdevUnmountAll(); fsExit(); } diff --git a/stratosphere/creport/source/creport_modules.cpp b/stratosphere/creport/source/creport_modules.cpp index ca7093afa..87393ed9c 100644 --- a/stratosphere/creport/source/creport_modules.cpp +++ b/stratosphere/creport/source/creport_modules.cpp @@ -44,16 +44,16 @@ namespace ams::creport { } - void ModuleList::SaveToFile(FILE *f_report) { - fprintf(f_report, " Number of Modules: %zu\n", this->num_modules); + void ModuleList::SaveToFile(ScopedFile &file) { + file.WriteFormat(" Number of Modules: %zu\n", this->num_modules); for (size_t i = 0; i < this->num_modules; i++) { const auto& module = this->modules[i]; - fprintf(f_report, " Module %02zu:\n", i); - fprintf(f_report, " Address: %016lx-%016lx\n", module.start_address, module.end_address); + file.WriteFormat(" Module %02zu:\n", i); + file.WriteFormat(" Address: %016lx-%016lx\n", module.start_address, module.end_address); if (std::strcmp(this->modules[i].name, "") != 0) { - fprintf(f_report, " Name: %s\n", module.name); + file.WriteFormat(" Name: %s\n", module.name); } - DumpMemoryHexToFile(f_report, " Build Id: ", module.build_id, sizeof(module.build_id)); + file.DumpMemory(" Build Id: ", module.build_id, sizeof(module.build_id)); } } diff --git a/stratosphere/creport/source/creport_modules.hpp b/stratosphere/creport/source/creport_modules.hpp index 661426de1..9de25e1ff 100644 --- a/stratosphere/creport/source/creport_modules.hpp +++ b/stratosphere/creport/source/creport_modules.hpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #pragma once +#include "creport_scoped_file.hpp" #include "creport_threads.hpp" namespace ams::creport { @@ -52,7 +53,7 @@ namespace ams::creport { void FindModulesFromThreadInfo(Handle debug_handle, const ThreadInfo &thread); const char *GetFormattedAddressString(uintptr_t address); - void SaveToFile(FILE *f_report); + void SaveToFile(ScopedFile &file); private: bool TryFindModule(uintptr_t *out_address, uintptr_t guess); void TryAddModule(uintptr_t guess); diff --git a/stratosphere/creport/source/creport_scoped_file.cpp b/stratosphere/creport/source/creport_scoped_file.cpp new file mode 100644 index 000000000..2d433abca --- /dev/null +++ b/stratosphere/creport/source/creport_scoped_file.cpp @@ -0,0 +1,93 @@ +/* + * 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 . + */ +#include "creport_scoped_file.hpp" + +namespace ams::creport { + + namespace { + + /* Convenience definitions. */ + constexpr size_t MaximumLineLength = 0x20; + + os::Mutex g_format_lock; + char g_format_buffer[2 * os::MemoryPageSize]; + + } + + void ScopedFile::WriteFormat(const char *fmt, ...) { + /* Acquire exclusive access to the format buffer. */ + std::scoped_lock lk(g_format_lock); + + /* Format to the buffer. */ + { + std::va_list vl; + va_start(vl, fmt); + std::vsnprintf(g_format_buffer, sizeof(g_format_buffer), fmt, vl); + va_end(vl); + } + + /* Write data. */ + this->Write(g_format_buffer, std::strlen(g_format_buffer)); + } + + void ScopedFile::DumpMemory(const char *prefix, const void *data, size_t size) { + const u8 *data_u8 = reinterpret_cast(data); + const int prefix_len = std::strlen(prefix); + size_t offset = 0; + size_t remaining = size; + bool first = true; + + while (remaining) { + const size_t cur_size = std::min(MaximumLineLength, remaining); + + /* Print the line prefix. */ + if (first) { + this->WriteFormat("%s", prefix); + first = false; + } else { + this->WriteFormat("%*s", prefix_len, ""); + } + + /* Dump up to 0x20 of hex memory. */ + { + char hex[MaximumLineLength * 2 + 2]; + for (size_t i = 0; i < cur_size; i++) { + std::snprintf(hex + i * 2, 3, "%02X", data_u8[offset++]); + } + hex[cur_size * 2 + 0] = '\n'; + hex[cur_size * 2 + 1] = '\x00'; + + this->Write(hex, std::strlen(hex)); + } + + /* Continue. */ + remaining -= cur_size; + } + } + + void ScopedFile::Write(const void *data, size_t size) { + /* If we're not open, we can't write. */ + if (!this->IsOpen()) { + return; + } + + /* Advance, if we write successfully. */ + if (R_SUCCEEDED(fs::WriteFile(this->file, this->offset, g_format_buffer, size, fs::WriteOption::Flush))) { + this->offset += size; + } + } + +} \ No newline at end of file diff --git a/stratosphere/creport/source/creport_scoped_file.hpp b/stratosphere/creport/source/creport_scoped_file.hpp new file mode 100644 index 000000000..151458884 --- /dev/null +++ b/stratosphere/creport/source/creport_scoped_file.hpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::creport { + + class ScopedFile { + NON_COPYABLE(ScopedFile); + NON_MOVEABLE(ScopedFile); + private: + fs::FileHandle file; + s64 offset; + bool opened; + public: + ScopedFile(const char *path) : file(), offset(), opened(false) { + if (R_SUCCEEDED(fs::CreateFile(path, 0))) { + this->opened = R_SUCCEEDED(fs::OpenFile(std::addressof(this->file), path, fs::OpenMode_Write | fs::OpenMode_Append)); + } + } + + ~ScopedFile() { + if (this->opened) { + fs::CloseFile(file); + } + } + + bool IsOpen() const { + return this->opened; + } + + void WriteFormat(const char *fmt, ...) __attribute__((format(printf, 2, 3))); + void DumpMemory(const char *prefix, const void *data, size_t size); + + void Write(const void *data, size_t size); + }; + +} \ No newline at end of file diff --git a/stratosphere/creport/source/creport_threads.cpp b/stratosphere/creport/source/creport_threads.cpp index ebee68846..e8da71a6e 100644 --- a/stratosphere/creport/source/creport_threads.cpp +++ b/stratosphere/creport/source/creport_threads.cpp @@ -59,53 +59,53 @@ namespace ams::creport { } - void ThreadList::SaveToFile(FILE *f_report) { - fprintf(f_report, "Number of Threads: %02zu\n", this->thread_count); + void ThreadList::SaveToFile(ScopedFile &file) { + file.WriteFormat("Number of Threads: %02zu\n", this->thread_count); for (size_t i = 0; i < this->thread_count; i++) { - fprintf(f_report, "Threads[%02zu]:\n", i); - this->threads[i].SaveToFile(f_report); + file.WriteFormat("Threads[%02zu]:\n", i); + this->threads[i].SaveToFile(file); } } - void ThreadInfo::SaveToFile(FILE *f_report) { - fprintf(f_report, " Thread ID: %016lx\n", this->thread_id); + void ThreadInfo::SaveToFile(ScopedFile &file) { + file.WriteFormat(" Thread ID: %016lx\n", this->thread_id); if (std::strcmp(this->name, "") != 0) { - fprintf(f_report, " Thread Name: %s\n", this->name); + file.WriteFormat(" Thread Name: %s\n", this->name); } if (this->stack_top != 0) { - fprintf(f_report, " Stack Region: %016lx-%016lx\n", this->stack_bottom, this->stack_top); + file.WriteFormat(" Stack Region: %016lx-%016lx\n", this->stack_bottom, this->stack_top); } - fprintf(f_report, " Registers:\n"); + file.WriteFormat(" Registers:\n"); { for (unsigned int i = 0; i <= 28; i++) { - fprintf(f_report, " X[%02u]: %s\n", i, this->module_list->GetFormattedAddressString(this->context.cpu_gprs[i].x)); + file.WriteFormat(" X[%02u]: %s\n", i, this->module_list->GetFormattedAddressString(this->context.cpu_gprs[i].x)); } - fprintf(f_report, " FP: %s\n", this->module_list->GetFormattedAddressString(this->context.fp)); - fprintf(f_report, " LR: %s\n", this->module_list->GetFormattedAddressString(this->context.lr)); - fprintf(f_report, " SP: %s\n", this->module_list->GetFormattedAddressString(this->context.sp)); - fprintf(f_report, " PC: %s\n", this->module_list->GetFormattedAddressString(this->context.pc.x)); + file.WriteFormat(" FP: %s\n", this->module_list->GetFormattedAddressString(this->context.fp)); + file.WriteFormat(" LR: %s\n", this->module_list->GetFormattedAddressString(this->context.lr)); + file.WriteFormat(" SP: %s\n", this->module_list->GetFormattedAddressString(this->context.sp)); + file.WriteFormat(" PC: %s\n", this->module_list->GetFormattedAddressString(this->context.pc.x)); } if (this->stack_trace_size != 0) { - fprintf(f_report, " Stack Trace:\n"); + file.WriteFormat(" Stack Trace:\n"); for (size_t i = 0; i < this->stack_trace_size; i++) { - fprintf(f_report, " ReturnAddress[%02zu]: %s\n", i, this->module_list->GetFormattedAddressString(this->stack_trace[i])); + file.WriteFormat(" ReturnAddress[%02zu]: %s\n", i, this->module_list->GetFormattedAddressString(this->stack_trace[i])); } } if (this->stack_dump_base != 0) { - fprintf(f_report, " Stack Dump: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + file.WriteFormat(" Stack Dump: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); for (size_t i = 0; i < 0x10; i++) { const size_t ofs = i * 0x10; - fprintf(f_report, " %012lx %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + file.WriteFormat(" %012lx %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", this->stack_dump_base + ofs, this->stack_dump[ofs + 0], this->stack_dump[ofs + 1], this->stack_dump[ofs + 2], this->stack_dump[ofs + 3], this->stack_dump[ofs + 4], this->stack_dump[ofs + 5], this->stack_dump[ofs + 6], this->stack_dump[ofs + 7], this->stack_dump[ofs + 8], this->stack_dump[ofs + 9], this->stack_dump[ofs + 10], this->stack_dump[ofs + 11], this->stack_dump[ofs + 12], this->stack_dump[ofs + 13], this->stack_dump[ofs + 14], this->stack_dump[ofs + 15]); } } if (this->tls_address != 0) { - fprintf(f_report, " TLS Address: %016lx\n", this->tls_address); - fprintf(f_report, " TLS Dump: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); + file.WriteFormat(" TLS Address: %016lx\n", this->tls_address); + file.WriteFormat(" TLS Dump: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); for (size_t i = 0; i < 0x10; i++) { const size_t ofs = i * 0x10; - fprintf(f_report, " %012lx %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + file.WriteFormat(" %012lx %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", this->tls_address + ofs, this->tls[ofs + 0], this->tls[ofs + 1], this->tls[ofs + 2], this->tls[ofs + 3], this->tls[ofs + 4], this->tls[ofs + 5], this->tls[ofs + 6], this->tls[ofs + 7], this->tls[ofs + 8], this->tls[ofs + 9], this->tls[ofs + 10], this->tls[ofs + 11], this->tls[ofs + 12], this->tls[ofs + 13], this->tls[ofs + 14], this->tls[ofs + 15]); } @@ -207,38 +207,38 @@ namespace ams::creport { } } - void ThreadInfo::DumpBinary(FILE *f_bin) { + void ThreadInfo::DumpBinary(ScopedFile &file) { /* Dump id and context. */ - fwrite(&this->thread_id, sizeof(this->thread_id), 1, f_bin); - fwrite(&this->context, sizeof(this->context), 1, f_bin); + file.Write(&this->thread_id, sizeof(this->thread_id)); + file.Write(&this->context, sizeof(this->context)); /* Dump TLS info and name. */ - fwrite(&this->tls_address, sizeof(this->tls_address), 1, f_bin); - fwrite(&this->tls, sizeof(this->tls), 1, f_bin); - fwrite(&this->name, sizeof(this->name), 1, f_bin); + file.Write(&this->tls_address, sizeof(this->tls_address)); + file.Write(&this->tls, sizeof(this->tls)); + file.Write(&this->name, sizeof(this->name)); /* Dump stack extents and stack dump. */ - fwrite(&this->stack_bottom, sizeof(this->stack_bottom), 1, f_bin); - fwrite(&this->stack_top, sizeof(this->stack_top), 1, f_bin); - fwrite(&this->stack_dump_base, sizeof(this->stack_dump_base), 1, f_bin); - fwrite(&this->stack_dump, sizeof(this->stack_dump), 1, f_bin); + file.Write(&this->stack_bottom, sizeof(this->stack_bottom)); + file.Write(&this->stack_top, sizeof(this->stack_top)); + file.Write(&this->stack_dump_base, sizeof(this->stack_dump_base)); + file.Write(&this->stack_dump, sizeof(this->stack_dump)); /* Dump stack trace. */ { const u64 sts = this->stack_trace_size; - fwrite(&sts, sizeof(sts), 1, f_bin); + file.Write(&sts, sizeof(sts)); } - fwrite(this->stack_trace, sizeof(this->stack_trace[0]), this->stack_trace_size, f_bin); + file.Write(this->stack_trace, this->stack_trace_size); } - void ThreadList::DumpBinary(FILE *f_bin, u64 crashed_thread_id) { + void ThreadList::DumpBinary(ScopedFile &file, u64 crashed_thread_id) { const u32 magic = DumpedThreadInfoMagic; const u32 count = this->thread_count; - fwrite(&magic, sizeof(magic), 1, f_bin); - fwrite(&count, sizeof(count), 1, f_bin); - fwrite(&crashed_thread_id, sizeof(crashed_thread_id), 1, f_bin); + file.Write(&magic, sizeof(magic)); + file.Write(&count, sizeof(count)); + file.Write(&crashed_thread_id, sizeof(crashed_thread_id)); for (size_t i = 0; i < this->thread_count; i++) { - this->threads[i].DumpBinary(f_bin); + this->threads[i].DumpBinary(file); } } diff --git a/stratosphere/creport/source/creport_threads.hpp b/stratosphere/creport/source/creport_threads.hpp index c531e5ffc..926aa961c 100644 --- a/stratosphere/creport/source/creport_threads.hpp +++ b/stratosphere/creport/source/creport_threads.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include "creport_scoped_file.hpp" namespace ams::creport { @@ -76,8 +77,8 @@ namespace ams::creport { } bool ReadFromProcess(Handle debug_handle, std::map &tls_map, u64 thread_id, bool is_64_bit); - void SaveToFile(FILE *f_report); - void DumpBinary(FILE *f_bin); + void SaveToFile(ScopedFile &file); + void DumpBinary(ScopedFile &file); private: void TryGetStackInfo(Handle debug_handle); }; @@ -104,8 +105,8 @@ namespace ams::creport { } void ReadFromProcess(Handle debug_handle, std::map &tls_map, bool is_64_bit); - void SaveToFile(FILE *f_report); - void DumpBinary(FILE *f_bin, u64 crashed_thread_id); + void SaveToFile(ScopedFile &file); + void DumpBinary(ScopedFile &file, u64 crashed_thread_id); }; } diff --git a/stratosphere/creport/source/creport_utils.cpp b/stratosphere/creport/source/creport_utils.cpp index 0e66d3ad7..6ec66eac3 100644 --- a/stratosphere/creport/source/creport_utils.cpp +++ b/stratosphere/creport/source/creport_utils.cpp @@ -17,41 +17,6 @@ namespace ams::creport { - namespace { - - /* Convenience definitions. */ - constexpr size_t MaximumLineLength = 0x20; - - } - - void DumpMemoryHexToFile(FILE *f, const char *prefix, const void *data, size_t size) { - const u8 *data_u8 = reinterpret_cast(data); - const int prefix_len = std::strlen(prefix); - size_t offset = 0; - size_t remaining = size; - bool first = true; - while (remaining) { - const size_t cur_size = std::max(MaximumLineLength, remaining); - - /* Print the line prefix. */ - if (first) { - fprintf(f, "%s", prefix); - first = false; - } else { - fprintf(f, "%*s", prefix_len, ""); - } - - /* Dump up to 0x20 of hex memory. */ - for (size_t i = 0; i < cur_size; i++) { - fprintf(f, "%02X", data_u8[offset++]); - } - - /* End line. */ - fprintf(f, "\n"); - remaining -= cur_size; - } - } - os::ProcessId ParseProcessIdArgument(const char *s) { /* Official creport uses this custom parsing logic... */ u64 out_val = 0; diff --git a/stratosphere/creport/source/creport_utils.hpp b/stratosphere/creport/source/creport_utils.hpp index 9f7979512..8010fbc32 100644 --- a/stratosphere/creport/source/creport_utils.hpp +++ b/stratosphere/creport/source/creport_utils.hpp @@ -19,7 +19,6 @@ namespace ams::creport { /* Utility functions. */ - void DumpMemoryHexToFile(FILE *f, const char *prefix, const void *data, size_t size); os::ProcessId ParseProcessIdArgument(const char *s); } \ No newline at end of file