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/Logging/Log.h b/Source/Core/Common/Logging/Log.h index 6c50789b9d..c2dd70937e 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -31,7 +31,7 @@ enum LOG_TYPE IOS, 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..7d910b3a8b 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -99,7 +99,7 @@ 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"}; m_log[LogTypes::IOS_STM] = {"IOS_STM", "IOS - State Transition Manager"}; 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 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/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 156c2698a8..111f87de3b 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -2,37 +2,67 @@ // 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())); +} + 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/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 60d6d23769..aca7f15e81 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -165,8 +165,10 @@ add_library(core IOS/ES/TitleInformation.cpp IOS/ES/TitleManagement.cpp IOS/ES/Views.cpp - IOS/FS/FileIO.cpp - IOS/FS/FS.cpp + IOS/FS/FileSystem.cpp + IOS/FS/FileSystemProxy.cpp + IOS/FS/HostBackend/File.cpp + IOS/FS/HostBackend/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 99f2b19876..641aaeaaab 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -195,8 +195,10 @@ - - + + + + @@ -445,8 +447,10 @@ - - + + + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 189b7d2088..5456cd4be5 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -732,7 +732,16 @@ IOS\DI - + + IOS\FS + + + IOS\FS + + + IOS\FS + + IOS\FS @@ -759,9 +768,6 @@ IOS\ES - - IOS\FS - IOS\Network @@ -1401,15 +1407,21 @@ IOS\ES - + + IOS\FS + + + IOS\FS + + + IOS\FS + + IOS\FS 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 d7e3cb3f44..0000000000 --- a/Source/Core/Core/IOS/FS/FileIO.cpp +++ /dev/null @@ -1,322 +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/CommonTitles.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; -} - -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) - : 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 df98921f32..0000000000 --- a/Source/Core/Core/IOS/FS/FileIO.h +++ /dev/null @@ -1,83 +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); -void CreateVirtualFATFilesystem(); - -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 new file mode 100644 index 0000000000..552d68ec8b --- /dev/null +++ b/Source/Core/Core/IOS/FS/FileSystem.cpp @@ -0,0 +1,54 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#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(Location location) +{ + 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} +{ +} + +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) + 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 new file mode 100644 index 0000000000..87890cdbad --- /dev/null +++ b/Source/Core/Core/IOS/FS/FileSystem.h @@ -0,0 +1,206 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#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; +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: + 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. 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. + 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; + +protected: + void Init(); +}; + +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/FileSystemProxy.cpp b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp new file mode 100644 index 0000000000..75d0c6c6b4 --- /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); + } + + 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->Release()}; + 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 new file mode 100644 index 0000000000..1e512a623a --- /dev/null +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -0,0 +1,379 @@ +// 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) const +{ + if (wii_path.compare(0, 1, "/") == 0) + return m_root_path + Common::EscapePath(wii_path); + + ASSERT(false); + return m_root_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(const std::string& root_path) : m_root_path{root_path} +{ + Init(); +} + +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(); + + // 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)) + return ResultCode::AlreadyExists; + + // create the file + File::CreateFullPath(file_name); // just to be sure + if (!File::CreateEmptyFile(file_name)) + { + ERROR_LOG(IOS_FS, "couldn't create new file"); + return ResultCode::Invalid; + } + + 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)) + return ResultCode::Invalid; + + std::string name(BuildFilename(path)); + + name += "/"; + File::CreateFullPath(name); + DEBUG_ASSERT_MSG(IOS_FS, 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)) + return ResultCode::Invalid; + + const std::string file_name = BuildFilename(path); + if (File::Delete(file_name)) + INFO_LOG(IOS_FS, "DeleteFile %s", file_name.c_str()); + else if (File::DeleteDirRecursively(file_name)) + INFO_LOG(IOS_FS, "DeleteDir %s", file_name.c_str()); + else + WARN_LOG(IOS_FS, "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)) + return ResultCode::Invalid; + const std::string old_name = BuildFilename(old_path); + + if (!IsValidWiiPath(new_path)) + return ResultCode::Invalid; + const 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)) + { + ERROR_LOG(IOS_FS, "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)) + return ResultCode::Invalid; + + // the Wii uses this function to define the type (dir or file) + const std::string dir_name(BuildFilename(path)); + + const File::FileInfo file_info(dir_name); + + if (!file_info.Exists()) + { + WARN_LOG(IOS_FS, "Search not found: %s", dir_name.c_str()); + return ResultCode::NotFound; + } + + if (!file_info.IsDirectory()) + { + // It's not a directory, so error. + 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); + 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)) + 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.Exists()) + 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)) + return ResultCode::Invalid; + return ResultCode::Success; +} + +Result HostFileSystem::GetNandStats() +{ + WARN_LOG(IOS_FS, "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)) + return ResultCode::Invalid; + + DirectoryStats stats{}; + std::string path(BuildFilename(wii_path)); + 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 + } + else + { + WARN_LOG(IOS_FS, "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..b30e1742f6 --- /dev/null +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.h @@ -0,0 +1,84 @@ +// 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(const std::string& root_path); + ~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) const; + std::shared_ptr OpenHostFile(const std::string& host_path); + + std::string m_root_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..661413300d --- /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, 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 FileHandle{this, 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 diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index 4abd2312a5..862f40c8cc 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -31,8 +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" @@ -242,9 +242,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 +368,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")); @@ -491,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) @@ -691,6 +694,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; @@ -725,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/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. 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, 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};