summaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-07-06 10:51:32 -0400
committerGravatar bunnei2018-07-06 10:51:32 -0400
commit77c684c1140f6bf3fb7d4560d06d2efb1a2ee5e2 (patch)
tree38ef6451732c5eecb0efdd198f3db4d33848453c /src/core/hle
parentMerge pull request #629 from Subv/depth_test (diff)
downloadyuzu-77c684c1140f6bf3fb7d4560d06d2efb1a2ee5e2.tar.gz
yuzu-77c684c1140f6bf3fb7d4560d06d2efb1a2ee5e2.tar.xz
yuzu-77c684c1140f6bf3fb7d4560d06d2efb1a2ee5e2.zip
Virtual Filesystem (#597)
* Add VfsFile and VfsDirectory classes * Finish abstract Vfs classes * Implement RealVfsFile (computer fs backend) * Finish RealVfsFile and RealVfsDirectory * Finished OffsetVfsFile * More changes * Fix import paths * Major refactor * Remove double const * Use experimental/filesystem or filesystem depending on compiler * Port partition_filesystem * More changes * More Overhaul * FSP_SRV fixes * Fixes and testing * Try to get filesystem to compile * Filesystem on linux * Remove std::filesystem and document/test * Compile fixes * Missing include * Bug fixes * Fixes * Rename v_file and v_dir * clang-format fix * Rename NGLOG_* to LOG_* * Most review changes * Fix TODO * Guess 'main' to be Directory by filename
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp225
-rw-r--r--src/core/hle/service/filesystem/filesystem.h133
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp114
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h2
5 files changed, 405 insertions, 71 deletions
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index a871b3eaa..7bb13c735 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -621,7 +621,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
621 IPC::ResponseBuilder rb{ctx, 4}; 621 IPC::ResponseBuilder rb{ctx, 4};
622 622
623 FileSys::Path unused; 623 FileSys::Path unused;
624 auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused); 624 auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData);
625 if (savedata.Failed()) { 625 if (savedata.Failed()) {
626 // Create the save data and return an error indicating that the operation was performed. 626 // Create the save data and return an error indicating that the operation was performed.
627 FileSystem::FormatFileSystem(FileSystem::Type::SaveData); 627 FileSystem::FormatFileSystem(FileSystem::Type::SaveData);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index f58b518b6..25810c165 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -2,36 +2,221 @@
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#include "boost/container/flat_map.hpp"
6#include "common/assert.h"
7#include "common/common_paths.h"
6#include "common/file_util.h" 8#include "common/file_util.h"
9#include "core/core.h"
10#include "core/file_sys/errors.h"
7#include "core/file_sys/filesystem.h" 11#include "core/file_sys/filesystem.h"
8#include "core/file_sys/savedata_factory.h" 12#include "core/file_sys/vfs.h"
9#include "core/file_sys/sdmc_factory.h" 13#include "core/file_sys/vfs_offset.h"
14#include "core/file_sys/vfs_real.h"
15#include "core/hle/kernel/process.h"
10#include "core/hle/service/filesystem/filesystem.h" 16#include "core/hle/service/filesystem/filesystem.h"
11#include "core/hle/service/filesystem/fsp_srv.h" 17#include "core/hle/service/filesystem/fsp_srv.h"
12 18
13namespace Service::FileSystem { 19namespace Service::FileSystem {
14 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 return ResultCode(-1);
46 if (!file->Resize(size))
47 return ResultCode(-1);
48 return RESULT_SUCCESS;
49}
50
51ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
52 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
53 if (!backing->DeleteFile(FileUtil::GetFilename(path)))
54 return ResultCode(-1);
55 return RESULT_SUCCESS;
56}
57
58ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
59 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
60 if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
61 dir = backing;
62 auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
63 if (new_dir == nullptr)
64 return ResultCode(-1);
65 return RESULT_SUCCESS;
66}
67
68ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
69 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
70 if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path)))
71 return ResultCode(-1);
72 return RESULT_SUCCESS;
73}
74
75ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
76 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
77 if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)))
78 return ResultCode(-1);
79 return RESULT_SUCCESS;
80}
81
82ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
83 const std::string& dest_path) const {
84 auto src = backing->GetFileRelative(src_path);
85 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
86 // Use more-optimized vfs implementation rename.
87 if (src == nullptr)
88 return FileSys::ERROR_PATH_NOT_FOUND;
89 if (!src->Rename(FileUtil::GetFilename(dest_path)))
90 return ResultCode(-1);
91 return RESULT_SUCCESS;
92 }
93
94 // Move by hand -- TODO(DarkLordZach): Optimize
95 auto c_res = CreateFile(dest_path, src->GetSize());
96 if (c_res != RESULT_SUCCESS)
97 return c_res;
98
99 auto dest = backing->GetFileRelative(dest_path);
100 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
101
102 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
103 "Could not write all of the bytes but everything else has succeded.");
104
105 if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path)))
106 return ResultCode(-1);
107
108 return RESULT_SUCCESS;
109}
110
111ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
112 const std::string& dest_path) const {
113 auto src = GetDirectoryRelativeWrapped(backing, src_path);
114 if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
115 // Use more-optimized vfs implementation rename.
116 if (src == nullptr)
117 return FileSys::ERROR_PATH_NOT_FOUND;
118 if (!src->Rename(FileUtil::GetFilename(dest_path)))
119 return ResultCode(-1);
120 return RESULT_SUCCESS;
121 }
122
123 // TODO(DarkLordZach): Implement renaming across the tree (move).
124 ASSERT_MSG(false,
125 "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
126 "don't match -- UNIMPLEMENTED",
127 src_path, dest_path);
128
129 return ResultCode(-1);
130}
131
132ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
133 FileSys::Mode mode) const {
134 auto file = backing->GetFileRelative(path);
135 if (file == nullptr)
136 return FileSys::ERROR_PATH_NOT_FOUND;
137
138 if ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) {
139 return MakeResult<FileSys::VirtualFile>(
140 std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
141 }
142
143 return MakeResult<FileSys::VirtualFile>(file);
144}
145
146ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
147 auto dir = GetDirectoryRelativeWrapped(backing, path);
148 if (dir == nullptr)
149 return ResultCode(-1);
150 return MakeResult(dir);
151}
152
153u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
154 if (backing->IsWritable())
155 return EMULATED_SD_REPORTED_SIZE;
156
157 return 0;
158}
159
160ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
161 const std::string& path) const {
162 auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
163 if (dir == nullptr)
164 return ResultCode(-1);
165 auto filename = FileUtil::GetFilename(path);
166 if (dir->GetFile(filename) != nullptr)
167 return MakeResult(FileSys::EntryType::File);
168 if (dir->GetSubdirectory(filename) != nullptr)
169 return MakeResult(FileSys::EntryType::Directory);
170 return ResultCode(-1);
171}
172
173// A deferred filesystem for nand save data.
174// This must be deferred because the directory is dependent on title id, which is not set at
175// registration time.
176struct SaveDataDeferredFilesystem : DeferredFilesystem {
177protected:
178 FileSys::VirtualDir CreateFilesystem() override {
179 u64 title_id = Core::CurrentProcess()->program_id;
180 // TODO(DarkLordZach): Users
181 u32 user_id = 0;
182 std::string nand_directory = fmt::format(
183 "{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id);
184
185 auto savedata =
186 std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write);
187 return savedata;
188 }
189};
190
15/** 191/**
16 * Map of registered file systems, identified by type. Once an file system is registered here, it 192 * Map of registered file systems, identified by type. Once an file system is registered here, it
17 * is never removed until UnregisterFileSystems is called. 193 * is never removed until UnregisterFileSystems is called.
18 */ 194 */
19static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; 195static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map;
196static FileSys::VirtualFile filesystem_romfs = nullptr;
20 197
21ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { 198ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) {
22 auto result = filesystem_map.emplace(type, std::move(factory)); 199 auto result = filesystem_map.emplace(type, std::move(factory));
23 200
24 bool inserted = result.second; 201 bool inserted = result.second;
25 ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); 202 ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
26 203
27 auto& filesystem = result.first->second; 204 auto& filesystem = result.first->second;
205 LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type));
206 return RESULT_SUCCESS;
207}
208
209ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) {
210 ASSERT_MSG(filesystem_romfs == nullptr,
211 "Tried to register more than one system with same id code");
212
213 filesystem_romfs = filesystem;
28 LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), 214 LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
29 static_cast<u32>(type)); 215 static_cast<u32>(Type::RomFS));
30 return RESULT_SUCCESS; 216 return RESULT_SUCCESS;
31} 217}
32 218
33ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, 219ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) {
34 FileSys::Path& path) {
35 LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); 220 LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
36 221
37 auto itr = filesystem_map.find(type); 222 auto itr = filesystem_map.find(type);
@@ -40,7 +225,13 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
40 return ResultCode(-1); 225 return ResultCode(-1);
41 } 226 }
42 227
43 return itr->second->Open(path); 228 return MakeResult(itr->second->Get());
229}
230
231ResultVal<FileSys::VirtualFile> OpenRomFS() {
232 if (filesystem_romfs == nullptr)
233 return ResultCode(-1);
234 return MakeResult(filesystem_romfs);
44} 235}
45 236
46ResultCode FormatFileSystem(Type type) { 237ResultCode FormatFileSystem(Type type) {
@@ -52,21 +243,21 @@ ResultCode FormatFileSystem(Type type) {
52 return ResultCode(-1); 243 return ResultCode(-1);
53 } 244 }
54 245
55 FileSys::Path unused; 246 return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory(
56 return itr->second->Format(unused); 247 itr->second->Get()->GetName())
248 ? RESULT_SUCCESS
249 : ResultCode(-1);
57} 250}
58 251
59void RegisterFileSystems() { 252void RegisterFileSystems() {
60 filesystem_map.clear(); 253 filesystem_map.clear();
254 filesystem_romfs = nullptr;
61 255
62 std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
63 std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); 256 std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
257 auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write);
258 RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC);
64 259
65 auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory)); 260 RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData);
66 RegisterFileSystem(std::move(savedata), Type::SaveData);
67
68 auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
69 RegisterFileSystem(std::move(sdcard), Type::SDMC);
70} 261}
71 262
72void InstallInterfaces(SM::ServiceManager& service_manager) { 263void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 56d26146e..d3de797f1 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -6,6 +6,8 @@
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/filesystem.h"
10#include "core/file_sys/vfs.h"
9#include "core/hle/result.h" 11#include "core/hle/result.h"
10 12
11namespace FileSys { 13namespace FileSys {
@@ -29,12 +31,136 @@ enum class Type {
29 SDMC = 3, 31 SDMC = 3,
30}; 32};
31 33
34// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
35// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
36// avoids repetitive code.
37class VfsDirectoryServiceWrapper {
38public:
39 explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing);
40
41 /**
42 * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
43 */
44 std::string GetName() const;
45
46 /**
47 * Create a file specified by its path
48 * @param path Path relative to the Archive
49 * @param size The size of the new file, filled with zeroes
50 * @return Result of the operation
51 */
52 ResultCode CreateFile(const std::string& path, u64 size) const;
53
54 /**
55 * Delete a file specified by its path
56 * @param path Path relative to the archive
57 * @return Result of the operation
58 */
59 ResultCode DeleteFile(const std::string& path) const;
60
61 /**
62 * Create a directory specified by its path
63 * @param path Path relative to the archive
64 * @return Result of the operation
65 */
66 ResultCode CreateDirectory(const std::string& path) const;
67
68 /**
69 * Delete a directory specified by its path
70 * @param path Path relative to the archive
71 * @return Result of the operation
72 */
73 ResultCode DeleteDirectory(const std::string& path) const;
74
75 /**
76 * Delete a directory specified by its path and anything under it
77 * @param path Path relative to the archive
78 * @return Result of the operation
79 */
80 ResultCode DeleteDirectoryRecursively(const std::string& path) const;
81
82 /**
83 * Rename a File specified by its path
84 * @param src_path Source path relative to the archive
85 * @param dest_path Destination path relative to the archive
86 * @return Result of the operation
87 */
88 ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
89
90 /**
91 * Rename a Directory specified by its path
92 * @param src_path Source path relative to the archive
93 * @param dest_path Destination path relative to the archive
94 * @return Result of the operation
95 */
96 ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
97
98 /**
99 * Open a file specified by its path, using the specified mode
100 * @param path Path relative to the archive
101 * @param mode Mode to open the file with
102 * @return Opened file, or error code
103 */
104 ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const;
105
106 /**
107 * Open a directory specified by its path
108 * @param path Path relative to the archive
109 * @return Opened directory, or error code
110 */
111 ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
112
113 /**
114 * Get the free space
115 * @return The number of free bytes in the archive
116 */
117 u64 GetFreeSpaceSize() const;
118
119 /**
120 * Get the type of the specified path
121 * @return The type of the specified path or error code
122 */
123 ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
124
125private:
126 FileSys::VirtualDir backing;
127};
128
129// A class that deferres the creation of a filesystem until a later time.
130// This is useful if construction depends on a variable not known when the filesystem is registered.
131// Construct this with a filesystem (VirtualDir) to avoid the deferrence feature or override the
132// CreateFilesystem method which will be called on first use.
133struct DeferredFilesystem {
134 DeferredFilesystem() = default;
135
136 explicit DeferredFilesystem(FileSys::VirtualDir vfs_directory) : fs(std::move(vfs_directory)) {}
137
138 FileSys::VirtualDir Get() {
139 if (fs == nullptr)
140 fs = CreateFilesystem();
141
142 return fs;
143 }
144
145 virtual ~DeferredFilesystem() = default;
146
147protected:
148 virtual FileSys::VirtualDir CreateFilesystem() {
149 return fs;
150 }
151
152private:
153 FileSys::VirtualDir fs;
154};
155
32/** 156/**
33 * Registers a FileSystem, instances of which can later be opened using its IdCode. 157 * Registers a FileSystem, instances of which can later be opened using its IdCode.
34 * @param factory FileSystem backend interface to use 158 * @param factory FileSystem backend interface to use
35 * @param type Type used to access this type of FileSystem 159 * @param type Type used to access this type of FileSystem
36 */ 160 */
37ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); 161ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& fs, Type type);
162
163ResultCode RegisterRomFS(FileSys::VirtualFile fs);
38 164
39/** 165/**
40 * Opens a file system 166 * Opens a file system
@@ -42,8 +168,9 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact
42 * @param path Path to the file system, used with Binary paths 168 * @param path Path to the file system, used with Binary paths
43 * @return FileSys::FileSystemBackend interface to the file system 169 * @return FileSys::FileSystemBackend interface to the file system
44 */ 170 */
45ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, 171ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type);
46 FileSys::Path& path); 172
173ResultVal<FileSys::VirtualFile> OpenRomFS();
47 174
48/** 175/**
49 * Formats a file system 176 * Formats a file system
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 216bfea0a..26e8a8c88 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -19,8 +19,8 @@ namespace Service::FileSystem {
19 19
20class IStorage final : public ServiceFramework<IStorage> { 20class IStorage final : public ServiceFramework<IStorage> {
21public: 21public:
22 IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) 22 IStorage(FileSys::VirtualFile backend_)
23 : ServiceFramework("IStorage"), backend(std::move(backend)) { 23 : ServiceFramework("IStorage"), backend(std::move(backend_)) {
24 static const FunctionInfo functions[] = { 24 static const FunctionInfo functions[] = {
25 {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, 25 {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
26 {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, 26 {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"},
@@ -29,7 +29,7 @@ public:
29 } 29 }
30 30
31private: 31private:
32 std::unique_ptr<FileSys::StorageBackend> backend; 32 FileSys::VirtualFile backend;
33 33
34 void Read(Kernel::HLERequestContext& ctx) { 34 void Read(Kernel::HLERequestContext& ctx) {
35 IPC::RequestParser rp{ctx}; 35 IPC::RequestParser rp{ctx};
@@ -51,8 +51,8 @@ private:
51 } 51 }
52 52
53 // Read the data from the Storage backend 53 // Read the data from the Storage backend
54 std::vector<u8> output(length); 54 std::vector<u8> output = backend->ReadBytes(length, offset);
55 ResultVal<size_t> res = backend->Read(offset, length, output.data()); 55 auto res = MakeResult<size_t>(output.size());
56 if (res.Failed()) { 56 if (res.Failed()) {
57 IPC::ResponseBuilder rb{ctx, 2}; 57 IPC::ResponseBuilder rb{ctx, 2};
58 rb.Push(res.Code()); 58 rb.Push(res.Code());
@@ -69,8 +69,8 @@ private:
69 69
70class IFile final : public ServiceFramework<IFile> { 70class IFile final : public ServiceFramework<IFile> {
71public: 71public:
72 explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) 72 explicit IFile(FileSys::VirtualFile backend_)
73 : ServiceFramework("IFile"), backend(std::move(backend)) { 73 : ServiceFramework("IFile"), backend(std::move(backend_)) {
74 static const FunctionInfo functions[] = { 74 static const FunctionInfo functions[] = {
75 {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, 75 {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"},
76 {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, 76 {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"},
@@ -80,7 +80,7 @@ public:
80 } 80 }
81 81
82private: 82private:
83 std::unique_ptr<FileSys::StorageBackend> backend; 83 FileSys::VirtualFile backend;
84 84
85 void Read(Kernel::HLERequestContext& ctx) { 85 void Read(Kernel::HLERequestContext& ctx) {
86 IPC::RequestParser rp{ctx}; 86 IPC::RequestParser rp{ctx};
@@ -103,8 +103,8 @@ private:
103 } 103 }
104 104
105 // Read the data from the Storage backend 105 // Read the data from the Storage backend
106 std::vector<u8> output(length); 106 std::vector<u8> output = backend->ReadBytes(length, offset);
107 ResultVal<size_t> res = backend->Read(offset, length, output.data()); 107 auto res = MakeResult<size_t>(output.size());
108 if (res.Failed()) { 108 if (res.Failed()) {
109 IPC::ResponseBuilder rb{ctx, 2}; 109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(res.Code()); 110 rb.Push(res.Code());
@@ -139,9 +139,10 @@ private:
139 return; 139 return;
140 } 140 }
141 141
142 // Write the data to the Storage backend
143 std::vector<u8> data = ctx.ReadBuffer(); 142 std::vector<u8> data = ctx.ReadBuffer();
144 ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); 143 data.resize(length);
144 // Write the data to the Storage backend
145 auto res = MakeResult<size_t>(backend->WriteBytes(data, offset));
145 if (res.Failed()) { 146 if (res.Failed()) {
146 IPC::ResponseBuilder rb{ctx, 2}; 147 IPC::ResponseBuilder rb{ctx, 2};
147 rb.Push(res.Code()); 148 rb.Push(res.Code());
@@ -154,7 +155,8 @@ private:
154 155
155 void Flush(Kernel::HLERequestContext& ctx) { 156 void Flush(Kernel::HLERequestContext& ctx) {
156 LOG_DEBUG(Service_FS, "called"); 157 LOG_DEBUG(Service_FS, "called");
157 backend->Flush(); 158
159 // Exists for SDK compatibiltity -- No need to flush file.
158 160
159 IPC::ResponseBuilder rb{ctx, 2}; 161 IPC::ResponseBuilder rb{ctx, 2};
160 rb.Push(RESULT_SUCCESS); 162 rb.Push(RESULT_SUCCESS);
@@ -163,7 +165,7 @@ private:
163 void SetSize(Kernel::HLERequestContext& ctx) { 165 void SetSize(Kernel::HLERequestContext& ctx) {
164 IPC::RequestParser rp{ctx}; 166 IPC::RequestParser rp{ctx};
165 const u64 size = rp.Pop<u64>(); 167 const u64 size = rp.Pop<u64>();
166 backend->SetSize(size); 168 backend->Resize(size);
167 LOG_DEBUG(Service_FS, "called, size={}", size); 169 LOG_DEBUG(Service_FS, "called, size={}", size);
168 170
169 IPC::ResponseBuilder rb{ctx, 2}; 171 IPC::ResponseBuilder rb{ctx, 2};
@@ -180,19 +182,38 @@ private:
180 } 182 }
181}; 183};
182 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
183class IDirectory final : public ServiceFramework<IDirectory> { 198class IDirectory final : public ServiceFramework<IDirectory> {
184public: 199public:
185 explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) 200 explicit IDirectory(FileSys::VirtualDir backend_)
186 : ServiceFramework("IDirectory"), backend(std::move(backend)) { 201 : ServiceFramework("IDirectory"), backend(std::move(backend_)) {
187 static const FunctionInfo functions[] = { 202 static const FunctionInfo functions[] = {
188 {0, &IDirectory::Read, "Read"}, 203 {0, &IDirectory::Read, "Read"},
189 {1, &IDirectory::GetEntryCount, "GetEntryCount"}, 204 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
190 }; 205 };
191 RegisterHandlers(functions); 206 RegisterHandlers(functions);
207
208 // Build entry index now to save time later.
209 BuildEntryIndex(entries, backend->GetFiles(), FileSys::File);
210 BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory);
192 } 211 }
193 212
194private: 213private:
195 std::unique_ptr<FileSys::DirectoryBackend> backend; 214 FileSys::VirtualDir backend;
215 std::vector<FileSys::Entry> entries;
216 u64 next_entry_index = 0;
196 217
197 void Read(Kernel::HLERequestContext& ctx) { 218 void Read(Kernel::HLERequestContext& ctx) {
198 IPC::RequestParser rp{ctx}; 219 IPC::RequestParser rp{ctx};
@@ -203,26 +224,31 @@ private:
203 // Calculate how many entries we can fit in the output buffer 224 // Calculate how many entries we can fit in the output buffer
204 u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); 225 u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
205 226
227 // Cap at total number of entries.
228 u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
229
206 // Read the data from the Directory backend 230 // Read the data from the Directory backend
207 std::vector<FileSys::Entry> entries(count_entries); 231 std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index,
208 u64 read_entries = backend->Read(count_entries, entries.data()); 232 entries.begin() + next_entry_index + actual_entries);
233
234 next_entry_index += actual_entries;
209 235
210 // Convert the data into a byte array 236 // Convert the data into a byte array
211 std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); 237 std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry));
212 std::memcpy(output.data(), entries.data(), output.size()); 238 std::memcpy(output.data(), entry_data.data(), output.size());
213 239
214 // Write the data to memory 240 // Write the data to memory
215 ctx.WriteBuffer(output); 241 ctx.WriteBuffer(output);
216 242
217 IPC::ResponseBuilder rb{ctx, 4}; 243 IPC::ResponseBuilder rb{ctx, 4};
218 rb.Push(RESULT_SUCCESS); 244 rb.Push(RESULT_SUCCESS);
219 rb.Push(read_entries); 245 rb.Push(actual_entries);
220 } 246 }
221 247
222 void GetEntryCount(Kernel::HLERequestContext& ctx) { 248 void GetEntryCount(Kernel::HLERequestContext& ctx) {
223 LOG_DEBUG(Service_FS, "called"); 249 LOG_DEBUG(Service_FS, "called");
224 250
225 u64 count = backend->GetEntryCount(); 251 u64 count = entries.size() - next_entry_index;
226 252
227 IPC::ResponseBuilder rb{ctx, 4}; 253 IPC::ResponseBuilder rb{ctx, 4};
228 rb.Push(RESULT_SUCCESS); 254 rb.Push(RESULT_SUCCESS);
@@ -232,7 +258,7 @@ private:
232 258
233class IFileSystem final : public ServiceFramework<IFileSystem> { 259class IFileSystem final : public ServiceFramework<IFileSystem> {
234public: 260public:
235 explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) 261 explicit IFileSystem(FileSys::VirtualDir backend)
236 : ServiceFramework("IFileSystem"), backend(std::move(backend)) { 262 : ServiceFramework("IFileSystem"), backend(std::move(backend)) {
237 static const FunctionInfo functions[] = { 263 static const FunctionInfo functions[] = {
238 {0, &IFileSystem::CreateFile, "CreateFile"}, 264 {0, &IFileSystem::CreateFile, "CreateFile"},
@@ -267,7 +293,7 @@ public:
267 LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); 293 LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
268 294
269 IPC::ResponseBuilder rb{ctx, 2}; 295 IPC::ResponseBuilder rb{ctx, 2};
270 rb.Push(backend->CreateFile(name, size)); 296 rb.Push(backend.CreateFile(name, size));
271 } 297 }
272 298
273 void DeleteFile(Kernel::HLERequestContext& ctx) { 299 void DeleteFile(Kernel::HLERequestContext& ctx) {
@@ -279,7 +305,7 @@ public:
279 LOG_DEBUG(Service_FS, "called file {}", name); 305 LOG_DEBUG(Service_FS, "called file {}", name);
280 306
281 IPC::ResponseBuilder rb{ctx, 2}; 307 IPC::ResponseBuilder rb{ctx, 2};
282 rb.Push(backend->DeleteFile(name)); 308 rb.Push(backend.DeleteFile(name));
283 } 309 }
284 310
285 void CreateDirectory(Kernel::HLERequestContext& ctx) { 311 void CreateDirectory(Kernel::HLERequestContext& ctx) {
@@ -291,7 +317,7 @@ public:
291 LOG_DEBUG(Service_FS, "called directory {}", name); 317 LOG_DEBUG(Service_FS, "called directory {}", name);
292 318
293 IPC::ResponseBuilder rb{ctx, 2}; 319 IPC::ResponseBuilder rb{ctx, 2};
294 rb.Push(backend->CreateDirectory(name)); 320 rb.Push(backend.CreateDirectory(name));
295 } 321 }
296 322
297 void RenameFile(Kernel::HLERequestContext& ctx) { 323 void RenameFile(Kernel::HLERequestContext& ctx) {
@@ -309,7 +335,7 @@ public:
309 LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); 335 LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
310 336
311 IPC::ResponseBuilder rb{ctx, 2}; 337 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(backend->RenameFile(src_name, dst_name)); 338 rb.Push(backend.RenameFile(src_name, dst_name));
313 } 339 }
314 340
315 void OpenFile(Kernel::HLERequestContext& ctx) { 341 void OpenFile(Kernel::HLERequestContext& ctx) {
@@ -322,14 +348,14 @@ public:
322 348
323 LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); 349 LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
324 350
325 auto result = backend->OpenFile(name, mode); 351 auto result = backend.OpenFile(name, mode);
326 if (result.Failed()) { 352 if (result.Failed()) {
327 IPC::ResponseBuilder rb{ctx, 2}; 353 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(result.Code()); 354 rb.Push(result.Code());
329 return; 355 return;
330 } 356 }
331 357
332 auto file = std::move(result.Unwrap()); 358 IFile file(result.Unwrap());
333 359
334 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 360 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
335 rb.Push(RESULT_SUCCESS); 361 rb.Push(RESULT_SUCCESS);
@@ -347,14 +373,14 @@ public:
347 373
348 LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); 374 LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
349 375
350 auto result = backend->OpenDirectory(name); 376 auto result = backend.OpenDirectory(name);
351 if (result.Failed()) { 377 if (result.Failed()) {
352 IPC::ResponseBuilder rb{ctx, 2}; 378 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(result.Code()); 379 rb.Push(result.Code());
354 return; 380 return;
355 } 381 }
356 382
357 auto directory = std::move(result.Unwrap()); 383 IDirectory directory(result.Unwrap());
358 384
359 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 385 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
360 rb.Push(RESULT_SUCCESS); 386 rb.Push(RESULT_SUCCESS);
@@ -369,7 +395,7 @@ public:
369 395
370 LOG_DEBUG(Service_FS, "called file {}", name); 396 LOG_DEBUG(Service_FS, "called file {}", name);
371 397
372 auto result = backend->GetEntryType(name); 398 auto result = backend.GetEntryType(name);
373 if (result.Failed()) { 399 if (result.Failed()) {
374 IPC::ResponseBuilder rb{ctx, 2}; 400 IPC::ResponseBuilder rb{ctx, 2};
375 rb.Push(result.Code()); 401 rb.Push(result.Code());
@@ -389,7 +415,7 @@ public:
389 } 415 }
390 416
391private: 417private:
392 std::unique_ptr<FileSys::FileSystemBackend> backend; 418 VfsDirectoryServiceWrapper backend;
393}; 419};
394 420
395FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { 421FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
@@ -490,8 +516,7 @@ void FSP_SRV::TryLoadRomFS() {
490 if (romfs) { 516 if (romfs) {
491 return; 517 return;
492 } 518 }
493 FileSys::Path unused; 519 auto res = OpenRomFS();
494 auto res = OpenFileSystem(Type::RomFS, unused);
495 if (res.Succeeded()) { 520 if (res.Succeeded()) {
496 romfs = std::move(res.Unwrap()); 521 romfs = std::move(res.Unwrap());
497 } 522 }
@@ -507,8 +532,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
507void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { 532void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
508 LOG_DEBUG(Service_FS, "called"); 533 LOG_DEBUG(Service_FS, "called");
509 534
510 FileSys::Path unused; 535 IFileSystem filesystem(OpenFileSystem(Type::SDMC).Unwrap());
511 auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
512 536
513 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 537 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
514 rb.Push(RESULT_SUCCESS); 538 rb.Push(RESULT_SUCCESS);
@@ -531,8 +555,7 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
531void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { 555void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
532 LOG_WARNING(Service_FS, "(STUBBED) called"); 556 LOG_WARNING(Service_FS, "(STUBBED) called");
533 557
534 FileSys::Path unused; 558 IFileSystem filesystem(OpenFileSystem(Type::SaveData).Unwrap());
535 auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap();
536 559
537 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 560 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
538 rb.Push(RESULT_SUCCESS); 561 rb.Push(RESULT_SUCCESS);
@@ -559,18 +582,11 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
559 return; 582 return;
560 } 583 }
561 584
562 // Attempt to open a StorageBackend interface to the RomFS 585 IStorage storage(romfs);
563 auto storage = romfs->OpenFile({}, {});
564 if (storage.Failed()) {
565 LOG_CRITICAL(Service_FS, "no storage interface available!");
566 IPC::ResponseBuilder rb{ctx, 2};
567 rb.Push(storage.Code());
568 return;
569 }
570 586
571 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 587 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
572 rb.Push(RESULT_SUCCESS); 588 rb.Push(RESULT_SUCCESS);
573 rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); 589 rb.PushIpcInterface<IStorage>(std::move(storage));
574} 590}
575 591
576void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { 592void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index acb78fac1..b1ef6397f 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -29,7 +29,7 @@ private:
29 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 29 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
30 void OpenRomStorage(Kernel::HLERequestContext& ctx); 30 void OpenRomStorage(Kernel::HLERequestContext& ctx);
31 31
32 std::unique_ptr<FileSys::FileSystemBackend> romfs; 32 FileSys::VirtualFile romfs;
33}; 33};
34 34
35} // namespace Service::FileSystem 35} // namespace Service::FileSystem