From 494f3f1899abe43f0342f5ff845228c5681d9f5d Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Fri, 24 Mar 2023 14:07:44 +0200 Subject: [PATCH] [FS] Implement OpenDirectory --- include/fs/archive_base.hpp | 16 ++++++++++ include/fs/archive_save_data.hpp | 1 + include/kernel/kernel_types.hpp | 3 +- include/services/fs.hpp | 2 ++ src/core/fs/archive_save_data.cpp | 23 +++++++++++++++ src/core/services/fs.cpp | 49 ++++++++++++++++++++++++++++--- 6 files changed, 89 insertions(+), 5 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index cdf3b66a..8374a4cd 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,17 @@ struct ArchiveSession { ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {} }; +struct DirectorySession { + ArchiveBase* archive = nullptr; + // For directories which are mirrored to a specific path on the disk, this contains that path + // Otherwise this is a nullopt + std::optional pathOnDisk; + bool isOpen; + + DirectorySession(ArchiveBase* archive, std::filesystem::path path, bool isOpen = true) : archive(archive), pathOnDisk(path), + isOpen(isOpen) {} +}; + // Represents a file descriptor obtained from OpenFile. If the optional is nullopt, opening the file failed. // Otherwise the fd of the opened file is returned (or nullptr if the opened file doesn't require one) using FileDescriptor = std::optional; @@ -201,6 +213,10 @@ public: virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; virtual ArchiveBase* openArchive(const FSPath& path) = 0; + virtual std::optional openDirectory(const FSPath& path) { + Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); + return std::nullopt; + } // Read size bytes from a file starting at offset "offset" into a certain buffer in memory // Returns the number of bytes read, or nullopt if the read failed diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index b8b9b04e..d45c0014 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -13,6 +13,7 @@ public: FormatInfo getFormatInfo(const FSPath& path) override; ArchiveBase* openArchive(const FSPath& path) override; + std::optional openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 3e4f510d..86f9ae07 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -27,7 +27,7 @@ namespace SVCResult { } enum class KernelObjectType : u8 { - AddressArbiter, Archive, File, MemoryBlock, Process, ResourceLimit, Session, Dummy, + AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy, // Bundle waitable objects together in the enum to let the compiler optimize certain checks better Event, Mutex, Port, Semaphore, Timer, Thread }; @@ -144,6 +144,7 @@ static const char* kernelObjectTypeToString(KernelObjectType t) { switch (t) { case KernelObjectType::AddressArbiter: return "address arbiter"; case KernelObjectType::Archive: return "archive"; + case KernelObjectType::Directory: return "directory"; case KernelObjectType::Event: return "event"; case KernelObjectType::File: return "file"; case KernelObjectType::MemoryBlock: return "memory block"; diff --git a/include/services/fs.hpp b/include/services/fs.hpp index ce2dd699..1ead706a 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -29,6 +29,7 @@ class FSService { ArchiveBase* getArchiveFromID(u32 id); std::optional openArchiveHandle(u32 archiveID, const FSPath& path); + std::optional openDirectoryHandle(ArchiveBase* archive, const FSPath& path); std::optional openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms); FSPath readPath(u32 type, u32 pointer, u32 size); @@ -42,6 +43,7 @@ class FSService { void initializeWithSdkVersion(u32 messagePointer); void isSdmcDetected(u32 messagePointer); void openArchive(u32 messagePointer); + void openDirectory(u32 messagePointer); void openFile(u32 messagePointer); void openFileDirectly(u32 messagePointer); void setPriority(u32 messagePointer); diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index c9685750..70af569f 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -53,6 +53,29 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe return FileError; } +std::optional SaveDataArchive::openDirectory(const FSPath& path) { + if (!cartHasSaveData()) { + printf("Tried to open SaveData directory without save data\n"); + return std::nullopt; + } + + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) + Helpers::panic("Unsafe path in SaveData::OpenDirectory"); + + fs::path p = IOFile::getAppData() / "SaveData"; + p += fs::path(path.utf16_string).make_preferred(); + + if (fs::is_directory(p)) { + return DirectorySession(this, p); + } else { + Helpers::panic("Directory not found in SaveData::OpenDirectory"); + } + } + + Helpers::panic("SaveDataArchive::OpenDirectory: Unimplemented path type"); + return std::nullopt; +} ArchiveBase::FormatInfo SaveDataArchive::getFormatInfo(const FSPath& path) { Helpers::panic("Unimplemented SaveData::GetFormatInfo"); diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 4af67e15..bc4f341e 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -14,6 +14,7 @@ namespace FSCommands { OpenFileDirectly = 0x08030204, DeleteFile = 0x08040142, CreateFile = 0x08080202, + OpenDirectory = 0x080B0102, OpenArchive = 0x080C00C2, CloseArchive = 0x080E0080, IsSdmcDetected = 0x08170000, @@ -66,7 +67,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id) { } } -std::optional FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath,const FilePerms& perms) { +std::optional FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms) { FileDescriptor opened = archive->openFile(path, perms); if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file auto handle = kernel.makeObject(KernelObjectType::File); @@ -80,6 +81,19 @@ std::optional FSService::openFileHandle(ArchiveBase* archive, const FSPa } } +std::optional FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) { + std::optional opened = archive->openDirectory(path); + if (opened.has_value()) { // If opened doesn't have a value, we failed to open the directory + auto handle = kernel.makeObject(KernelObjectType::Directory); + auto& object = kernel.getObjects()[handle]; + object.data = new DirectorySession(opened.value()); + + return handle; + } else { + return std::nullopt; + } +} + std::optional FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { ArchiveBase* archive = getArchiveFromID(archiveID); @@ -123,8 +137,9 @@ void FSService::handleSyncRequest(u32 messagePointer) { case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(messagePointer); break; case FSCommands::IsSdmcDetected: isSdmcDetected(messagePointer); break; case FSCommands::OpenArchive: openArchive(messagePointer); break; - case FSCommands::OpenFile: openFile(messagePointer); break; - case FSCommands::OpenFileDirectly: openFileDirectly(messagePointer); break; + case FSCommands::OpenDirectory: openDirectory(messagePointer); break; + case FSCommands::OpenFile: [[likely]] openFile(messagePointer); break; + case FSCommands::OpenFileDirectly: [[likely]] openFileDirectly(messagePointer); break; case FSCommands::SetPriority: setPriority(messagePointer); break; default: Helpers::panic("FS service requested. Command: %08X\n", command); } @@ -210,6 +225,32 @@ void FSService::openFile(u32 messagePointer) { } } +void FSService::openDirectory(u32 messagePointer) { + log("FS::OpenDirectory\n"); + const Handle archiveHandle = (Handle)mem.read64(messagePointer + 4); + const u32 pathType = mem.read32(messagePointer + 12); + const u32 pathSize = mem.read32(messagePointer + 16); + const u32 pathPointer = mem.read32(messagePointer + 24); + + KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive); + if (archiveObject == nullptr) [[unlikely]] { + log("FS::OpenDirectory: Invalid archive handle %d\n", archiveHandle); + mem.write32(messagePointer + 4, Result::Failure); + return; + } + + ArchiveBase* archive = archiveObject->getData()->archive; + const auto dirPath = readPath(pathType, pathPointer, pathSize); + std::optional dir = openDirectoryHandle(archive, dirPath); + + if (dir.has_value()) { + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 12, dir.value()); + } else { + Helpers::panic("FS::OpenDirectory failed"); + } +} + void FSService::openFileDirectly(u32 messagePointer) { const u32 archiveID = mem.read32(messagePointer + 8); const u32 archivePathType = mem.read32(messagePointer + 12); @@ -247,7 +288,7 @@ void FSService::openFileDirectly(u32 messagePointer) { } void FSService::createFile(u32 messagePointer) { - const u32 archiveHandle = mem.read64(messagePointer + 8); + const Handle archiveHandle = mem.read64(messagePointer + 8); const u32 filePathType = mem.read32(messagePointer + 16); const u32 filePathSize = mem.read32(messagePointer + 20); const u32 attributes = mem.read32(messagePointer + 24);