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] 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,