From 1bdfedf3c6dfed1b8f503696d44ea74e0db8c465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Fri, 2 Mar 2018 23:27:29 +0100 Subject: [PATCH 01/10] Common: Add a Result class This adds a lightweight, easy to use std::variant wrapper intended to be used as a return type for functions that can return either a result or an error code. --- Source/Core/Common/Common.vcxproj | 1 + Source/Core/Common/Common.vcxproj.filters | 1 + Source/Core/Common/Result.h | 30 +++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 Source/Core/Common/Result.h diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index a518b531f1..5b64898b7b 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -138,6 +138,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 50ea0f619d..58b75ff30f 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -59,6 +59,7 @@ + diff --git a/Source/Core/Common/Result.h b/Source/Core/Common/Result.h new file mode 100644 index 0000000000..2c4f47b3d7 --- /dev/null +++ b/Source/Core/Common/Result.h @@ -0,0 +1,30 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Common +{ +template +class Result final +{ +public: + Result(ResultCode code) : m_variant{code} {} + Result(const T& t) : m_variant{t} {} + Result(T&& t) : m_variant{std::move(t)} {} + explicit operator bool() const { return Succeeded(); } + bool Succeeded() const { return std::holds_alternative(m_variant); } + // Must only be called when Succeeded() returns false. + ResultCode Error() const { return std::get(m_variant); } + // Must only be called when Succeeded() returns true. + const T& operator*() const { return std::get(m_variant); } + const T* operator->() const { return &std::get(m_variant); } + T& operator*() { return std::get(m_variant); } + T* operator->() { return &std::get(m_variant); } +private: + std::variant m_variant; +}; +} // namespace Common From f1dbb8952c3060e5ebe9cdb60cc9e4539e93a4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 3 Mar 2018 00:56:15 +0100 Subject: [PATCH 02/10] IOS: Introduce a new FileSystem interface Add a new FileSystem class that can be used to perform operations on the emulated Wii filesystem. This will allow separating the IPC code (reading from and writing to memory to handle IOS FS commands) and the actual filesystem code in a much better way. This also paves the way for implementing another filesystem backend in the future -- NAND images for more complete emulation, including filesystem metadata like permissions or file orders, which some games and homebrew actually care about -- without needing to make any more changes to the other parts of the codebase, in addition to making filesystem behaviour tests easier to write. --- Source/Core/Core/Core.vcxproj | 1 + Source/Core/Core/Core.vcxproj.filters | 3 + Source/Core/Core/IOS/FS/FileSystem.h | 173 ++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 Source/Core/Core/IOS/FS/FileSystem.h diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 99f2b19876..79c478f980 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -446,6 +446,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 189b7d2088..d3b4d3be27 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -1404,6 +1404,9 @@ IOS\FS + + IOS\FS + IOS\ES diff --git a/Source/Core/Core/IOS/FS/FileSystem.h b/Source/Core/Core/IOS/FS/FileSystem.h new file mode 100644 index 0000000000..6607c9ab02 --- /dev/null +++ b/Source/Core/Core/IOS/FS/FileSystem.h @@ -0,0 +1,173 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/Result.h" + +class PointerWrap; + +namespace IOS::HLE::FS +{ +enum class ResultCode +{ + Success, + Invalid, + AccessDenied, + SuperblockWriteFailed, + SuperblockInitFailed, + AlreadyExists, + NotFound, + FstFull, + NoFreeSpace, + NoFreeHandle, + TooManyPathComponents, + InUse, + BadBlock, + EccError, + CriticalEccError, + FileNotEmpty, + CheckFailed, + UnknownError, + ShortRead, +}; + +template +using Result = Common::Result; + +using Uid = u32; +using Gid = u16; +using Fd = u32; + +enum class Mode : u8 +{ + None = 0, + Read = 1, + Write = 2, + ReadWrite = 3, +}; + +enum class SeekMode : u32 +{ + Set = 0, + Current = 1, + End = 2, +}; + +using FileAttribute = u8; + +struct Metadata +{ + Uid uid; + Gid gid; + FileAttribute attribute; + Mode owner_mode, group_mode, other_mode; + bool is_file; + u32 size; + u16 fst_index; +}; + +struct NandStats +{ + u32 cluster_size; + u32 free_clusters; + u32 used_clusters; + u32 bad_clusters; + u32 reserved_clusters; + u32 free_inodes; + u32 used_inodes; +}; + +struct DirectoryStats +{ + u32 used_clusters; + u32 used_inodes; +}; + +struct FileStatus +{ + u32 offset; + u32 size; +}; + +class FileSystem +{ +public: + virtual ~FileSystem() = default; + + virtual void DoState(PointerWrap& p) = 0; + + /// Format the file system. + virtual ResultCode Format(Uid uid) = 0; + + /// Get a file descriptor for accessing a file. + virtual Result OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) = 0; + /// Close a file descriptor. + virtual ResultCode Close(Fd fd) = 0; + /// Read `size` bytes from the file descriptor. Returns the number of bytes read. + virtual Result ReadBytesFromFile(Fd fd, u8* ptr, u32 size) = 0; + /// Write `size` bytes to the file descriptor. Returns the number of bytes written. + virtual Result WriteBytesToFile(Fd fd, const u8* ptr, u32 size) = 0; + /// Reposition the file offset for a file descriptor. + virtual Result SeekFile(Fd fd, u32 offset, SeekMode mode) = 0; + /// Get status for a file descriptor. + virtual Result GetFileStatus(Fd fd) = 0; + + template + Result ReadFile(Fd fd, T* ptr, u32 count) + { + const Result bytes = ReadBytesFromFile(fd, reinterpret_cast(ptr), sizeof(T) * count); + if (!bytes) + return bytes.Error(); + if (*bytes != sizeof(T) * count) + return ResultCode::ShortRead; + return count; + } + + template + Result WriteFile(Fd fd, const T* ptr, u32 count) + { + const auto result = WriteBytesToFile(fd, reinterpret_cast(ptr), sizeof(T) * count); + if (!result) + return result.Error(); + return count; + } + + /// Create a file with the specified path and metadata. + virtual ResultCode CreateFile(Uid caller_uid, Gid caller_gid, const std::string& path, + FileAttribute attribute, Mode owner_mode, Mode group_mode, + Mode other_mode) = 0; + /// Create a directory with the specified path and metadata. + virtual ResultCode CreateDirectory(Uid caller_uid, Gid caller_gid, const std::string& path, + FileAttribute attribute, Mode owner_mode, Mode group_mode, + Mode other_mode) = 0; + + /// Delete a file or directory with the specified path. + virtual ResultCode Delete(Uid caller_uid, Gid caller_gid, const std::string& path) = 0; + /// Rename a file or directory with the specified path. + virtual ResultCode Rename(Uid caller_uid, Gid caller_gid, const std::string& old_path, + const std::string& new_path) = 0; + + /// List the children of a directory (non-recursively). + virtual Result> ReadDirectory(Uid caller_uid, Gid caller_gid, + const std::string& path) = 0; + + /// Get metadata about a file. + virtual Result GetMetadata(Uid caller_uid, Gid caller_gid, const std::string& path) = 0; + /// Set metadata for a file. + virtual ResultCode SetMetadata(Uid caller_uid, const std::string& path, Uid uid, Gid gid, + FileAttribute attribute, Mode owner_mode, Mode group_mode, + Mode other_mode) = 0; + + /// Get usage information about the NAND (block size, cluster and inode counts). + virtual Result GetNandStats() = 0; + /// Get usage information about a directory (used cluster and inode counts). + virtual Result GetDirectoryStats(const std::string& path) = 0; +}; + +} // namespace IOS::HLE::FS From 1eec459e308bd41d492b515229e318dcaf121251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 3 Mar 2018 14:38:43 +0100 Subject: [PATCH 03/10] IOS: Extract existing FS code into a host backend Extract the existing FS code into a HostBackend implementing the filesystem interface. Compared to the original code, this uses less static state. The open host files map is now a member variable as it should have been. Filesystem handles are now also easier to savestate. Some variable names and log messages were cleaned up. Nothing else has been changed. --- Source/Core/Core/CMakeLists.txt | 2 + Source/Core/Core/Core.vcxproj | 4 + Source/Core/Core/Core.vcxproj.filters | 12 + Source/Core/Core/IOS/FS/HostBackend/FS.cpp | 433 +++++++++++++++++++ Source/Core/Core/IOS/FS/HostBackend/FS.h | 83 ++++ Source/Core/Core/IOS/FS/HostBackend/File.cpp | 203 +++++++++ 6 files changed, 737 insertions(+) create mode 100644 Source/Core/Core/IOS/FS/HostBackend/FS.cpp create mode 100644 Source/Core/Core/IOS/FS/HostBackend/FS.h create mode 100644 Source/Core/Core/IOS/FS/HostBackend/File.cpp diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 141d305887..f22d5ed3c9 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -165,6 +165,8 @@ add_library(core IOS/ES/TitleInformation.cpp IOS/ES/TitleManagement.cpp IOS/ES/Views.cpp + IOS/FS/HostBackend/File.cpp + IOS/FS/HostBackend/FS.cpp IOS/FS/FileIO.cpp IOS/FS/FS.cpp IOS/Network/ICMPLin.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 79c478f980..847086a188 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -197,6 +197,8 @@ + + @@ -448,6 +450,8 @@ + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index d3b4d3be27..f997b6a741 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -735,6 +735,12 @@ IOS\FS + + IOS\FS + + + IOS\FS + IOS\ES @@ -1407,6 +1413,12 @@ IOS\FS + + IOS\FS + + + IOS\FS + IOS\ES diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp new file mode 100644 index 0000000000..37f14871c4 --- /dev/null +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -0,0 +1,433 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Common/Assert.h" +#include "Common/ChunkFile.h" +#include "Common/FileUtil.h" +#include "Common/Logging/Log.h" +#include "Common/NandPaths.h" +#include "Core/IOS/ES/ES.h" +#include "Core/IOS/FS/HostBackend/FS.h" +#include "Core/IOS/IOS.h" + +namespace IOS::HLE::FS +{ +static bool IsValidWiiPath(const std::string& path) +{ + return path.compare(0, 1, "/") == 0; +} + +std::string HostFileSystem::BuildFilename(const std::string& wii_path) +{ + std::string nand_path = File::GetUserPath(D_SESSION_WIIROOT_IDX); + if (wii_path.compare(0, 1, "/") == 0) + return nand_path + Common::EscapePath(wii_path); + + ASSERT(false); + return nand_path; +} + +// Get total filesize of contents of a directory (recursive) +// Only used for ES_GetUsage atm, could be useful elsewhere? +static u64 ComputeTotalFileSize(const File::FSTEntry& parent_entry) +{ + u64 sizeOfFiles = 0; + for (const File::FSTEntry& entry : parent_entry.children) + { + if (entry.isDirectory) + sizeOfFiles += ComputeTotalFileSize(entry); + else + sizeOfFiles += entry.size; + } + return sizeOfFiles; +} + +HostFileSystem::HostFileSystem() = default; + +HostFileSystem::~HostFileSystem() = default; + +void HostFileSystem::DoState(PointerWrap& p) +{ + // Temporarily close the file, to prevent any issues with the savestating of /tmp + for (Handle& handle : m_handles) + handle.host_file.reset(); + + // handle /tmp + std::string Path = BuildFilename("/tmp"); + if (p.GetMode() == PointerWrap::MODE_READ) + { + File::DeleteDirRecursively(Path); + File::CreateDir(Path); + + // now restore from the stream + while (1) + { + char type = 0; + p.Do(type); + if (!type) + break; + std::string file_name; + p.Do(file_name); + std::string name = Path + "/" + file_name; + switch (type) + { + case 'd': + { + File::CreateDir(name); + break; + } + case 'f': + { + u32 size = 0; + p.Do(size); + + File::IOFile handle(name, "wb"); + char buf[65536]; + u32 count = size; + while (count > 65536) + { + p.DoArray(buf); + handle.WriteArray(&buf[0], 65536); + count -= 65536; + } + p.DoArray(&buf[0], count); + handle.WriteArray(&buf[0], count); + break; + } + } + } + } + else + { + // recurse through tmp and save dirs and files + + File::FSTEntry parent_entry = File::ScanDirectoryTree(Path, true); + std::deque todo; + todo.insert(todo.end(), parent_entry.children.begin(), parent_entry.children.end()); + + while (!todo.empty()) + { + File::FSTEntry& entry = todo.front(); + std::string name = entry.physicalName; + name.erase(0, Path.length() + 1); + char type = entry.isDirectory ? 'd' : 'f'; + p.Do(type); + p.Do(name); + if (entry.isDirectory) + { + todo.insert(todo.end(), entry.children.begin(), entry.children.end()); + } + else + { + u32 size = (u32)entry.size; + p.Do(size); + + File::IOFile handle(entry.physicalName, "rb"); + char buf[65536]; + u32 count = size; + while (count > 65536) + { + handle.ReadArray(&buf[0], 65536); + p.DoArray(buf); + count -= 65536; + } + handle.ReadArray(&buf[0], count); + p.DoArray(&buf[0], count); + } + todo.pop_front(); + } + + char type = 0; + p.Do(type); + } + + for (Handle& handle : m_handles) + { + p.Do(handle.opened); + p.Do(handle.mode); + p.Do(handle.wii_path); + p.Do(handle.file_offset); + if (handle.opened) + handle.host_file = OpenHostFile(BuildFilename(handle.wii_path)); + } +} + +ResultCode HostFileSystem::Format(Uid uid) +{ + const std::string root = BuildFilename("/"); + if (!File::DeleteDirRecursively(root) || !File::CreateDir(root)) + return ResultCode::UnknownError; + return ResultCode::Success; +} + +ResultCode HostFileSystem::CreateFile(Uid, Gid, const std::string& path, FileAttribute attribute, + Mode owner_mode, Mode group_mode, Mode other_mode) +{ + std::string file_name(BuildFilename(path)); + // check if the file already exist + if (File::Exists(file_name)) + { + INFO_LOG(IOS_FILEIO, "\tresult = FS_EEXIST"); + return ResultCode::AlreadyExists; + } + + // create the file + File::CreateFullPath(file_name); // just to be sure + if (!File::CreateEmptyFile(file_name)) + { + ERROR_LOG(IOS_FILEIO, "couldn't create new file"); + return ResultCode::Invalid; + } + + INFO_LOG(IOS_FILEIO, "\tresult = IPC_SUCCESS"); + return ResultCode::Success; +} + +ResultCode HostFileSystem::CreateDirectory(Uid, Gid, const std::string& path, + FileAttribute attribute, Mode owner_mode, + Mode group_mode, Mode other_mode) +{ + if (!IsValidWiiPath(path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + return ResultCode::Invalid; + } + + std::string name(BuildFilename(path)); + + name += "/"; + File::CreateFullPath(name); + DEBUG_ASSERT_MSG(IOS_FILEIO, File::IsDirectory(name), "CREATE_DIR %s failed", name.c_str()); + + return ResultCode::Success; +} + +ResultCode HostFileSystem::Delete(Uid, Gid, const std::string& path) +{ + if (!IsValidWiiPath(path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + return ResultCode::Invalid; + } + + const std::string file_name = BuildFilename(path); + if (File::Delete(file_name)) + INFO_LOG(IOS_FILEIO, "DeleteFile %s", file_name.c_str()); + else if (File::DeleteDirRecursively(file_name)) + INFO_LOG(IOS_FILEIO, "DeleteDir %s", file_name.c_str()); + else + WARN_LOG(IOS_FILEIO, "DeleteFile %s - failed!!!", file_name.c_str()); + + return ResultCode::Success; +} + +ResultCode HostFileSystem::Rename(Uid, Gid, const std::string& old_path, + const std::string& new_path) +{ + if (!IsValidWiiPath(old_path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", old_path.c_str()); + return ResultCode::Invalid; + } + std::string old_name = BuildFilename(old_path); + + if (!IsValidWiiPath(new_path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", new_path.c_str()); + return ResultCode::Invalid; + } + + std::string new_name = BuildFilename(new_path); + + // try to make the basis directory + File::CreateFullPath(new_name); + + // if there is already a file, delete it + if (File::Exists(old_name) && File::Exists(new_name)) + { + File::Delete(new_name); + } + + // finally try to rename the file + if (File::Rename(old_name, new_name)) + { + INFO_LOG(IOS_FILEIO, "Rename %s to %s", old_name.c_str(), new_name.c_str()); + } + else + { + ERROR_LOG(IOS_FILEIO, "Rename %s to %s - failed", old_name.c_str(), new_name.c_str()); + return ResultCode::NotFound; + } + + return ResultCode::Success; +} + +Result> HostFileSystem::ReadDirectory(Uid, Gid, const std::string& path) +{ + if (!IsValidWiiPath(path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + return ResultCode::Invalid; + } + + // the Wii uses this function to define the type (dir or file) + const std::string dir_name(BuildFilename(path)); + + INFO_LOG(IOS_FILEIO, "IOCTL_READ_DIR %s", dir_name.c_str()); + + const File::FileInfo file_info(dir_name); + + if (!file_info.Exists()) + { + WARN_LOG(IOS_FILEIO, "Search not found: %s", dir_name.c_str()); + return ResultCode::NotFound; + } + + if (!file_info.IsDirectory()) + { + // It's not a directory, so error. + // Games don't usually seem to care WHICH error they get, as long as it's < + // Well the system menu CARES! + WARN_LOG(IOS_FILEIO, "\tNot a directory - return FS_EINVAL"); + return ResultCode::Invalid; + } + + File::FSTEntry entry = File::ScanDirectoryTree(dir_name, false); + + for (File::FSTEntry& child : entry.children) + { + // Decode escaped invalid file system characters so that games (such as + // Harry Potter and the Half-Blood Prince) can find what they expect. + child.virtualName = Common::UnescapeFileName(child.virtualName); + } + + // NOTE(leoetlino): this is absolutely wrong, but there is no way to fix this properly + // if we use the host filesystem. + std::sort(entry.children.begin(), entry.children.end(), + [](const File::FSTEntry& one, const File::FSTEntry& two) { + return one.virtualName < two.virtualName; + }); + + std::vector output; + for (File::FSTEntry& child : entry.children) + { + output.emplace_back(child.virtualName); + INFO_LOG(IOS_FILEIO, "\tFound: %s", child.virtualName.c_str()); + } + return output; +} + +Result HostFileSystem::GetMetadata(Uid, Gid, const std::string& path) +{ + Metadata metadata; + metadata.uid = 0; + metadata.gid = 0x3031; // this is also known as makercd, 01 (0x3031) for nintendo and 08 + // (0x3038) for MH3 etc + + if (!IsValidWiiPath(path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + return ResultCode::Invalid; + } + + std::string file_name = BuildFilename(path); + metadata.owner_mode = Mode::ReadWrite; + metadata.group_mode = Mode::ReadWrite; + metadata.other_mode = Mode::ReadWrite; + metadata.attribute = 0x00; // no attributes + + // Hack: if the path that is being accessed is within an installed title directory, get the + // UID/GID from the installed title TMD. + u64 title_id; + if (IsTitlePath(file_name, Common::FROM_SESSION_ROOT, &title_id)) + { + IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(title_id); + if (tmd.IsValid()) + metadata.gid = tmd.GetGroupId(); + } + + const File::FileInfo info{file_name}; + metadata.is_file = info.IsFile(); + metadata.size = info.GetSize(); + if (info.IsDirectory()) + { + INFO_LOG(IOS_FILEIO, "GET_ATTR Directory %s - all permission flags are set", file_name.c_str()); + } + else + { + if (info.Exists()) + { + INFO_LOG(IOS_FILEIO, "GET_ATTR %s - all permission flags are set", file_name.c_str()); + } + else + { + INFO_LOG(IOS_FILEIO, "GET_ATTR unknown %s", file_name.c_str()); + return ResultCode::NotFound; + } + } + return metadata; +} + +ResultCode HostFileSystem::SetMetadata(Uid caller_uid, const std::string& path, Uid uid, Gid gid, + FileAttribute attribute, Mode owner_mode, Mode group_mode, + Mode other_mode) +{ + if (!IsValidWiiPath(path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + return ResultCode::Invalid; + } + return ResultCode::Success; +} + +Result HostFileSystem::GetNandStats() +{ + WARN_LOG(IOS_FILEIO, "GET STATS - returning static values for now"); + + // TODO: scrape the real amounts from somewhere... + NandStats stats{}; + stats.cluster_size = 0x4000; + stats.free_clusters = 0x5DEC; + stats.used_clusters = 0x1DD4; + stats.bad_clusters = 0x10; + stats.reserved_clusters = 0x02F0; + stats.free_inodes = 0x146B; + stats.used_inodes = 0x0394; + + return stats; +} + +Result HostFileSystem::GetDirectoryStats(const std::string& wii_path) +{ + if (!IsValidWiiPath(wii_path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + return ResultCode::Invalid; + } + + DirectoryStats stats{}; + std::string path(BuildFilename(wii_path)); + INFO_LOG(IOS_FILEIO, "IOCTL_GETUSAGE %s", path.c_str()); + if (File::IsDirectory(path)) + { + File::FSTEntry parent_dir = File::ScanDirectoryTree(path, true); + // add one for the folder itself + stats.used_inodes = 1 + (u32)parent_dir.size; + + u64 total_size = ComputeTotalFileSize(parent_dir); // "Real" size to convert to nand blocks + + stats.used_clusters = (u32)(total_size / (16 * 1024)); // one block is 16kb + + INFO_LOG(IOS_FILEIO, "fsBlock: %i, inodes: %i", stats.used_clusters, stats.used_inodes); + } + else + { + WARN_LOG(IOS_FILEIO, "fsBlock failed, cannot find directory: %s", path.c_str()); + } + return stats; +} + +} // namespace IOS::HLE::FS diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.h b/Source/Core/Core/IOS/FS/HostBackend/FS.h new file mode 100644 index 0000000000..cb7283d1f0 --- /dev/null +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.h @@ -0,0 +1,83 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/File.h" +#include "Core/IOS/FS/FileSystem.h" + +namespace IOS::HLE::FS +{ +/// Backend that uses the host file system as backend. +/// +/// Ignores metadata like permissions, attributes and various checks and also +/// sometimes returns wrong information because metadata is not available. +class HostFileSystem final : public FileSystem +{ +public: + HostFileSystem(); + ~HostFileSystem(); + + void DoState(PointerWrap& p) override; + + ResultCode Format(Uid uid) override; + + Result OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) override; + ResultCode Close(Fd fd) override; + Result ReadBytesFromFile(Fd fd, u8* ptr, u32 size) override; + Result WriteBytesToFile(Fd fd, const u8* ptr, u32 size) override; + Result SeekFile(Fd fd, u32 offset, SeekMode mode) override; + Result GetFileStatus(Fd fd) override; + + ResultCode CreateFile(Uid caller_uid, Gid caller_gid, const std::string& path, + FileAttribute attribute, Mode owner_mode, Mode group_mode, + Mode other_mode) override; + + ResultCode CreateDirectory(Uid caller_uid, Gid caller_gid, const std::string& path, + FileAttribute attribute, Mode owner_mode, Mode group_mode, + Mode other_mode) override; + + ResultCode Delete(Uid caller_uid, Gid caller_gid, const std::string& path) override; + ResultCode Rename(Uid caller_uid, Gid caller_gid, const std::string& old_path, + const std::string& new_path) override; + + Result> ReadDirectory(Uid caller_uid, Gid caller_gid, + const std::string& path) override; + + Result GetMetadata(Uid caller_uid, Gid caller_gid, const std::string& path) override; + ResultCode SetMetadata(Uid caller_uid, const std::string& path, Uid uid, Gid gid, + FileAttribute attribute, Mode owner_mode, Mode group_mode, + Mode other_mode) override; + + Result GetNandStats() override; + Result GetDirectoryStats(const std::string& path) override; + +private: + struct Handle + { + bool opened = false; + Mode mode = Mode::None; + std::string wii_path; + std::shared_ptr host_file; + u32 file_offset = 0; + }; + Handle* AssignFreeHandle(); + Handle* GetHandleFromFd(Fd fd); + Fd ConvertHandleToFd(const Handle* handle) const; + + std::string BuildFilename(const std::string& wii_path); + std::shared_ptr OpenHostFile(const std::string& host_path); + + std::array m_handles{}; + std::map> m_open_files; +}; + +} // namespace IOS::HLE::FS diff --git a/Source/Core/Core/IOS/FS/HostBackend/File.cpp b/Source/Core/Core/IOS/FS/HostBackend/File.cpp new file mode 100644 index 0000000000..405c877c45 --- /dev/null +++ b/Source/Core/Core/IOS/FS/HostBackend/File.cpp @@ -0,0 +1,203 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/File.h" +#include "Common/FileUtil.h" +#include "Common/Logging/Log.h" +#include "Core/IOS/FS/HostBackend/FS.h" + +namespace IOS::HLE::FS +{ +// This isn't theadsafe, but it's only called from the CPU thread. +std::shared_ptr HostFileSystem::OpenHostFile(const std::string& host_path) +{ + // On the wii, all file operations are strongly ordered. + // If a game opens the same file twice (or 8 times, looking at you PokePark Wii) + // and writes to one file handle, it will be able to immediately read the written + // data from the other handle. + // On 'real' operating systems, there are various buffers and caches meaning + // applications doing such naughty things will not get expected results. + + // So we fix this by catching any attempts to open the same file twice and + // only opening one file. Accesses to a single file handle are ordered. + // + // Hall of Shame: + // - PokePark Wii (gets stuck on the loading screen of Pikachu falling) + // - PokePark 2 (Also gets stuck while loading) + // - Wii System Menu (Can't access the system settings, gets stuck on blank screen) + // - The Beatles: Rock Band (saving doesn't work) + + // Check if the file has already been opened. + std::shared_ptr file; + auto search = m_open_files.find(host_path); + if (search != m_open_files.end()) + { + file = search->second.lock(); // Lock a shared pointer to use. + } + else + { + // This code will be called when all references to the shared pointer below have been removed. + auto deleter = [this, host_path](File::IOFile* ptr) { + delete ptr; // IOFile's deconstructor closes the file. + m_open_files.erase(host_path); // erase the weak pointer from the list of open files. + }; + + // All files are opened read/write. Actual access rights will be controlled per handle by the + // read/write functions below + file = std::shared_ptr(new File::IOFile(host_path, "r+b"), + deleter); // Use the custom deleter from above. + + // Store a weak pointer to our newly opened file in the cache. + m_open_files[host_path] = std::weak_ptr(file); + } + return file; +} + +Result HostFileSystem::OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) +{ + Handle* handle = AssignFreeHandle(); + if (!handle) + return ResultCode::NoFreeHandle; + + const std::string host_path = BuildFilename(path); + if (!File::IsFile(host_path)) + { + *handle = Handle{}; + return ResultCode::NotFound; + } + + handle->host_file = OpenHostFile(host_path); + handle->wii_path = path; + handle->mode = mode; + handle->file_offset = 0; + return ConvertHandleToFd(handle); +} + +ResultCode HostFileSystem::Close(Fd fd) +{ + Handle* handle = GetHandleFromFd(fd); + if (!handle) + return ResultCode::Invalid; + + // Let go of our pointer to the file, it will automatically close if we are the last handle + // accessing it. + *handle = Handle{}; + return ResultCode::Success; +} + +Result HostFileSystem::ReadBytesFromFile(Fd fd, u8* ptr, u32 count) +{ + Handle* handle = GetHandleFromFd(fd); + if (!handle || !handle->host_file->IsOpen()) + return ResultCode::Invalid; + + if ((u8(handle->mode) & u8(Mode::Read)) == 0) + return ResultCode::AccessDenied; + + const u32 file_size = static_cast(handle->host_file->GetSize()); + // IOS has this check in the read request handler. + if (count + handle->file_offset > file_size) + count = file_size - handle->file_offset; + + // File might be opened twice, need to seek before we read + handle->host_file->Seek(handle->file_offset, SEEK_SET); + const u32 actually_read = static_cast(fread(ptr, 1, count, handle->host_file->GetHandle())); + + if (actually_read != count && ferror(handle->host_file->GetHandle())) + return ResultCode::AccessDenied; + + // IOS returns the number of bytes read and adds that value to the seek position, + // instead of adding the *requested* read length. + handle->file_offset += actually_read; + return actually_read; +} + +Result HostFileSystem::WriteBytesToFile(Fd fd, const u8* ptr, u32 count) +{ + Handle* handle = GetHandleFromFd(fd); + if (!handle || !handle->host_file->IsOpen()) + return ResultCode::Invalid; + + if ((u8(handle->mode) & u8(Mode::Write)) == 0) + return ResultCode::AccessDenied; + + // File might be opened twice, need to seek before we read + handle->host_file->Seek(handle->file_offset, SEEK_SET); + if (!handle->host_file->WriteBytes(ptr, count)) + return ResultCode::AccessDenied; + + handle->file_offset += count; + return count; +} + +Result HostFileSystem::SeekFile(Fd fd, std::uint32_t offset, SeekMode mode) +{ + Handle* handle = GetHandleFromFd(fd); + if (!handle || !handle->host_file->IsOpen()) + return ResultCode::Invalid; + + u32 new_position = 0; + switch (mode) + { + case SeekMode::Set: + new_position = offset; + break; + case SeekMode::Current: + new_position = handle->file_offset + offset; + break; + case SeekMode::End: + new_position = handle->host_file->GetSize() + offset; + break; + default: + return ResultCode::Invalid; + } + + // This differs from POSIX behaviour which allows seeking past the end of the file. + if (handle->host_file->GetSize() < new_position) + return ResultCode::Invalid; + + handle->file_offset = new_position; + return handle->file_offset; +} + +Result HostFileSystem::GetFileStatus(Fd fd) +{ + const Handle* handle = GetHandleFromFd(fd); + if (!handle || !handle->host_file->IsOpen()) + return ResultCode::Invalid; + + FileStatus status; + status.size = handle->host_file->GetSize(); + status.offset = handle->file_offset; + return status; +} + +HostFileSystem::Handle* HostFileSystem::AssignFreeHandle() +{ + const auto it = std::find_if(m_handles.begin(), m_handles.end(), + [](const Handle& handle) { return !handle.opened; }); + if (it == m_handles.end()) + return nullptr; + + *it = Handle{}; + it->opened = true; + return &*it; +} + +HostFileSystem::Handle* HostFileSystem::GetHandleFromFd(Fd fd) +{ + if (fd >= m_handles.size() || !m_handles[fd].opened) + return nullptr; + return &m_handles[fd]; +} + +Fd HostFileSystem::ConvertHandleToFd(const Handle* handle) const +{ + return handle - m_handles.data(); +} + +} // namespace IOS::HLE::FS From fcfe4e2a26ca02b242435edb91179c4a749b1ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 3 Mar 2018 18:25:51 +0100 Subject: [PATCH 04/10] IOS: Change GetFS() to return a FileSystem Now that we have a proper filesystem interface, it makes more sense to return it instead of the emulated IOS device (which isn't really usable for any purpose other than emulated IPC). --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/Core.vcxproj | 1 + Source/Core/Core/Core.vcxproj.filters | 3 +++ Source/Core/Core/IOS/FS/FileSystem.cpp | 15 +++++++++++++++ Source/Core/Core/IOS/FS/FileSystem.h | 3 +++ Source/Core/Core/IOS/IOS.cpp | 9 +++++++-- Source/Core/Core/IOS/IOS.h | 9 +++++++-- 7 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 Source/Core/Core/IOS/FS/FileSystem.cpp diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index f22d5ed3c9..d3e0cd5b97 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -165,6 +165,7 @@ add_library(core IOS/ES/TitleInformation.cpp IOS/ES/TitleManagement.cpp IOS/ES/Views.cpp + IOS/FS/FileSystem.cpp IOS/FS/HostBackend/File.cpp IOS/FS/HostBackend/FS.cpp IOS/FS/FileIO.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 847086a188..0abe26aa84 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -197,6 +197,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index f997b6a741..cff27f6add 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -735,6 +735,9 @@ IOS\FS + + IOS\FS + IOS\FS diff --git a/Source/Core/Core/IOS/FS/FileSystem.cpp b/Source/Core/Core/IOS/FS/FileSystem.cpp new file mode 100644 index 0000000000..74cee8e9cd --- /dev/null +++ b/Source/Core/Core/IOS/FS/FileSystem.cpp @@ -0,0 +1,15 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/IOS/FS/FileSystem.h" + +#include "Core/IOS/FS/HostBackend/FS.h" + +namespace IOS::HLE::FS +{ +std::unique_ptr MakeFileSystem() +{ + return std::make_unique(); +} +} // namespace IOS::HLE::FS diff --git a/Source/Core/Core/IOS/FS/FileSystem.h b/Source/Core/Core/IOS/FS/FileSystem.h index 6607c9ab02..f56df41998 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.h +++ b/Source/Core/Core/IOS/FS/FileSystem.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -170,4 +171,6 @@ public: virtual Result GetDirectoryStats(const std::string& path) = 0; }; +std::unique_ptr MakeFileSystem(); + } // namespace IOS::HLE::FS diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index 4abd2312a5..bd704fc895 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -33,6 +33,7 @@ #include "Core/IOS/ES/ES.h" #include "Core/IOS/FS/FS.h" #include "Core/IOS/FS/FileIO.h" +#include "Core/IOS/FS/FileSystem.h" #include "Core/IOS/MIOS.h" #include "Core/IOS/Network/IP/Top.h" #include "Core/IOS/Network/KD/NetKDRequest.h" @@ -242,9 +243,9 @@ u32 Kernel::GetVersion() const return static_cast(m_title_id); } -std::shared_ptr Kernel::GetFS() +std::shared_ptr Kernel::GetFS() { - return std::static_pointer_cast(m_device_map.at("/dev/fs")); + return m_fs; } std::shared_ptr Kernel::GetES() @@ -368,6 +369,9 @@ void Kernel::AddDevice(std::unique_ptr device) void Kernel::AddCoreDevices() { + m_fs = FS::MakeFileSystem(); + ASSERT(m_fs); + std::lock_guard lock(m_device_map_mutex); AddDevice(std::make_unique(*this, "/dev/fs")); AddDevice(std::make_unique(*this, "/dev/es")); @@ -691,6 +695,7 @@ void Kernel::DoState(PointerWrap& p) p.Do(m_ppc_gid); m_iosc.DoState(p); + m_fs->DoState(p); if (m_title_id == Titles::MIOS) return; diff --git a/Source/Core/Core/IOS/IOS.h b/Source/Core/Core/IOS/IOS.h index 0f58359683..1d3e94b50b 100644 --- a/Source/Core/Core/IOS/IOS.h +++ b/Source/Core/Core/IOS/IOS.h @@ -23,11 +23,15 @@ namespace IOS { namespace HLE { +namespace FS +{ +class FileSystem; +} + namespace Device { class Device; class ES; -class FS; } struct Request; @@ -94,7 +98,7 @@ public: // These are *always* part of the IOS kernel and always available. // They are also the only available resource managers even before loading any module. - std::shared_ptr GetFS(); + std::shared_ptr GetFS(); std::shared_ptr GetES(); void SDIO_EventNotify(); @@ -146,6 +150,7 @@ protected: u64 m_last_reply_time = 0; IOSC m_iosc; + std::shared_ptr m_fs; }; // HLE for an IOS tied to emulation: base kernel which may have additional modules loaded. From 78478a651d63ca8cb45d1735144d9a4427c03fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 3 Mar 2018 19:35:46 +0100 Subject: [PATCH 05/10] Boot: Move CreateVirtualFATFilesystem to Boot_WiiWAD It has nothing to do in the filesystem code. (It also smells like a workaround for some kind of timing issue.) --- Source/Core/Core/Boot/Boot_WiiWAD.cpp | 45 ++++++++++++++++++++++----- Source/Core/Core/IOS/FS/FileIO.cpp | 36 --------------------- Source/Core/Core/IOS/FS/FileIO.h | 1 - 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/Source/Core/Core/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 156c2698a8..5ecfd48471 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -2,37 +2,68 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include +#include #include #include -#include "Common/CommonPaths.h" #include "Common/CommonTypes.h" -#include "Common/FileUtil.h" #include "Common/MsgHandler.h" -#include "Common/NandPaths.h" #include "Core/Boot/Boot.h" #include "Core/CommonTitles.h" #include "Core/IOS/ES/ES.h" #include "Core/IOS/ES/Formats.h" -#include "Core/IOS/FS/FileIO.h" +#include "Core/IOS/FS/FileSystem.h" #include "Core/IOS/IOS.h" #include "Core/WiiUtils.h" #include "DiscIO/WiiWad.h" +// cdb.vff is a virtual Fat filesystem created on first launch of sysmenu +// we create it here as it is faster ~3 minutes for me when sysmenu does it +// ~1 second created here +static void CreateVirtualFATFilesystem(std::shared_ptr fs) +{ + constexpr u32 SYSMENU_UID = 0x1000; + constexpr u16 SYSMENU_GID = 0x1; + + const std::string cdb_path = "/title/00000001/00000002/data/cdb.vff"; + fs->CreateFile(SYSMENU_UID, SYSMENU_GID, cdb_path, 0, IOS::HLE::FS::Mode::ReadWrite, + IOS::HLE::FS::Mode::ReadWrite, IOS::HLE::FS::Mode::ReadWrite); + + constexpr size_t CDB_SIZE = 0x01400000; + const auto metadata = fs->GetMetadata(SYSMENU_UID, SYSMENU_GID, cdb_path); + if (!metadata || metadata->size >= CDB_SIZE) + return; + + const auto fd = fs->OpenFile(SYSMENU_UID, SYSMENU_GID, cdb_path, IOS::HLE::FS::Mode::Write); + if (!fd) + return; + + constexpr u8 CDB_HEADER[0x20] = {'V', 'F', 'F', 0x20, 0xfe, 0xff, 1, 0, 1, 0x40, 0, 0, 0, 0x20}; + constexpr u8 CDB_FAT[4] = {0xf0, 0xff, 0xff, 0xff}; + std::vector data(CDB_SIZE); + std::copy_n(CDB_HEADER, sizeof(CDB_HEADER), data.begin()); + std::copy_n(CDB_FAT, sizeof(CDB_FAT), data.begin() + sizeof(CDB_HEADER)); + std::copy_n(CDB_FAT, sizeof(CDB_FAT), data.begin() + 0x14020); + // write the final 0 to 0 file from the second FAT to 20 MiB + data[CDB_SIZE - 1] = 0; + fs->WriteFile(*fd, data.data(), static_cast(data.size())); + fs->Close(*fd); +} + bool CBoot::BootNANDTitle(const u64 title_id) { UpdateStateFlags([](StateFlags* state) { state->type = 0x04; // TYPE_NANDBOOT }); + auto* ios = IOS::HLE::GetIOS(); + if (title_id == Titles::SYSTEM_MENU) - IOS::HLE::CreateVirtualFATFilesystem(); + CreateVirtualFATFilesystem(ios->GetFS()); SetupWiiMemory(); - auto* ios = IOS::HLE::GetIOS(); return ios->GetES()->LaunchTitle(title_id); } diff --git a/Source/Core/Core/IOS/FS/FileIO.cpp b/Source/Core/Core/IOS/FS/FileIO.cpp index d7e3cb3f44..1c35ff6615 100644 --- a/Source/Core/Core/IOS/FS/FileIO.cpp +++ b/Source/Core/Core/IOS/FS/FileIO.cpp @@ -16,7 +16,6 @@ #include "Common/File.h" #include "Common/FileUtil.h" #include "Common/NandPaths.h" -#include "Core/CommonTitles.h" #include "Core/HW/Memmap.h" #include "Core/IOS/IOS.h" @@ -37,41 +36,6 @@ std::string BuildFilename(const std::string& wii_path) return nand_path; } -void CreateVirtualFATFilesystem() -{ - const int cdbSize = 0x01400000; - const std::string cdbPath = - Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + "cdb.vff"; - if ((int)File::GetSize(cdbPath) < cdbSize) - { - // cdb.vff is a virtual Fat filesystem created on first launch of sysmenu - // we create it here as it is faster ~3 minutes for me when sysmenu does it ~1 second created - // here - const u8 cdbHDR[0x20] = {'V', 'F', 'F', 0x20, 0xfe, 0xff, 1, 0, 1, 0x40, 0, 0, 0, 0x20}; - const u8 cdbFAT[4] = {0xf0, 0xff, 0xff, 0xff}; - - File::IOFile cdbFile(cdbPath, "wb"); - if (cdbFile) - { - cdbFile.WriteBytes(cdbHDR, 0x20); - cdbFile.WriteBytes(cdbFAT, 0x4); - cdbFile.Seek(0x14020, SEEK_SET); - cdbFile.WriteBytes(cdbFAT, 0x4); - // 20 MiB file - cdbFile.Seek(cdbSize - 1, SEEK_SET); - // write the final 0 to 0 file from the second FAT to 20 MiB - cdbFile.WriteBytes(cdbHDR + 14, 1); - if (!cdbFile.IsGood()) - { - cdbFile.Close(); - File::Delete(cdbPath); - } - cdbFile.Flush(); - cdbFile.Close(); - } - } -} - namespace Device { FileIO::FileIO(Kernel& ios, const std::string& device_name) diff --git a/Source/Core/Core/IOS/FS/FileIO.h b/Source/Core/Core/IOS/FS/FileIO.h index df98921f32..f44b76f344 100644 --- a/Source/Core/Core/IOS/FS/FileIO.h +++ b/Source/Core/Core/IOS/FS/FileIO.h @@ -23,7 +23,6 @@ namespace IOS namespace HLE { std::string BuildFilename(const std::string& wii_path); -void CreateVirtualFATFilesystem(); namespace Device { From 5a7b966b6d7b7b292a4b80f904d092f2d81b94d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 3 Mar 2018 18:55:01 +0100 Subject: [PATCH 06/10] IOS: Rewrite FS to use FileSystem This is the large change in the branch. This lets us use either the host filesystem or (in the future) a NAND image exactly the same way, and make sure the IPC emulation code behaves identically. Less duplicated code. Note that "FileIO" and "FS" were merged, because it actually doesn't make a lot of sense to split them: IOS handles requests for both /dev/fs and files in the same resource manager, and as it turns out, /dev/fs commands can *also* be sent to non /dev/fs file descriptors! If we kept /dev/fs and files split, there would be no way to emulate that correctly. I'm not aware of anything that does that (yet?) but I think it's important to be correct. --- Source/Core/Common/Logging/Log.h | 1 + Source/Core/Common/Logging/LogManager.cpp | 1 + Source/Core/Common/Swap.h | 12 + Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/Core.vcxproj | 6 +- Source/Core/Core/Core.vcxproj.filters | 14 +- Source/Core/Core/IOS/Device.h | 1 - Source/Core/Core/IOS/FS/FS.cpp | 650 -------------------- Source/Core/Core/IOS/FS/FS.h | 73 --- Source/Core/Core/IOS/FS/FileIO.cpp | 286 --------- Source/Core/Core/IOS/FS/FileIO.h | 82 --- Source/Core/Core/IOS/FS/FileSystem.cpp | 6 + Source/Core/Core/IOS/FS/FileSystem.h | 3 + Source/Core/Core/IOS/FS/FileSystemProxy.cpp | 466 ++++++++++++++ Source/Core/Core/IOS/FS/FileSystemProxy.h | 86 +++ Source/Core/Core/IOS/FS/HostBackend/FS.cpp | 5 +- Source/Core/Core/IOS/IOS.cpp | 9 +- Source/Core/Core/State.cpp | 2 +- 18 files changed, 589 insertions(+), 1117 deletions(-) delete mode 100644 Source/Core/Core/IOS/FS/FS.cpp delete mode 100644 Source/Core/Core/IOS/FS/FS.h delete mode 100644 Source/Core/Core/IOS/FS/FileIO.cpp delete mode 100644 Source/Core/Core/IOS/FS/FileIO.h create mode 100644 Source/Core/Core/IOS/FS/FileSystemProxy.cpp create mode 100644 Source/Core/Core/IOS/FS/FileSystemProxy.h diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h index 6c50789b9d..ee190e858b 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -32,6 +32,7 @@ enum LOG_TYPE IOS_DI, IOS_ES, IOS_FILEIO, + IOS_FS, IOS_NET, IOS_SD, IOS_SSL, diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index fcf606ad0b..0a3493a264 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -100,6 +100,7 @@ LogManager::LogManager() m_log[LogTypes::IOS_DI] = {"IOS_DI", "IOS - Drive Interface"}; m_log[LogTypes::IOS_ES] = {"IOS_ES", "IOS - ETicket Services"}; m_log[LogTypes::IOS_FILEIO] = {"IOS_FILEIO", "IOS - FileIO"}; + m_log[LogTypes::IOS_FS] = {"IOS_FS", "IOS - Filesystem Services"}; m_log[LogTypes::IOS_SD] = {"IOS_SD", "IOS - SDIO"}; m_log[LogTypes::IOS_SSL] = {"IOS_SSL", "IOS - SSL"}; m_log[LogTypes::IOS_STM] = {"IOS_STM", "IOS - State Transition Manager"}; diff --git a/Source/Core/Common/Swap.h b/Source/Core/Common/Swap.h index c5b143dbcb..207faac93c 100644 --- a/Source/Core/Common/Swap.h +++ b/Source/Core/Common/Swap.h @@ -166,4 +166,16 @@ inline T FromBigEndian(T data) swap(reinterpret_cast(&data)); return data; } + +template +struct BigEndianValue +{ + static_assert(std::is_arithmetic(), "value_type must be an arithmetic type"); + BigEndianValue() = default; + explicit BigEndianValue(value_type val) { *this = val; } + operator value_type() const { return FromBigEndian(raw); } + void operator=(value_type v) { raw = FromBigEndian(v); } +private: + value_type raw; +}; } // Namespace Common diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index d3e0cd5b97..3c7f04261c 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -166,10 +166,9 @@ add_library(core IOS/ES/TitleManagement.cpp IOS/ES/Views.cpp IOS/FS/FileSystem.cpp + IOS/FS/FileSystemProxy.cpp IOS/FS/HostBackend/File.cpp IOS/FS/HostBackend/FS.cpp - IOS/FS/FileIO.cpp - IOS/FS/FS.cpp IOS/Network/ICMPLin.cpp IOS/Network/MACUtils.cpp IOS/Network/Socket.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 0abe26aa84..641aaeaaab 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -195,9 +195,8 @@ - - + @@ -448,9 +447,8 @@ - - + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index cff27f6add..5456cd4be5 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -732,10 +732,10 @@ IOS\DI - + IOS\FS - + IOS\FS @@ -768,9 +768,6 @@ IOS\ES - - IOS\FS - IOS\Network @@ -1410,10 +1407,10 @@ IOS\ES - + IOS\FS - + IOS\FS @@ -1425,9 +1422,6 @@ IOS\ES - - IOS\FS - IOS\USB\Bluetooth diff --git a/Source/Core/Core/IOS/Device.h b/Source/Core/Core/IOS/Device.h index 46cb0d0479..32dc456037 100644 --- a/Source/Core/Core/IOS/Device.h +++ b/Source/Core/Core/IOS/Device.h @@ -173,7 +173,6 @@ public: enum class DeviceType : u32 { Static, // Devices which appear in s_device_map. - FileIO, // FileIO devices which are created dynamically. OH0, // OH0 child devices which are created dynamically. }; diff --git a/Source/Core/Core/IOS/FS/FS.cpp b/Source/Core/Core/IOS/FS/FS.cpp deleted file mode 100644 index b9c5889390..0000000000 --- a/Source/Core/Core/IOS/FS/FS.cpp +++ /dev/null @@ -1,650 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "Core/IOS/FS/FS.h" - -#include -#include -#include -#include -#include - -#include "Common/Assert.h" -#include "Common/ChunkFile.h" -#include "Common/CommonPaths.h" -#include "Common/CommonTypes.h" -#include "Common/File.h" -#include "Common/FileUtil.h" -#include "Common/Logging/Log.h" -#include "Common/MsgHandler.h" -#include "Common/NandPaths.h" -#include "Core/HW/Memmap.h" -#include "Core/HW/SystemTimers.h" -#include "Core/IOS/ES/ES.h" -#include "Core/IOS/ES/Formats.h" -#include "Core/IOS/FS/FileIO.h" - -namespace IOS -{ -namespace HLE -{ -static bool IsValidWiiPath(const std::string& path) -{ - return path.compare(0, 1, "/") == 0; -} - -namespace Device -{ -FS::FS(Kernel& ios, const std::string& device_name) : Device(ios, device_name) -{ - const std::string tmp_dir = BuildFilename("/tmp"); - File::DeleteDirRecursively(tmp_dir); - File::CreateDir(tmp_dir); -} - -void FS::DoState(PointerWrap& p) -{ - DoStateShared(p); - - // handle /tmp - - std::string Path = File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/tmp"; - if (p.GetMode() == PointerWrap::MODE_READ) - { - File::DeleteDirRecursively(Path); - File::CreateDir(Path); - - // now restore from the stream - while (1) - { - char type = 0; - p.Do(type); - if (!type) - break; - std::string filename; - p.Do(filename); - std::string name = Path + DIR_SEP + filename; - switch (type) - { - case 'd': - { - File::CreateDir(name); - break; - } - case 'f': - { - u32 size = 0; - p.Do(size); - - File::IOFile handle(name, "wb"); - char buf[65536]; - u32 count = size; - while (count > 65536) - { - p.DoArray(buf); - handle.WriteArray(&buf[0], 65536); - count -= 65536; - } - p.DoArray(&buf[0], count); - handle.WriteArray(&buf[0], count); - break; - } - } - } - } - else - { - // recurse through tmp and save dirs and files - - File::FSTEntry parentEntry = File::ScanDirectoryTree(Path, true); - std::deque todo; - todo.insert(todo.end(), parentEntry.children.begin(), parentEntry.children.end()); - - while (!todo.empty()) - { - File::FSTEntry& entry = todo.front(); - std::string name = entry.physicalName; - name.erase(0, Path.length() + 1); - char type = entry.isDirectory ? 'd' : 'f'; - p.Do(type); - p.Do(name); - if (entry.isDirectory) - { - todo.insert(todo.end(), entry.children.begin(), entry.children.end()); - } - else - { - u32 size = (u32)entry.size; - p.Do(size); - - File::IOFile handle(entry.physicalName, "rb"); - char buf[65536]; - u32 count = size; - while (count > 65536) - { - handle.ReadArray(&buf[0], 65536); - p.DoArray(buf); - count -= 65536; - } - handle.ReadArray(&buf[0], count); - p.DoArray(&buf[0], count); - } - todo.pop_front(); - } - - char type = 0; - p.Do(type); - } -} - -// Get total filesize of contents of a directory (recursive) -// Only used for ES_GetUsage atm, could be useful elsewhere? -static u64 ComputeTotalFileSize(const File::FSTEntry& parentEntry) -{ - u64 sizeOfFiles = 0; - for (const File::FSTEntry& entry : parentEntry.children) - { - if (entry.isDirectory) - sizeOfFiles += ComputeTotalFileSize(entry); - else - sizeOfFiles += entry.size; - } - return sizeOfFiles; -} - -IPCCommandResult FS::IOCtl(const IOCtlRequest& request) -{ - Memory::Memset(request.buffer_out, 0, request.buffer_out_size); - - switch (request.request) - { - case IOCTL_GET_STATS: - return GetStats(request); - case IOCTL_CREATE_DIR: - return CreateDirectory(request); - case IOCTL_SET_ATTR: - return SetAttribute(request); - case IOCTL_GET_ATTR: - return GetAttribute(request); - case IOCTL_DELETE_FILE: - return DeleteFile(request); - case IOCTL_RENAME_FILE: - return RenameFile(request); - case IOCTL_CREATE_FILE: - return CreateFile(request); - case IOCTL_SHUTDOWN: - return Shutdown(request); - default: - request.DumpUnknown(GetDeviceName(), LogTypes::IOS_FILEIO); - break; - } - - return GetFSReply(FS_EINVAL); -} - -IPCCommandResult FS::IOCtlV(const IOCtlVRequest& request) -{ - switch (request.request) - { - case IOCTLV_READ_DIR: - return ReadDirectory(request); - case IOCTLV_GETUSAGE: - return GetUsage(request); - default: - request.DumpUnknown(GetDeviceName(), LogTypes::IOS_FILEIO); - break; - } - - return GetFSReply(IPC_SUCCESS); -} - -// ~1/1000th of a second is too short and causes hangs in Wii Party -// Play it safe at 1/500th -IPCCommandResult FS::GetFSReply(const s32 return_value) const -{ - return {return_value, true, SystemTimers::GetTicksPerSecond() / 500}; -} - -IPCCommandResult FS::GetStats(const IOCtlRequest& request) -{ - if (request.buffer_out_size < 0x1c) - return GetFSReply(-1017); - - WARN_LOG(IOS_FILEIO, "FS: GET STATS - returning static values for now"); - - // TODO: scrape the real amounts from somewhere... - NANDStat fs; - fs.BlockSize = 0x4000; - fs.FreeUserBlocks = 0x5DEC; - fs.UsedUserBlocks = 0x1DD4; - fs.FreeSysBlocks = 0x10; - fs.UsedSysBlocks = 0x02F0; - fs.Free_INodes = 0x146B; - fs.Used_Inodes = 0x0394; - - std::memcpy(Memory::GetPointer(request.buffer_out), &fs, sizeof(NANDStat)); - - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::CreateDirectory(const IOCtlRequest& request) -{ - DEBUG_ASSERT(request.buffer_out_size == 0); - u32 Addr = request.buffer_in; - - u32 OwnerID = Memory::Read_U32(Addr); - Addr += 4; - u16 GroupID = Memory::Read_U16(Addr); - Addr += 2; - - const std::string wii_path = Memory::GetString(Addr, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return GetFSReply(FS_EINVAL); - } - - std::string DirName(BuildFilename(wii_path)); - Addr += 64; - Addr += 9; // owner attribs, permission - u8 Attribs = Memory::Read_U8(Addr); - - INFO_LOG(IOS_FILEIO, "FS: CREATE_DIR %s, OwnerID %#x, GroupID %#x, Attributes %#x", - DirName.c_str(), OwnerID, GroupID, Attribs); - - DirName += DIR_SEP; - File::CreateFullPath(DirName); - DEBUG_ASSERT_MSG(IOS_FILEIO, File::IsDirectory(DirName), "FS: CREATE_DIR %s failed", - DirName.c_str()); - - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::SetAttribute(const IOCtlRequest& request) -{ - u32 Addr = request.buffer_in; - - u32 OwnerID = Memory::Read_U32(Addr); - Addr += 4; - u16 GroupID = Memory::Read_U16(Addr); - Addr += 2; - - const std::string wii_path = Memory::GetString(Addr, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return GetFSReply(FS_EINVAL); - } - - std::string Filename = BuildFilename(wii_path); - Addr += 64; - u8 OwnerPerm = Memory::Read_U8(Addr); - Addr += 1; - u8 GroupPerm = Memory::Read_U8(Addr); - Addr += 1; - u8 OtherPerm = Memory::Read_U8(Addr); - Addr += 1; - u8 Attributes = Memory::Read_U8(Addr); - Addr += 1; - - INFO_LOG(IOS_FILEIO, "FS: SetAttrib %s", Filename.c_str()); - DEBUG_LOG(IOS_FILEIO, " OwnerID: 0x%08x", OwnerID); - DEBUG_LOG(IOS_FILEIO, " GroupID: 0x%04x", GroupID); - DEBUG_LOG(IOS_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm); - DEBUG_LOG(IOS_FILEIO, " GroupPerm: 0x%02x", GroupPerm); - DEBUG_LOG(IOS_FILEIO, " OtherPerm: 0x%02x", OtherPerm); - DEBUG_LOG(IOS_FILEIO, " Attributes: 0x%02x", Attributes); - - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::GetAttribute(const IOCtlRequest& request) -{ - DEBUG_ASSERT_MSG(IOS_FILEIO, request.buffer_out_size == 76, - " GET_ATTR needs an 76 bytes large output buffer but it is %i bytes large", - request.buffer_out_size); - - u32 OwnerID = 0; - u16 GroupID = 0x3031; // this is also known as makercd, 01 (0x3031) for nintendo and 08 - // (0x3038) for MH3 etc - - const std::string wii_path = Memory::GetString(request.buffer_in, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return GetFSReply(FS_EINVAL); - } - - std::string Filename = BuildFilename(wii_path); - u8 OwnerPerm = 0x3; // read/write - u8 GroupPerm = 0x3; // read/write - u8 OtherPerm = 0x3; // read/write - u8 Attributes = 0x00; // no attributes - - // Hack: if the path that is being accessed is within an installed title directory, get the - // UID/GID from the installed title TMD. - u64 title_id; - if (IsTitlePath(Filename, Common::FROM_SESSION_ROOT, &title_id)) - { - IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(title_id); - if (tmd.IsValid()) - { - GroupID = tmd.GetGroupId(); - } - } - - if (File::IsDirectory(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: GET_ATTR Directory %s - all permission flags are set", - Filename.c_str()); - } - else - { - if (File::Exists(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: GET_ATTR %s - all permission flags are set", Filename.c_str()); - } - else - { - INFO_LOG(IOS_FILEIO, "FS: GET_ATTR unknown %s", Filename.c_str()); - return GetFSReply(FS_ENOENT); - } - } - - // write answer to buffer - if (request.buffer_out_size == 76) - { - u32 Addr = request.buffer_out; - Memory::Write_U32(OwnerID, Addr); - Addr += 4; - Memory::Write_U16(GroupID, Addr); - Addr += 2; - memcpy(Memory::GetPointer(Addr), Memory::GetPointer(request.buffer_in), 64); - Addr += 64; - Memory::Write_U8(OwnerPerm, Addr); - Addr += 1; - Memory::Write_U8(GroupPerm, Addr); - Addr += 1; - Memory::Write_U8(OtherPerm, Addr); - Addr += 1; - Memory::Write_U8(Attributes, Addr); - Addr += 1; - } - - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::DeleteFile(const IOCtlRequest& request) -{ - DEBUG_ASSERT(request.buffer_out_size == 0); - int Offset = 0; - - const std::string wii_path = Memory::GetString(request.buffer_in + Offset, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return GetFSReply(FS_EINVAL); - } - - std::string Filename = BuildFilename(wii_path); - Offset += 64; - if (File::Delete(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: DeleteFile %s", Filename.c_str()); - } - else if (File::DeleteDir(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: DeleteDir %s", Filename.c_str()); - } - else - { - WARN_LOG(IOS_FILEIO, "FS: DeleteFile %s - failed!!!", Filename.c_str()); - } - - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::RenameFile(const IOCtlRequest& request) -{ - DEBUG_ASSERT(request.buffer_out_size == 0); - int Offset = 0; - - const std::string wii_path = Memory::GetString(request.buffer_in + Offset, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return GetFSReply(FS_EINVAL); - } - std::string Filename = BuildFilename(wii_path); - Offset += 64; - - const std::string wii_path_rename = Memory::GetString(request.buffer_in + Offset, 64); - if (!IsValidWiiPath(wii_path_rename)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path_rename.c_str()); - return GetFSReply(FS_EINVAL); - } - - std::string FilenameRename = BuildFilename(wii_path_rename); - Offset += 64; - - // try to make the basis directory - File::CreateFullPath(FilenameRename); - - // if there is already a file, delete it - if (File::Exists(Filename) && File::Exists(FilenameRename)) - { - File::Delete(FilenameRename); - } - - // finally try to rename the file - if (File::Rename(Filename, FilenameRename)) - { - INFO_LOG(IOS_FILEIO, "FS: Rename %s to %s", Filename.c_str(), FilenameRename.c_str()); - } - else - { - ERROR_LOG(IOS_FILEIO, "FS: Rename %s to %s - failed", Filename.c_str(), FilenameRename.c_str()); - return GetFSReply(FS_ENOENT); - } - - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::CreateFile(const IOCtlRequest& request) -{ - DEBUG_ASSERT(request.buffer_out_size == 0); - - u32 Addr = request.buffer_in; - u32 OwnerID = Memory::Read_U32(Addr); - Addr += 4; - u16 GroupID = Memory::Read_U16(Addr); - Addr += 2; - - const std::string wii_path = Memory::GetString(Addr, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return GetFSReply(FS_EINVAL); - } - - std::string Filename(BuildFilename(wii_path)); - Addr += 64; - u8 OwnerPerm = Memory::Read_U8(Addr); - Addr++; - u8 GroupPerm = Memory::Read_U8(Addr); - Addr++; - u8 OtherPerm = Memory::Read_U8(Addr); - Addr++; - u8 Attributes = Memory::Read_U8(Addr); - Addr++; - - INFO_LOG(IOS_FILEIO, "FS: CreateFile %s", Filename.c_str()); - DEBUG_LOG(IOS_FILEIO, " OwnerID: 0x%08x", OwnerID); - DEBUG_LOG(IOS_FILEIO, " GroupID: 0x%04x", GroupID); - DEBUG_LOG(IOS_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm); - DEBUG_LOG(IOS_FILEIO, " GroupPerm: 0x%02x", GroupPerm); - DEBUG_LOG(IOS_FILEIO, " OtherPerm: 0x%02x", OtherPerm); - DEBUG_LOG(IOS_FILEIO, " Attributes: 0x%02x", Attributes); - - // check if the file already exist - if (File::Exists(Filename)) - { - INFO_LOG(IOS_FILEIO, "\tresult = FS_EEXIST"); - return GetFSReply(FS_EEXIST); - } - - // create the file - File::CreateFullPath(Filename); // just to be sure - bool Result = File::CreateEmptyFile(Filename); - if (!Result) - { - ERROR_LOG(IOS_FILEIO, "FS: couldn't create new file"); - PanicAlert("FS: couldn't create new file"); - return GetFSReply(FS_EINVAL); - } - - INFO_LOG(IOS_FILEIO, "\tresult = IPC_SUCCESS"); - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::Shutdown(const IOCtlRequest& request) -{ - // TODO: stop emulation - INFO_LOG(IOS_FILEIO, "Wii called Shutdown()"); - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::ReadDirectory(const IOCtlVRequest& request) -{ - const std::string relative_path = - Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); - - if (!IsValidWiiPath(relative_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", relative_path.c_str()); - return GetFSReply(FS_EINVAL); - } - - // the Wii uses this function to define the type (dir or file) - std::string DirName(BuildFilename(relative_path)); - - INFO_LOG(IOS_FILEIO, "FS: IOCTL_READ_DIR %s", DirName.c_str()); - - const File::FileInfo file_info(DirName); - - if (!file_info.Exists()) - { - WARN_LOG(IOS_FILEIO, "FS: Search not found: %s", DirName.c_str()); - return GetFSReply(FS_ENOENT); - } - - if (!file_info.IsDirectory()) - { - // It's not a directory, so error. - // Games don't usually seem to care WHICH error they get, as long as it's < - // Well the system menu CARES! - WARN_LOG(IOS_FILEIO, "\tNot a directory - return FS_EINVAL"); - return GetFSReply(FS_EINVAL); - } - - File::FSTEntry entry = File::ScanDirectoryTree(DirName, false); - - // it is one - if ((request.in_vectors.size() == 1) && (request.io_vectors.size() == 1)) - { - size_t numFile = entry.children.size(); - INFO_LOG(IOS_FILEIO, "\t%zu files found", numFile); - - Memory::Write_U32((u32)numFile, request.io_vectors[0].address); - } - else - { - for (File::FSTEntry& child : entry.children) - { - // Decode escaped invalid file system characters so that games (such as - // Harry Potter and the Half-Blood Prince) can find what they expect. - child.virtualName = Common::UnescapeFileName(child.virtualName); - } - - std::sort(entry.children.begin(), entry.children.end(), - [](const File::FSTEntry& one, const File::FSTEntry& two) { - return one.virtualName < two.virtualName; - }); - - u32 MaxEntries = Memory::Read_U32(request.in_vectors[0].address); - - memset(Memory::GetPointer(request.io_vectors[0].address), 0, request.io_vectors[0].size); - - size_t numFiles = 0; - char* pFilename = (char*)Memory::GetPointer((u32)(request.io_vectors[0].address)); - - for (size_t i = 0; i < entry.children.size() && i < MaxEntries; i++) - { - const std::string& FileName = entry.children[i].virtualName; - - strcpy(pFilename, FileName.c_str()); - pFilename += FileName.length(); - *pFilename++ = 0x00; // termination - numFiles++; - - INFO_LOG(IOS_FILEIO, "\tFound: %s", FileName.c_str()); - } - - Memory::Write_U32((u32)numFiles, request.io_vectors[1].address); - } - - return GetFSReply(IPC_SUCCESS); -} - -IPCCommandResult FS::GetUsage(const IOCtlVRequest& request) -{ - DEBUG_ASSERT(request.io_vectors.size() == 2); - DEBUG_ASSERT(request.io_vectors[0].size == 4); - DEBUG_ASSERT(request.io_vectors[1].size == 4); - - // this command sucks because it asks of the number of used - // fsBlocks and inodes - // It should be correct, but don't count on it... - std::string relativepath = - Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); - - if (!IsValidWiiPath(relativepath)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", relativepath.c_str()); - return GetFSReply(FS_EINVAL); - } - - std::string path(BuildFilename(relativepath)); - u32 fsBlocks = 0; - u32 iNodes = 0; - - INFO_LOG(IOS_FILEIO, "IOCTL_GETUSAGE %s", path.c_str()); - if (File::IsDirectory(path)) - { - File::FSTEntry parentDir = File::ScanDirectoryTree(path, true); - // add one for the folder itself - iNodes = 1 + (u32)parentDir.size; - - u64 totalSize = ComputeTotalFileSize(parentDir); // "Real" size, to be converted to nand blocks - - fsBlocks = (u32)(totalSize / (16 * 1024)); // one bock is 16kb - - INFO_LOG(IOS_FILEIO, "FS: fsBlock: %i, iNodes: %i", fsBlocks, iNodes); - } - else - { - fsBlocks = 0; - iNodes = 0; - WARN_LOG(IOS_FILEIO, "FS: fsBlock failed, cannot find directory: %s", path.c_str()); - } - - Memory::Write_U32(fsBlocks, request.io_vectors[0].address); - Memory::Write_U32(iNodes, request.io_vectors[1].address); - - return GetFSReply(IPC_SUCCESS); -} -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/FS/FS.h b/Source/Core/Core/IOS/FS/FS.h deleted file mode 100644 index 0859e874cb..0000000000 --- a/Source/Core/Core/IOS/FS/FS.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include - -#include "Common/CommonTypes.h" -#include "Core/IOS/Device.h" -#include "Core/IOS/IOS.h" - -class PointerWrap; - -namespace IOS -{ -namespace HLE -{ -struct NANDStat -{ - u32 BlockSize; - u32 FreeUserBlocks; - u32 UsedUserBlocks; - u32 FreeSysBlocks; - u32 UsedSysBlocks; - u32 Free_INodes; - u32 Used_Inodes; -}; - -namespace Device -{ -class FS : public Device -{ -public: - FS(Kernel& ios, const std::string& device_name); - - void DoState(PointerWrap& p) override; - - IPCCommandResult IOCtl(const IOCtlRequest& request) override; - IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - -private: - enum - { - IOCTL_GET_STATS = 0x02, - IOCTL_CREATE_DIR = 0x03, - IOCTLV_READ_DIR = 0x04, - IOCTL_SET_ATTR = 0x05, - IOCTL_GET_ATTR = 0x06, - IOCTL_DELETE_FILE = 0x07, - IOCTL_RENAME_FILE = 0x08, - IOCTL_CREATE_FILE = 0x09, - IOCTLV_GETUSAGE = 0x0C, - IOCTL_SHUTDOWN = 0x0D - }; - - IPCCommandResult GetFSReply(s32 return_value) const; - - IPCCommandResult GetStats(const IOCtlRequest& request); - IPCCommandResult CreateDirectory(const IOCtlRequest& request); - IPCCommandResult SetAttribute(const IOCtlRequest& request); - IPCCommandResult GetAttribute(const IOCtlRequest& request); - IPCCommandResult DeleteFile(const IOCtlRequest& request); - IPCCommandResult RenameFile(const IOCtlRequest& request); - IPCCommandResult CreateFile(const IOCtlRequest& request); - IPCCommandResult Shutdown(const IOCtlRequest& request); - - IPCCommandResult ReadDirectory(const IOCtlVRequest& request); - IPCCommandResult GetUsage(const IOCtlVRequest& request); -}; -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/FS/FileIO.cpp b/Source/Core/Core/IOS/FS/FileIO.cpp deleted file mode 100644 index 1c35ff6615..0000000000 --- a/Source/Core/Core/IOS/FS/FileIO.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "Core/IOS/FS/FileIO.h" - -#include -#include -#include -#include -#include - -#include "Common/Assert.h" -#include "Common/ChunkFile.h" -#include "Common/CommonTypes.h" -#include "Common/File.h" -#include "Common/FileUtil.h" -#include "Common/NandPaths.h" -#include "Core/HW/Memmap.h" -#include "Core/IOS/IOS.h" - -namespace IOS -{ -namespace HLE -{ -static std::map> openFiles; - -// This is used by several of the FileIO and /dev/fs functions -std::string BuildFilename(const std::string& wii_path) -{ - std::string nand_path = File::GetUserPath(D_SESSION_WIIROOT_IDX); - if (wii_path.compare(0, 1, "/") == 0) - return nand_path + Common::EscapePath(wii_path); - - ASSERT(false); - return nand_path; -} - -namespace Device -{ -FileIO::FileIO(Kernel& ios, const std::string& device_name) - : Device(ios, device_name, DeviceType::FileIO) -{ -} - -IPCCommandResult FileIO::Close(u32 fd) -{ - INFO_LOG(IOS_FILEIO, "FileIO: Close %s", m_name.c_str()); - m_Mode = 0; - - // Let go of our pointer to the file, it will automatically close if we are the last handle - // accessing it. - m_file.reset(); - - m_is_active = false; - return GetDefaultReply(IPC_SUCCESS); -} - -IPCCommandResult FileIO::Open(const OpenRequest& request) -{ - m_Mode = request.flags; - - static const char* const Modes[] = {"Unk Mode", "Read only", "Write only", "Read and Write"}; - - m_filepath = BuildFilename(m_name); - - // The file must exist before we can open it - // It should be created by ISFS_CreateFile, not here - if (!File::IsFile(m_filepath)) - { - WARN_LOG(IOS_FILEIO, "FileIO: Open (%s) failed - File doesn't exist %s", Modes[m_Mode], - m_filepath.c_str()); - return GetDefaultReply(FS_ENOENT); - } - - INFO_LOG(IOS_FILEIO, "FileIO: Open %s (%s == %08X)", m_name.c_str(), Modes[m_Mode], m_Mode); - OpenFile(); - - m_is_active = true; - return GetDefaultReply(IPC_SUCCESS); -} - -// This isn't theadsafe, but it's only called from the CPU thread. -void FileIO::OpenFile() -{ - // On the wii, all file operations are strongly ordered. - // If a game opens the same file twice (or 8 times, looking at you PokePark Wii) - // and writes to one file handle, it will be able to immediately read the written - // data from the other handle. - // On 'real' operating systems, there are various buffers and caches meaning - // applications doing such naughty things will not get expected results. - - // So we fix this by catching any attempts to open the same file twice and - // only opening one file. Accesses to a single file handle are ordered. - // - // Hall of Shame: - // - PokePark Wii (gets stuck on the loading screen of Pikachu falling) - // - PokePark 2 (Also gets stuck while loading) - // - Wii System Menu (Can't access the system settings, gets stuck on blank screen) - // - The Beatles: Rock Band (saving doesn't work) - - // Check if the file has already been opened. - auto search = openFiles.find(m_name); - if (search != openFiles.end()) - { - m_file = search->second.lock(); // Lock a shared pointer to use. - } - else - { - std::string path = m_name; - // This code will be called when all references to the shared pointer below have been removed. - auto deleter = [path](File::IOFile* ptr) { - delete ptr; // IOFile's deconstructor closes the file. - openFiles.erase(path); // erase the weak pointer from the list of open files. - }; - - // All files are opened read/write. Actual access rights will be controlled per handle by the - // read/write functions below - m_file = std::shared_ptr(new File::IOFile(m_filepath, "r+b"), - deleter); // Use the custom deleter from above. - - // Store a weak pointer to our newly opened file in the cache. - openFiles[path] = std::weak_ptr(m_file); - } -} - -IPCCommandResult FileIO::Seek(const SeekRequest& request) -{ - if (!m_file->IsOpen()) - return GetDefaultReply(FS_ENOENT); - - const u32 file_size = static_cast(m_file->GetSize()); - DEBUG_LOG(IOS_FILEIO, "FileIO: Seek Pos: 0x%08x, Mode: %i (%s, Length=0x%08x)", request.offset, - request.mode, m_name.c_str(), file_size); - - u32 new_position = 0; - switch (request.mode) - { - case IOS_SEEK_SET: - new_position = request.offset; - break; - - case IOS_SEEK_CUR: - new_position = m_SeekPos + request.offset; - break; - - case IOS_SEEK_END: - new_position = file_size + request.offset; - break; - - default: - return GetDefaultReply(FS_EINVAL); - } - - if (new_position > file_size) - return GetDefaultReply(FS_EINVAL); - - m_SeekPos = new_position; - return GetDefaultReply(new_position); -} - -IPCCommandResult FileIO::Read(const ReadWriteRequest& request) -{ - if (!m_file->IsOpen()) - { - ERROR_LOG(IOS_FILEIO, "Failed to read from %s (Addr=0x%08x Size=0x%x) - file could " - "not be opened or does not exist", - m_name.c_str(), request.buffer, request.size); - return GetDefaultReply(FS_ENOENT); - } - - if (m_Mode == IOS_OPEN_WRITE) - { - WARN_LOG(IOS_FILEIO, "Attempted to read 0x%x bytes to 0x%08x on a write-only file %s", - request.size, request.buffer, m_name.c_str()); - return GetDefaultReply(FS_EACCESS); - } - - u32 requested_read_length = request.size; - const u32 file_size = static_cast(m_file->GetSize()); - // IOS has this check in the read request handler. - if (requested_read_length + m_SeekPos > file_size) - requested_read_length = file_size - m_SeekPos; - - DEBUG_LOG(IOS_FILEIO, "Read 0x%x bytes to 0x%08x from %s", request.size, request.buffer, - m_name.c_str()); - m_file->Seek(m_SeekPos, SEEK_SET); // File might be opened twice, need to seek before we read - const u32 number_of_bytes_read = static_cast( - fread(Memory::GetPointer(request.buffer), 1, requested_read_length, m_file->GetHandle())); - - if (number_of_bytes_read != requested_read_length && ferror(m_file->GetHandle())) - return GetDefaultReply(FS_EACCESS); - - // IOS returns the number of bytes read and adds that value to the seek position, - // instead of adding the *requested* read length. - m_SeekPos += number_of_bytes_read; - return GetDefaultReply(number_of_bytes_read); -} - -IPCCommandResult FileIO::Write(const ReadWriteRequest& request) -{ - s32 return_value = FS_EACCESS; - if (m_file->IsOpen()) - { - if (m_Mode == IOS_OPEN_READ) - { - WARN_LOG(IOS_FILEIO, - "FileIO: Attempted to write 0x%x bytes from 0x%08x to a read-only file %s", - request.size, request.buffer, m_name.c_str()); - } - else - { - DEBUG_LOG(IOS_FILEIO, "FileIO: Write 0x%04x bytes from 0x%08x to %s", request.size, - request.buffer, m_name.c_str()); - m_file->Seek(m_SeekPos, - SEEK_SET); // File might be opened twice, need to seek before we write - if (m_file->WriteBytes(Memory::GetPointer(request.buffer), request.size)) - { - return_value = request.size; - m_SeekPos += request.size; - } - } - } - else - { - ERROR_LOG(IOS_FILEIO, "FileIO: Failed to read from %s (Addr=0x%08x Size=0x%x) - file could " - "not be opened or does not exist", - m_name.c_str(), request.buffer, request.size); - return_value = FS_ENOENT; - } - - return GetDefaultReply(return_value); -} - -IPCCommandResult FileIO::IOCtl(const IOCtlRequest& request) -{ - DEBUG_LOG(IOS_FILEIO, "FileIO: IOCtl (Device=%s)", m_name.c_str()); - - switch (request.request) - { - case ISFS_IOCTL_GETFILESTATS: - return GetFileStats(request); - - default: - request.Log(GetDeviceName(), LogTypes::IOS_FILEIO, LogTypes::LERROR); - break; - } - - return GetDefaultReply(IPC_SUCCESS); -} - -void FileIO::PrepareForState(PointerWrap::Mode mode) -{ - // Temporally close the file, to prevent any issues with the savestating of /tmp - // it can be opened again with another call to OpenFile() - m_file.reset(); -} - -void FileIO::DoState(PointerWrap& p) -{ - DoStateShared(p); - - p.Do(m_Mode); - p.Do(m_SeekPos); - - m_filepath = BuildFilename(m_name); - - // The file was closed during state (and might now be pointing at another file) - // Open it again - OpenFile(); -} - -IPCCommandResult FileIO::GetFileStats(const IOCtlRequest& request) -{ - if (!m_file->IsOpen()) - return GetDefaultReply(FS_ENOENT); - - DEBUG_LOG(IOS_FILEIO, "File: %s, Length: %" PRIu64 ", Pos: %u", m_name.c_str(), m_file->GetSize(), - m_SeekPos); - Memory::Write_U32(static_cast(m_file->GetSize()), request.buffer_out); - Memory::Write_U32(m_SeekPos, request.buffer_out + 4); - return GetDefaultReply(IPC_SUCCESS); -} -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/FS/FileIO.h b/Source/Core/Core/IOS/FS/FileIO.h deleted file mode 100644 index f44b76f344..0000000000 --- a/Source/Core/Core/IOS/FS/FileIO.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include - -#include "Common/ChunkFile.h" -#include "Common/CommonTypes.h" -#include "Core/IOS/Device.h" -#include "Core/IOS/IOS.h" - -class PointerWrap; - -namespace File -{ -class IOFile; -} - -namespace IOS -{ -namespace HLE -{ -std::string BuildFilename(const std::string& wii_path); - -namespace Device -{ -class FileIO : public Device -{ -public: - FileIO(Kernel& ios, const std::string& device_name); - - IPCCommandResult Close(u32 fd) override; - IPCCommandResult Open(const OpenRequest& request) override; - IPCCommandResult Seek(const SeekRequest& request) override; - IPCCommandResult Read(const ReadWriteRequest& request) override; - IPCCommandResult Write(const ReadWriteRequest& request) override; - IPCCommandResult IOCtl(const IOCtlRequest& request) override; - void PrepareForState(PointerWrap::Mode mode) override; - void DoState(PointerWrap& p) override; - - void OpenFile(); - -private: - enum - { - ISFS_FUNCNULL = 0, - ISFS_FUNCGETSTAT = 1, - ISFS_FUNCREADDIR = 2, - ISFS_FUNCGETATTR = 3, - ISFS_FUNCGETUSAGE = 4 - }; - - enum - { - ISFS_IOCTL_FORMAT = 1, - ISFS_IOCTL_GETSTATS = 2, - ISFS_IOCTL_CREATEDIR = 3, - ISFS_IOCTL_READDIR = 4, - ISFS_IOCTL_SETATTR = 5, - ISFS_IOCTL_GETATTR = 6, - ISFS_IOCTL_DELETE = 7, - ISFS_IOCTL_RENAME = 8, - ISFS_IOCTL_CREATEFILE = 9, - ISFS_IOCTL_SETFILEVERCTRL = 10, - ISFS_IOCTL_GETFILESTATS = 11, - ISFS_IOCTL_GETUSAGE = 12, - ISFS_IOCTL_SHUTDOWN = 13 - }; - - IPCCommandResult GetFileStats(const IOCtlRequest& request); - - u32 m_Mode = 0; - u32 m_SeekPos = 0; - - std::string m_filepath; - std::shared_ptr m_file; -}; -} // namespace Device -} // namespace HLE -} // namespace IOS diff --git a/Source/Core/Core/IOS/FS/FileSystem.cpp b/Source/Core/Core/IOS/FS/FileSystem.cpp index 74cee8e9cd..50aa764ee7 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.cpp +++ b/Source/Core/Core/IOS/FS/FileSystem.cpp @@ -12,4 +12,10 @@ std::unique_ptr MakeFileSystem() { return std::make_unique(); } + +void FileSystem::Init() +{ + if (Delete(0, 0, "/tmp") == ResultCode::Success) + CreateDirectory(0, 0, "/tmp", 0, Mode::ReadWrite, Mode::ReadWrite, Mode::ReadWrite); +} } // namespace IOS::HLE::FS diff --git a/Source/Core/Core/IOS/FS/FileSystem.h b/Source/Core/Core/IOS/FS/FileSystem.h index f56df41998..94fc430230 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.h +++ b/Source/Core/Core/IOS/FS/FileSystem.h @@ -169,6 +169,9 @@ public: virtual Result GetNandStats() = 0; /// Get usage information about a directory (used cluster and inode counts). virtual Result GetDirectoryStats(const std::string& path) = 0; + +protected: + void Init(); }; std::unique_ptr MakeFileSystem(); diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp new file mode 100644 index 0000000000..e73d502f8a --- /dev/null +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp @@ -0,0 +1,466 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/IOS/FS/FileSystemProxy.h" + +#include + +#include "Common/Assert.h" +#include "Common/ChunkFile.h" +#include "Common/StringUtil.h" +#include "Common/Swap.h" +#include "Core/HW/Memmap.h" +#include "Core/HW/SystemTimers.h" +#include "Core/IOS/FS/FileSystem.h" + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +using namespace IOS::HLE::FS; + +static s32 ConvertResult(ResultCode code) +{ + if (code == ResultCode::Success) + return IPC_SUCCESS; + return -(static_cast(code) + 100); +} + +// XXX: timing is not the same for all commands and in all cases. +static IPCCommandResult GetFSReply(s32 return_value) +{ + return {return_value, true, SystemTimers::GetTicksPerSecond() / 500}; +} + +FS::FS(Kernel& ios, const std::string& device_name) : Device(ios, device_name) +{ +} + +void FS::DoState(PointerWrap& p) +{ + p.Do(m_fd_map); +} + +static void LogResult(const std::string& command, ResultCode code) +{ + GENERIC_LOG(LogTypes::IOS_FS, (code == ResultCode::Success ? LogTypes::LINFO : LogTypes::LERROR), + "%s: result %d", command.c_str(), ConvertResult(code)); +} + +template +static void LogResult(const std::string& command, const Result& result) +{ + LogResult(command, result.Succeeded() ? ResultCode::Success : result.Error()); +} + +IPCCommandResult FS::Open(const OpenRequest& request) +{ + if (m_fd_map.size() >= 16) + return GetDefaultReply(ConvertResult(ResultCode::NoFreeHandle)); + + if (request.path.size() >= 64) + return GetDefaultReply(ConvertResult(ResultCode::Invalid)); + + if (request.path == "/dev/fs") + { + m_fd_map[request.fd] = {request.gid, request.uid, INVALID_FD}; + return GetDefaultReply(IPC_SUCCESS); + } + + const auto backend_fd = m_ios.GetFS()->OpenFile(request.uid, request.gid, request.path, + static_cast(request.flags & 3)); + LogResult(StringFromFormat("OpenFile(%s)", request.path.c_str()), backend_fd); + if (!backend_fd) + return GetFSReply(ConvertResult(backend_fd.Error())); + + m_fd_map[request.fd] = {request.gid, request.uid, *backend_fd}; + std::strncpy(m_fd_map[request.fd].name.data(), request.path.c_str(), 64); + return GetFSReply(IPC_SUCCESS); +} + +IPCCommandResult FS::Close(u32 fd) +{ + if (m_fd_map[fd].fs_fd != INVALID_FD) + { + const ResultCode result = m_ios.GetFS()->Close(m_fd_map[fd].fs_fd); + LogResult(StringFromFormat("Close(%s)", m_fd_map[fd].name.data()), result); + m_fd_map.erase(fd); + if (result != ResultCode::Success) + return GetFSReply(ConvertResult(result)); + } + else + { + m_fd_map.erase(fd); + } + return GetFSReply(IPC_SUCCESS); +} + +IPCCommandResult FS::Read(const ReadWriteRequest& request) +{ + const Handle& handle = m_fd_map[request.fd]; + if (handle.fs_fd == INVALID_FD) + return GetDefaultReply(ConvertResult(ResultCode::Invalid)); + + const Result result = m_ios.GetFS()->ReadBytesFromFile( + handle.fs_fd, Memory::GetPointer(request.buffer), request.size); + LogResult( + StringFromFormat("Read(%s, 0x%08x, %u)", handle.name.data(), request.buffer, request.size), + result); + if (!result) + return GetDefaultReply(ConvertResult(result.Error())); + return GetDefaultReply(*result); +} + +IPCCommandResult FS::Write(const ReadWriteRequest& request) +{ + const Handle& handle = m_fd_map[request.fd]; + if (handle.fs_fd == INVALID_FD) + return GetDefaultReply(ConvertResult(ResultCode::Invalid)); + + const Result result = m_ios.GetFS()->WriteBytesToFile( + handle.fs_fd, Memory::GetPointer(request.buffer), request.size); + LogResult( + StringFromFormat("Write(%s, 0x%08x, %u)", handle.name.data(), request.buffer, request.size), + result); + if (!result) + return GetDefaultReply(ConvertResult(result.Error())); + return GetDefaultReply(*result); +} + +IPCCommandResult FS::Seek(const SeekRequest& request) +{ + const Handle& handle = m_fd_map[request.fd]; + if (handle.fs_fd == INVALID_FD) + return GetDefaultReply(ConvertResult(ResultCode::Invalid)); + + const Result result = + m_ios.GetFS()->SeekFile(handle.fs_fd, request.offset, IOS::HLE::FS::SeekMode(request.mode)); + LogResult( + StringFromFormat("Seek(%s, 0x%08x, %u)", handle.name.data(), request.offset, request.mode), + result); + if (!result) + return GetDefaultReply(ConvertResult(result.Error())); + return GetDefaultReply(*result); +} + +#pragma pack(push, 1) +struct ISFSParams +{ + Common::BigEndianValue uid; + Common::BigEndianValue gid; + char path[64]; + Mode owner_mode; + Mode group_mode; + Mode other_mode; + FileAttribute attribute; +}; + +struct ISFSNandStats +{ + Common::BigEndianValue cluster_size; + Common::BigEndianValue free_clusters; + Common::BigEndianValue used_clusters; + Common::BigEndianValue bad_clusters; + Common::BigEndianValue reserved_clusters; + Common::BigEndianValue free_inodes; + Common::BigEndianValue used_inodes; +}; + +struct ISFSFileStats +{ + Common::BigEndianValue size; + Common::BigEndianValue seek_position; +}; +#pragma pack(pop) + +template +static Result GetParams(const IOCtlRequest& request) +{ + if (request.buffer_in_size < sizeof(T)) + return ResultCode::Invalid; + + T params; + Memory::CopyFromEmu(¶ms, request.buffer_in, sizeof(params)); + return params; +} + +IPCCommandResult FS::IOCtl(const IOCtlRequest& request) +{ + const auto it = m_fd_map.find(request.fd); + if (it == m_fd_map.end()) + return GetDefaultReply(ConvertResult(ResultCode::Invalid)); + + switch (request.request) + { + case ISFS_IOCTL_FORMAT: + return Format(it->second, request); + case ISFS_IOCTL_GETSTATS: + return GetStats(it->second, request); + case ISFS_IOCTL_CREATEDIR: + return CreateDirectory(it->second, request); + case ISFS_IOCTL_SETATTR: + return SetAttribute(it->second, request); + case ISFS_IOCTL_GETATTR: + return GetAttribute(it->second, request); + case ISFS_IOCTL_DELETE: + return DeleteFile(it->second, request); + case ISFS_IOCTL_RENAME: + return RenameFile(it->second, request); + case ISFS_IOCTL_CREATEFILE: + return CreateFile(it->second, request); + case ISFS_IOCTL_SETFILEVERCTRL: + return SetFileVersionControl(it->second, request); + case ISFS_IOCTL_GETFILESTATS: + return GetFileStats(it->second, request); + case ISFS_IOCTL_SHUTDOWN: + return Shutdown(it->second, request); + default: + return GetFSReply(ConvertResult(ResultCode::Invalid)); + } +} + +IPCCommandResult FS::IOCtlV(const IOCtlVRequest& request) +{ + const auto it = m_fd_map.find(request.fd); + if (it == m_fd_map.end()) + return GetDefaultReply(ConvertResult(ResultCode::Invalid)); + + switch (request.request) + { + case ISFS_IOCTLV_READDIR: + return ReadDirectory(it->second, request); + case ISFS_IOCTLV_GETUSAGE: + return GetUsage(it->second, request); + default: + return GetFSReply(ConvertResult(ResultCode::Invalid)); + } +} + +IPCCommandResult FS::Format(const Handle& handle, const IOCtlRequest& request) +{ + if (handle.uid != 0) + return GetFSReply(ConvertResult(ResultCode::AccessDenied)); + + const ResultCode result = m_ios.GetFS()->Format(handle.uid); + return GetFSReply(ConvertResult(result)); +} + +IPCCommandResult FS::GetStats(const Handle& handle, const IOCtlRequest& request) +{ + if (request.buffer_out_size < sizeof(ISFSNandStats)) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + + const Result stats = m_ios.GetFS()->GetNandStats(); + LogResult("GetNandStats", stats); + if (!stats) + return GetDefaultReply(ConvertResult(stats.Error())); + + ISFSNandStats out; + out.cluster_size = stats->cluster_size; + out.free_clusters = stats->free_clusters; + out.used_clusters = stats->used_clusters; + out.bad_clusters = stats->bad_clusters; + out.reserved_clusters = stats->reserved_clusters; + out.free_inodes = stats->free_inodes; + out.used_inodes = stats->used_inodes; + Memory::CopyToEmu(request.buffer_out, &out, sizeof(out)); + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult FS::CreateDirectory(const Handle& handle, const IOCtlRequest& request) +{ + const auto params = GetParams(request); + if (!params) + return GetFSReply(ConvertResult(params.Error())); + + const ResultCode result = + m_ios.GetFS()->CreateDirectory(handle.uid, handle.gid, params->path, params->attribute, + params->owner_mode, params->group_mode, params->other_mode); + LogResult(StringFromFormat("CreateDirectory(%s)", params->path), result); + return GetFSReply(ConvertResult(result)); +} + +IPCCommandResult FS::ReadDirectory(const Handle& handle, const IOCtlVRequest& request) +{ + if (request.in_vectors.empty() || request.in_vectors.size() != request.io_vectors.size() || + request.in_vectors.size() > 2 || request.in_vectors[0].size != 64) + { + return GetFSReply(ConvertResult(ResultCode::Invalid)); + } + + u32 file_list_address, file_count_address, max_count; + if (request.in_vectors.size() == 2) + { + if (request.in_vectors[1].size != 4 || request.io_vectors[1].size != 4) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + max_count = Memory::Read_U32(request.in_vectors[1].address); + file_count_address = request.io_vectors[1].address; + file_list_address = request.io_vectors[0].address; + if (request.io_vectors[0].size != 13 * max_count) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + Memory::Write_U32(max_count, file_count_address); + } + else + { + if (request.io_vectors[0].size != 4) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + max_count = Memory::Read_U32(request.io_vectors[0].address); + file_count_address = request.io_vectors[0].address; + file_list_address = 0; + } + + const std::string directory = Memory::GetString(request.in_vectors[0].address, 64); + const Result> list = + m_ios.GetFS()->ReadDirectory(handle.uid, handle.gid, directory); + LogResult(StringFromFormat("ReadDirectory(%s)", directory.c_str()), list); + if (!list) + return GetFSReply(ConvertResult(list.Error())); + + if (!file_list_address) + { + Memory::Write_U32(static_cast(list->size()), file_count_address); + return GetFSReply(IPC_SUCCESS); + } + + for (size_t i = 0; i < list->size() && i < max_count; ++i) + { + Memory::Memset(file_list_address, 0, 13); + Memory::CopyToEmu(file_list_address, (*list)[i].data(), (*list)[i].size()); + Memory::Write_U8(0, file_list_address + 12); + file_list_address += 13; + } + return GetFSReply(IPC_SUCCESS); +} + +IPCCommandResult FS::SetAttribute(const Handle& handle, const IOCtlRequest& request) +{ + const auto params = GetParams(request); + if (!params) + return GetFSReply(ConvertResult(params.Error())); + + const ResultCode result = m_ios.GetFS()->SetMetadata( + handle.uid, params->path, params->uid, params->gid, params->attribute, params->owner_mode, + params->group_mode, params->other_mode); + LogResult(StringFromFormat("SetMetadata(%s)", params->path), result); + return GetFSReply(ConvertResult(result)); +} + +IPCCommandResult FS::GetAttribute(const Handle& handle, const IOCtlRequest& request) +{ + if (request.buffer_in_size < 64 || request.buffer_out_size < sizeof(ISFSParams)) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + + const std::string path = Memory::GetString(request.buffer_in, 64); + const Result metadata = m_ios.GetFS()->GetMetadata(handle.uid, handle.gid, path); + LogResult(StringFromFormat("GetMetadata(%s)", path.c_str()), metadata); + if (!metadata) + return GetFSReply(ConvertResult(metadata.Error())); + + // Yes, the other members aren't copied at all. Actually, IOS does not even memset + // the struct at all, which means uninitialised bytes from the stack are returned. + // For the sake of determinism, let's just zero initialise the struct. + ISFSParams out{}; + out.uid = metadata->uid; + out.gid = metadata->gid; + out.attribute = metadata->attribute; + out.owner_mode = metadata->owner_mode; + out.group_mode = metadata->group_mode; + out.other_mode = metadata->other_mode; + Memory::CopyToEmu(request.buffer_out, &out, sizeof(out)); + return GetFSReply(IPC_SUCCESS); +} + +IPCCommandResult FS::DeleteFile(const Handle& handle, const IOCtlRequest& request) +{ + if (request.buffer_in_size < 64) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + + const std::string path = Memory::GetString(request.buffer_in, 64); + const ResultCode result = m_ios.GetFS()->Delete(handle.uid, handle.gid, path); + LogResult(StringFromFormat("Delete(%s)", path.c_str()), result); + return GetFSReply(ConvertResult(result)); +} + +IPCCommandResult FS::RenameFile(const Handle& handle, const IOCtlRequest& request) +{ + if (request.buffer_in_size < 64 * 2) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + + const std::string old_path = Memory::GetString(request.buffer_in, 64); + const std::string new_path = Memory::GetString(request.buffer_in + 64, 64); + const ResultCode result = m_ios.GetFS()->Rename(handle.uid, handle.gid, old_path, new_path); + LogResult(StringFromFormat("Rename(%s, %s)", old_path.c_str(), new_path.c_str()), result); + return GetFSReply(ConvertResult(result)); +} + +IPCCommandResult FS::CreateFile(const Handle& handle, const IOCtlRequest& request) +{ + const auto params = GetParams(request); + if (!params) + return GetFSReply(ConvertResult(params.Error())); + + const ResultCode result = + m_ios.GetFS()->CreateFile(handle.uid, handle.gid, params->path, params->attribute, + params->owner_mode, params->group_mode, params->other_mode); + LogResult(StringFromFormat("CreateFile(%s)", params->path), result); + return GetFSReply(ConvertResult(result)); +} + +IPCCommandResult FS::SetFileVersionControl(const Handle& handle, const IOCtlRequest& request) +{ + const auto params = GetParams(request); + if (!params) + return GetFSReply(ConvertResult(params.Error())); + + // FS_SetFileVersionControl(ctx->uid, params->path, params->attribute) + ERROR_LOG(IOS_FS, "SetFileVersionControl(%s, 0x%x): Stubbed", params->path, params->attribute); + return GetFSReply(IPC_SUCCESS); +} + +IPCCommandResult FS::GetFileStats(const Handle& handle, const IOCtlRequest& request) +{ + if (request.buffer_out_size < 8 || handle.fs_fd == INVALID_FD) + return GetFSReply(ConvertResult(ResultCode::Invalid)); + + const Result status = m_ios.GetFS()->GetFileStatus(handle.fs_fd); + LogResult(StringFromFormat("GetFileStatus(%s)", handle.name.data()), status); + if (!status) + return GetDefaultReply(ConvertResult(status.Error())); + + ISFSFileStats out; + out.size = status->size; + out.seek_position = status->offset; + Memory::CopyToEmu(request.buffer_out, &out, sizeof(out)); + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult FS::GetUsage(const Handle& handle, const IOCtlVRequest& request) +{ + if (!request.HasNumberOfValidVectors(1, 2) || request.in_vectors[0].size != 64 || + request.io_vectors[0].size != 4 || request.io_vectors[1].size != 4) + { + return GetFSReply(ConvertResult(ResultCode::Invalid)); + } + + const std::string directory = Memory::GetString(request.in_vectors[0].address, 64); + const Result stats = m_ios.GetFS()->GetDirectoryStats(directory); + LogResult(StringFromFormat("GetDirectoryStats(%s)", directory.c_str()), stats); + if (!stats) + return GetFSReply(ConvertResult(stats.Error())); + + Memory::Write_U32(stats->used_clusters, request.io_vectors[0].address); + Memory::Write_U32(stats->used_inodes, request.io_vectors[1].address); + return GetFSReply(IPC_SUCCESS); +} + +IPCCommandResult FS::Shutdown(const Handle& handle, const IOCtlRequest& request) +{ + INFO_LOG(IOS_FS, "Shutdown"); + return GetFSReply(IPC_SUCCESS); +} +} // namespace Device +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.h b/Source/Core/Core/IOS/FS/FileSystemProxy.h new file mode 100644 index 0000000000..c14f77f049 --- /dev/null +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.h @@ -0,0 +1,86 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/IOS/Device.h" +#include "Core/IOS/FS/FileSystem.h" +#include "Core/IOS/IOS.h" + +class PointerWrap; + +namespace IOS +{ +namespace HLE +{ +namespace Device +{ +constexpr IOS::HLE::FS::Fd INVALID_FD = 0xffffffff; + +class FS : public Device +{ +public: + FS(Kernel& ios, const std::string& device_name); + + void DoState(PointerWrap& p) override; + + IPCCommandResult Open(const OpenRequest& request) override; + IPCCommandResult Close(u32 fd) override; + IPCCommandResult Read(const ReadWriteRequest& request) override; + IPCCommandResult Write(const ReadWriteRequest& request) override; + IPCCommandResult Seek(const SeekRequest& request) override; + IPCCommandResult IOCtl(const IOCtlRequest& request) override; + IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; + +private: + struct Handle + { + u16 gid = 0; + u32 uid = 0; + IOS::HLE::FS::Fd fs_fd = INVALID_FD; + // We use a std::array to keep this savestate friendly. + std::array name{}; + }; + + enum + { + ISFS_IOCTL_FORMAT = 1, + ISFS_IOCTL_GETSTATS = 2, + ISFS_IOCTL_CREATEDIR = 3, + ISFS_IOCTLV_READDIR = 4, + ISFS_IOCTL_SETATTR = 5, + ISFS_IOCTL_GETATTR = 6, + ISFS_IOCTL_DELETE = 7, + ISFS_IOCTL_RENAME = 8, + ISFS_IOCTL_CREATEFILE = 9, + ISFS_IOCTL_SETFILEVERCTRL = 10, + ISFS_IOCTL_GETFILESTATS = 11, + ISFS_IOCTLV_GETUSAGE = 12, + ISFS_IOCTL_SHUTDOWN = 13, + }; + + IPCCommandResult Format(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult GetStats(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult CreateDirectory(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult ReadDirectory(const Handle& handle, const IOCtlVRequest& request); + IPCCommandResult SetAttribute(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult GetAttribute(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult DeleteFile(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult RenameFile(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult CreateFile(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult SetFileVersionControl(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult GetFileStats(const Handle& handle, const IOCtlRequest& request); + IPCCommandResult GetUsage(const Handle& handle, const IOCtlVRequest& request); + IPCCommandResult Shutdown(const Handle& handle, const IOCtlRequest& request); + + std::map m_fd_map; +}; +} // namespace Device +} // namespace HLE +} // namespace IOS diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp index 37f14871c4..0a69e1acc0 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -45,7 +45,10 @@ static u64 ComputeTotalFileSize(const File::FSTEntry& parent_entry) return sizeOfFiles; } -HostFileSystem::HostFileSystem() = default; +HostFileSystem::HostFileSystem() +{ + Init(); +} HostFileSystem::~HostFileSystem() = default; diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index bd704fc895..862f40c8cc 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -31,9 +31,8 @@ #include "Core/IOS/Device.h" #include "Core/IOS/DeviceStub.h" #include "Core/IOS/ES/ES.h" -#include "Core/IOS/FS/FS.h" -#include "Core/IOS/FS/FileIO.h" #include "Core/IOS/FS/FileSystem.h" +#include "Core/IOS/FS/FileSystemProxy.h" #include "Core/IOS/MIOS.h" #include "Core/IOS/Network/IP/Top.h" #include "Core/IOS/Network/KD/NetKDRequest.h" @@ -495,7 +494,7 @@ IPCCommandResult Kernel::OpenDevice(OpenRequest& request) } else if (request.path.find('/') == 0) { - device = std::make_shared(*this, request.path); + device = GetDeviceByName("/dev/fs"); } if (!device) @@ -730,10 +729,6 @@ void Kernel::DoState(PointerWrap& p) m_fdmap[i] = GetDeviceByName(device_name); break; } - case Device::Device::DeviceType::FileIO: - m_fdmap[i] = std::make_shared(*this, ""); - m_fdmap[i]->DoState(p); - break; case Device::Device::DeviceType::OH0: m_fdmap[i] = std::make_shared(*this, ""); m_fdmap[i]->DoState(p); diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 727ecddf59..8956763c0f 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 94; // Last changed in PR 6456 +static const u32 STATE_VERSION = 95; // Last changed in PR 6421 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, From de15e09a4f74bc9608bf9792e4a03aa93736256f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 5 Mar 2018 22:47:53 +0100 Subject: [PATCH 07/10] Log: Rename IOS_FILEIO to IOS_FS Let's use the actual name of the system module. Also, the FS code is not all about files. --- Source/Core/Common/Logging/Log.h | 1 - Source/Core/Common/Logging/LogManager.cpp | 1 - Source/Core/Core/IOS/FS/HostBackend/FS.cpp | 56 +++++++++++----------- Source/Core/Core/WiiRoot.cpp | 5 +- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h index ee190e858b..c2dd70937e 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -31,7 +31,6 @@ enum LOG_TYPE IOS, IOS_DI, IOS_ES, - IOS_FILEIO, IOS_FS, IOS_NET, IOS_SD, diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index 0a3493a264..7d910b3a8b 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -99,7 +99,6 @@ LogManager::LogManager() m_log[LogTypes::IOS] = {"IOS", "IOS"}; m_log[LogTypes::IOS_DI] = {"IOS_DI", "IOS - Drive Interface"}; m_log[LogTypes::IOS_ES] = {"IOS_ES", "IOS - ETicket Services"}; - m_log[LogTypes::IOS_FILEIO] = {"IOS_FILEIO", "IOS - FileIO"}; m_log[LogTypes::IOS_FS] = {"IOS_FS", "IOS - Filesystem Services"}; m_log[LogTypes::IOS_SD] = {"IOS_SD", "IOS - SDIO"}; m_log[LogTypes::IOS_SSL] = {"IOS_SSL", "IOS - SSL"}; diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp index 0a69e1acc0..7efb461a75 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -173,7 +173,7 @@ ResultCode HostFileSystem::CreateFile(Uid, Gid, const std::string& path, FileAtt // check if the file already exist if (File::Exists(file_name)) { - INFO_LOG(IOS_FILEIO, "\tresult = FS_EEXIST"); + INFO_LOG(IOS_FS, "\tresult = FS_EEXIST"); return ResultCode::AlreadyExists; } @@ -181,11 +181,11 @@ ResultCode HostFileSystem::CreateFile(Uid, Gid, const std::string& path, FileAtt File::CreateFullPath(file_name); // just to be sure if (!File::CreateEmptyFile(file_name)) { - ERROR_LOG(IOS_FILEIO, "couldn't create new file"); + ERROR_LOG(IOS_FS, "couldn't create new file"); return ResultCode::Invalid; } - INFO_LOG(IOS_FILEIO, "\tresult = IPC_SUCCESS"); + INFO_LOG(IOS_FS, "\tresult = IPC_SUCCESS"); return ResultCode::Success; } @@ -195,7 +195,7 @@ ResultCode HostFileSystem::CreateDirectory(Uid, Gid, const std::string& path, { if (!IsValidWiiPath(path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; } @@ -203,7 +203,7 @@ ResultCode HostFileSystem::CreateDirectory(Uid, Gid, const std::string& path, name += "/"; File::CreateFullPath(name); - DEBUG_ASSERT_MSG(IOS_FILEIO, File::IsDirectory(name), "CREATE_DIR %s failed", name.c_str()); + DEBUG_ASSERT_MSG(IOS_FS, File::IsDirectory(name), "CREATE_DIR %s failed", name.c_str()); return ResultCode::Success; } @@ -212,17 +212,17 @@ ResultCode HostFileSystem::Delete(Uid, Gid, const std::string& path) { if (!IsValidWiiPath(path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; } const std::string file_name = BuildFilename(path); if (File::Delete(file_name)) - INFO_LOG(IOS_FILEIO, "DeleteFile %s", file_name.c_str()); + INFO_LOG(IOS_FS, "DeleteFile %s", file_name.c_str()); else if (File::DeleteDirRecursively(file_name)) - INFO_LOG(IOS_FILEIO, "DeleteDir %s", file_name.c_str()); + INFO_LOG(IOS_FS, "DeleteDir %s", file_name.c_str()); else - WARN_LOG(IOS_FILEIO, "DeleteFile %s - failed!!!", file_name.c_str()); + WARN_LOG(IOS_FS, "DeleteFile %s - failed!!!", file_name.c_str()); return ResultCode::Success; } @@ -232,14 +232,14 @@ ResultCode HostFileSystem::Rename(Uid, Gid, const std::string& old_path, { if (!IsValidWiiPath(old_path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", old_path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", old_path.c_str()); return ResultCode::Invalid; } std::string old_name = BuildFilename(old_path); if (!IsValidWiiPath(new_path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", new_path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", new_path.c_str()); return ResultCode::Invalid; } @@ -257,11 +257,11 @@ ResultCode HostFileSystem::Rename(Uid, Gid, const std::string& old_path, // finally try to rename the file if (File::Rename(old_name, new_name)) { - INFO_LOG(IOS_FILEIO, "Rename %s to %s", old_name.c_str(), new_name.c_str()); + INFO_LOG(IOS_FS, "Rename %s to %s", old_name.c_str(), new_name.c_str()); } else { - ERROR_LOG(IOS_FILEIO, "Rename %s to %s - failed", old_name.c_str(), new_name.c_str()); + ERROR_LOG(IOS_FS, "Rename %s to %s - failed", old_name.c_str(), new_name.c_str()); return ResultCode::NotFound; } @@ -272,20 +272,20 @@ Result> HostFileSystem::ReadDirectory(Uid, Gid, const s { if (!IsValidWiiPath(path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; } // the Wii uses this function to define the type (dir or file) const std::string dir_name(BuildFilename(path)); - INFO_LOG(IOS_FILEIO, "IOCTL_READ_DIR %s", dir_name.c_str()); + INFO_LOG(IOS_FS, "IOCTL_READ_DIR %s", dir_name.c_str()); const File::FileInfo file_info(dir_name); if (!file_info.Exists()) { - WARN_LOG(IOS_FILEIO, "Search not found: %s", dir_name.c_str()); + WARN_LOG(IOS_FS, "Search not found: %s", dir_name.c_str()); return ResultCode::NotFound; } @@ -294,7 +294,7 @@ Result> HostFileSystem::ReadDirectory(Uid, Gid, const s // It's not a directory, so error. // Games don't usually seem to care WHICH error they get, as long as it's < // Well the system menu CARES! - WARN_LOG(IOS_FILEIO, "\tNot a directory - return FS_EINVAL"); + WARN_LOG(IOS_FS, "\tNot a directory - return FS_EINVAL"); return ResultCode::Invalid; } @@ -318,7 +318,7 @@ Result> HostFileSystem::ReadDirectory(Uid, Gid, const s for (File::FSTEntry& child : entry.children) { output.emplace_back(child.virtualName); - INFO_LOG(IOS_FILEIO, "\tFound: %s", child.virtualName.c_str()); + INFO_LOG(IOS_FS, "\tFound: %s", child.virtualName.c_str()); } return output; } @@ -332,7 +332,7 @@ Result HostFileSystem::GetMetadata(Uid, Gid, const std::string& path) if (!IsValidWiiPath(path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; } @@ -357,17 +357,17 @@ Result HostFileSystem::GetMetadata(Uid, Gid, const std::string& path) metadata.size = info.GetSize(); if (info.IsDirectory()) { - INFO_LOG(IOS_FILEIO, "GET_ATTR Directory %s - all permission flags are set", file_name.c_str()); + INFO_LOG(IOS_FS, "GET_ATTR Directory %s - all permission flags are set", file_name.c_str()); } else { if (info.Exists()) { - INFO_LOG(IOS_FILEIO, "GET_ATTR %s - all permission flags are set", file_name.c_str()); + INFO_LOG(IOS_FS, "GET_ATTR %s - all permission flags are set", file_name.c_str()); } else { - INFO_LOG(IOS_FILEIO, "GET_ATTR unknown %s", file_name.c_str()); + INFO_LOG(IOS_FS, "GET_ATTR unknown %s", file_name.c_str()); return ResultCode::NotFound; } } @@ -380,7 +380,7 @@ ResultCode HostFileSystem::SetMetadata(Uid caller_uid, const std::string& path, { if (!IsValidWiiPath(path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; } return ResultCode::Success; @@ -388,7 +388,7 @@ ResultCode HostFileSystem::SetMetadata(Uid caller_uid, const std::string& path, Result HostFileSystem::GetNandStats() { - WARN_LOG(IOS_FILEIO, "GET STATS - returning static values for now"); + WARN_LOG(IOS_FS, "GET STATS - returning static values for now"); // TODO: scrape the real amounts from somewhere... NandStats stats{}; @@ -407,13 +407,13 @@ Result HostFileSystem::GetDirectoryStats(const std::string& wii_ { if (!IsValidWiiPath(wii_path)) { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + WARN_LOG(IOS_FS, "Not a valid path: %s", wii_path.c_str()); return ResultCode::Invalid; } DirectoryStats stats{}; std::string path(BuildFilename(wii_path)); - INFO_LOG(IOS_FILEIO, "IOCTL_GETUSAGE %s", path.c_str()); + INFO_LOG(IOS_FS, "IOCTL_GETUSAGE %s", path.c_str()); if (File::IsDirectory(path)) { File::FSTEntry parent_dir = File::ScanDirectoryTree(path, true); @@ -424,11 +424,11 @@ Result HostFileSystem::GetDirectoryStats(const std::string& wii_ stats.used_clusters = (u32)(total_size / (16 * 1024)); // one block is 16kb - INFO_LOG(IOS_FILEIO, "fsBlock: %i, inodes: %i", stats.used_clusters, stats.used_inodes); + INFO_LOG(IOS_FS, "fsBlock: %i, inodes: %i", stats.used_clusters, stats.used_inodes); } else { - WARN_LOG(IOS_FILEIO, "fsBlock failed, cannot find directory: %s", path.c_str()); + WARN_LOG(IOS_FS, "fsBlock failed, cannot find directory: %s", path.c_str()); } return stats; } diff --git a/Source/Core/Core/WiiRoot.cpp b/Source/Core/Core/WiiRoot.cpp index 7a2f5104b3..f6d696a435 100644 --- a/Source/Core/Core/WiiRoot.cpp +++ b/Source/Core/Core/WiiRoot.cpp @@ -57,12 +57,11 @@ void InitializeWiiRoot(bool use_temporary) s_temp_wii_root = File::CreateTempDir(); if (s_temp_wii_root.empty()) { - ERROR_LOG(IOS_FILEIO, "Could not create temporary directory"); + ERROR_LOG(IOS_FS, "Could not create temporary directory"); return; } File::CopyDir(File::GetSysDirectory() + WII_USER_DIR, s_temp_wii_root); - WARN_LOG(IOS_FILEIO, "Using temporary directory %s for minimal Wii FS", - s_temp_wii_root.c_str()); + WARN_LOG(IOS_FS, "Using temporary directory %s for minimal Wii FS", s_temp_wii_root.c_str()); File::SetUserPath(D_SESSION_WIIROOT_IDX, s_temp_wii_root); // Generate a SYSCONF with default settings for the temporary Wii NAND. SysConf sysconf{Common::FromWhichRoot::FROM_SESSION_ROOT}; From 2d8be6a77d4afe8e863119c85c2c2cc472c0540b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 8 Mar 2018 18:32:49 +0100 Subject: [PATCH 08/10] IOS/FS: Make it harder to forget closing FDs Return a FileHandle which will automatically close the FD when the handle goes out of scope. For the rare cases where this behaviour is undesirable, the FD can be released from the handle. --- Source/Core/Core/Boot/Boot_WiiWAD.cpp | 1 - Source/Core/Core/IOS/FS/FileSystem.cpp | 30 ++++++++++++++++++++ Source/Core/Core/IOS/FS/FileSystem.h | 25 ++++++++++++++-- Source/Core/Core/IOS/FS/FileSystemProxy.cpp | 6 ++-- Source/Core/Core/IOS/FS/HostBackend/FS.h | 2 +- Source/Core/Core/IOS/FS/HostBackend/File.cpp | 4 +-- 6 files changed, 59 insertions(+), 9 deletions(-) diff --git a/Source/Core/Core/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 5ecfd48471..111f87de3b 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -49,7 +49,6 @@ static void CreateVirtualFATFilesystem(std::shared_ptr // write the final 0 to 0 file from the second FAT to 20 MiB data[CDB_SIZE - 1] = 0; fs->WriteFile(*fd, data.data(), static_cast(data.size())); - fs->Close(*fd); } bool CBoot::BootNANDTitle(const u64 title_id) diff --git a/Source/Core/Core/IOS/FS/FileSystem.cpp b/Source/Core/Core/IOS/FS/FileSystem.cpp index 50aa764ee7..f3910c3374 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.cpp +++ b/Source/Core/Core/IOS/FS/FileSystem.cpp @@ -4,6 +4,7 @@ #include "Core/IOS/FS/FileSystem.h" +#include "Common/Assert.h" #include "Core/IOS/FS/HostBackend/FS.h" namespace IOS::HLE::FS @@ -13,6 +14,35 @@ std::unique_ptr MakeFileSystem() return std::make_unique(); } +FileHandle::FileHandle(FileSystem* fs, Fd fd) : m_fs{fs}, m_fd{fd} +{ +} + +FileHandle::FileHandle(FileHandle&& other) : m_fs{other.m_fs}, m_fd{other.m_fd} +{ + other.m_fd.reset(); +} + +FileHandle& FileHandle::operator=(FileHandle&& other) +{ + if (*this != other) + *this = std::move(other); + return *this; +} + +FileHandle::~FileHandle() +{ + if (m_fd && m_fs) + ASSERT(m_fs->Close(*m_fd) == FS::ResultCode::Success); +} + +Fd FileHandle::Release() +{ + const Fd fd = m_fd.value(); + m_fd.reset(); + return fd; +} + void FileSystem::Init() { if (Delete(0, 0, "/tmp") == ResultCode::Success) diff --git a/Source/Core/Core/IOS/FS/FileSystem.h b/Source/Core/Core/IOS/FS/FileSystem.h index 94fc430230..25894a60b0 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.h +++ b/Source/Core/Core/IOS/FS/FileSystem.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include @@ -96,6 +97,26 @@ struct FileStatus u32 size; }; +class FileSystem; +class FileHandle final +{ +public: + FileHandle(FileSystem* fs, Fd fd); + FileHandle(FileHandle&&); + ~FileHandle(); + FileHandle(const FileHandle&) = delete; + FileHandle& operator=(const FileHandle&) = delete; + FileHandle& operator=(FileHandle&&); + + operator Fd() const { return m_fd.value(); } + /// Release the FD so that it is not automatically closed. + Fd Release(); + +private: + FileSystem* m_fs; + std::optional m_fd; +}; + class FileSystem { public: @@ -106,8 +127,8 @@ public: /// Format the file system. virtual ResultCode Format(Uid uid) = 0; - /// Get a file descriptor for accessing a file. - virtual Result OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) = 0; + /// Get a file descriptor for accessing a file. The FD will be automatically closed after use. + virtual Result OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) = 0; /// Close a file descriptor. virtual ResultCode Close(Fd fd) = 0; /// Read `size` bytes from the file descriptor. Returns the number of bytes read. diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp index e73d502f8a..75d0c6c6b4 100644 --- a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp @@ -70,13 +70,13 @@ IPCCommandResult FS::Open(const OpenRequest& request) return GetDefaultReply(IPC_SUCCESS); } - const auto backend_fd = m_ios.GetFS()->OpenFile(request.uid, request.gid, request.path, - static_cast(request.flags & 3)); + auto backend_fd = m_ios.GetFS()->OpenFile(request.uid, request.gid, request.path, + static_cast(request.flags & 3)); LogResult(StringFromFormat("OpenFile(%s)", request.path.c_str()), backend_fd); if (!backend_fd) return GetFSReply(ConvertResult(backend_fd.Error())); - m_fd_map[request.fd] = {request.gid, request.uid, *backend_fd}; + m_fd_map[request.fd] = {request.gid, request.uid, backend_fd->Release()}; std::strncpy(m_fd_map[request.fd].name.data(), request.path.c_str(), 64); return GetFSReply(IPC_SUCCESS); } diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.h b/Source/Core/Core/IOS/FS/HostBackend/FS.h index cb7283d1f0..60f5d4dfd4 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.h +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.h @@ -30,7 +30,7 @@ public: ResultCode Format(Uid uid) override; - Result OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) override; + Result OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) override; ResultCode Close(Fd fd) override; Result ReadBytesFromFile(Fd fd, u8* ptr, u32 size) override; Result WriteBytesToFile(Fd fd, const u8* ptr, u32 size) override; diff --git a/Source/Core/Core/IOS/FS/HostBackend/File.cpp b/Source/Core/Core/IOS/FS/HostBackend/File.cpp index 405c877c45..661413300d 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/File.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/File.cpp @@ -57,7 +57,7 @@ std::shared_ptr HostFileSystem::OpenHostFile(const std::string& ho return file; } -Result HostFileSystem::OpenFile(Uid uid, Gid gid, const std::string& path, Mode mode) +Result HostFileSystem::OpenFile(Uid, Gid, const std::string& path, Mode mode) { Handle* handle = AssignFreeHandle(); if (!handle) @@ -74,7 +74,7 @@ Result HostFileSystem::OpenFile(Uid uid, Gid gid, const std::string& path, M handle->wii_path = path; handle->mode = mode; handle->file_offset = 0; - return ConvertHandleToFd(handle); + return FileHandle{this, ConvertHandleToFd(handle)}; } ResultCode HostFileSystem::Close(Fd fd) From c56f31906ceabf8a708d5c81f03bfced1e4b7669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 12 Mar 2018 18:00:58 +0100 Subject: [PATCH 09/10] IOS/FS: Remove some redundant log messages The FS IPC code already logs all commands, so keeping the log messages in the backend is redundant. --- Source/Core/Core/IOS/FS/HostBackend/FS.cpp | 68 ++-------------------- 1 file changed, 5 insertions(+), 63 deletions(-) diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp index 7efb461a75..4e18e0ac34 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -172,10 +172,7 @@ ResultCode HostFileSystem::CreateFile(Uid, Gid, const std::string& path, FileAtt std::string file_name(BuildFilename(path)); // check if the file already exist if (File::Exists(file_name)) - { - INFO_LOG(IOS_FS, "\tresult = FS_EEXIST"); return ResultCode::AlreadyExists; - } // create the file File::CreateFullPath(file_name); // just to be sure @@ -185,7 +182,6 @@ ResultCode HostFileSystem::CreateFile(Uid, Gid, const std::string& path, FileAtt return ResultCode::Invalid; } - INFO_LOG(IOS_FS, "\tresult = IPC_SUCCESS"); return ResultCode::Success; } @@ -194,10 +190,7 @@ ResultCode HostFileSystem::CreateDirectory(Uid, Gid, const std::string& path, Mode group_mode, Mode other_mode) { if (!IsValidWiiPath(path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; - } std::string name(BuildFilename(path)); @@ -211,10 +204,7 @@ ResultCode HostFileSystem::CreateDirectory(Uid, Gid, const std::string& path, ResultCode HostFileSystem::Delete(Uid, Gid, const std::string& path) { if (!IsValidWiiPath(path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; - } const std::string file_name = BuildFilename(path); if (File::Delete(file_name)) @@ -231,19 +221,12 @@ ResultCode HostFileSystem::Rename(Uid, Gid, const std::string& old_path, const std::string& new_path) { if (!IsValidWiiPath(old_path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", old_path.c_str()); return ResultCode::Invalid; - } - std::string old_name = BuildFilename(old_path); + const std::string old_name = BuildFilename(old_path); if (!IsValidWiiPath(new_path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", new_path.c_str()); return ResultCode::Invalid; - } - - std::string new_name = BuildFilename(new_path); + const std::string new_name = BuildFilename(new_path); // try to make the basis directory File::CreateFullPath(new_name); @@ -255,11 +238,7 @@ ResultCode HostFileSystem::Rename(Uid, Gid, const std::string& old_path, } // finally try to rename the file - if (File::Rename(old_name, new_name)) - { - INFO_LOG(IOS_FS, "Rename %s to %s", old_name.c_str(), new_name.c_str()); - } - else + if (!File::Rename(old_name, new_name)) { ERROR_LOG(IOS_FS, "Rename %s to %s - failed", old_name.c_str(), new_name.c_str()); return ResultCode::NotFound; @@ -271,16 +250,11 @@ ResultCode HostFileSystem::Rename(Uid, Gid, const std::string& old_path, Result> HostFileSystem::ReadDirectory(Uid, Gid, const std::string& path) { if (!IsValidWiiPath(path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; - } // the Wii uses this function to define the type (dir or file) const std::string dir_name(BuildFilename(path)); - INFO_LOG(IOS_FS, "IOCTL_READ_DIR %s", dir_name.c_str()); - const File::FileInfo file_info(dir_name); if (!file_info.Exists()) @@ -292,9 +266,6 @@ Result> HostFileSystem::ReadDirectory(Uid, Gid, const s if (!file_info.IsDirectory()) { // It's not a directory, so error. - // Games don't usually seem to care WHICH error they get, as long as it's < - // Well the system menu CARES! - WARN_LOG(IOS_FS, "\tNot a directory - return FS_EINVAL"); return ResultCode::Invalid; } @@ -316,10 +287,7 @@ Result> HostFileSystem::ReadDirectory(Uid, Gid, const s std::vector output; for (File::FSTEntry& child : entry.children) - { output.emplace_back(child.virtualName); - INFO_LOG(IOS_FS, "\tFound: %s", child.virtualName.c_str()); - } return output; } @@ -331,10 +299,7 @@ Result HostFileSystem::GetMetadata(Uid, Gid, const std::string& path) // (0x3038) for MH3 etc if (!IsValidWiiPath(path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; - } std::string file_name = BuildFilename(path); metadata.owner_mode = Mode::ReadWrite; @@ -355,22 +320,8 @@ Result HostFileSystem::GetMetadata(Uid, Gid, const std::string& path) const File::FileInfo info{file_name}; metadata.is_file = info.IsFile(); metadata.size = info.GetSize(); - if (info.IsDirectory()) - { - INFO_LOG(IOS_FS, "GET_ATTR Directory %s - all permission flags are set", file_name.c_str()); - } - else - { - if (info.Exists()) - { - INFO_LOG(IOS_FS, "GET_ATTR %s - all permission flags are set", file_name.c_str()); - } - else - { - INFO_LOG(IOS_FS, "GET_ATTR unknown %s", file_name.c_str()); - return ResultCode::NotFound; - } - } + if (!info.Exists()) + return ResultCode::NotFound; return metadata; } @@ -379,10 +330,7 @@ ResultCode HostFileSystem::SetMetadata(Uid caller_uid, const std::string& path, Mode other_mode) { if (!IsValidWiiPath(path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", path.c_str()); return ResultCode::Invalid; - } return ResultCode::Success; } @@ -406,14 +354,10 @@ Result HostFileSystem::GetNandStats() Result HostFileSystem::GetDirectoryStats(const std::string& wii_path) { if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FS, "Not a valid path: %s", wii_path.c_str()); return ResultCode::Invalid; - } DirectoryStats stats{}; std::string path(BuildFilename(wii_path)); - INFO_LOG(IOS_FS, "IOCTL_GETUSAGE %s", path.c_str()); if (File::IsDirectory(path)) { File::FSTEntry parent_dir = File::ScanDirectoryTree(path, true); @@ -423,8 +367,6 @@ Result HostFileSystem::GetDirectoryStats(const std::string& wii_ u64 total_size = ComputeTotalFileSize(parent_dir); // "Real" size to convert to nand blocks stats.used_clusters = (u32)(total_size / (16 * 1024)); // one block is 16kb - - INFO_LOG(IOS_FS, "fsBlock: %i, inodes: %i", stats.used_clusters, stats.used_inodes); } else { From 4e547772da89bc541352ae7376c369eb8ceb7f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 12 Mar 2018 18:18:22 +0100 Subject: [PATCH 10/10] IOS/FS: Make the NAND root path a parameter Cleaner than having the backend figure out which NAND root to use. --- Source/Core/Core/IOS/FS/FileSystem.cpp | 7 +++++-- Source/Core/Core/IOS/FS/FileSystem.h | 8 +++++++- Source/Core/Core/IOS/FS/HostBackend/FS.cpp | 11 ++++++----- Source/Core/Core/IOS/FS/HostBackend/FS.h | 5 +++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Source/Core/Core/IOS/FS/FileSystem.cpp b/Source/Core/Core/IOS/FS/FileSystem.cpp index f3910c3374..552d68ec8b 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.cpp +++ b/Source/Core/Core/IOS/FS/FileSystem.cpp @@ -5,13 +5,16 @@ #include "Core/IOS/FS/FileSystem.h" #include "Common/Assert.h" +#include "Common/FileUtil.h" #include "Core/IOS/FS/HostBackend/FS.h" namespace IOS::HLE::FS { -std::unique_ptr MakeFileSystem() +std::unique_ptr MakeFileSystem(Location location) { - return std::make_unique(); + const std::string nand_root = + File::GetUserPath(location == Location::Session ? D_SESSION_WIIROOT_IDX : D_WIIROOT_IDX); + return std::make_unique(nand_root); } FileHandle::FileHandle(FileSystem* fs, Fd fd) : m_fs{fs}, m_fd{fd} diff --git a/Source/Core/Core/IOS/FS/FileSystem.h b/Source/Core/Core/IOS/FS/FileSystem.h index 25894a60b0..87890cdbad 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.h +++ b/Source/Core/Core/IOS/FS/FileSystem.h @@ -195,6 +195,12 @@ protected: void Init(); }; -std::unique_ptr MakeFileSystem(); +enum class Location +{ + Configured, + Session, +}; + +std::unique_ptr MakeFileSystem(Location location = Location::Session); } // namespace IOS::HLE::FS diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp index 4e18e0ac34..1e512a623a 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -20,14 +20,13 @@ static bool IsValidWiiPath(const std::string& path) return path.compare(0, 1, "/") == 0; } -std::string HostFileSystem::BuildFilename(const std::string& wii_path) +std::string HostFileSystem::BuildFilename(const std::string& wii_path) const { - std::string nand_path = File::GetUserPath(D_SESSION_WIIROOT_IDX); if (wii_path.compare(0, 1, "/") == 0) - return nand_path + Common::EscapePath(wii_path); + return m_root_path + Common::EscapePath(wii_path); ASSERT(false); - return nand_path; + return m_root_path; } // Get total filesize of contents of a directory (recursive) @@ -45,7 +44,7 @@ static u64 ComputeTotalFileSize(const File::FSTEntry& parent_entry) return sizeOfFiles; } -HostFileSystem::HostFileSystem() +HostFileSystem::HostFileSystem(const std::string& root_path) : m_root_path{root_path} { Init(); } @@ -54,6 +53,8 @@ HostFileSystem::~HostFileSystem() = default; void HostFileSystem::DoState(PointerWrap& p) { + p.Do(m_root_path); + // Temporarily close the file, to prevent any issues with the savestating of /tmp for (Handle& handle : m_handles) handle.host_file.reset(); diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.h b/Source/Core/Core/IOS/FS/HostBackend/FS.h index 60f5d4dfd4..b30e1742f6 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.h +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.h @@ -23,7 +23,7 @@ namespace IOS::HLE::FS class HostFileSystem final : public FileSystem { public: - HostFileSystem(); + HostFileSystem(const std::string& root_path); ~HostFileSystem(); void DoState(PointerWrap& p) override; @@ -73,9 +73,10 @@ private: Handle* GetHandleFromFd(Fd fd); Fd ConvertHandleToFd(const Handle* handle) const; - std::string BuildFilename(const std::string& wii_path); + std::string BuildFilename(const std::string& wii_path) const; std::shared_ptr OpenHostFile(const std::string& host_path); + std::string m_root_path; std::array m_handles{}; std::map> m_open_files; };