mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-30 23:49:01 -05:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -621,7 +621,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::ResponseBuilder rb{ctx, 4}; | ||||
|  | ||||
|     FileSys::Path unused; | ||||
|     auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused); | ||||
|     auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData); | ||||
|     if (savedata.Failed()) { | ||||
|         // Create the save data and return an error indicating that the operation was performed. | ||||
|         FileSystem::FormatFileSystem(FileSystem::Type::SaveData); | ||||
|   | ||||
| @@ -2,36 +2,221 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <boost/container/flat_map.hpp> | ||||
| #include "boost/container/flat_map.hpp" | ||||
| #include "common/assert.h" | ||||
| #include "common/common_paths.h" | ||||
| #include "common/file_util.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/errors.h" | ||||
| #include "core/file_sys/filesystem.h" | ||||
| #include "core/file_sys/savedata_factory.h" | ||||
| #include "core/file_sys/sdmc_factory.h" | ||||
| #include "core/file_sys/vfs.h" | ||||
| #include "core/file_sys/vfs_offset.h" | ||||
| #include "core/file_sys/vfs_real.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/hle/service/filesystem/fsp_srv.h" | ||||
|  | ||||
| namespace Service::FileSystem { | ||||
|  | ||||
| // Size of emulated sd card free space, reported in bytes. | ||||
| // Just using 32GB because thats reasonable | ||||
| // TODO(DarkLordZach): Eventually make this configurable in settings. | ||||
| constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; | ||||
|  | ||||
| static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, | ||||
|                                                        const std::string& dir_name) { | ||||
|     if (dir_name == "." || dir_name == "" || dir_name == "/" || dir_name == "\\") | ||||
|         return base; | ||||
|  | ||||
|     return base->GetDirectoryRelative(dir_name); | ||||
| } | ||||
|  | ||||
| VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_) | ||||
|     : backing(backing_) {} | ||||
|  | ||||
| std::string VfsDirectoryServiceWrapper::GetName() const { | ||||
|     return backing->GetName(); | ||||
| } | ||||
|  | ||||
| ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const { | ||||
|     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||||
|     auto file = dir->CreateFile(FileUtil::GetFilename(path)); | ||||
|     if (file == nullptr) | ||||
|         return ResultCode(-1); | ||||
|     if (!file->Resize(size)) | ||||
|         return ResultCode(-1); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const { | ||||
|     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||||
|     if (!backing->DeleteFile(FileUtil::GetFilename(path))) | ||||
|         return ResultCode(-1); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const { | ||||
|     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||||
|     if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) | ||||
|         dir = backing; | ||||
|     auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); | ||||
|     if (new_dir == nullptr) | ||||
|         return ResultCode(-1); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const { | ||||
|     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||||
|     if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) | ||||
|         return ResultCode(-1); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const { | ||||
|     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||||
|     if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) | ||||
|         return ResultCode(-1); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path, | ||||
|                                                   const std::string& dest_path) const { | ||||
|     auto src = backing->GetFileRelative(src_path); | ||||
|     if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||||
|         // Use more-optimized vfs implementation rename. | ||||
|         if (src == nullptr) | ||||
|             return FileSys::ERROR_PATH_NOT_FOUND; | ||||
|         if (!src->Rename(FileUtil::GetFilename(dest_path))) | ||||
|             return ResultCode(-1); | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
|  | ||||
|     // Move by hand -- TODO(DarkLordZach): Optimize | ||||
|     auto c_res = CreateFile(dest_path, src->GetSize()); | ||||
|     if (c_res != RESULT_SUCCESS) | ||||
|         return c_res; | ||||
|  | ||||
|     auto dest = backing->GetFileRelative(dest_path); | ||||
|     ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); | ||||
|  | ||||
|     ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), | ||||
|                "Could not write all of the bytes but everything else has succeded."); | ||||
|  | ||||
|     if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) | ||||
|         return ResultCode(-1); | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path, | ||||
|                                                        const std::string& dest_path) const { | ||||
|     auto src = GetDirectoryRelativeWrapped(backing, src_path); | ||||
|     if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||||
|         // Use more-optimized vfs implementation rename. | ||||
|         if (src == nullptr) | ||||
|             return FileSys::ERROR_PATH_NOT_FOUND; | ||||
|         if (!src->Rename(FileUtil::GetFilename(dest_path))) | ||||
|             return ResultCode(-1); | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
|  | ||||
|     // TODO(DarkLordZach): Implement renaming across the tree (move). | ||||
|     ASSERT_MSG(false, | ||||
|                "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs " | ||||
|                "don't match -- UNIMPLEMENTED", | ||||
|                src_path, dest_path); | ||||
|  | ||||
|     return ResultCode(-1); | ||||
| } | ||||
|  | ||||
| ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path, | ||||
|                                                                      FileSys::Mode mode) const { | ||||
|     auto file = backing->GetFileRelative(path); | ||||
|     if (file == nullptr) | ||||
|         return FileSys::ERROR_PATH_NOT_FOUND; | ||||
|  | ||||
|     if ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) { | ||||
|         return MakeResult<FileSys::VirtualFile>( | ||||
|             std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize())); | ||||
|     } | ||||
|  | ||||
|     return MakeResult<FileSys::VirtualFile>(file); | ||||
| } | ||||
|  | ||||
| ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) { | ||||
|     auto dir = GetDirectoryRelativeWrapped(backing, path); | ||||
|     if (dir == nullptr) | ||||
|         return ResultCode(-1); | ||||
|     return MakeResult(dir); | ||||
| } | ||||
|  | ||||
| u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const { | ||||
|     if (backing->IsWritable()) | ||||
|         return EMULATED_SD_REPORTED_SIZE; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | ||||
|     const std::string& path) const { | ||||
|     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||||
|     if (dir == nullptr) | ||||
|         return ResultCode(-1); | ||||
|     auto filename = FileUtil::GetFilename(path); | ||||
|     if (dir->GetFile(filename) != nullptr) | ||||
|         return MakeResult(FileSys::EntryType::File); | ||||
|     if (dir->GetSubdirectory(filename) != nullptr) | ||||
|         return MakeResult(FileSys::EntryType::Directory); | ||||
|     return ResultCode(-1); | ||||
| } | ||||
|  | ||||
| // A deferred filesystem for nand save data. | ||||
| // This must be deferred because the directory is dependent on title id, which is not set at | ||||
| // registration time. | ||||
| struct SaveDataDeferredFilesystem : DeferredFilesystem { | ||||
| protected: | ||||
|     FileSys::VirtualDir CreateFilesystem() override { | ||||
|         u64 title_id = Core::CurrentProcess()->program_id; | ||||
|         // TODO(DarkLordZach): Users | ||||
|         u32 user_id = 0; | ||||
|         std::string nand_directory = fmt::format( | ||||
|             "{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id); | ||||
|  | ||||
|         auto savedata = | ||||
|             std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write); | ||||
|         return savedata; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Map of registered file systems, identified by type. Once an file system is registered here, it | ||||
|  * is never removed until UnregisterFileSystems is called. | ||||
|  */ | ||||
| static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; | ||||
| static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map; | ||||
| static FileSys::VirtualFile filesystem_romfs = nullptr; | ||||
|  | ||||
| ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { | ||||
| ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) { | ||||
|     auto result = filesystem_map.emplace(type, std::move(factory)); | ||||
|  | ||||
|     bool inserted = result.second; | ||||
|     ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); | ||||
|  | ||||
|     auto& filesystem = result.first->second; | ||||
|     LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), | ||||
|               static_cast<u32>(type)); | ||||
|     LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type)); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | ||||
|                                                                       FileSys::Path& path) { | ||||
| ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) { | ||||
|     ASSERT_MSG(filesystem_romfs == nullptr, | ||||
|                "Tried to register more than one system with same id code"); | ||||
|  | ||||
|     filesystem_romfs = filesystem; | ||||
|     LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), | ||||
|               static_cast<u32>(Type::RomFS)); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) { | ||||
|     LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); | ||||
|  | ||||
|     auto itr = filesystem_map.find(type); | ||||
| @@ -40,7 +225,13 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | ||||
|         return ResultCode(-1); | ||||
|     } | ||||
|  | ||||
|     return itr->second->Open(path); | ||||
|     return MakeResult(itr->second->Get()); | ||||
| } | ||||
|  | ||||
| ResultVal<FileSys::VirtualFile> OpenRomFS() { | ||||
|     if (filesystem_romfs == nullptr) | ||||
|         return ResultCode(-1); | ||||
|     return MakeResult(filesystem_romfs); | ||||
| } | ||||
|  | ||||
| ResultCode FormatFileSystem(Type type) { | ||||
| @@ -52,21 +243,21 @@ ResultCode FormatFileSystem(Type type) { | ||||
|         return ResultCode(-1); | ||||
|     } | ||||
|  | ||||
|     FileSys::Path unused; | ||||
|     return itr->second->Format(unused); | ||||
|     return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory( | ||||
|                itr->second->Get()->GetName()) | ||||
|                ? RESULT_SUCCESS | ||||
|                : ResultCode(-1); | ||||
| } | ||||
|  | ||||
| void RegisterFileSystems() { | ||||
|     filesystem_map.clear(); | ||||
|     filesystem_romfs = nullptr; | ||||
|  | ||||
|     std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); | ||||
|     std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); | ||||
|     auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write); | ||||
|     RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC); | ||||
|  | ||||
|     auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory)); | ||||
|     RegisterFileSystem(std::move(savedata), Type::SaveData); | ||||
|  | ||||
|     auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory)); | ||||
|     RegisterFileSystem(std::move(sdcard), Type::SDMC); | ||||
|     RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData); | ||||
| } | ||||
|  | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager) { | ||||
|   | ||||
| @@ -6,6 +6,8 @@ | ||||
|  | ||||
| #include <memory> | ||||
| #include "common/common_types.h" | ||||
| #include "core/file_sys/filesystem.h" | ||||
| #include "core/file_sys/vfs.h" | ||||
| #include "core/hle/result.h" | ||||
|  | ||||
| namespace FileSys { | ||||
| @@ -29,12 +31,136 @@ enum class Type { | ||||
|     SDMC = 3, | ||||
| }; | ||||
|  | ||||
| // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of | ||||
| // pointers and booleans. This makes using a VfsDirectory with switch services much easier and | ||||
| // avoids repetitive code. | ||||
| class VfsDirectoryServiceWrapper { | ||||
| public: | ||||
|     explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing); | ||||
|  | ||||
|     /** | ||||
|      * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||||
|      */ | ||||
|     std::string GetName() const; | ||||
|  | ||||
|     /** | ||||
|      * Create a file specified by its path | ||||
|      * @param path Path relative to the Archive | ||||
|      * @param size The size of the new file, filled with zeroes | ||||
|      * @return Result of the operation | ||||
|      */ | ||||
|     ResultCode CreateFile(const std::string& path, u64 size) const; | ||||
|  | ||||
|     /** | ||||
|      * Delete a file specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Result of the operation | ||||
|      */ | ||||
|     ResultCode DeleteFile(const std::string& path) const; | ||||
|  | ||||
|     /** | ||||
|      * Create a directory specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Result of the operation | ||||
|      */ | ||||
|     ResultCode CreateDirectory(const std::string& path) const; | ||||
|  | ||||
|     /** | ||||
|      * Delete a directory specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Result of the operation | ||||
|      */ | ||||
|     ResultCode DeleteDirectory(const std::string& path) const; | ||||
|  | ||||
|     /** | ||||
|      * Delete a directory specified by its path and anything under it | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Result of the operation | ||||
|      */ | ||||
|     ResultCode DeleteDirectoryRecursively(const std::string& path) const; | ||||
|  | ||||
|     /** | ||||
|      * Rename a File specified by its path | ||||
|      * @param src_path Source path relative to the archive | ||||
|      * @param dest_path Destination path relative to the archive | ||||
|      * @return Result of the operation | ||||
|      */ | ||||
|     ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const; | ||||
|  | ||||
|     /** | ||||
|      * Rename a Directory specified by its path | ||||
|      * @param src_path Source path relative to the archive | ||||
|      * @param dest_path Destination path relative to the archive | ||||
|      * @return Result of the operation | ||||
|      */ | ||||
|     ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const; | ||||
|  | ||||
|     /** | ||||
|      * Open a file specified by its path, using the specified mode | ||||
|      * @param path Path relative to the archive | ||||
|      * @param mode Mode to open the file with | ||||
|      * @return Opened file, or error code | ||||
|      */ | ||||
|     ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const; | ||||
|  | ||||
|     /** | ||||
|      * Open a directory specified by its path | ||||
|      * @param path Path relative to the archive | ||||
|      * @return Opened directory, or error code | ||||
|      */ | ||||
|     ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); | ||||
|  | ||||
|     /** | ||||
|      * Get the free space | ||||
|      * @return The number of free bytes in the archive | ||||
|      */ | ||||
|     u64 GetFreeSpaceSize() const; | ||||
|  | ||||
|     /** | ||||
|      * Get the type of the specified path | ||||
|      * @return The type of the specified path or error code | ||||
|      */ | ||||
|     ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const; | ||||
|  | ||||
| private: | ||||
|     FileSys::VirtualDir backing; | ||||
| }; | ||||
|  | ||||
| // A class that deferres the creation of a filesystem until a later time. | ||||
| // This is useful if construction depends on a variable not known when the filesystem is registered. | ||||
| // Construct this with a filesystem (VirtualDir) to avoid the deferrence feature or override the | ||||
| //     CreateFilesystem method which will be called on first use. | ||||
| struct DeferredFilesystem { | ||||
|     DeferredFilesystem() = default; | ||||
|  | ||||
|     explicit DeferredFilesystem(FileSys::VirtualDir vfs_directory) : fs(std::move(vfs_directory)) {} | ||||
|  | ||||
|     FileSys::VirtualDir Get() { | ||||
|         if (fs == nullptr) | ||||
|             fs = CreateFilesystem(); | ||||
|  | ||||
|         return fs; | ||||
|     } | ||||
|  | ||||
|     virtual ~DeferredFilesystem() = default; | ||||
|  | ||||
| protected: | ||||
|     virtual FileSys::VirtualDir CreateFilesystem() { | ||||
|         return fs; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     FileSys::VirtualDir fs; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Registers a FileSystem, instances of which can later be opened using its IdCode. | ||||
|  * @param factory FileSystem backend interface to use | ||||
|  * @param type Type used to access this type of FileSystem | ||||
|  */ | ||||
| ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); | ||||
| ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& fs, Type type); | ||||
|  | ||||
| ResultCode RegisterRomFS(FileSys::VirtualFile fs); | ||||
|  | ||||
| /** | ||||
|  * Opens a file system | ||||
| @@ -42,8 +168,9 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact | ||||
|  * @param path Path to the file system, used with Binary paths | ||||
|  * @return FileSys::FileSystemBackend interface to the file system | ||||
|  */ | ||||
| ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | ||||
|                                                                       FileSys::Path& path); | ||||
| ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type); | ||||
|  | ||||
| ResultVal<FileSys::VirtualFile> OpenRomFS(); | ||||
|  | ||||
| /** | ||||
|  * Formats a file system | ||||
|   | ||||
| @@ -19,8 +19,8 @@ namespace Service::FileSystem { | ||||
|  | ||||
| class IStorage final : public ServiceFramework<IStorage> { | ||||
| public: | ||||
|     IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) | ||||
|         : ServiceFramework("IStorage"), backend(std::move(backend)) { | ||||
|     IStorage(FileSys::VirtualFile backend_) | ||||
|         : ServiceFramework("IStorage"), backend(std::move(backend_)) { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"},   {2, nullptr, "Flush"}, | ||||
|             {3, nullptr, "SetSize"},      {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, | ||||
| @@ -29,7 +29,7 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::unique_ptr<FileSys::StorageBackend> backend; | ||||
|     FileSys::VirtualFile backend; | ||||
|  | ||||
|     void Read(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
| @@ -51,8 +51,8 @@ private: | ||||
|         } | ||||
|  | ||||
|         // Read the data from the Storage backend | ||||
|         std::vector<u8> output(length); | ||||
|         ResultVal<size_t> res = backend->Read(offset, length, output.data()); | ||||
|         std::vector<u8> output = backend->ReadBytes(length, offset); | ||||
|         auto res = MakeResult<size_t>(output.size()); | ||||
|         if (res.Failed()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(res.Code()); | ||||
| @@ -69,8 +69,8 @@ private: | ||||
|  | ||||
| class IFile final : public ServiceFramework<IFile> { | ||||
| public: | ||||
|     explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) | ||||
|         : ServiceFramework("IFile"), backend(std::move(backend)) { | ||||
|     explicit IFile(FileSys::VirtualFile backend_) | ||||
|         : ServiceFramework("IFile"), backend(std::move(backend_)) { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IFile::Read, "Read"},       {1, &IFile::Write, "Write"}, | ||||
|             {2, &IFile::Flush, "Flush"},     {3, &IFile::SetSize, "SetSize"}, | ||||
| @@ -80,7 +80,7 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::unique_ptr<FileSys::StorageBackend> backend; | ||||
|     FileSys::VirtualFile backend; | ||||
|  | ||||
|     void Read(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
| @@ -103,8 +103,8 @@ private: | ||||
|         } | ||||
|  | ||||
|         // Read the data from the Storage backend | ||||
|         std::vector<u8> output(length); | ||||
|         ResultVal<size_t> res = backend->Read(offset, length, output.data()); | ||||
|         std::vector<u8> output = backend->ReadBytes(length, offset); | ||||
|         auto res = MakeResult<size_t>(output.size()); | ||||
|         if (res.Failed()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(res.Code()); | ||||
| @@ -139,9 +139,10 @@ private: | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Write the data to the Storage backend | ||||
|         std::vector<u8> data = ctx.ReadBuffer(); | ||||
|         ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); | ||||
|         data.resize(length); | ||||
|         // Write the data to the Storage backend | ||||
|         auto res = MakeResult<size_t>(backend->WriteBytes(data, offset)); | ||||
|         if (res.Failed()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(res.Code()); | ||||
| @@ -154,7 +155,8 @@ private: | ||||
|  | ||||
|     void Flush(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_FS, "called"); | ||||
|         backend->Flush(); | ||||
|  | ||||
|         // Exists for SDK compatibiltity -- No need to flush file. | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
| @@ -163,7 +165,7 @@ private: | ||||
|     void SetSize(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         const u64 size = rp.Pop<u64>(); | ||||
|         backend->SetSize(size); | ||||
|         backend->Resize(size); | ||||
|         LOG_DEBUG(Service_FS, "called, size={}", size); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
| @@ -180,19 +182,38 @@ private: | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data, | ||||
|                             FileSys::EntryType type) { | ||||
|     for (const auto& new_entry : new_data) { | ||||
|         FileSys::Entry entry; | ||||
|         entry.filename[0] = '\0'; | ||||
|         std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1); | ||||
|         entry.type = type; | ||||
|         entry.file_size = new_entry->GetSize(); | ||||
|         entries.emplace_back(std::move(entry)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| class IDirectory final : public ServiceFramework<IDirectory> { | ||||
| public: | ||||
|     explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) | ||||
|         : ServiceFramework("IDirectory"), backend(std::move(backend)) { | ||||
|     explicit IDirectory(FileSys::VirtualDir backend_) | ||||
|         : ServiceFramework("IDirectory"), backend(std::move(backend_)) { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IDirectory::Read, "Read"}, | ||||
|             {1, &IDirectory::GetEntryCount, "GetEntryCount"}, | ||||
|         }; | ||||
|         RegisterHandlers(functions); | ||||
|  | ||||
|         // Build entry index now to save time later. | ||||
|         BuildEntryIndex(entries, backend->GetFiles(), FileSys::File); | ||||
|         BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::unique_ptr<FileSys::DirectoryBackend> backend; | ||||
|     FileSys::VirtualDir backend; | ||||
|     std::vector<FileSys::Entry> entries; | ||||
|     u64 next_entry_index = 0; | ||||
|  | ||||
|     void Read(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
| @@ -203,26 +224,31 @@ private: | ||||
|         // Calculate how many entries we can fit in the output buffer | ||||
|         u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); | ||||
|  | ||||
|         // Cap at total number of entries. | ||||
|         u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); | ||||
|  | ||||
|         // Read the data from the Directory backend | ||||
|         std::vector<FileSys::Entry> entries(count_entries); | ||||
|         u64 read_entries = backend->Read(count_entries, entries.data()); | ||||
|         std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index, | ||||
|                                                entries.begin() + next_entry_index + actual_entries); | ||||
|  | ||||
|         next_entry_index += actual_entries; | ||||
|  | ||||
|         // Convert the data into a byte array | ||||
|         std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); | ||||
|         std::memcpy(output.data(), entries.data(), output.size()); | ||||
|         std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry)); | ||||
|         std::memcpy(output.data(), entry_data.data(), output.size()); | ||||
|  | ||||
|         // Write the data to memory | ||||
|         ctx.WriteBuffer(output); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 4}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.Push(read_entries); | ||||
|         rb.Push(actual_entries); | ||||
|     } | ||||
|  | ||||
|     void GetEntryCount(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_FS, "called"); | ||||
|  | ||||
|         u64 count = backend->GetEntryCount(); | ||||
|         u64 count = entries.size() - next_entry_index; | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 4}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
| @@ -232,7 +258,7 @@ private: | ||||
|  | ||||
| class IFileSystem final : public ServiceFramework<IFileSystem> { | ||||
| public: | ||||
|     explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) | ||||
|     explicit IFileSystem(FileSys::VirtualDir backend) | ||||
|         : ServiceFramework("IFileSystem"), backend(std::move(backend)) { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &IFileSystem::CreateFile, "CreateFile"}, | ||||
| @@ -267,7 +293,7 @@ public: | ||||
|         LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(backend->CreateFile(name, size)); | ||||
|         rb.Push(backend.CreateFile(name, size)); | ||||
|     } | ||||
|  | ||||
|     void DeleteFile(Kernel::HLERequestContext& ctx) { | ||||
| @@ -279,7 +305,7 @@ public: | ||||
|         LOG_DEBUG(Service_FS, "called file {}", name); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(backend->DeleteFile(name)); | ||||
|         rb.Push(backend.DeleteFile(name)); | ||||
|     } | ||||
|  | ||||
|     void CreateDirectory(Kernel::HLERequestContext& ctx) { | ||||
| @@ -291,7 +317,7 @@ public: | ||||
|         LOG_DEBUG(Service_FS, "called directory {}", name); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(backend->CreateDirectory(name)); | ||||
|         rb.Push(backend.CreateDirectory(name)); | ||||
|     } | ||||
|  | ||||
|     void RenameFile(Kernel::HLERequestContext& ctx) { | ||||
| @@ -309,7 +335,7 @@ public: | ||||
|         LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(backend->RenameFile(src_name, dst_name)); | ||||
|         rb.Push(backend.RenameFile(src_name, dst_name)); | ||||
|     } | ||||
|  | ||||
|     void OpenFile(Kernel::HLERequestContext& ctx) { | ||||
| @@ -322,14 +348,14 @@ public: | ||||
|  | ||||
|         LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); | ||||
|  | ||||
|         auto result = backend->OpenFile(name, mode); | ||||
|         auto result = backend.OpenFile(name, mode); | ||||
|         if (result.Failed()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(result.Code()); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         auto file = std::move(result.Unwrap()); | ||||
|         IFile file(result.Unwrap()); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
| @@ -347,14 +373,14 @@ public: | ||||
|  | ||||
|         LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); | ||||
|  | ||||
|         auto result = backend->OpenDirectory(name); | ||||
|         auto result = backend.OpenDirectory(name); | ||||
|         if (result.Failed()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(result.Code()); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         auto directory = std::move(result.Unwrap()); | ||||
|         IDirectory directory(result.Unwrap()); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
| @@ -369,7 +395,7 @@ public: | ||||
|  | ||||
|         LOG_DEBUG(Service_FS, "called file {}", name); | ||||
|  | ||||
|         auto result = backend->GetEntryType(name); | ||||
|         auto result = backend.GetEntryType(name); | ||||
|         if (result.Failed()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(result.Code()); | ||||
| @@ -389,7 +415,7 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::unique_ptr<FileSys::FileSystemBackend> backend; | ||||
|     VfsDirectoryServiceWrapper backend; | ||||
| }; | ||||
|  | ||||
| FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | ||||
| @@ -490,8 +516,7 @@ void FSP_SRV::TryLoadRomFS() { | ||||
|     if (romfs) { | ||||
|         return; | ||||
|     } | ||||
|     FileSys::Path unused; | ||||
|     auto res = OpenFileSystem(Type::RomFS, unused); | ||||
|     auto res = OpenRomFS(); | ||||
|     if (res.Succeeded()) { | ||||
|         romfs = std::move(res.Unwrap()); | ||||
|     } | ||||
| @@ -507,8 +532,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { | ||||
| void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_DEBUG(Service_FS, "called"); | ||||
|  | ||||
|     FileSys::Path unused; | ||||
|     auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap(); | ||||
|     IFileSystem filesystem(OpenFileSystem(Type::SDMC).Unwrap()); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| @@ -531,8 +555,7 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) { | ||||
| void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_FS, "(STUBBED) called"); | ||||
|  | ||||
|     FileSys::Path unused; | ||||
|     auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap(); | ||||
|     IFileSystem filesystem(OpenFileSystem(Type::SaveData).Unwrap()); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| @@ -559,18 +582,11 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Attempt to open a StorageBackend interface to the RomFS | ||||
|     auto storage = romfs->OpenFile({}, {}); | ||||
|     if (storage.Failed()) { | ||||
|         LOG_CRITICAL(Service_FS, "no storage interface available!"); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(storage.Code()); | ||||
|         return; | ||||
|     } | ||||
|     IStorage storage(romfs); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); | ||||
|     rb.PushIpcInterface<IStorage>(std::move(storage)); | ||||
| } | ||||
|  | ||||
| void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { | ||||
|   | ||||
| @@ -29,7 +29,7 @@ private: | ||||
|     void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | ||||
|     void OpenRomStorage(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     std::unique_ptr<FileSys::FileSystemBackend> romfs; | ||||
|     FileSys::VirtualFile romfs; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::FileSystem | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Zach Hilman
					Zach Hilman