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.
This commit is contained in:
parent
78478a651d
commit
5a7b966b6d
|
@ -32,6 +32,7 @@ enum LOG_TYPE
|
|||
IOS_DI,
|
||||
IOS_ES,
|
||||
IOS_FILEIO,
|
||||
IOS_FS,
|
||||
IOS_NET,
|
||||
IOS_SD,
|
||||
IOS_SSL,
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -166,4 +166,16 @@ inline T FromBigEndian(T data)
|
|||
swap<sizeof(data)>(reinterpret_cast<u8*>(&data));
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
struct BigEndianValue
|
||||
{
|
||||
static_assert(std::is_arithmetic<value_type>(), "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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -195,9 +195,8 @@
|
|||
<ClCompile Include="IOS\ES\TitleInformation.cpp" />
|
||||
<ClCompile Include="IOS\ES\TitleManagement.cpp" />
|
||||
<ClCompile Include="IOS\ES\Views.cpp" />
|
||||
<ClCompile Include="IOS\FS\FileIO.cpp" />
|
||||
<ClCompile Include="IOS\FS\FS.cpp" />
|
||||
<ClCompile Include="IOS\FS\FileSystem.cpp" />
|
||||
<ClCompile Include="IOS\FS\FileSystemProxy.cpp" />
|
||||
<ClCompile Include="IOS\FS\HostBackend\FS.cpp" />
|
||||
<ClCompile Include="IOS\FS\HostBackend\File.cpp" />
|
||||
<ClCompile Include="IOS\Network\ICMPLin.cpp" />
|
||||
|
@ -448,9 +447,8 @@
|
|||
<ClInclude Include="IOS\DI\DI.h" />
|
||||
<ClInclude Include="IOS\ES\ES.h" />
|
||||
<ClInclude Include="IOS\ES\Formats.h" />
|
||||
<ClInclude Include="IOS\FS\FileIO.h" />
|
||||
<ClInclude Include="IOS\FS\FileSystem.h" />
|
||||
<ClInclude Include="IOS\FS\FS.h" />
|
||||
<ClInclude Include="IOS\FS\FileSystemProxy.h" />
|
||||
<ClInclude Include="IOS\FS\HostBackend\File.h" />
|
||||
<ClInclude Include="IOS\FS\HostBackend\FS.h" />
|
||||
<ClInclude Include="IOS\Network\ICMPLin.h" />
|
||||
|
|
|
@ -732,10 +732,10 @@
|
|||
<ClCompile Include="IOS\DI\DI.cpp">
|
||||
<Filter>IOS\DI</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\FS\FileIO.cpp">
|
||||
<ClCompile Include="IOS\FS\FileSystem.cpp">
|
||||
<Filter>IOS\FS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\FS\FileSystem.cpp">
|
||||
<ClCompile Include="IOS\FS\FileSystemProxy.cpp">
|
||||
<Filter>IOS\FS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\FS\HostBackend\FS.cpp">
|
||||
|
@ -768,9 +768,6 @@
|
|||
<ClCompile Include="IOS\ES\Views.cpp">
|
||||
<Filter>IOS\ES</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\FS\FS.cpp">
|
||||
<Filter>IOS\FS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\Network\ICMPLin.cpp">
|
||||
<Filter>IOS\Network</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1410,10 +1407,10 @@
|
|||
<ClInclude Include="IOS\ES\ES.h">
|
||||
<Filter>IOS\ES</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\FS\FileIO.h">
|
||||
<ClInclude Include="IOS\FS\FileSystem.h">
|
||||
<Filter>IOS\FS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\FS\FileSystem.h">
|
||||
<ClInclude Include="IOS\FS\FileSystemProxy.h">
|
||||
<Filter>IOS\FS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\FS\HostBackend\File.h">
|
||||
|
@ -1425,9 +1422,6 @@
|
|||
<ClInclude Include="IOS\ES\Formats.h">
|
||||
<Filter>IOS\ES</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\FS\FS.h">
|
||||
<Filter>IOS\FS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\USB\Bluetooth\hci.h">
|
||||
<Filter>IOS\USB\Bluetooth</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -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.
|
||||
};
|
||||
|
||||
|
|
|
@ -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 <algorithm>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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<File::FSTEntry> 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
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#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
|
|
@ -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 <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#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<std::string, std::weak_ptr<File::IOFile>> 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<File::IOFile>(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<File::IOFile>(m_file);
|
||||
}
|
||||
}
|
||||
|
||||
IPCCommandResult FileIO::Seek(const SeekRequest& request)
|
||||
{
|
||||
if (!m_file->IsOpen())
|
||||
return GetDefaultReply(FS_ENOENT);
|
||||
|
||||
const u32 file_size = static_cast<u32>(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<u32>(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<u32>(
|
||||
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<u32>(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
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#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<File::IOFile> m_file;
|
||||
};
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -12,4 +12,10 @@ std::unique_ptr<FileSystem> MakeFileSystem()
|
|||
{
|
||||
return std::make_unique<HostFileSystem>();
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -169,6 +169,9 @@ public:
|
|||
virtual Result<NandStats> GetNandStats() = 0;
|
||||
/// Get usage information about a directory (used cluster and inode counts).
|
||||
virtual Result<DirectoryStats> GetDirectoryStats(const std::string& path) = 0;
|
||||
|
||||
protected:
|
||||
void Init();
|
||||
};
|
||||
|
||||
std::unique_ptr<FileSystem> MakeFileSystem();
|
||||
|
|
|
@ -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 <cstring>
|
||||
|
||||
#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<s32>(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 <typename T>
|
||||
static void LogResult(const std::string& command, const Result<T>& 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<Mode>(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<u32> 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<u32> 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<u32> 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> uid;
|
||||
Common::BigEndianValue<Gid> gid;
|
||||
char path[64];
|
||||
Mode owner_mode;
|
||||
Mode group_mode;
|
||||
Mode other_mode;
|
||||
FileAttribute attribute;
|
||||
};
|
||||
|
||||
struct ISFSNandStats
|
||||
{
|
||||
Common::BigEndianValue<u32> cluster_size;
|
||||
Common::BigEndianValue<u32> free_clusters;
|
||||
Common::BigEndianValue<u32> used_clusters;
|
||||
Common::BigEndianValue<u32> bad_clusters;
|
||||
Common::BigEndianValue<u32> reserved_clusters;
|
||||
Common::BigEndianValue<u32> free_inodes;
|
||||
Common::BigEndianValue<u32> used_inodes;
|
||||
};
|
||||
|
||||
struct ISFSFileStats
|
||||
{
|
||||
Common::BigEndianValue<u32> size;
|
||||
Common::BigEndianValue<u32> seek_position;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
template <typename T>
|
||||
static Result<T> 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<NandStats> 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<ISFSParams>(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<std::vector<std::string>> 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<u32>(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<ISFSParams>(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> 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<ISFSParams>(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<ISFSParams>(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<FileStatus> 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<DirectoryStats> 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
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#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<char, 64> 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<u32, Handle> m_fd_map;
|
||||
};
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -45,7 +45,10 @@ static u64 ComputeTotalFileSize(const File::FSTEntry& parent_entry)
|
|||
return sizeOfFiles;
|
||||
}
|
||||
|
||||
HostFileSystem::HostFileSystem() = default;
|
||||
HostFileSystem::HostFileSystem()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
HostFileSystem::~HostFileSystem() = default;
|
||||
|
||||
|
|
|
@ -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<Device::FileIO>(*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<Device::FileIO>(*this, "");
|
||||
m_fdmap[i]->DoState(p);
|
||||
break;
|
||||
case Device::Device::DeviceType::OH0:
|
||||
m_fdmap[i] = std::make_shared<Device::OH0Device>(*this, "");
|
||||
m_fdmap[i]->DoState(p);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue