summaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-07-18 21:07:11 -0400
committerGravatar bunnei2018-07-18 18:07:11 -0700
commit29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef (patch)
tree3202e2ce55ab6387a4ca366a509eccdd963434c3 /src/core/hle
parentMerge pull request #683 from DarkLordZach/touch (diff)
downloadyuzu-29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef.tar.gz
yuzu-29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef.tar.xz
yuzu-29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef.zip
Virtual Filesystem 2: Electric Boogaloo (#676)
* Virtual Filesystem * Fix delete bug and documentate * Review fixes + other stuff * Fix puyo regression
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/service/am/am.cpp1
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp209
-rw-r--r--src/core/hle/service/filesystem/filesystem.h110
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp177
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h2
5 files changed, 383 insertions, 116 deletions
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 0f0ab1e6a..97ef07bf9 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -5,7 +5,6 @@
5#include <cinttypes> 5#include <cinttypes>
6#include <stack> 6#include <stack>
7#include "core/core.h" 7#include "core/core.h"
8#include "core/file_sys/filesystem.h"
9#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/process.h" 10#include "core/hle/kernel/process.h"
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 902256757..ec528ef40 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -2,17 +2,204 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <boost/container/flat_map.hpp> 5#pragma optimize("", off)
6
7#include "common/assert.h"
6#include "common/file_util.h" 8#include "common/file_util.h"
9#include "core/core.h"
7#include "core/file_sys/errors.h" 10#include "core/file_sys/errors.h"
8#include "core/file_sys/filesystem.h"
9#include "core/file_sys/savedata_factory.h" 11#include "core/file_sys/savedata_factory.h"
10#include "core/file_sys/sdmc_factory.h" 12#include "core/file_sys/sdmc_factory.h"
13#include "core/file_sys/vfs.h"
14#include "core/file_sys/vfs_offset.h"
15#include "core/file_sys/vfs_real.h"
11#include "core/hle/service/filesystem/filesystem.h" 16#include "core/hle/service/filesystem/filesystem.h"
12#include "core/hle/service/filesystem/fsp_srv.h" 17#include "core/hle/service/filesystem/fsp_srv.h"
13 18
14namespace Service::FileSystem { 19namespace Service::FileSystem {
15 20
21// Size of emulated sd card free space, reported in bytes.
22// Just using 32GB because thats reasonable
23// TODO(DarkLordZach): Eventually make this configurable in settings.
24constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
25
26static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
27 const std::string& dir_name) {
28 if (dir_name == "." || dir_name == "" || dir_name == "/" || dir_name == "\\")
29 return base;
30
31 return base->GetDirectoryRelative(dir_name);
32}
33
34VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
35 : backing(backing_) {}
36
37std::string VfsDirectoryServiceWrapper::GetName() const {
38 return backing->GetName();
39}
40
41ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
42 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
43 auto file = dir->CreateFile(FileUtil::GetFilename(path));
44 if (file == nullptr) {
45 // TODO(DarkLordZach): Find a better error code for this
46 return ResultCode(-1);
47 }
48 if (!file->Resize(size)) {
49 // TODO(DarkLordZach): Find a better error code for this
50 return ResultCode(-1);
51 }
52 return RESULT_SUCCESS;
53}
54
55ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
56 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
57 if (path == "/" || path == "\\") {
58 // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
59 return RESULT_SUCCESS;
60 }
61 if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
62 return FileSys::ERROR_PATH_NOT_FOUND;
63 if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
64 // TODO(DarkLordZach): Find a better error code for this
65 return ResultCode(-1);
66 }
67 return RESULT_SUCCESS;
68}
69
70ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
71 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
72 if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
73 dir = backing;
74 auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
75 if (new_dir == nullptr) {
76 // TODO(DarkLordZach): Find a better error code for this
77 return ResultCode(-1);
78 }
79 return RESULT_SUCCESS;
80}
81
82ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
83 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
84 if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
85 // TODO(DarkLordZach): Find a better error code for this
86 return ResultCode(-1);
87 }
88 return RESULT_SUCCESS;
89}
90
91ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
92 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
93 if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
94 // TODO(DarkLordZach): Find a better error code for this
95 return ResultCode(-1);
96 }
97 return RESULT_SUCCESS;
98}
99
100ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
101 const std::string& dest_path) const {
102 auto src = backing->GetFileRelative(src_path);
103 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
104 // Use more-optimized vfs implementation rename.
105 if (src == nullptr)
106 return FileSys::ERROR_PATH_NOT_FOUND;
107 if (!src->Rename(FileUtil::GetFilename(dest_path))) {
108 // TODO(DarkLordZach): Find a better error code for this
109 return ResultCode(-1);
110 }
111 return RESULT_SUCCESS;
112 }
113
114 // Move by hand -- TODO(DarkLordZach): Optimize
115 auto c_res = CreateFile(dest_path, src->GetSize());
116 if (c_res != RESULT_SUCCESS)
117 return c_res;
118
119 auto dest = backing->GetFileRelative(dest_path);
120 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
121
122 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
123 "Could not write all of the bytes but everything else has succeded.");
124
125 if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) {
126 // TODO(DarkLordZach): Find a better error code for this
127 return ResultCode(-1);
128 }
129
130 return RESULT_SUCCESS;
131}
132
133ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
134 const std::string& dest_path) const {
135 auto src = GetDirectoryRelativeWrapped(backing, src_path);
136 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
137 // Use more-optimized vfs implementation rename.
138 if (src == nullptr)
139 return FileSys::ERROR_PATH_NOT_FOUND;
140 if (!src->Rename(FileUtil::GetFilename(dest_path))) {
141 // TODO(DarkLordZach): Find a better error code for this
142 return ResultCode(-1);
143 }
144 return RESULT_SUCCESS;
145 }
146
147 // TODO(DarkLordZach): Implement renaming across the tree (move).
148 ASSERT_MSG(false,
149 "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
150 "don't match -- UNIMPLEMENTED",
151 src_path, dest_path);
152
153 // TODO(DarkLordZach): Find a better error code for this
154 return ResultCode(-1);
155}
156
157ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
158 FileSys::Mode mode) const {
159 auto npath = path;
160 while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
161 npath = npath.substr(1);
162 auto file = backing->GetFileRelative(npath);
163 if (file == nullptr)
164 return FileSys::ERROR_PATH_NOT_FOUND;
165
166 if (mode == FileSys::Mode::Append) {
167 return MakeResult<FileSys::VirtualFile>(
168 std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
169 }
170
171 return MakeResult<FileSys::VirtualFile>(file);
172}
173
174ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
175 auto dir = GetDirectoryRelativeWrapped(backing, path);
176 if (dir == nullptr) {
177 // TODO(DarkLordZach): Find a better error code for this
178 return ResultCode(-1);
179 }
180 return MakeResult(dir);
181}
182
183u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
184 if (backing->IsWritable())
185 return EMULATED_SD_REPORTED_SIZE;
186
187 return 0;
188}
189
190ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
191 const std::string& path) const {
192 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
193 if (dir == nullptr)
194 return FileSys::ERROR_PATH_NOT_FOUND;
195 auto filename = FileUtil::GetFilename(path);
196 if (dir->GetFile(filename) != nullptr)
197 return MakeResult(FileSys::EntryType::File);
198 if (dir->GetSubdirectory(filename) != nullptr)
199 return MakeResult(FileSys::EntryType::Directory);
200 return FileSys::ERROR_PATH_NOT_FOUND;
201}
202
16/** 203/**
17 * Map of registered file systems, identified by type. Once an file system is registered here, it 204 * Map of registered file systems, identified by type. Once an file system is registered here, it
18 * is never removed until UnregisterFileSystems is called. 205 * is never removed until UnregisterFileSystems is called.
@@ -42,7 +229,7 @@ ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
42 return RESULT_SUCCESS; 229 return RESULT_SUCCESS;
43} 230}
44 231
45ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id) { 232ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id) {
46 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id); 233 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id);
47 234
48 if (romfs_factory == nullptr) { 235 if (romfs_factory == nullptr) {
@@ -53,19 +240,19 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id) {
53 return romfs_factory->Open(title_id); 240 return romfs_factory->Open(title_id);
54} 241}
55 242
56ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSaveData( 243ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
57 FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct) { 244 FileSys::SaveDataDescriptor save_struct) {
58 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", 245 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
59 static_cast<u8>(space), SaveStructDebugInfo(save_struct)); 246 static_cast<u8>(space), save_struct.DebugInfo());
60 247
61 if (save_data_factory == nullptr) { 248 if (save_data_factory == nullptr) {
62 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound); 249 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
63 } 250 }
64 251
65 return save_data_factory->Open(space, save_struct); 252 return save_data_factory->Open(space, save_struct);
66} 253}
67 254
68ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC() { 255ResultVal<FileSys::VirtualDir> OpenSDMC() {
69 LOG_TRACE(Service_FS, "Opening SDMC"); 256 LOG_TRACE(Service_FS, "Opening SDMC");
70 257
71 if (sdmc_factory == nullptr) { 258 if (sdmc_factory == nullptr) {
@@ -80,8 +267,10 @@ void RegisterFileSystems() {
80 save_data_factory = nullptr; 267 save_data_factory = nullptr;
81 sdmc_factory = nullptr; 268 sdmc_factory = nullptr;
82 269
83 std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); 270 auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
84 std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); 271 FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write);
272 auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
273 FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write);
85 274
86 auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); 275 auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
87 save_data_factory = std::move(savedata); 276 save_data_factory = std::move(savedata);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 45272d326..d4483daa5 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -6,15 +6,13 @@
6 6
7#include <memory> 7#include <memory>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/file_sys/directory.h"
10#include "core/file_sys/mode.h"
9#include "core/file_sys/romfs_factory.h" 11#include "core/file_sys/romfs_factory.h"
10#include "core/file_sys/savedata_factory.h" 12#include "core/file_sys/savedata_factory.h"
11#include "core/file_sys/sdmc_factory.h" 13#include "core/file_sys/sdmc_factory.h"
12#include "core/hle/result.h" 14#include "core/hle/result.h"
13 15
14namespace FileSys {
15class FileSystemBackend;
16} // namespace FileSys
17
18namespace Service { 16namespace Service {
19 17
20namespace SM { 18namespace SM {
@@ -29,11 +27,10 @@ ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
29 27
30// TODO(DarkLordZach): BIS Filesystem 28// TODO(DarkLordZach): BIS Filesystem
31// ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); 29// ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
32 30ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id);
33ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id); 31ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
34ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSaveData( 32 FileSys::SaveDataDescriptor save_struct);
35 FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct); 33ResultVal<FileSys::VirtualDir> OpenSDMC();
36ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC();
37 34
38// TODO(DarkLordZach): BIS Filesystem 35// TODO(DarkLordZach): BIS Filesystem
39// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS(); 36// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
@@ -41,5 +38,100 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC();
41/// Registers all Filesystem services with the specified service manager. 38/// Registers all Filesystem services with the specified service manager.
42void InstallInterfaces(SM::ServiceManager& service_manager); 39void InstallInterfaces(SM::ServiceManager& service_manager);
43 40
41// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
42// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
43// avoids repetitive code.
44class VfsDirectoryServiceWrapper {
45public:
46 explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing);
47
48 /**
49 * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
50 */
51 std::string GetName() const;
52
53 /**
54 * Create a file specified by its path
55 * @param path Path relative to the Archive
56 * @param size The size of the new file, filled with zeroes
57 * @return Result of the operation
58 */
59 ResultCode CreateFile(const std::string& path, u64 size) const;
60
61 /**
62 * Delete a file specified by its path
63 * @param path Path relative to the archive
64 * @return Result of the operation
65 */
66 ResultCode DeleteFile(const std::string& path) const;
67
68 /**
69 * Create a directory specified by its path
70 * @param path Path relative to the archive
71 * @return Result of the operation
72 */
73 ResultCode CreateDirectory(const std::string& path) const;
74
75 /**
76 * Delete a directory specified by its path
77 * @param path Path relative to the archive
78 * @return Result of the operation
79 */
80 ResultCode DeleteDirectory(const std::string& path) const;
81
82 /**
83 * Delete a directory specified by its path and anything under it
84 * @param path Path relative to the archive
85 * @return Result of the operation
86 */
87 ResultCode DeleteDirectoryRecursively(const std::string& path) const;
88
89 /**
90 * Rename a File specified by its path
91 * @param src_path Source path relative to the archive
92 * @param dest_path Destination path relative to the archive
93 * @return Result of the operation
94 */
95 ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
96
97 /**
98 * Rename a Directory specified by its path
99 * @param src_path Source path relative to the archive
100 * @param dest_path Destination path relative to the archive
101 * @return Result of the operation
102 */
103 ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
104
105 /**
106 * Open a file specified by its path, using the specified mode
107 * @param path Path relative to the archive
108 * @param mode Mode to open the file with
109 * @return Opened file, or error code
110 */
111 ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const;
112
113 /**
114 * Open a directory specified by its path
115 * @param path Path relative to the archive
116 * @return Opened directory, or error code
117 */
118 ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
119
120 /**
121 * Get the free space
122 * @return The number of free bytes in the archive
123 */
124 u64 GetFreeSpaceSize() const;
125
126 /**
127 * Get the type of the specified path
128 * @return The type of the specified path or error code
129 */
130 ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
131
132private:
133 FileSys::VirtualDir backing;
134};
135
44} // namespace FileSystem 136} // namespace FileSystem
45} // namespace Service 137} // namespace Service
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 22d3e645d..1b003bd84 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -8,11 +8,7 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/file_sys/directory.h" 9#include "core/file_sys/directory.h"
10#include "core/file_sys/errors.h" 10#include "core/file_sys/errors.h"
11#include "core/file_sys/filesystem.h"
12#include "core/file_sys/storage.h"
13#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/client_port.h"
15#include "core/hle/kernel/client_session.h"
16#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
17#include "core/hle/service/filesystem/filesystem.h" 13#include "core/hle/service/filesystem/filesystem.h"
18#include "core/hle/service/filesystem/fsp_srv.h" 14#include "core/hle/service/filesystem/fsp_srv.h"
@@ -25,13 +21,13 @@ enum class StorageId : u8 {
25 GameCard = 2, 21 GameCard = 2,
26 NandSystem = 3, 22 NandSystem = 3,
27 NandUser = 4, 23 NandUser = 4,
28 SdCard = 5 24 SdCard = 5,
29}; 25};
30 26
31class IStorage final : public ServiceFramework<IStorage> { 27class IStorage final : public ServiceFramework<IStorage> {
32public: 28public:
33 IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) 29 IStorage(FileSys::VirtualFile backend_)
34 : ServiceFramework("IStorage"), backend(std::move(backend)) { 30 : ServiceFramework("IStorage"), backend(std::move(backend_)) {
35 static const FunctionInfo functions[] = { 31 static const FunctionInfo functions[] = {
36 {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, 32 {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
37 {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, 33 {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"},
@@ -40,7 +36,7 @@ public:
40 } 36 }
41 37
42private: 38private:
43 std::unique_ptr<FileSys::StorageBackend> backend; 39 FileSys::VirtualFile backend;
44 40
45 void Read(Kernel::HLERequestContext& ctx) { 41 void Read(Kernel::HLERequestContext& ctx) {
46 IPC::RequestParser rp{ctx}; 42 IPC::RequestParser rp{ctx};
@@ -62,14 +58,7 @@ private:
62 } 58 }
63 59
64 // Read the data from the Storage backend 60 // Read the data from the Storage backend
65 std::vector<u8> output(length); 61 std::vector<u8> output = backend->ReadBytes(length, offset);
66 ResultVal<size_t> res = backend->Read(offset, length, output.data());
67 if (res.Failed()) {
68 IPC::ResponseBuilder rb{ctx, 2};
69 rb.Push(res.Code());
70 return;
71 }
72
73 // Write the data to memory 62 // Write the data to memory
74 ctx.WriteBuffer(output); 63 ctx.WriteBuffer(output);
75 64
@@ -80,8 +69,8 @@ private:
80 69
81class IFile final : public ServiceFramework<IFile> { 70class IFile final : public ServiceFramework<IFile> {
82public: 71public:
83 explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) 72 explicit IFile(FileSys::VirtualFile backend_)
84 : ServiceFramework("IFile"), backend(std::move(backend)) { 73 : ServiceFramework("IFile"), backend(std::move(backend_)) {
85 static const FunctionInfo functions[] = { 74 static const FunctionInfo functions[] = {
86 {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, 75 {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"},
87 {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, 76 {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"},
@@ -91,7 +80,7 @@ public:
91 } 80 }
92 81
93private: 82private:
94 std::unique_ptr<FileSys::StorageBackend> backend; 83 FileSys::VirtualFile backend;
95 84
96 void Read(Kernel::HLERequestContext& ctx) { 85 void Read(Kernel::HLERequestContext& ctx) {
97 IPC::RequestParser rp{ctx}; 86 IPC::RequestParser rp{ctx};
@@ -114,20 +103,14 @@ private:
114 } 103 }
115 104
116 // Read the data from the Storage backend 105 // Read the data from the Storage backend
117 std::vector<u8> output(length); 106 std::vector<u8> output = backend->ReadBytes(length, offset);
118 ResultVal<size_t> res = backend->Read(offset, length, output.data());
119 if (res.Failed()) {
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(res.Code());
122 return;
123 }
124 107
125 // Write the data to memory 108 // Write the data to memory
126 ctx.WriteBuffer(output); 109 ctx.WriteBuffer(output);
127 110
128 IPC::ResponseBuilder rb{ctx, 4}; 111 IPC::ResponseBuilder rb{ctx, 4};
129 rb.Push(RESULT_SUCCESS); 112 rb.Push(RESULT_SUCCESS);
130 rb.Push(static_cast<u64>(*res)); 113 rb.Push(static_cast<u64>(output.size()));
131 } 114 }
132 115
133 void Write(Kernel::HLERequestContext& ctx) { 116 void Write(Kernel::HLERequestContext& ctx) {
@@ -150,14 +133,21 @@ private:
150 return; 133 return;
151 } 134 }
152 135
153 // Write the data to the Storage backend
154 std::vector<u8> data = ctx.ReadBuffer(); 136 std::vector<u8> data = ctx.ReadBuffer();
155 ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); 137 std::vector<u8> actual_data(length);
156 if (res.Failed()) { 138
157 IPC::ResponseBuilder rb{ctx, 2}; 139 ASSERT_MSG(
158 rb.Push(res.Code()); 140 data.size() <= length,
159 return; 141 "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
160 } 142 length, data.size());
143
144 std::copy(data.begin(), data.end(), actual_data.begin());
145 // Write the data to the Storage backend
146 auto written = backend->WriteBytes(data, offset);
147
148 ASSERT_MSG(written == length,
149 "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
150 written);
161 151
162 IPC::ResponseBuilder rb{ctx, 2}; 152 IPC::ResponseBuilder rb{ctx, 2};
163 rb.Push(RESULT_SUCCESS); 153 rb.Push(RESULT_SUCCESS);
@@ -165,7 +155,8 @@ private:
165 155
166 void Flush(Kernel::HLERequestContext& ctx) { 156 void Flush(Kernel::HLERequestContext& ctx) {
167 LOG_DEBUG(Service_FS, "called"); 157 LOG_DEBUG(Service_FS, "called");
168 backend->Flush(); 158
159 // Exists for SDK compatibiltity -- No need to flush file.
169 160
170 IPC::ResponseBuilder rb{ctx, 2}; 161 IPC::ResponseBuilder rb{ctx, 2};
171 rb.Push(RESULT_SUCCESS); 162 rb.Push(RESULT_SUCCESS);
@@ -174,7 +165,7 @@ private:
174 void SetSize(Kernel::HLERequestContext& ctx) { 165 void SetSize(Kernel::HLERequestContext& ctx) {
175 IPC::RequestParser rp{ctx}; 166 IPC::RequestParser rp{ctx};
176 const u64 size = rp.Pop<u64>(); 167 const u64 size = rp.Pop<u64>();
177 backend->SetSize(size); 168 backend->Resize(size);
178 LOG_DEBUG(Service_FS, "called, size={}", size); 169 LOG_DEBUG(Service_FS, "called, size={}", size);
179 170
180 IPC::ResponseBuilder rb{ctx, 2}; 171 IPC::ResponseBuilder rb{ctx, 2};
@@ -191,19 +182,39 @@ private:
191 } 182 }
192}; 183};
193 184
185template <typename T>
186static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
187 FileSys::EntryType type) {
188 for (const auto& new_entry : new_data) {
189 FileSys::Entry entry;
190 entry.filename[0] = '\0';
191 std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1);
192 entry.type = type;
193 entry.file_size = new_entry->GetSize();
194 entries.emplace_back(std::move(entry));
195 }
196}
197
194class IDirectory final : public ServiceFramework<IDirectory> { 198class IDirectory final : public ServiceFramework<IDirectory> {
195public: 199public:
196 explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) 200 explicit IDirectory(FileSys::VirtualDir backend_)
197 : ServiceFramework("IDirectory"), backend(std::move(backend)) { 201 : ServiceFramework("IDirectory"), backend(std::move(backend_)) {
198 static const FunctionInfo functions[] = { 202 static const FunctionInfo functions[] = {
199 {0, &IDirectory::Read, "Read"}, 203 {0, &IDirectory::Read, "Read"},
200 {1, &IDirectory::GetEntryCount, "GetEntryCount"}, 204 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
201 }; 205 };
202 RegisterHandlers(functions); 206 RegisterHandlers(functions);
207
208 // TODO(DarkLordZach): Verify that this is the correct behavior.
209 // Build entry index now to save time later.
210 BuildEntryIndex(entries, backend->GetFiles(), FileSys::File);
211 BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory);
203 } 212 }
204 213
205private: 214private:
206 std::unique_ptr<FileSys::DirectoryBackend> backend; 215 FileSys::VirtualDir backend;
216 std::vector<FileSys::Entry> entries;
217 u64 next_entry_index = 0;
207 218
208 void Read(Kernel::HLERequestContext& ctx) { 219 void Read(Kernel::HLERequestContext& ctx) {
209 IPC::RequestParser rp{ctx}; 220 IPC::RequestParser rp{ctx};
@@ -214,26 +225,31 @@ private:
214 // Calculate how many entries we can fit in the output buffer 225 // Calculate how many entries we can fit in the output buffer
215 u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); 226 u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
216 227
228 // Cap at total number of entries.
229 u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
230
217 // Read the data from the Directory backend 231 // Read the data from the Directory backend
218 std::vector<FileSys::Entry> entries(count_entries); 232 std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index,
219 u64 read_entries = backend->Read(count_entries, entries.data()); 233 entries.begin() + next_entry_index + actual_entries);
234
235 next_entry_index += actual_entries;
220 236
221 // Convert the data into a byte array 237 // Convert the data into a byte array
222 std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); 238 std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry));
223 std::memcpy(output.data(), entries.data(), output.size()); 239 std::memcpy(output.data(), entry_data.data(), output.size());
224 240
225 // Write the data to memory 241 // Write the data to memory
226 ctx.WriteBuffer(output); 242 ctx.WriteBuffer(output);
227 243
228 IPC::ResponseBuilder rb{ctx, 4}; 244 IPC::ResponseBuilder rb{ctx, 4};
229 rb.Push(RESULT_SUCCESS); 245 rb.Push(RESULT_SUCCESS);
230 rb.Push(read_entries); 246 rb.Push(actual_entries);
231 } 247 }
232 248
233 void GetEntryCount(Kernel::HLERequestContext& ctx) { 249 void GetEntryCount(Kernel::HLERequestContext& ctx) {
234 LOG_DEBUG(Service_FS, "called"); 250 LOG_DEBUG(Service_FS, "called");
235 251
236 u64 count = backend->GetEntryCount(); 252 u64 count = entries.size() - next_entry_index;
237 253
238 IPC::ResponseBuilder rb{ctx, 4}; 254 IPC::ResponseBuilder rb{ctx, 4};
239 rb.Push(RESULT_SUCCESS); 255 rb.Push(RESULT_SUCCESS);
@@ -243,7 +259,7 @@ private:
243 259
244class IFileSystem final : public ServiceFramework<IFileSystem> { 260class IFileSystem final : public ServiceFramework<IFileSystem> {
245public: 261public:
246 explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) 262 explicit IFileSystem(FileSys::VirtualDir backend)
247 : ServiceFramework("IFileSystem"), backend(std::move(backend)) { 263 : ServiceFramework("IFileSystem"), backend(std::move(backend)) {
248 static const FunctionInfo functions[] = { 264 static const FunctionInfo functions[] = {
249 {0, &IFileSystem::CreateFile, "CreateFile"}, 265 {0, &IFileSystem::CreateFile, "CreateFile"},
@@ -278,7 +294,7 @@ public:
278 LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); 294 LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
279 295
280 IPC::ResponseBuilder rb{ctx, 2}; 296 IPC::ResponseBuilder rb{ctx, 2};
281 rb.Push(backend->CreateFile(name, size)); 297 rb.Push(backend.CreateFile(name, size));
282 } 298 }
283 299
284 void DeleteFile(Kernel::HLERequestContext& ctx) { 300 void DeleteFile(Kernel::HLERequestContext& ctx) {
@@ -290,7 +306,7 @@ public:
290 LOG_DEBUG(Service_FS, "called file {}", name); 306 LOG_DEBUG(Service_FS, "called file {}", name);
291 307
292 IPC::ResponseBuilder rb{ctx, 2}; 308 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(backend->DeleteFile(name)); 309 rb.Push(backend.DeleteFile(name));
294 } 310 }
295 311
296 void CreateDirectory(Kernel::HLERequestContext& ctx) { 312 void CreateDirectory(Kernel::HLERequestContext& ctx) {
@@ -302,7 +318,7 @@ public:
302 LOG_DEBUG(Service_FS, "called directory {}", name); 318 LOG_DEBUG(Service_FS, "called directory {}", name);
303 319
304 IPC::ResponseBuilder rb{ctx, 2}; 320 IPC::ResponseBuilder rb{ctx, 2};
305 rb.Push(backend->CreateDirectory(name)); 321 rb.Push(backend.CreateDirectory(name));
306 } 322 }
307 323
308 void RenameFile(Kernel::HLERequestContext& ctx) { 324 void RenameFile(Kernel::HLERequestContext& ctx) {
@@ -320,7 +336,7 @@ public:
320 LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); 336 LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
321 337
322 IPC::ResponseBuilder rb{ctx, 2}; 338 IPC::ResponseBuilder rb{ctx, 2};
323 rb.Push(backend->RenameFile(src_name, dst_name)); 339 rb.Push(backend.RenameFile(src_name, dst_name));
324 } 340 }
325 341
326 void OpenFile(Kernel::HLERequestContext& ctx) { 342 void OpenFile(Kernel::HLERequestContext& ctx) {
@@ -333,14 +349,14 @@ public:
333 349
334 LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); 350 LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
335 351
336 auto result = backend->OpenFile(name, mode); 352 auto result = backend.OpenFile(name, mode);
337 if (result.Failed()) { 353 if (result.Failed()) {
338 IPC::ResponseBuilder rb{ctx, 2}; 354 IPC::ResponseBuilder rb{ctx, 2};
339 rb.Push(result.Code()); 355 rb.Push(result.Code());
340 return; 356 return;
341 } 357 }
342 358
343 auto file = std::move(result.Unwrap()); 359 IFile file(result.Unwrap());
344 360
345 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 361 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
346 rb.Push(RESULT_SUCCESS); 362 rb.Push(RESULT_SUCCESS);
@@ -358,14 +374,14 @@ public:
358 374
359 LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); 375 LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
360 376
361 auto result = backend->OpenDirectory(name); 377 auto result = backend.OpenDirectory(name);
362 if (result.Failed()) { 378 if (result.Failed()) {
363 IPC::ResponseBuilder rb{ctx, 2}; 379 IPC::ResponseBuilder rb{ctx, 2};
364 rb.Push(result.Code()); 380 rb.Push(result.Code());
365 return; 381 return;
366 } 382 }
367 383
368 auto directory = std::move(result.Unwrap()); 384 IDirectory directory(result.Unwrap());
369 385
370 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 386 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
371 rb.Push(RESULT_SUCCESS); 387 rb.Push(RESULT_SUCCESS);
@@ -380,7 +396,7 @@ public:
380 396
381 LOG_DEBUG(Service_FS, "called file {}", name); 397 LOG_DEBUG(Service_FS, "called file {}", name);
382 398
383 auto result = backend->GetEntryType(name); 399 auto result = backend.GetEntryType(name);
384 if (result.Failed()) { 400 if (result.Failed()) {
385 IPC::ResponseBuilder rb{ctx, 2}; 401 IPC::ResponseBuilder rb{ctx, 2};
386 rb.Push(result.Code()); 402 rb.Push(result.Code());
@@ -400,7 +416,7 @@ public:
400 } 416 }
401 417
402private: 418private:
403 std::unique_ptr<FileSys::FileSystemBackend> backend; 419 VfsDirectoryServiceWrapper backend;
404}; 420};
405 421
406FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { 422FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
@@ -536,17 +552,19 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
536 LOG_INFO(Service_FS, "called with unknown={:08X}", unk); 552 LOG_INFO(Service_FS, "called with unknown={:08X}", unk);
537 auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); 553 auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
538 554
539 auto filesystem = OpenSaveData(space_id, save_struct); 555 auto dir = OpenSaveData(space_id, save_struct);
540 556
541 if (filesystem.Failed()) { 557 if (dir.Failed()) {
542 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 558 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
543 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound)); 559 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
544 return; 560 return;
545 } 561 }
546 562
563 IFileSystem filesystem(std::move(dir.Unwrap()));
564
547 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 565 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
548 rb.Push(RESULT_SUCCESS); 566 rb.Push(RESULT_SUCCESS);
549 rb.PushIpcInterface<IFileSystem>(std::move(filesystem.Unwrap())); 567 rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
550} 568}
551 569
552void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 570void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -569,18 +587,11 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
569 return; 587 return;
570 } 588 }
571 589
572 auto storage = romfs.Unwrap()->OpenFile({}, {}); 590 IStorage storage(std::move(romfs.Unwrap()));
573
574 if (storage.Failed()) {
575 LOG_CRITICAL(Service_FS, "no storage interface available!");
576 IPC::ResponseBuilder rb{ctx, 2};
577 rb.Push(storage.Code());
578 return;
579 }
580 591
581 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 592 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
582 rb.Push(RESULT_SUCCESS); 593 rb.Push(RESULT_SUCCESS);
583 rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); 594 rb.PushIpcInterface<IStorage>(std::move(storage));
584} 595}
585 596
586void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { 597void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
@@ -591,33 +602,9 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
591 602
592 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", 603 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}",
593 static_cast<u8>(storage_id), title_id); 604 static_cast<u8>(storage_id), title_id);
594 if (title_id != Core::System::GetInstance().CurrentProcess()->program_id) {
595 LOG_CRITICAL(
596 Service_FS,
597 "Attempting to access RomFS of another title id (current={:016X}, requested={:016X}).",
598 Core::System::GetInstance().CurrentProcess()->program_id, title_id);
599 }
600 605
601 auto romfs = OpenRomFS(title_id); 606 IPC::ResponseBuilder rb{ctx, 2};
602 if (romfs.Failed()) { 607 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
603 LOG_CRITICAL(Service_FS, "no file system interface available!");
604 IPC::ResponseBuilder rb{ctx, 2};
605 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::RomFSNotFound));
606 return;
607 }
608
609 auto storage = romfs.Unwrap()->OpenFile({}, {});
610
611 if (storage.Failed()) {
612 LOG_CRITICAL(Service_FS, "no storage interface available!");
613 IPC::ResponseBuilder rb{ctx, 2};
614 rb.Push(storage.Code());
615 return;
616 }
617
618 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
619 rb.Push(RESULT_SUCCESS);
620 rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));
621} 608}
622 609
623} // namespace Service::FileSystem 610} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 4653eee4e..07f99c93d 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -27,7 +27,7 @@ private:
27 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 27 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
28 void OpenRomStorage(Kernel::HLERequestContext& ctx); 28 void OpenRomStorage(Kernel::HLERequestContext& ctx);
29 29
30 std::unique_ptr<FileSys::FileSystemBackend> romfs; 30 FileSys::VirtualFile romfs;
31}; 31};
32 32
33} // namespace Service::FileSystem 33} // namespace Service::FileSystem