ams: support building unit test programs on windows/linux/macos
This commit is contained in:
@@ -90,7 +90,7 @@ namespace ams::fs {
|
||||
}
|
||||
}
|
||||
|
||||
Result FileStorageBasedFileSystem::Initialize(std::shared_ptr<fs::fsa::IFileSystem> base_file_system, const char *path, fs::OpenMode mode) {
|
||||
Result FileStorageBasedFileSystem::Initialize(std::shared_ptr<fs::fsa::IFileSystem> base_file_system, const fs::Path &path, fs::OpenMode mode) {
|
||||
/* Open the file. */
|
||||
std::unique_ptr<fs::fsa::IFile> base_file;
|
||||
R_TRY(base_file_system->OpenFile(std::addressof(base_file), path, mode));
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "fsa/fs_user_mount_table.hpp"
|
||||
#include "fsa/fs_directory_accessor.hpp"
|
||||
#include "fsa/fs_file_accessor.hpp"
|
||||
@@ -21,7 +22,7 @@
|
||||
|
||||
#define AMS_FS_IMPL_ACCESS_LOG_AMS_API_VERSION "ams_version: " STRINGIZE(ATMOSPHERE_RELEASE_VERSION_MAJOR) "." STRINGIZE(ATMOSPHERE_RELEASE_VERSION_MINOR) "." STRINGIZE(ATMOSPHERE_RELEASE_VERSION_MICRO)
|
||||
|
||||
/* TODO: Other boards? */
|
||||
/* TODO: Other specs? */
|
||||
#define AMS_FS_IMPL_ACCESS_LOG_SPEC "spec: NX"
|
||||
|
||||
namespace ams::fs {
|
||||
@@ -48,13 +49,15 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result GetGlobalAccessLogMode(u32 *out) {
|
||||
/* Use libnx bindings. */
|
||||
return ::fsGetGlobalAccessLogMode(out);
|
||||
const auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->GetGlobalAccessLogMode(out));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetGlobalAccessLogMode(u32 mode) {
|
||||
/* Use libnx bindings. */
|
||||
return ::fsSetGlobalAccessLogMode(mode);
|
||||
const auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->SetGlobalAccessLogMode(mode));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetLocalAccessLog(bool enabled) {
|
||||
@@ -167,6 +170,15 @@ namespace ams::fs::impl {
|
||||
}
|
||||
}
|
||||
|
||||
template<> const char *IdString::ToString<fs::GameCardPartition>(fs::GameCardPartition id) {
|
||||
switch (id) {
|
||||
case fs::GameCardPartition::Update: return "Update";
|
||||
case fs::GameCardPartition::Normal: return "Normal";
|
||||
case fs::GameCardPartition::Secure: return "Secure";
|
||||
default: return ToValueString(static_cast<int>(id));
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class AccessLogPrinterCallbackManager {
|
||||
@@ -199,8 +211,9 @@ namespace ams::fs::impl {
|
||||
}
|
||||
|
||||
Result OutputAccessLogToSdCardImpl(const char *log, size_t size) {
|
||||
/* Use libnx bindings. */
|
||||
return ::fsOutputAccessLogToSdCard(log, size);
|
||||
const auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->OutputAccessLogToSdCard(sf::InBuffer(log, size)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void OutputAccessLogToSdCard(const char *format, std::va_list vl) {
|
||||
@@ -291,10 +304,11 @@ namespace ams::fs::impl {
|
||||
OutputAccessLogImpl(log_buffer.get(), log_buffer_size);
|
||||
}
|
||||
|
||||
void GetProgramIndexFortAccessLog(u32 *out_index, u32 *out_count) {
|
||||
void GetProgramIndexForAccessLog(u32 *out_index, u32 *out_count) {
|
||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||
/* Use libnx bindings if available. */
|
||||
R_ABORT_UNLESS(::fsGetProgramIndexForAccessLog(out_index, out_count));
|
||||
const auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
R_ABORT_UNLESS(fsp->GetProgramIndexForAccessLog(out_index, out_count));
|
||||
} else {
|
||||
/* Use hardcoded defaults. */
|
||||
*out_index = 0;
|
||||
@@ -305,7 +319,7 @@ namespace ams::fs::impl {
|
||||
void OutputAccessLogStart() {
|
||||
/* Get the program index. */
|
||||
u32 program_index = 0, program_count = 0;
|
||||
GetProgramIndexFortAccessLog(std::addressof(program_index), std::addressof(program_count));
|
||||
GetProgramIndexForAccessLog(std::addressof(program_index), std::addressof(program_count));
|
||||
|
||||
/* Print the log buffer. */
|
||||
if (program_count < 2) {
|
||||
|
||||
@@ -14,25 +14,182 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern u32 __nx_fs_num_sessions;
|
||||
|
||||
}
|
||||
#include "fs_remote_file_system_proxy.hpp"
|
||||
#include "fs_remote_file_system_proxy_for_loader.hpp"
|
||||
#include "impl/fs_library.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
/* TODO: FileSystemProxySessionSetting */
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
namespace {
|
||||
|
||||
constinit std::aligned_storage_t<0x80> g_fsp_service_object_buffer;
|
||||
constinit std::aligned_storage_t<0x80> g_fsp_ldr_service_object_buffer;
|
||||
constinit bool g_use_static_fsp_service_object_buffer = false;
|
||||
constinit bool g_use_static_fsp_ldr_service_object_buffer = false;
|
||||
|
||||
class HipcClientAllocator {
|
||||
public:
|
||||
using Policy = sf::StatelessAllocationPolicy<HipcClientAllocator>;
|
||||
public:
|
||||
constexpr HipcClientAllocator() = default;
|
||||
|
||||
void *Allocate(size_t size) {
|
||||
if (g_use_static_fsp_service_object_buffer) {
|
||||
return std::addressof(g_fsp_service_object_buffer);
|
||||
} else if (g_use_static_fsp_ldr_service_object_buffer) {
|
||||
return std::addressof(g_fsp_ldr_service_object_buffer);
|
||||
} else {
|
||||
return ::ams::fs::impl::Allocate(size);
|
||||
}
|
||||
}
|
||||
|
||||
void Deallocate(void *ptr, size_t size) {
|
||||
if (ptr == std::addressof(g_fsp_service_object_buffer)) {
|
||||
return;
|
||||
} else if (ptr == std::addressof(g_fsp_ldr_service_object_buffer)) {
|
||||
return;
|
||||
} else {
|
||||
return ::ams::fs::impl::Deallocate(ptr, size);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum class FileSystemProxySessionSetting {
|
||||
SystemNormal = 0,
|
||||
Application = 1,
|
||||
SystemMulti = 2,
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE int GetSessionCount(FileSystemProxySessionSetting setting) {
|
||||
switch (setting) {
|
||||
case FileSystemProxySessionSetting::Application: return 3;
|
||||
case FileSystemProxySessionSetting::SystemNormal: return 1;
|
||||
case FileSystemProxySessionSetting::SystemMulti: return 2;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constinit bool g_is_fsp_object_initialized = false;
|
||||
constinit FileSystemProxySessionSetting g_fsp_session_setting = FileSystemProxySessionSetting::SystemNormal;
|
||||
|
||||
/* TODO: SessionResourceManager */
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constinit sf::SharedPointer<fssrv::sf::IFileSystemProxy> g_fsp_service_object;
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxy> GetFileSystemProxyServiceObjectImpl() {
|
||||
/* Ensure the library is initialized. */
|
||||
::ams::fs::impl::InitializeFileSystemLibrary();
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxy> fsp_object;
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_OS_HORIZON)
|
||||
/* Try to use the custom object. */
|
||||
fsp_object = g_fsp_service_object;
|
||||
|
||||
/* If we don't have one, create a remote object. */
|
||||
if (fsp_object == nullptr) {
|
||||
/* Make our next allocation use our static reserved buffer for the service object. */
|
||||
g_use_static_fsp_service_object_buffer = true;
|
||||
ON_SCOPE_EXIT { g_use_static_fsp_service_object_buffer = false; };
|
||||
|
||||
using ObjectFactory = sf::ObjectFactory<HipcClientAllocator::Policy>;
|
||||
|
||||
/* Create the object. */
|
||||
fsp_object = ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystemProxy, RemoteFileSystemProxy>(GetSessionCount(g_fsp_session_setting));
|
||||
AMS_ABORT_UNLESS(fsp_object != nullptr);
|
||||
|
||||
/* Set the current process. */
|
||||
fsp_object->SetCurrentProcess({});
|
||||
}
|
||||
#else
|
||||
/* On non-horizon, use the system object. */
|
||||
fsp_object = fssrv::impl::GetFileSystemProxyServiceObject();
|
||||
AMS_ABORT_UNLESS(fsp_object != nullptr);
|
||||
|
||||
/* Set the current process. */
|
||||
fsp_object->SetCurrentProcess({});
|
||||
#endif
|
||||
|
||||
|
||||
/* Return the object. */
|
||||
return fsp_object;
|
||||
}
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> GetFileSystemProxyForLoaderServiceObjectImpl() {
|
||||
/* Ensure the library is initialized. */
|
||||
::ams::fs::impl::InitializeFileSystemLibrary();
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> fsp_ldr_object;
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_OS_HORIZON)
|
||||
/* Make our next allocation use our static reserved buffer for the service object. */
|
||||
g_use_static_fsp_ldr_service_object_buffer = true;
|
||||
ON_SCOPE_EXIT { g_use_static_fsp_ldr_service_object_buffer = false; };
|
||||
|
||||
using ObjectFactory = sf::ObjectFactory<HipcClientAllocator::Policy>;
|
||||
|
||||
/* Create the object. */
|
||||
fsp_ldr_object = ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystemProxyForLoader, RemoteFileSystemProxyForLoader>();
|
||||
AMS_ABORT_UNLESS(fsp_ldr_object != nullptr);
|
||||
#else
|
||||
/* On non-horizon, use the system object. */
|
||||
fsp_ldr_object = fssrv::impl::GetFileSystemProxyForLoaderServiceObject();
|
||||
AMS_ABORT_UNLESS(fsp_ldr_object != nullptr);
|
||||
#endif
|
||||
|
||||
/* Return the object. */
|
||||
return fsp_ldr_object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxy> GetFileSystemProxyServiceObject() {
|
||||
AMS_FUNCTION_LOCAL_STATIC(sf::SharedPointer<fssrv::sf::IFileSystemProxy>, s_fsp_service_object, GetFileSystemProxyServiceObjectImpl());
|
||||
return s_fsp_service_object;
|
||||
}
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> GetFileSystemProxyForLoaderServiceObject() {
|
||||
AMS_FUNCTION_LOCAL_STATIC(sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader>, s_fsp_ldr_service_object, GetFileSystemProxyForLoaderServiceObjectImpl());
|
||||
return s_fsp_ldr_service_object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeForHostTool() {
|
||||
#if !defined(ATMOSPHERE_OS_HORIZON)
|
||||
AMS_ABORT_UNLESS(impl::GetFileSystemProxyServiceObject() != nullptr);
|
||||
R_ABORT_UNLESS(::ams::fs::MountHostRoot());
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitializeForSystem() {
|
||||
__nx_fs_num_sessions = 1;
|
||||
R_ABORT_UNLESS(::fsInitialize());
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
AMS_ABORT_UNLESS(!g_is_fsp_object_initialized);
|
||||
AMS_ABORT_UNLESS(g_fsp_session_setting == FileSystemProxySessionSetting::SystemNormal);
|
||||
g_fsp_session_setting = FileSystemProxySessionSetting::SystemNormal;
|
||||
|
||||
/* Nintendo doesn't do this, but we have to for timing reasons. */
|
||||
AMS_ABORT_UNLESS(impl::GetFileSystemProxyServiceObject() != nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitializeWithMultiSessionForSystem() {
|
||||
__nx_fs_num_sessions = 2;
|
||||
R_ABORT_UNLESS(::fsInitialize());
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
AMS_ABORT_UNLESS(!g_is_fsp_object_initialized);
|
||||
AMS_ABORT_UNLESS(g_fsp_session_setting == FileSystemProxySessionSetting::SystemNormal);
|
||||
g_fsp_session_setting = FileSystemProxySessionSetting::SystemMulti;
|
||||
|
||||
/* Nintendo doesn't do this, but we have to for timing reasons. */
|
||||
AMS_ABORT_UNLESS(impl::GetFileSystemProxyServiceObject() != nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,26 +15,43 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result MountApplicationPackage(const char *name, const char *common_path) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Validate the path. */
|
||||
R_UNLESS(common_path != nullptr, fs::ResultInvalidPath());
|
||||
/* Validate the path. */
|
||||
R_UNLESS(common_path != nullptr, fs::ResultInvalidPath());
|
||||
|
||||
/* Open a filesystem using libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), ncm::InvalidProgramId.value, static_cast<::FsFileSystemType>(impl::FileSystemProxyType_Package), common_path));
|
||||
/* Convert the path for ipc. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), common_path));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInApplicationA());
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenFileSystemWithId(std::addressof(fs), sf_path, ncm::InvalidProgramId.value, impl::FileSystemProxyType_Package));
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInApplicationA());
|
||||
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_APPLICATION_PACKAGE(name, common_path)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
#include "impl/fs_storage_service_object_adapter.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -46,43 +49,45 @@ namespace ams::fs {
|
||||
namespace impl {
|
||||
|
||||
Result MountBisImpl(const char *name, BisPartitionId id, const char *root_path) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
|
||||
/* Open the partition. This uses libnx bindings. */
|
||||
/* NOTE: Nintendo ignores the root_path here. */
|
||||
AMS_UNUSED(root_path);
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenBisFileSystem(std::addressof(fs), static_cast<::FsBisPartitionId>(id), ""));
|
||||
/* Convert the path for ipc. */
|
||||
/* NOTE: Nintendo ignores the root_path here. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
sf_path.str[0] = '\x00';
|
||||
AMS_UNUSED(root_path);
|
||||
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<BisCommonMountNameGenerator>(id);
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInBisA());
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenBisFileSystem(std::addressof(fs), sf_path, static_cast<u32>(id)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInBisB());
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<BisCommonMountNameGenerator>(id);
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInBisA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa), std::move(generator));
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInBisB());
|
||||
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_BIS(name, id, root_path)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetBisRootForHostImpl(BisPartitionId id, const char *root_path) {
|
||||
/* Ensure the path isn't too long. */
|
||||
size_t len = strnlen(root_path, fs::EntryNameLengthMax + 1);
|
||||
R_UNLESS(len <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
|
||||
|
||||
fssrv::sf::Path sf_path;
|
||||
if (len > 0) {
|
||||
const bool ending_sep = PathNormalizer::IsSeparator(root_path[len - 1]);
|
||||
FspPathPrintf(std::addressof(sf_path), "%s%s", root_path, ending_sep ? "" : "/");
|
||||
} else {
|
||||
sf_path.str[0] = '\x00';
|
||||
}
|
||||
|
||||
/* TODO: Libnx binding for fsSetBisRootForHost */
|
||||
AMS_UNUSED(id);
|
||||
AMS_ABORT();
|
||||
AMS_UNUSED(id, root_path);
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,21 +115,23 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result OpenBisPartition(std::unique_ptr<fs::IStorage> *out, BisPartitionId id) {
|
||||
/* Open the partition. This uses libnx bindings. */
|
||||
FsStorage s;
|
||||
R_TRY(fsOpenBisStorage(std::addressof(s), static_cast<::FsBisPartitionId>(id)));
|
||||
/* Open the partition. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IStorage> s;
|
||||
AMS_FS_R_TRY(fsp->OpenBisStorage(std::addressof(s), static_cast<u32>(id)));
|
||||
|
||||
/* Allocate a new storage wrapper. */
|
||||
auto storage = std::make_unique<RemoteStorage>(s);
|
||||
R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInBisC());
|
||||
auto storage = std::make_unique<impl::StorageServiceObjectAdapter>(std::move(s));
|
||||
AMS_FS_R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInBisC());
|
||||
|
||||
*out = std::move(storage);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InvalidateBisCache() {
|
||||
/* TODO: Libnx binding for this command. */
|
||||
AMS_ABORT();
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_ABORT_UNLESS(fsp->InvalidateBisCache());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -56,7 +58,7 @@ namespace ams::fs {
|
||||
g_mounted_stratosphere_romfs = true;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
fsa::IFileSystem &GetStratosphereRomFsFileSystem() {
|
||||
@@ -70,32 +72,35 @@ namespace ams::fs {
|
||||
Result OpenCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr<fsa::IFileSystem> *out, const char *path, ncm::ProgramId program_id) {
|
||||
/* Print a path suitable for the remote service. */
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(FspPathPrintf(std::addressof(sf_path), "%s", path));
|
||||
R_TRY(FormatToFspPath(std::addressof(sf_path), "%s", path));
|
||||
|
||||
/* Open the filesystem using libnx bindings. */
|
||||
static_assert(sizeof(CodeVerificationData) == sizeof(::FsCodeInfo));
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsldrOpenCodeFileSystem(reinterpret_cast<::FsCodeInfo *>(out_verification_data), program_id.value, sf_path.str, std::addressof(fs)));
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyForLoaderServiceObject();
|
||||
R_TRY(fsp->SetCurrentProcess({}));
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenCodeFileSystem(std::addressof(fs), out_verification_data, sf_path, program_id));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA());
|
||||
|
||||
*out = std::move(fsa);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenPackageFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, const char *common_path) {
|
||||
/* Open a filesystem using libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), ncm::InvalidProgramId.value, static_cast<::FsFileSystemType>(impl::FileSystemProxyType_Package), common_path));
|
||||
Result OpenPackageFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, const fssrv::sf::FspPath &path) {
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenFileSystemWithId(std::addressof(fs), path, ncm::InvalidProgramId.value, impl::FileSystemProxyType_Package));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA());
|
||||
|
||||
*out = std::move(fsa);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenSdCardCodeFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, ncm::ProgramId program_id) {
|
||||
@@ -104,9 +109,9 @@ namespace ams::fs {
|
||||
|
||||
/* Print a path to the program's package. */
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(FspPathPrintf(std::addressof(sf_path), "%s:/atmosphere/contents/%016lx/exefs.nsp", impl::SdCardFileSystemMountName, program_id.value));
|
||||
R_TRY(FormatToFspPath(std::addressof(sf_path), "%s:/atmosphere/contents/%016" PRIx64 "/exefs.nsp", impl::SdCardFileSystemMountName, program_id.value));
|
||||
|
||||
return OpenPackageFileSystemImpl(out, sf_path.str);
|
||||
R_RETURN(OpenPackageFileSystemImpl(out, sf_path));
|
||||
}
|
||||
|
||||
Result OpenStratosphereCodeFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, ncm::ProgramId program_id) {
|
||||
@@ -120,11 +125,12 @@ namespace ams::fs {
|
||||
auto &romfs_fs = GetStratosphereRomFsFileSystem();
|
||||
|
||||
/* Print a path to the program's package. */
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(FspPathPrintf(std::addressof(sf_path), "/atmosphere/contents/%016lX/exefs.nsp", program_id.value));
|
||||
fs::Path path;
|
||||
R_TRY(path.InitializeWithFormat("/atmosphere/contents/%016" PRIx64 "/exefs.nsp", program_id.value));
|
||||
R_TRY(path.Normalize(fs::PathFlags{}));
|
||||
|
||||
/* Open the package within stratosphere.romfs. */
|
||||
R_TRY(romfs_fs.OpenFile(std::addressof(package_file), sf_path.str, fs::OpenMode_Read));
|
||||
R_TRY(romfs_fs.OpenFile(std::addressof(package_file), path, fs::OpenMode_Read));
|
||||
}
|
||||
|
||||
/* Create a file storage for the program's package. */
|
||||
@@ -139,7 +145,7 @@ namespace ams::fs {
|
||||
R_TRY(package_fs->Initialize(package_storage));
|
||||
|
||||
*out = std::move(package_fs);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr<fsa::IFileSystem> *out, const char *path, ncm::ProgramId program_id) {
|
||||
@@ -150,7 +156,7 @@ namespace ams::fs {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(OpenStratosphereCodeFileSystemImpl(out, program_id)));
|
||||
|
||||
/* Otherwise, fall back to a normal code fs. */
|
||||
return OpenCodeFileSystemImpl(out_verification_data, out, path, program_id);
|
||||
R_RETURN(OpenCodeFileSystemImpl(out_verification_data, out, path, program_id));
|
||||
}
|
||||
|
||||
Result OpenHblCodeFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out) {
|
||||
@@ -159,93 +165,94 @@ namespace ams::fs {
|
||||
|
||||
/* Print a path to the hbl package. */
|
||||
fssrv::sf::Path sf_path;
|
||||
R_TRY(FspPathPrintf(std::addressof(sf_path), "%s:/%s", impl::SdCardFileSystemMountName, hbl_path[0] == '/' ? hbl_path + 1 : hbl_path));
|
||||
R_TRY(FormatToFspPath(std::addressof(sf_path), "%s:/%s", impl::SdCardFileSystemMountName, hbl_path[0] == '/' ? hbl_path + 1 : hbl_path));
|
||||
|
||||
return OpenPackageFileSystemImpl(out, sf_path.str);
|
||||
R_RETURN(OpenPackageFileSystemImpl(out, sf_path));
|
||||
}
|
||||
|
||||
Result OpenSdCardFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out) {
|
||||
/* Open the SD card. This uses libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
|
||||
Result OpenSdCardFileSystemImpl(std::shared_ptr<fsa::IFileSystem> *out) {
|
||||
/* Open the SD card filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenSdCardFileSystem(std::addressof(fs)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
auto fsa = std::make_shared<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA());
|
||||
|
||||
*out = std::move(fsa);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
class OpenFileOnlyFileSystem : public fsa::IFileSystem, public impl::Newable {
|
||||
private:
|
||||
virtual Result DoCommit() override final {
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result DoOpenDirectory(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
||||
virtual Result DoOpenDirectory(std::unique_ptr<fsa::IDirectory> *out_dir, const fs::Path &path, OpenDirectoryMode mode) override final {
|
||||
AMS_UNUSED(out_dir, path, mode);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoGetEntryType(DirectoryEntryType *out, const char *path) override final {
|
||||
virtual Result DoGetEntryType(DirectoryEntryType *out, const fs::Path &path) override final {
|
||||
AMS_UNUSED(out, path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoCreateFile(const char *path, s64 size, int flags) override final {
|
||||
virtual Result DoCreateFile(const fs::Path &path, s64 size, int flags) override final {
|
||||
AMS_UNUSED(path, size, flags);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoDeleteFile(const char *path) override final {
|
||||
virtual Result DoDeleteFile(const fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoCreateDirectory(const char *path) override final {
|
||||
virtual Result DoCreateDirectory(const fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectory(const char *path) override final {
|
||||
virtual Result DoDeleteDirectory(const fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectoryRecursively(const char *path) override final {
|
||||
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoRenameFile(const char *old_path, const char *new_path) override final {
|
||||
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override final {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override final {
|
||||
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override final {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoCleanDirectoryRecursively(const char *path) override final {
|
||||
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override final {
|
||||
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override final {
|
||||
AMS_UNUSED(out, path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override final {
|
||||
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override final {
|
||||
AMS_UNUSED(out, path);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
|
||||
virtual Result DoCommitProvisionally(s64 counter) override final {
|
||||
AMS_UNUSED(counter);
|
||||
return fs::ResultUnsupportedOperation();
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -261,32 +268,40 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
/* Open an SD card filesystem. */
|
||||
std::unique_ptr<fsa::IFileSystem> sd_fs;
|
||||
std::shared_ptr<fsa::IFileSystem> sd_fs;
|
||||
if (R_FAILED(OpenSdCardFileSystemImpl(std::addressof(sd_fs)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create a redirection filesystem to the relevant content folder. */
|
||||
char path[fs::EntryNameLengthMax + 1];
|
||||
util::SNPrintf(path, sizeof(path), "/atmosphere/contents/%016lx/exefs", program_id.value);
|
||||
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_fs), path);
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_fs));
|
||||
if (subdir_fs == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
fs::Path path;
|
||||
R_ABORT_UNLESS(path.InitializeWithFormat("/atmosphere/contents/%016" PRIx64 "/exefs", program_id.value));
|
||||
R_ABORT_UNLESS(path.Normalize(fs::PathFlags{}));
|
||||
|
||||
R_ABORT_UNLESS(subdir_fs->Initialize(path));
|
||||
|
||||
m_sd_content_fs.emplace(std::move(subdir_fs));
|
||||
}
|
||||
private:
|
||||
bool IsFileStubbed(const char *path) {
|
||||
bool IsFileStubbed(const fs::Path &path) {
|
||||
/* If we don't have an sd content fs, nothing is stubbed. */
|
||||
if (!m_sd_content_fs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create a path representing the stub. */
|
||||
char stub_path[fs::EntryNameLengthMax + 1];
|
||||
util::SNPrintf(stub_path, sizeof(stub_path), "%s.stub", path);
|
||||
fs::Path stub_path;
|
||||
if (R_FAILED(stub_path.InitializeWithFormat("%s.stub", path.GetString()))) {
|
||||
return false;
|
||||
}
|
||||
if (R_FAILED(stub_path.Normalize(fs::PathFlags{}))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Query whether we have the file. */
|
||||
bool has_file;
|
||||
@@ -297,7 +312,7 @@ namespace ams::fs {
|
||||
return has_file;
|
||||
}
|
||||
|
||||
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const fs::Path &path, OpenMode mode) override final {
|
||||
/* Only allow opening files with mode = read. */
|
||||
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
|
||||
|
||||
@@ -310,7 +325,7 @@ namespace ams::fs {
|
||||
R_UNLESS(!this->IsFileStubbed(path), fs::ResultPathNotFound());
|
||||
|
||||
/* Open a file from the base code fs. */
|
||||
return m_code_fs.OpenFile(out_file, path, mode);
|
||||
R_RETURN(m_code_fs.OpenFile(out_file, path, mode));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -341,10 +356,10 @@ namespace ams::fs {
|
||||
m_program_id = program_id;
|
||||
m_initialized = true;
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const fs::Path &path, OpenMode mode) override final {
|
||||
/* Ensure that we're initialized. */
|
||||
R_UNLESS(m_initialized, fs::ResultNotInitialized());
|
||||
|
||||
@@ -355,81 +370,111 @@ namespace ams::fs {
|
||||
{
|
||||
fsa::IFileSystem *ecs = fssystem::GetExternalCodeFileSystem(m_program_id);
|
||||
if (ecs != nullptr) {
|
||||
return ecs->OpenFile(out_file, path, mode);
|
||||
R_RETURN(ecs->OpenFile(out_file, path, mode));
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're hbl, open from the hbl fs. */
|
||||
if (m_hbl_fs) {
|
||||
return m_hbl_fs->OpenFile(out_file, path, mode);
|
||||
R_RETURN(m_hbl_fs->OpenFile(out_file, path, mode));
|
||||
}
|
||||
|
||||
/* If we're not hbl, fall back to our code filesystem. */
|
||||
return m_code_fs->OpenFile(out_file, path, mode);
|
||||
R_RETURN(m_code_fs->OpenFile(out_file, path, mode));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Result MountCode(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id) {
|
||||
/* Clear the output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Clear the output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Validate the path isn't null. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
/* Validate the path isn't null. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
|
||||
/* Open the code file system. */
|
||||
std::unique_ptr<fsa::IFileSystem> fsa;
|
||||
R_TRY(OpenCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
|
||||
/* Open the code file system. */
|
||||
std::unique_ptr<fsa::IFileSystem> fsa;
|
||||
R_TRY(OpenCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CODE(name, path, program_id)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountCodeForAtmosphereWithRedirection(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) {
|
||||
/* Clear the output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Clear the output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Validate the path isn't null. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
/* Validate the path isn't null. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
|
||||
/* Create an AtmosphereCodeFileSystem. */
|
||||
auto ams_code_fs = std::make_unique<AtmosphereCodeFileSystem>();
|
||||
R_UNLESS(ams_code_fs != nullptr, fs::ResultAllocationFailureInCodeA());
|
||||
/* Create an AtmosphereCodeFileSystem. */
|
||||
auto ams_code_fs = std::make_unique<AtmosphereCodeFileSystem>();
|
||||
R_UNLESS(ams_code_fs != nullptr, fs::ResultAllocationFailureInCodeA());
|
||||
|
||||
/* Initialize the code file system. */
|
||||
R_TRY(ams_code_fs->Initialize(out, path, program_id, is_hbl, is_specific));
|
||||
/* Initialize the code file system. */
|
||||
R_TRY(ams_code_fs->Initialize(out, path, program_id, is_hbl, is_specific));
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(ams_code_fs));
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(ams_code_fs)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CODE(name, path, program_id)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountCodeForAtmosphere(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id) {
|
||||
/* Clear the output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Clear the output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Validate the path isn't null. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
/* Validate the path isn't null. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
|
||||
/* Open the code file system. */
|
||||
std::unique_ptr<fsa::IFileSystem> fsa;
|
||||
R_TRY(OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
|
||||
/* Open the code file system. */
|
||||
std::unique_ptr<fsa::IFileSystem> fsa;
|
||||
R_TRY(OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
|
||||
|
||||
/* Create a wrapper fs. */
|
||||
auto wrap_fsa = std::make_unique<SdCardRedirectionCodeFileSystem>(std::move(fsa), program_id, false);
|
||||
R_UNLESS(wrap_fsa != nullptr, fs::ResultAllocationFailureInCodeA());
|
||||
/* Create a wrapper fs. */
|
||||
auto wrap_fsa = std::make_unique<SdCardRedirectionCodeFileSystem>(std::move(fsa), program_id, false);
|
||||
R_UNLESS(wrap_fsa != nullptr, fs::ResultAllocationFailureInCodeA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(wrap_fsa));
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(wrap_fsa)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CODE(name, path, program_id)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -30,45 +32,75 @@ namespace ams::fs {
|
||||
}
|
||||
}
|
||||
|
||||
Result MountContentImpl(const char *name, const char *path, u64 id, impl::FileSystemProxyType type) {
|
||||
/* Open a filesystem using libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), id, static_cast<::FsFileSystemType>(type), path));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, u64 id, ContentType content_type) {
|
||||
Result MountContentImpl(const char *name, const char *path, u64 id, ContentType type) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
|
||||
/* Validate the path. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
|
||||
/* Mount the content. */
|
||||
return MountContentImpl(name, path, id, ConvertToFileSystemProxyType(content_type));
|
||||
/* Convert the path for fsp. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
|
||||
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenFileSystemWithId(std::addressof(fs), sf_path, id, ConvertToFileSystemProxyType(type)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentA());
|
||||
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, ContentType content_type) {
|
||||
/* This API only supports mounting Meta content. */
|
||||
AMS_ABORT_UNLESS(content_type == ContentType_Meta);
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* This API only supports mounting Meta content. */
|
||||
R_UNLESS(content_type == ContentType_Meta, fs::ResultInvalidArgument());
|
||||
|
||||
return MountContent(name, path, ncm::InvalidProgramId, content_type);
|
||||
R_RETURN(MountContentImpl(name, path, ncm::InvalidProgramId.value, content_type));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_PATH(name, path, content_type)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type) {
|
||||
return MountContent(name, path, id.value, content_type);
|
||||
auto mount_impl = [=]() -> Result {
|
||||
R_RETURN(MountContentImpl(name, path, id.value, content_type));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_PATH_AND_PROGRAM_ID(name, path, id, content_type)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type) {
|
||||
return MountContent(name, path, id.value, content_type);
|
||||
auto mount_impl = [=]() -> Result {
|
||||
R_RETURN(MountContentImpl(name, path, id.value, content_type));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_PATH_AND_DATA_ID(name, path, id, content_type)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -28,7 +30,7 @@ namespace ams::fs {
|
||||
|
||||
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
|
||||
/* Determine how much space we need. */
|
||||
const size_t needed_size = strnlen(GetContentStorageMountName(id), MountNameLengthMax) + 2;
|
||||
const size_t needed_size = util::Strnlen(GetContentStorageMountName(id), MountNameLengthMax) + 2;
|
||||
AMS_ABORT_UNLESS(dst_size >= needed_size);
|
||||
|
||||
/* Generate the name. */
|
||||
@@ -56,43 +58,55 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result MountContentStorage(const char *name, ContentStorageId id) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
|
||||
/* It can take some time for the system partition to be ready (if it's on the SD card). */
|
||||
/* Thus, we will retry up to 10 times, waiting one second each time. */
|
||||
constexpr size_t MaxRetries = 10;
|
||||
constexpr auto RetryInterval = TimeSpan::FromSeconds(1);
|
||||
/* It can take some time for the system partition to be ready (if it's on the SD card). */
|
||||
/* Thus, we will retry up to 10 times, waiting one second each time. */
|
||||
constexpr size_t MaxRetries = 10;
|
||||
constexpr auto RetryInterval = TimeSpan::FromSeconds(1);
|
||||
|
||||
/* Mount the content storage, use libnx bindings. */
|
||||
::FsFileSystem fs;
|
||||
for (size_t i = 0; i < MaxRetries; i++) {
|
||||
/* Try to open the filesystem. */
|
||||
R_TRY_CATCH(fsOpenContentStorageFileSystem(std::addressof(fs), static_cast<::FsContentStorageId>(id))) {
|
||||
R_CATCH(fs::ResultSystemPartitionNotReady) {
|
||||
if (i < MaxRetries - 1) {
|
||||
os::SleepThread(RetryInterval);
|
||||
continue;
|
||||
} else {
|
||||
return fs::ResultSystemPartitionNotReady();
|
||||
/* Mount the content storage */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
for (size_t i = 0; i < MaxRetries; i++) {
|
||||
/* Try to open the filesystem. */
|
||||
R_TRY_CATCH(fsp->OpenContentStorageFileSystem(std::addressof(fs), static_cast<u32>(id))) {
|
||||
R_CATCH(fs::ResultSystemPartitionNotReady) {
|
||||
if (i < MaxRetries - 1) {
|
||||
os::SleepThread(RetryInterval);
|
||||
continue;
|
||||
} else {
|
||||
R_THROW(fs::ResultSystemPartitionNotReady());
|
||||
}
|
||||
}
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* The filesystem was opened successfully. */
|
||||
break;
|
||||
}
|
||||
/* The filesystem was opened successfully. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA());
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA());
|
||||
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<ContentStorageCommonMountNameGenerator>(id);
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInContentStorageB());
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<ContentStorageCommonMountNameGenerator>(id);
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInContentStorageB());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa), std::move(generator));
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_STORAGE(name, id)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,19 +15,27 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_storage_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
Result OpenDataStorageByDataId(std::unique_ptr<ams::fs::IStorage> *out, ncm::DataId data_id, ncm::StorageId storage_id) {
|
||||
/* Open storage using libnx bindings. */
|
||||
::FsStorage s;
|
||||
R_TRY_CATCH(fsOpenDataStorageByDataId(std::addressof(s), data_id.value, static_cast<::NcmStorageId>(storage_id))) {
|
||||
Result OpenDataStorageByDataIdImpl(sf::SharedPointer<fssrv::sf::IStorage> *out, ncm::DataId data_id, ncm::StorageId storage_id) {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
R_TRY_CATCH(fsp->OpenDataStorageByDataId(out, data_id, static_cast<u8>(storage_id))) {
|
||||
R_CONVERT(ncm::ResultContentMetaNotFound, fs::ResultTargetNotFound());
|
||||
} R_END_TRY_CATCH;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
auto storage = std::make_unique<RemoteStorage>(s);
|
||||
Result OpenDataStorageByDataId(std::unique_ptr<ams::fs::IStorage> *out, ncm::DataId data_id, ncm::StorageId storage_id) {
|
||||
/* Open storage. */
|
||||
sf::SharedPointer<fssrv::sf::IStorage> s;
|
||||
AMS_FS_R_TRY(OpenDataStorageByDataIdImpl(std::addressof(s), data_id, storage_id));
|
||||
|
||||
auto storage = std::make_unique<impl::StorageServiceObjectAdapter>(std::move(s));
|
||||
R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInDataA());
|
||||
|
||||
*out = std::move(storage);
|
||||
@@ -48,13 +56,13 @@ namespace ams::fs::impl {
|
||||
}
|
||||
|
||||
Result QueryMountDataCacheSize(size_t *out, ncm::DataId data_id, ncm::StorageId storage_id) {
|
||||
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
std::unique_ptr<fs::IStorage> storage;
|
||||
R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id));
|
||||
AMS_FS_R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id));
|
||||
|
||||
size_t size = 0;
|
||||
R_TRY(RomFsFileSystem::GetRequiredWorkingMemorySize(std::addressof(size), storage.get()));
|
||||
AMS_FS_R_TRY(RomFsFileSystem::GetRequiredWorkingMemorySize(std::addressof(size), storage.get()));
|
||||
|
||||
constexpr size_t MinimumCacheSize = 32;
|
||||
*out = std::max(size, MinimumCacheSize);
|
||||
@@ -63,25 +71,25 @@ namespace ams::fs::impl {
|
||||
|
||||
Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
AMS_FS_R_TRY(impl::CheckMountName(name));
|
||||
|
||||
return MountDataImpl(name, data_id, storage_id, nullptr, 0, false, false, false);
|
||||
}
|
||||
|
||||
Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
AMS_FS_R_TRY(impl::CheckMountName(name));
|
||||
|
||||
R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, false, false);
|
||||
}
|
||||
|
||||
Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_data_cache, bool use_path_cache) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
AMS_FS_R_TRY(impl::CheckMountName(name));
|
||||
|
||||
R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, use_data_cache, use_path_cache);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -26,12 +28,13 @@ namespace ams::fs {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Open the filesystem, use libnx bindings. */
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenSaveDataFileSystem(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(DeviceSaveDataSpaceId), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenSaveDataFileSystem(std::addressof(fs), static_cast<u8>(DeviceSaveDataSpaceId), attribute));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInDeviceSaveDataA());
|
||||
|
||||
/* Register. */
|
||||
@@ -41,11 +44,27 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result MountDeviceSaveData(const char *name) {
|
||||
return MountDeviceSaveDataImpl(name, SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId));
|
||||
const auto attr = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId);
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(MountDeviceSaveDataImpl(name, attr), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, name));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountDeviceSaveData(const char *name, const ncm::ApplicationId application_id) {
|
||||
return MountDeviceSaveDataImpl(name, SaveDataAttribute::Make(application_id, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId));
|
||||
const auto attr = SaveDataAttribute::Make(application_id, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId);
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(MountDeviceSaveDataImpl(name, attr), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_DEVICE_SAVE_DATA_APPLICATION_ID(name, application_id)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,24 +19,35 @@
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result EnsureDirectoryRecursively(const char *path) {
|
||||
Result EnsureDirectory(const char *path) {
|
||||
/* Get the filesystem accessor and sub path. */
|
||||
impl::FileSystemAccessor *accessor;
|
||||
const char *sub_path;
|
||||
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
|
||||
/* Set up the true sub path. */
|
||||
fs::Path sub_fs_path;
|
||||
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
|
||||
|
||||
/* Use the system implementation. */
|
||||
return fssystem::EnsureDirectoryRecursively(accessor->GetRawFileSystemUnsafe(), sub_path);
|
||||
return fssystem::EnsureDirectory(accessor->GetRawFileSystemUnsafe(), sub_fs_path);
|
||||
}
|
||||
|
||||
Result EnsureParentDirectoryRecursively(const char *path) {
|
||||
Result EnsureParentDirectory(const char *path) {
|
||||
/* Get the filesystem accessor and sub path. */
|
||||
impl::FileSystemAccessor *accessor;
|
||||
const char *sub_path;
|
||||
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
|
||||
/* Set up the true sub path. */
|
||||
fs::Path sub_fs_path;
|
||||
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
|
||||
|
||||
/* Convert the path to the parent directory path. */
|
||||
R_TRY(sub_fs_path.RemoveChild());
|
||||
|
||||
/* Use the system implementation. */
|
||||
return fssystem::EnsureParentDirectoryRecursively(accessor->GetRawFileSystemUnsafe(), sub_path);
|
||||
return fssystem::EnsureDirectory(accessor->GetRawFileSystemUnsafe(), sub_fs_path);
|
||||
}
|
||||
|
||||
Result HasFile(bool *out, const char *path) {
|
||||
@@ -45,7 +56,11 @@ namespace ams::fs {
|
||||
const char *sub_path;
|
||||
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
|
||||
return fssystem::HasFile(out, accessor->GetRawFileSystemUnsafe(), sub_path);
|
||||
/* Set up the true sub path. */
|
||||
fs::Path sub_fs_path;
|
||||
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
|
||||
|
||||
return fssystem::HasFile(out, accessor->GetRawFileSystemUnsafe(), sub_fs_path);
|
||||
}
|
||||
|
||||
Result HasDirectory(bool *out, const char *path) {
|
||||
@@ -54,7 +69,11 @@ namespace ams::fs {
|
||||
const char *sub_path;
|
||||
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
|
||||
return fssystem::HasDirectory(out, accessor->GetRawFileSystemUnsafe(), sub_path);
|
||||
/* Set up the true sub path. */
|
||||
fs::Path sub_fs_path;
|
||||
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
|
||||
|
||||
return fssystem::HasDirectory(out, accessor->GetRawFileSystemUnsafe(), sub_fs_path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -38,7 +40,7 @@ namespace ams::fs {
|
||||
|
||||
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
|
||||
/* Determine how much space we need. */
|
||||
const size_t needed_size = strnlen(impl::GameCardFileSystemMountName, MountNameLengthMax) + strnlen(GetGameCardMountNameSuffix(m_partition), MountNameLengthMax) + sizeof(GameCardHandle) * 2 + 2;
|
||||
const size_t needed_size = util::Strnlen(impl::GameCardFileSystemMountName, MountNameLengthMax) + util::Strnlen(GetGameCardMountNameSuffix(m_partition), MountNameLengthMax) + sizeof(GameCardHandle) * 2 + 2;
|
||||
AMS_ABORT_UNLESS(dst_size >= needed_size);
|
||||
|
||||
/* Generate the name. */
|
||||
@@ -53,36 +55,49 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result GetGameCardHandle(GameCardHandle *out) {
|
||||
/* TODO: fs::DeviceOperator */
|
||||
/* Open a DeviceOperator. */
|
||||
::FsDeviceOperator d;
|
||||
R_TRY(fsOpenDeviceOperator(std::addressof(d)));
|
||||
ON_SCOPE_EXIT { fsDeviceOperatorClose(std::addressof(d)); };
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get the handle. */
|
||||
static_assert(sizeof(GameCardHandle) == sizeof(::FsGameCardHandle));
|
||||
return fsDeviceOperatorGetGameCardHandle(std::addressof(d), reinterpret_cast<::FsGameCardHandle *>(out));
|
||||
u32 handle;
|
||||
AMS_FS_R_TRY(device_operator->GetGameCardHandle(std::addressof(handle)));
|
||||
|
||||
*out = handle;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
|
||||
/* Open gamecard filesystem. This uses libnx bindings. */
|
||||
::FsFileSystem fs;
|
||||
const ::FsGameCardHandle _hnd = {handle};
|
||||
R_TRY(fsOpenGameCardFileSystem(std::addressof(fs), std::addressof(_hnd), static_cast<::FsGameCardPartition>(partition)));
|
||||
/* Open the gamecard filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenGameCardFileSystem(std::addressof(fs), static_cast<u32>(handle), static_cast<u32>(partition)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInGameCardC());
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInGameCardC());
|
||||
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<GameCardCommonMountNameGenerator>(handle, partition);
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInGameCardD());
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<GameCardCommonMountNameGenerator>(handle, partition);
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInGameCardD());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa), std::move(generator));
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_GAME_CARD_PARTITION(name, handle, partition)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
241
libraries/libstratosphere/source/fs/fs_host.cpp
Normal file
241
libraries/libstratosphere/source/fs/fs_host.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
class HostRootCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
|
||||
public:
|
||||
explicit HostRootCommonMountNameGenerator() { /* ... */ }
|
||||
|
||||
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
|
||||
/* Determine how much space we need. */
|
||||
constexpr size_t RequiredNameBufferSizeSize = AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME_LEN + 1 + 1;
|
||||
AMS_ASSERT(dst_size >= RequiredNameBufferSizeSize);
|
||||
AMS_UNUSED(RequiredNameBufferSizeSize);
|
||||
|
||||
/* Generate the name. */
|
||||
const auto size = util::SNPrintf(dst, dst_size, AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME ":");
|
||||
AMS_ASSERT(static_cast<size_t>(size) == RequiredNameBufferSizeSize - 1);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class HostCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
|
||||
private:
|
||||
char m_path[fs::EntryNameLengthMax + 1];
|
||||
public:
|
||||
HostCommonMountNameGenerator(const char *path) {
|
||||
util::Strlcpy<char>(m_path, path, sizeof(m_path));
|
||||
}
|
||||
|
||||
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
|
||||
/* Determine how much space we need. */
|
||||
const size_t required_size = AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME_LEN + 1 + util::Strnlen<char>(m_path, sizeof(m_path)) + 1; /* @Host:%s */
|
||||
R_UNLESS(dst_size >= required_size, fs::ResultTooLongPath());
|
||||
|
||||
|
||||
/* Generate the name. */
|
||||
const auto size = util::SNPrintf(dst, dst_size, AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME ":%s", m_path);
|
||||
AMS_ASSERT(static_cast<size_t>(size) == required_size - 1);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
Result OpenHostFileSystemImpl(std::unique_ptr<fs::fsa::IFileSystem> *out, const fssrv::sf::FspPath &path, const MountHostOption &option) {
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
if (option != MountHostOption::None) {
|
||||
R_TRY(fsp->OpenHostFileSystemWithOption(std::addressof(fs), path, option._value));
|
||||
} else {
|
||||
R_TRY(fsp->OpenHostFileSystem(std::addressof(fs), path));
|
||||
}
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInHostA());
|
||||
|
||||
/* Set the output. */
|
||||
*out = std::move(fsa);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result PreMountHost(std::unique_ptr<fs::HostCommonMountNameGenerator> *out, const char *name, const char *path) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
/* Check the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Check that path is valid. */
|
||||
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Create a new HostCommonMountNameGenerator. */
|
||||
*out = std::make_unique<HostCommonMountNameGenerator>(path);
|
||||
R_UNLESS(out->get() != nullptr, fs::ResultAllocationFailureInHostB());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
Result OpenHostFileSystem(std::unique_ptr<fs::fsa::IFileSystem> *out, const char *name, const char *path, const fs::MountHostOption &option) {
|
||||
/* Validate arguments. */
|
||||
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(name != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Check mount name isn't windows path or reserved. */
|
||||
R_UNLESS(!fs::IsWindowsDrive(name), fs::ResultInvalidMountName());
|
||||
R_UNLESS(!fs::impl::IsReservedMountName(name), fs::ResultInvalidMountName());
|
||||
|
||||
/* Convert the path for fsp. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
|
||||
|
||||
/* Ensure that the path doesn't correspond to the root. */
|
||||
if (sf_path.str[0] == 0) {
|
||||
sf_path.str[0] = '.';
|
||||
sf_path.str[1] = 0;
|
||||
}
|
||||
|
||||
/* Open the host file system. */
|
||||
R_RETURN(OpenHostFileSystemImpl(out, sf_path, option));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result MountHost(const char *name, const char *root_path) {
|
||||
/* Pre-mount host. */
|
||||
std::unique_ptr<fs::HostCommonMountNameGenerator> generator;
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(PreMountHost(std::addressof(generator), name, root_path), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST(name, root_path)));
|
||||
|
||||
/* Open the filesystem. */
|
||||
std::unique_ptr<fs::fsa::IFileSystem> fsa;
|
||||
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(impl::OpenHostFileSystem(std::addressof(fsa), name, root_path, MountHostOption::None), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST(name, root_path)));
|
||||
|
||||
/* Declare registration helper. */
|
||||
auto register_impl = [&]() -> Result {
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
|
||||
};
|
||||
|
||||
/* Mount the filesystem. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST(name, root_path)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountHost(const char *name, const char *root_path, const MountHostOption &option) {
|
||||
/* Pre-mount host. */
|
||||
std::unique_ptr<fs::HostCommonMountNameGenerator> generator;
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(PreMountHost(std::addressof(generator), name, root_path), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_WITH_OPTION(name, root_path, option)));
|
||||
|
||||
/* Open the filesystem. */
|
||||
std::unique_ptr<fs::fsa::IFileSystem> fsa;
|
||||
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(impl::OpenHostFileSystem(std::addressof(fsa), name, root_path, option), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_WITH_OPTION(name, root_path, option)));
|
||||
|
||||
/* Declare registration helper. */
|
||||
auto register_impl = [&]() -> Result {
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
|
||||
};
|
||||
|
||||
/* Mount the filesystem. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_WITH_OPTION(name, root_path, option)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountHostRoot() {
|
||||
/* Create host root path. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
sf_path.str[0] = 0;
|
||||
|
||||
/* Open the filesystem. */
|
||||
std::unique_ptr<fs::fsa::IFileSystem> fsa;
|
||||
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(OpenHostFileSystemImpl(std::addressof(fsa), sf_path, MountHostOption::None), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT()));
|
||||
|
||||
/* Declare registration helper. */
|
||||
auto register_impl = [&]() -> Result {
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<HostRootCommonMountNameGenerator>();
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInHostC());
|
||||
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(impl::HostRootFileSystemMountName, std::move(fsa), std::move(generator)));
|
||||
};
|
||||
|
||||
/* Mount the filesystem. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT()));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(impl::HostRootFileSystemMountName);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountHostRoot(const MountHostOption &option) {
|
||||
/* Create host root path. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
sf_path.str[0] = 0;
|
||||
|
||||
/* Open the filesystem. */
|
||||
std::unique_ptr<fs::fsa::IFileSystem> fsa;
|
||||
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(OpenHostFileSystemImpl(std::addressof(fsa), sf_path, option), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT_WITH_OPTION(option)));
|
||||
|
||||
/* Declare registration helper. */
|
||||
auto register_impl = [&]() -> Result {
|
||||
/* Allocate a new mountname generator. */
|
||||
auto generator = std::make_unique<HostRootCommonMountNameGenerator>();
|
||||
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInHostC());
|
||||
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(impl::HostRootFileSystemMountName, std::move(fsa), std::move(generator)));
|
||||
};
|
||||
|
||||
/* Mount the filesystem. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT_WITH_OPTION(option)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(impl::HostRootFileSystemMountName);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void UnmountHostRoot() {
|
||||
return Unmount(impl::HostRootFileSystemMountName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,23 +15,36 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result MountImageDirectory(const char *name, ImageDirectoryId id) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Open the image directory. This uses libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenImageDirectoryFileSystem(std::addressof(fs), static_cast<::FsImageDirectoryId>(id)));
|
||||
/* Open the image directory. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenImageDirectoryFileSystem(std::addressof(fs), static_cast<u32>(id)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInImageDirectoryA());
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInImageDirectoryA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
/* Register. */
|
||||
R_RETURN(fsa::Register(name, std::move(fsa)));
|
||||
};
|
||||
|
||||
/* Perform the mount. */
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_IMAGE_DIRECTORY(name, id)));
|
||||
|
||||
/* Enable access logging. */
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,570 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
Result CheckSharedName(const char *path, int len) {
|
||||
if (len == 1) {
|
||||
R_UNLESS(path[0] != StringTraits::Dot, fs::ResultInvalidPathFormat());
|
||||
} else if (len == 2) {
|
||||
R_UNLESS(path[0] != StringTraits::Dot || path[1] != StringTraits::Dot, fs::ResultInvalidPathFormat());
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ParseWindowsPath(const char **out_path, char *out, size_t *out_windows_path_len, bool *out_normalized, const char *path, size_t max_out_size, bool has_mount_name) {
|
||||
/* Prepare to parse. */
|
||||
const char * const path_start = path;
|
||||
if (out_normalized != nullptr) {
|
||||
*out_normalized = true;
|
||||
}
|
||||
|
||||
/* Handle start of path. */
|
||||
bool skipped_mount = false;
|
||||
auto prefix_len = 0;
|
||||
if (has_mount_name) {
|
||||
if (PathNormalizer::IsSeparator(path[0]) && path[1] == StringTraits::AlternateDirectorySeparator && path[2] == StringTraits::AlternateDirectorySeparator) {
|
||||
path += 1;
|
||||
prefix_len = 1;
|
||||
} else {
|
||||
/* Advance past separators. */
|
||||
while (PathNormalizer::IsSeparator(path[0])) {
|
||||
++path;
|
||||
}
|
||||
if (path != path_start) {
|
||||
if (path - path_start == 1 || IsWindowsDrive(path)) {
|
||||
prefix_len = 1;
|
||||
} else {
|
||||
if (path - path_start > 2 && out_normalized != nullptr) {
|
||||
*out_normalized = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
path -= 2;
|
||||
skipped_mount = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (PathNormalizer::IsSeparator(path[0]) && !IsUnc(path)) {
|
||||
path += 1;
|
||||
prefix_len = 1;
|
||||
}
|
||||
|
||||
|
||||
/* Parse the path. */
|
||||
const char *trimmed_path = path_start;
|
||||
if (IsWindowsDrive(path)) {
|
||||
/* Find the first separator. */
|
||||
int i;
|
||||
for (i = 2; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
|
||||
if (PathNormalizer::IsAnySeparator(path[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
trimmed_path = path + i;
|
||||
|
||||
const size_t win_path_len = trimmed_path - path_start;
|
||||
if (out != nullptr) {
|
||||
R_UNLESS(win_path_len <= max_out_size, fs::ResultTooLongPath());
|
||||
std::memcpy(out, path_start, win_path_len);
|
||||
}
|
||||
*out_path = trimmed_path;
|
||||
*out_windows_path_len = win_path_len;
|
||||
} else if (IsUnc(path)) {
|
||||
if (PathNormalizer::IsAnySeparator(path[2])) {
|
||||
AMS_ASSERT(!has_mount_name);
|
||||
return fs::ResultInvalidPathFormat();
|
||||
}
|
||||
|
||||
int cur_part_ofs = 0;
|
||||
bool needs_sep_fix = false;
|
||||
for (auto i = 2; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
|
||||
if (cur_part_ofs == 0 && path[i] == StringTraits::AlternateDirectorySeparator) {
|
||||
needs_sep_fix = true;
|
||||
if (out_normalized != nullptr) {
|
||||
*out_normalized = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
if (PathNormalizer::IsAnySeparator(path[i])) {
|
||||
if (path[i] == StringTraits::AlternateDirectorySeparator) {
|
||||
needs_sep_fix = true;
|
||||
}
|
||||
|
||||
if (cur_part_ofs != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
R_UNLESS(!PathNormalizer::IsSeparator(path[i + 1]), fs::ResultInvalidPathFormat());
|
||||
|
||||
R_TRY(CheckSharedName(path + 2, i - 2));
|
||||
|
||||
cur_part_ofs = i + 1;
|
||||
}
|
||||
if (path[i] == '$' || path[i] == StringTraits::DriveSeparator) {
|
||||
R_UNLESS(cur_part_ofs != 0, fs::ResultInvalidCharacter());
|
||||
R_UNLESS(PathNormalizer::IsAnySeparator(path[i + 1]) || PathNormalizer::IsNullTerminator(path[i + 1]), fs::ResultInvalidPathFormat());
|
||||
trimmed_path = path + i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (trimmed_path == path_start) {
|
||||
int tr_part_ofs = 0;
|
||||
int i;
|
||||
for (i = 2; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
|
||||
if (PathNormalizer::IsAnySeparator(path[i])) {
|
||||
if (tr_part_ofs != 0) {
|
||||
R_TRY(CheckSharedName(path + tr_part_ofs, i - tr_part_ofs));
|
||||
trimmed_path = path + i;
|
||||
break;
|
||||
}
|
||||
|
||||
R_UNLESS(!PathNormalizer::IsSeparator(path[i + 1]), fs::ResultInvalidPathFormat());
|
||||
|
||||
R_TRY(CheckSharedName(path + 2, i - 2));
|
||||
|
||||
cur_part_ofs = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (tr_part_ofs != 0 && trimmed_path == path_start) {
|
||||
R_TRY(CheckSharedName(path + tr_part_ofs, i - tr_part_ofs));
|
||||
trimmed_path = path + i;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t win_path_len = trimmed_path - path;
|
||||
const bool prepend_sep = prefix_len != 0 || skipped_mount;
|
||||
if (out != nullptr) {
|
||||
R_UNLESS(win_path_len <= max_out_size, fs::ResultTooLongPath());
|
||||
if (prepend_sep) {
|
||||
*(out++) = StringTraits::DirectorySeparator;
|
||||
}
|
||||
std::memcpy(out, path, win_path_len);
|
||||
out[0] = StringTraits::AlternateDirectorySeparator;
|
||||
out[1] = StringTraits::AlternateDirectorySeparator;
|
||||
if (needs_sep_fix) {
|
||||
for (size_t i = 2; i < win_path_len; ++i) {
|
||||
if (PathNormalizer::IsSeparator(out[i])) {
|
||||
out[i] = StringTraits::AlternateDirectorySeparator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*out_path = trimmed_path;
|
||||
*out_windows_path_len = win_path_len + (prepend_sep ? 1 : 0);
|
||||
} else {
|
||||
*out_path = trimmed_path;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result SkipWindowsPath(const char **out_path, bool *out_normalized, const char *path, bool has_mount_name) {
|
||||
size_t windows_path_len;
|
||||
return ParseWindowsPath(out_path, nullptr, std::addressof(windows_path_len), out_normalized, path, 0, has_mount_name);
|
||||
}
|
||||
|
||||
Result ParseMountName(const char **out_path, char *out, size_t *out_mount_name_len, const char *path, size_t max_out_size) {
|
||||
/* Decide on a start. */
|
||||
const char *start = PathNormalizer::IsSeparator(path[0]) ? path + 1 : path;
|
||||
|
||||
/* Find the end of the mount name. */
|
||||
const char *cur;
|
||||
for (cur = start; cur < start + MountNameLengthMax + 1; ++cur) {
|
||||
if (*cur == StringTraits::DriveSeparator) {
|
||||
++cur;
|
||||
break;
|
||||
}
|
||||
if (PathNormalizer::IsSeparator(*cur)) {
|
||||
*out_path = path;
|
||||
*out_mount_name_len = 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
R_UNLESS(start < cur - 1, fs::ResultInvalidPathFormat());
|
||||
R_UNLESS(cur[-1] == StringTraits::DriveSeparator, fs::ResultInvalidPathFormat());
|
||||
|
||||
/* Check the mount name doesn't contain a dot. */
|
||||
if (cur != start) {
|
||||
for (const char *p = start; p < cur; ++p) {
|
||||
R_UNLESS(*p != StringTraits::Dot, fs::ResultInvalidCharacter());
|
||||
}
|
||||
}
|
||||
|
||||
const size_t mount_name_len = cur - path;
|
||||
if (out != nullptr) {
|
||||
R_UNLESS(mount_name_len <= max_out_size, fs::ResultTooLongPath());
|
||||
std::memcpy(out, path, mount_name_len);
|
||||
}
|
||||
*out_path = cur;
|
||||
*out_mount_name_len = mount_name_len;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result SkipMountName(const char **out_path, const char *path) {
|
||||
size_t mount_name_len;
|
||||
return ParseMountName(out_path, nullptr, std::addressof(mount_name_len), path, 0);
|
||||
}
|
||||
|
||||
bool IsParentDirectoryPathReplacementNeeded(const char *path) {
|
||||
if (!PathNormalizer::IsAnySeparator(path[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto i = 0; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
|
||||
if (path[i + 0] == StringTraits::AlternateDirectorySeparator &&
|
||||
path[i + 1] == StringTraits::Dot &&
|
||||
path[i + 2] == StringTraits::Dot &&
|
||||
(PathNormalizer::IsAnySeparator(path[i + 3]) || PathNormalizer::IsNullTerminator(path[i + 3])))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PathNormalizer::IsAnySeparator(path[i + 0]) &&
|
||||
path[i + 1] == StringTraits::Dot &&
|
||||
path[i + 2] == StringTraits::Dot &&
|
||||
path[i + 3] == StringTraits::AlternateDirectorySeparator)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReplaceParentDirectoryPath(char *dst, const char *src) {
|
||||
dst[0] = StringTraits::DirectorySeparator;
|
||||
|
||||
int i = 1;
|
||||
while (!PathNormalizer::IsNullTerminator(src[i])) {
|
||||
if (PathNormalizer::IsAnySeparator(src[i - 1]) &&
|
||||
src[i + 0] == StringTraits::Dot &&
|
||||
src[i + 1] == StringTraits::Dot &&
|
||||
PathNormalizer::IsAnySeparator(src[i + 2]))
|
||||
{
|
||||
dst[i - 1] = StringTraits::DirectorySeparator;
|
||||
dst[i + 0] = StringTraits::Dot;
|
||||
dst[i + 1] = StringTraits::Dot;
|
||||
dst[i - 2] = StringTraits::DirectorySeparator;
|
||||
i += 3;
|
||||
} else {
|
||||
if (src[i - 1] == StringTraits::AlternateDirectorySeparator &&
|
||||
src[i + 0] == StringTraits::Dot &&
|
||||
src[i + 1] == StringTraits::Dot &&
|
||||
PathNormalizer::IsNullTerminator(src[i + 2]))
|
||||
{
|
||||
dst[i - 1] = StringTraits::DirectorySeparator;
|
||||
dst[i + 0] = StringTraits::Dot;
|
||||
dst[i + 1] = StringTraits::Dot;
|
||||
i += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
dst[i] = src[i];
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
dst[i] = StringTraits::NullTerminator;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result PathNormalizer::Normalize(char *out, size_t *out_len, const char *path, size_t max_out_size, bool unc_preserved, bool has_mount_name) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(out_len != nullptr);
|
||||
AMS_ASSERT(path != nullptr);
|
||||
|
||||
/* If we should, handle the mount name. */
|
||||
size_t prefix_len = 0;
|
||||
if (has_mount_name) {
|
||||
size_t mount_name_len = 0;
|
||||
R_TRY(ParseMountName(std::addressof(path), out, std::addressof(mount_name_len), path, max_out_size));
|
||||
prefix_len += mount_name_len;
|
||||
}
|
||||
|
||||
/* Deal with unc. */
|
||||
bool is_unc_path = false;
|
||||
if (unc_preserved) {
|
||||
const char * const path_start = path;
|
||||
size_t windows_path_len = 0;
|
||||
R_TRY(ParseWindowsPath(std::addressof(path), out + prefix_len, std::addressof(windows_path_len), nullptr, path, max_out_size, has_mount_name));
|
||||
prefix_len += windows_path_len;
|
||||
is_unc_path = path != path_start;
|
||||
}
|
||||
|
||||
/* Paths must start with / */
|
||||
R_UNLESS(prefix_len != 0 || IsSeparator(path[0]), fs::ResultInvalidPathFormat());
|
||||
|
||||
/* Check if parent directory path replacement is needed. */
|
||||
std::unique_ptr<char[], fs::impl::Deleter> replacement_path;
|
||||
if (IsParentDirectoryPathReplacementNeeded(path)) {
|
||||
/* Allocate a buffer to hold the replacement path. */
|
||||
replacement_path = fs::impl::MakeUnique<char[]>(EntryNameLengthMax + 1);
|
||||
R_UNLESS(replacement_path != nullptr, fs::ResultAllocationFailureInNew());
|
||||
|
||||
/* Replace the path. */
|
||||
ReplaceParentDirectoryPath(replacement_path.get(), path);
|
||||
|
||||
/* Set path to be the replacement path. */
|
||||
path = replacement_path.get();
|
||||
}
|
||||
|
||||
bool skip_next_sep = false;
|
||||
size_t i = 0;
|
||||
size_t len = prefix_len;
|
||||
|
||||
while (!IsNullTerminator(path[i])) {
|
||||
if (IsSeparator(path[i])) {
|
||||
/* Swallow separators. */
|
||||
while (IsSeparator(path[++i])) { }
|
||||
if (IsNullTerminator(path[i])) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle skip if needed */
|
||||
if (!skip_next_sep) {
|
||||
if (len + 1 == max_out_size) {
|
||||
out[len] = StringTraits::NullTerminator;
|
||||
*out_len = len;
|
||||
return fs::ResultTooLongPath();
|
||||
}
|
||||
|
||||
out[len++] = StringTraits::DirectorySeparator;
|
||||
}
|
||||
skip_next_sep = false;
|
||||
}
|
||||
|
||||
/* See length of current dir. */
|
||||
size_t dir_len = 0;
|
||||
while (!IsSeparator(path[i + dir_len]) && !IsNullTerminator(path[i + dir_len])) {
|
||||
++dir_len;
|
||||
}
|
||||
|
||||
if (IsCurrentDirectory(path + i)) {
|
||||
skip_next_sep = true;
|
||||
} else if (IsParentDirectory(path + i)) {
|
||||
AMS_ASSERT(IsSeparator(out[len - 1]));
|
||||
if (!is_unc_path) {
|
||||
AMS_ASSERT(IsSeparator(out[prefix_len]));
|
||||
}
|
||||
|
||||
/* Walk up a directory. */
|
||||
if (len == prefix_len + 1) {
|
||||
R_UNLESS(is_unc_path, fs::ResultDirectoryUnobtainable());
|
||||
--len;
|
||||
} else {
|
||||
len -= 2;
|
||||
do {
|
||||
if (IsSeparator(out[len])) {
|
||||
break;
|
||||
}
|
||||
--len;
|
||||
} while (len != prefix_len);
|
||||
}
|
||||
|
||||
if (!is_unc_path) {
|
||||
AMS_ASSERT(IsSeparator(out[prefix_len]));
|
||||
}
|
||||
|
||||
AMS_ASSERT(len < max_out_size);
|
||||
} else {
|
||||
/* Copy, possibly truncating. */
|
||||
if (len + dir_len + 1 <= max_out_size) {
|
||||
for (size_t j = 0; j < dir_len; ++j) {
|
||||
out[len++] = path[i+j];
|
||||
}
|
||||
} else {
|
||||
const size_t copy_len = max_out_size - 1 - len;
|
||||
for (size_t j = 0; j < copy_len; ++j) {
|
||||
out[len++] = path[i+j];
|
||||
}
|
||||
out[len] = StringTraits::NullTerminator;
|
||||
*out_len = len;
|
||||
return fs::ResultTooLongPath();
|
||||
}
|
||||
}
|
||||
|
||||
i += dir_len;
|
||||
}
|
||||
|
||||
if (skip_next_sep) {
|
||||
--len;
|
||||
}
|
||||
|
||||
if (!is_unc_path && len == prefix_len && max_out_size > len) {
|
||||
out[len++] = StringTraits::DirectorySeparator;
|
||||
}
|
||||
|
||||
R_UNLESS(max_out_size >= len - 1, fs::ResultTooLongPath());
|
||||
|
||||
/* Null terminate. */
|
||||
out[len] = StringTraits::NullTerminator;
|
||||
*out_len = len;
|
||||
|
||||
/* Assert normalized. */
|
||||
{
|
||||
bool normalized = false;
|
||||
const auto is_norm_result = IsNormalized(std::addressof(normalized), out, unc_preserved, has_mount_name);
|
||||
R_ASSERT(is_norm_result);
|
||||
AMS_ASSERT(normalized);
|
||||
AMS_UNUSED(is_norm_result, normalized);
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result PathNormalizer::IsNormalized(bool *out, const char *path, bool unc_preserved, bool has_mount_name) {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
AMS_ASSERT(path != nullptr);
|
||||
|
||||
/* Save the start of the path. */
|
||||
const char *path_start = path;
|
||||
|
||||
/* If we should, skip the mount name. */
|
||||
if (has_mount_name) {
|
||||
R_TRY(SkipMountName(std::addressof(path), path));
|
||||
R_UNLESS(IsSeparator(*path), fs::ResultInvalidPathFormat());
|
||||
}
|
||||
|
||||
/* If we should, handle unc. */
|
||||
bool is_unc_path = false;
|
||||
if (unc_preserved) {
|
||||
path_start = path;
|
||||
|
||||
/* Skip the windows path. */
|
||||
bool normalized_windows = false;
|
||||
R_TRY(SkipWindowsPath(std::addressof(path), std::addressof(normalized_windows), path, has_mount_name));
|
||||
|
||||
/* If we're not windows-normalized, we're not normalized. */
|
||||
if (!normalized_windows) {
|
||||
*out = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Handle the case where we're dealing with a unc path. */
|
||||
if (path != path_start) {
|
||||
is_unc_path = true;
|
||||
if (IsSeparator(path_start[0]) && IsSeparator(path_start[1])) {
|
||||
*out = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
if (IsNullTerminator(path[0])) {
|
||||
*out = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if parent directory path replacement is needed. */
|
||||
if (IsParentDirectoryPathReplacementNeeded(path)) {
|
||||
*out = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Nintendo uses a state machine here. */
|
||||
enum class PathState {
|
||||
Start,
|
||||
Normal,
|
||||
FirstSeparator,
|
||||
Separator,
|
||||
CurrentDir,
|
||||
ParentDir,
|
||||
};
|
||||
|
||||
PathState state = PathState::Start;
|
||||
for (const char *cur = path; *cur != StringTraits::NullTerminator; ++cur) {
|
||||
const char c = *cur;
|
||||
switch (state) {
|
||||
case PathState::Start:
|
||||
if (IsSeparator(c)) {
|
||||
state = PathState::FirstSeparator;
|
||||
} else {
|
||||
R_UNLESS(path != path_start, fs::ResultInvalidPathFormat());
|
||||
if (c == StringTraits::Dot) {
|
||||
state = PathState::CurrentDir;
|
||||
} else {
|
||||
state = PathState::Normal;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PathState::Normal:
|
||||
if (IsSeparator(c)) {
|
||||
state = PathState::Separator;
|
||||
}
|
||||
break;
|
||||
case PathState::FirstSeparator:
|
||||
case PathState::Separator:
|
||||
if (IsSeparator(c)) {
|
||||
*out = false;
|
||||
return ResultSuccess();
|
||||
} else if (c == StringTraits::Dot) {
|
||||
state = PathState::CurrentDir;
|
||||
} else {
|
||||
state = PathState::Normal;
|
||||
}
|
||||
break;
|
||||
case PathState::CurrentDir:
|
||||
if (IsSeparator(c)) {
|
||||
*out = false;
|
||||
return ResultSuccess();
|
||||
} else if (c == StringTraits::Dot) {
|
||||
state = PathState::ParentDir;
|
||||
} else {
|
||||
state = PathState::Normal;
|
||||
}
|
||||
break;
|
||||
case PathState::ParentDir:
|
||||
if (IsSeparator(c)) {
|
||||
*out = false;
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
state = PathState::Normal;
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case PathState::Start:
|
||||
return fs::ResultInvalidPathFormat();
|
||||
case PathState::Normal:
|
||||
*out = true;
|
||||
break;
|
||||
case PathState::FirstSeparator:
|
||||
*out = !is_unc_path;
|
||||
break;
|
||||
case PathState::CurrentDir:
|
||||
case PathState::ParentDir:
|
||||
case PathState::Separator:
|
||||
*out = false;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
class PathVerifier {
|
||||
private:
|
||||
u32 m_invalid_chars[6];
|
||||
u32 m_separators[2];
|
||||
public:
|
||||
PathVerifier() {
|
||||
/* Convert all invalid characters. */
|
||||
u32 *dst_invalid = m_invalid_chars;
|
||||
for (const char *cur = ":*?<>|"; *cur != '\x00'; ++cur) {
|
||||
AMS_ASSERT(dst_invalid < std::end(m_invalid_chars));
|
||||
|
||||
const auto result = util::ConvertCharacterUtf8ToUtf32(dst_invalid, cur);
|
||||
AMS_ASSERT(result == util::CharacterEncodingResult_Success);
|
||||
AMS_UNUSED(result);
|
||||
|
||||
++dst_invalid;
|
||||
}
|
||||
AMS_ASSERT(dst_invalid == std::end(m_invalid_chars));
|
||||
|
||||
/* Convert all separators. */
|
||||
u32 *dst_sep = m_separators;
|
||||
for (const char *cur = "/\\"; *cur != '\x00'; ++cur) {
|
||||
AMS_ASSERT(dst_sep < std::end(m_separators));
|
||||
|
||||
const auto result = util::ConvertCharacterUtf8ToUtf32(dst_sep, cur);
|
||||
AMS_ASSERT(result == util::CharacterEncodingResult_Success);
|
||||
AMS_UNUSED(result);
|
||||
|
||||
++dst_sep;
|
||||
}
|
||||
AMS_ASSERT(dst_sep == std::end(m_separators));
|
||||
}
|
||||
|
||||
Result Verify(const char *path, int max_path_len, int max_name_len) const {
|
||||
AMS_ASSERT(path != nullptr);
|
||||
|
||||
auto cur = path;
|
||||
auto name_len = 0;
|
||||
|
||||
for (auto path_len = 0; path_len <= max_path_len && name_len <= max_name_len; ++path_len) {
|
||||
/* We're done, if the path is terminated. */
|
||||
R_SUCCEED_IF(*cur == '\x00');
|
||||
|
||||
/* Get the current utf-8 character. */
|
||||
util::CharacterEncodingResult result;
|
||||
char char_buf[4] = {};
|
||||
result = util::PickOutCharacterFromUtf8String(char_buf, std::addressof(cur));
|
||||
R_UNLESS(result == util::CharacterEncodingResult_Success, fs::ResultInvalidCharacter());
|
||||
|
||||
/* Convert the current utf-8 character to utf-32. */
|
||||
u32 path_char = 0;
|
||||
result = util::ConvertCharacterUtf8ToUtf32(std::addressof(path_char), char_buf);
|
||||
R_UNLESS(result == util::CharacterEncodingResult_Success, fs::ResultInvalidCharacter());
|
||||
|
||||
/* Check if the character is invalid. */
|
||||
for (const auto invalid : m_invalid_chars) {
|
||||
R_UNLESS(path_char != invalid, fs::ResultInvalidCharacter());
|
||||
}
|
||||
|
||||
/* Increment name length. */
|
||||
++name_len;
|
||||
|
||||
/* Check for separator. */
|
||||
for (const auto sep : m_separators) {
|
||||
if (path_char == sep) {
|
||||
name_len = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The path was too long. */
|
||||
return fs::ResultTooLongPath();
|
||||
}
|
||||
};
|
||||
|
||||
PathVerifier g_path_verifier;
|
||||
|
||||
}
|
||||
|
||||
Result VerifyPath(const char *path, int max_path_len, int max_name_len) {
|
||||
return g_path_verifier.Verify(path, max_path_len, max_name_len);
|
||||
}
|
||||
|
||||
bool IsSubPath(const char *lhs, const char *rhs) {
|
||||
AMS_ASSERT(lhs != nullptr);
|
||||
AMS_ASSERT(rhs != nullptr);
|
||||
|
||||
/* Special case certain paths. */
|
||||
if (IsUnc(lhs) && !IsUnc(rhs)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsUnc(lhs) && IsUnc(rhs)) {
|
||||
return false;
|
||||
}
|
||||
if (PathNormalizer::IsSeparator(lhs[0]) && PathNormalizer::IsNullTerminator(lhs[1]) && PathNormalizer::IsSeparator(rhs[0]) && !PathNormalizer::IsNullTerminator(rhs[1])) {
|
||||
return true;
|
||||
}
|
||||
if (PathNormalizer::IsSeparator(rhs[0]) && PathNormalizer::IsNullTerminator(rhs[1]) && PathNormalizer::IsSeparator(lhs[0]) && !PathNormalizer::IsNullTerminator(lhs[1])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check subpath. */
|
||||
for (size_t i = 0; /* No terminating condition */; i++) {
|
||||
if (PathNormalizer::IsNullTerminator(lhs[i])) {
|
||||
return PathNormalizer::IsSeparator(rhs[i]);
|
||||
} else if (PathNormalizer::IsNullTerminator(rhs[i])) {
|
||||
return PathNormalizer::IsSeparator(lhs[i]);
|
||||
} else if (lhs[i] != rhs[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -118,7 +118,7 @@ namespace ams::fs {
|
||||
|
||||
Priority GetPriority(os::ThreadType *thread) {
|
||||
fs::Priority priority;
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityImpl(std::addressof(priority), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityImpl(std::addressof(priority), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
|
||||
return priority;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace ams::fs {
|
||||
|
||||
PriorityRaw GetPriorityRaw(os::ThreadType *thread) {
|
||||
fs::PriorityRaw priority_raw;
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityRawImpl(std::addressof(priority_raw), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityRawImpl(std::addressof(priority_raw), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
|
||||
return priority_raw;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
void SetPriority(os::ThreadType *thread, Priority priority) {
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityImpl(os::GetCurrentThread(), priority), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityImpl(os::GetCurrentThread(), priority), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
|
||||
}
|
||||
|
||||
void SetPriorityRawOnCurrentThread(PriorityRaw priority_raw) {
|
||||
@@ -154,7 +154,7 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
void SetPriorityRaw(os::ThreadType *thread, PriorityRaw priority_raw) {
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityRawImpl(os::GetCurrentThread(), priority_raw), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
|
||||
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityRawImpl(os::GetCurrentThread(), priority_raw), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fs_remote_file_system_proxy.hpp"
|
||||
#include "fs_remote_file_system_proxy_for_loader.hpp"
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
extern "C" {
|
||||
|
||||
extern u32 __nx_fs_num_sessions;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
RemoteFileSystemProxy::RemoteFileSystemProxy(int session_count) {
|
||||
/* Ensure we can connect to sm. */
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
ON_SCOPE_EXIT { R_ABORT_UNLESS(sm::Finalize()); };
|
||||
|
||||
/* Initialize libnx. */
|
||||
__nx_fs_num_sessions = static_cast<u32>(session_count);
|
||||
R_ABORT_UNLESS(::fsInitialize());
|
||||
}
|
||||
|
||||
RemoteFileSystemProxy::~RemoteFileSystemProxy() {
|
||||
::fsExit();
|
||||
}
|
||||
|
||||
RemoteFileSystemProxyForLoader::RemoteFileSystemProxyForLoader() {
|
||||
/* Ensure we can connect to sm. */
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
ON_SCOPE_EXIT { R_ABORT_UNLESS(sm::Finalize()); };
|
||||
|
||||
R_ABORT_UNLESS(::fsldrInitialize());
|
||||
}
|
||||
|
||||
RemoteFileSystemProxyForLoader::~RemoteFileSystemProxyForLoader() {
|
||||
::fsldrExit();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
#include "../fssrv/impl/fssrv_allocator_for_service_framework.hpp"
|
||||
#include "impl/fs_remote_event_notifier.hpp"
|
||||
#include "impl/fs_remote_device_operator.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
class RemoteFileSystemProxy {
|
||||
NON_COPYABLE(RemoteFileSystemProxy);
|
||||
NON_MOVEABLE(RemoteFileSystemProxy);
|
||||
private:
|
||||
using ObjectFactory = fssrv::impl::FileSystemObjectFactory;
|
||||
public:
|
||||
RemoteFileSystemProxy(int session_count);
|
||||
~RemoteFileSystemProxy();
|
||||
public:
|
||||
/* Command interface */
|
||||
Result OpenFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
|
||||
/* Libnx does this for us automatically. */
|
||||
AMS_UNUSED(client_pid);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenDataFileSystemByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenFileSystemWithPatch(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result OpenFileSystemWithId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type) {
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), program_id, static_cast<::FsFileSystemType>(type), path.str));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenDataFileSystemByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result OpenBisFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result OpenBisStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 id) {
|
||||
FsStorage s;
|
||||
R_TRY(fsOpenBisStorage(std::addressof(s), static_cast<::FsBisPartitionId>(id)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IStorage, fssrv::impl::RemoteStorage>(s));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InvalidateBisCache() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenHostFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result OpenSdCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FormatSdCardFileSystem() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result DeleteSaveDataFileSystem(u64 save_data_id) {
|
||||
AMS_UNUSED(save_data_id);
|
||||
AMS_ABORT("TODO: libnx binding");
|
||||
}
|
||||
|
||||
Result CreateSaveDataFileSystem(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result CreateSaveDataFileSystemBySystemSaveDataId(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info) {
|
||||
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
|
||||
static_assert(sizeof(creation_info) == sizeof(::FsSaveDataCreationInfo));
|
||||
R_RETURN(fsCreateSaveDataFileSystemBySystemSaveDataId(reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)), reinterpret_cast<const ::FsSaveDataCreationInfo *>(std::addressof(creation_info))));
|
||||
}
|
||||
|
||||
Result RegisterSaveDataFileSystemAtomicDeletion(const ams::sf::InBuffer &save_data_ids) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result DeleteSaveDataFileSystemBySaveDataSpaceId(u8 indexer_space_id, u64 save_data_id) {
|
||||
R_RETURN(fsDeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<::FsSaveDataSpaceId>(indexer_space_id), save_data_id));
|
||||
}
|
||||
|
||||
Result FormatSdCardDryRun() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result IsExFatSupported(ams::sf::Out<bool> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result DeleteSaveDataFileSystemBySaveDataAttribute(u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
|
||||
R_RETURN(fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
|
||||
}
|
||||
|
||||
Result OpenGameCardStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 handle, u32 partition) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result OpenGameCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 handle, u32 partition) {
|
||||
::FsFileSystem fs;
|
||||
const ::FsGameCardHandle _hnd = {handle};
|
||||
R_TRY(fsOpenGameCardFileSystem(std::addressof(fs), std::addressof(_hnd), static_cast<::FsGameCardPartition>(partition)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ExtendSaveDataFileSystem(u8 space_id, u64 save_data_id, s64 available_size, s64 journal_size) {
|
||||
R_RETURN(::fsExtendSaveDataFileSystem(static_cast<::FsSaveDataSpaceId>(space_id), save_data_id, available_size, journal_size));
|
||||
}
|
||||
|
||||
Result DeleteCacheStorage(u16 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result GetCacheStorageSize(ams::sf::Out<s64> out_size, ams::sf::Out<s64> out_journal_size, u16 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result CreateSaveDataFileSystemWithHashSalt(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info, const fs::HashSalt &salt) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenHostFileSystemWithOption(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 option) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result OpenSaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenSaveDataFileSystem(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenSaveDataFileSystemBySystemSaveDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenReadOnlySaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(const ams::sf::OutBuffer &buffer, u8 space_id, u64 save_data_id) {
|
||||
R_RETURN(fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(buffer.GetPointer(), buffer.GetSize(), static_cast<::FsSaveDataSpaceId>(space_id), save_data_id));
|
||||
}
|
||||
|
||||
Result ReadSaveDataFileSystemExtraData(const ams::sf::OutBuffer &buffer, u64 save_data_id) {
|
||||
R_RETURN(fsReadSaveDataFileSystemExtraData(buffer.GetPointer(), buffer.GetSize(), save_data_id));
|
||||
}
|
||||
|
||||
Result WriteSaveDataFileSystemExtraData(u64 save_data_id, u8 space_id, const ams::sf::InBuffer &buffer) {
|
||||
R_RETURN(fsWriteSaveDataFileSystemExtraData(buffer.GetPointer(), buffer.GetSize(), static_cast<::FsSaveDataSpaceId>(space_id), save_data_id));
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result OpenImageDirectoryFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenImageDirectoryFileSystem(std::addressof(fs), static_cast<::FsImageDirectoryId>(id)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result OpenContentStorageFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenContentStorageFileSystem(std::addressof(fs), static_cast<::FsContentStorageId>(id)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result OpenDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenDataStorageByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::ProgramId program_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenDataStorageByDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id) {
|
||||
::FsStorage s;
|
||||
R_TRY(fsOpenDataStorageByDataId(std::addressof(s), data_id.value, static_cast<::NcmStorageId>(storage_id)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IStorage, fssrv::impl::RemoteStorage>(s));
|
||||
R_SUCCEED();
|
||||
}
|
||||
Result OpenPatchDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenDataFileSystemWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenDataStorageWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u8 index) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenDataStorageByPath(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenDeviceOperator(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDeviceOperator>> out) {
|
||||
::FsDeviceOperator d;
|
||||
R_TRY(fsOpenDeviceOperator(std::addressof(d)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IDeviceOperator, impl::RemoteDeviceOperator>(d));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenSdCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
|
||||
::FsEventNotifier e;
|
||||
R_TRY(fsOpenSdCardDetectionEventNotifier(std::addressof(e)));
|
||||
|
||||
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IEventNotifier, impl::RemoteEventNotifier>(e));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenGameCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenSystemDataUpdateEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result NotifySystemDataUpdateEvent() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result SetCurrentPosixTime(s64 posix_time) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result GetRightsId(ams::sf::Out<fs::RightsId> out, ncm::ProgramId program_id, ncm::StorageId storage_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result RegisterExternalKey(const fs::RightsId &rights_id, const spl::AccessKey &access_key) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result UnregisterAllExternalKey() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
|
||||
Result GetRightsIdByPath(ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path) {
|
||||
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
|
||||
R_RETURN(fsGetRightsIdByPath(path.str, reinterpret_cast<::FsRightsId *>(out.GetPointer())));
|
||||
}
|
||||
|
||||
Result GetRightsIdAndKeyGenerationByPath(ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path) {
|
||||
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
|
||||
R_RETURN(fsGetRightsIdAndKeyGenerationByPath(path.str, out_key_generation.GetPointer(), reinterpret_cast<::FsRightsId *>(out.GetPointer())));
|
||||
}
|
||||
|
||||
Result SetCurrentPosixTimeWithTimeDifference(s64 posix_time, s32 time_difference) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result GetFreeSpaceSizeForSaveData(ams::sf::Out<s64> out, u8 space_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result VerifySaveDataFileSystemBySaveDataSpaceId() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result CorruptSaveDataFileSystemBySaveDataSpaceId() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result QuerySaveDataInternalStorageTotalSize() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result GetSaveDataCommitId() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result UnregisterExternalKey(const fs::RightsId &rights_id) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result SetSdCardEncryptionSeed(const fs::EncryptionSeed &seed) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result SetSdCardAccessibility(bool accessible) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result IsSdCardAccessible(ams::sf::Out<bool> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result IsSignedSystemPartitionOnSdCardValid(ams::sf::Out<bool> out) {
|
||||
R_RETURN(fsIsSignedSystemPartitionOnSdCardValid(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result OpenAccessFailureDetectionEventNotifier() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result SetBisRootForHost(u32 id, const fssrv::sf::FspPath &path) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result SetSaveDataSize(s64 size, s64 journal_size) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result SetSaveDataRootPath(const fssrv::sf::FspPath &path) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result DisableAutoSaveDataCreation() {
|
||||
R_RETURN(::fsDisableAutoSaveDataCreation());
|
||||
}
|
||||
|
||||
Result SetGlobalAccessLogMode(u32 mode) {
|
||||
R_RETURN(::fsSetGlobalAccessLogMode(mode));
|
||||
}
|
||||
|
||||
Result GetGlobalAccessLogMode(sf::Out<u32> out) {
|
||||
R_RETURN(::fsGetGlobalAccessLogMode(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result OutputAccessLogToSdCard(const ams::sf::InBuffer &buf) {
|
||||
R_RETURN(::fsOutputAccessLogToSdCard(reinterpret_cast<const char *>(buf.GetPointer()), buf.GetSize()));
|
||||
}
|
||||
|
||||
Result RegisterUpdatePartition() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OpenRegisteredUpdatePartition(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
Result GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {
|
||||
R_RETURN(::fsGetProgramIndexForAccessLog(out_idx.GetPointer(), out_count.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetFsStackUsage(ams::sf::Out<u32> out, u32 type) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result UnsetSaveDataRootPath() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OutputMultiProgramTagAccessLog() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result FlushAccessLogOnSdCard() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OutputApplicationInfoAccessLog() {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result RegisterDebugConfiguration(u32 key, s64 value) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result UnregisterDebugConfiguration(u32 key) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result OverrideSaveDataTransferTokenSignVerificationKey(const ams::sf::InBuffer &buf) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
Result CorruptSaveDataFileSystemByOffset(u8 space_id, u64 save_data_id, s64 offset) {
|
||||
AMS_ABORT("TODO");
|
||||
}
|
||||
|
||||
/* ... */
|
||||
};
|
||||
static_assert(fssrv::sf::IsIFileSystemProxy<RemoteFileSystemProxy>);
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
#include "../fssrv/impl/fssrv_allocator_for_service_framework.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
class RemoteFileSystemProxyForLoader {
|
||||
NON_COPYABLE(RemoteFileSystemProxyForLoader);
|
||||
NON_MOVEABLE(RemoteFileSystemProxyForLoader);
|
||||
private:
|
||||
using ObjectFactory = fssrv::impl::FileSystemObjectFactory;
|
||||
public:
|
||||
RemoteFileSystemProxyForLoader();
|
||||
~RemoteFileSystemProxyForLoader();
|
||||
public:
|
||||
Result OpenCodeFileSystemDeprecated(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
|
||||
::FsCodeInfo dummy;
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsldrOpenCodeFileSystem(std::addressof(dummy), program_id.value, path.str, std::addressof(fs)));
|
||||
|
||||
out_fs.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenCodeFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsldrOpenCodeFileSystem(reinterpret_cast<::FsCodeInfo *>(out_verif.GetPointer()), program_id.value, path.str, std::addressof(fs)));
|
||||
|
||||
out_fs.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IsArchivedProgram(ams::sf::Out<bool> out, u64 process_id) {
|
||||
R_RETURN(fsldrIsArchivedProgram(process_id, out.GetPointer()));
|
||||
}
|
||||
|
||||
Result SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
|
||||
/* Libnx does this for us automatically. */
|
||||
AMS_UNUSED(client_pid);
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
static_assert(fssrv::sf::IsIFileSystemProxyForLoader<RemoteFileSystemProxyForLoader>);
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -15,17 +15,37 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fs/fs_rights_id.hpp>
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result GetRightsId(RightsId *out, const char *path) {
|
||||
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
|
||||
return fsGetRightsIdByPath(path, reinterpret_cast<::FsRightsId *>(out));
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Convert the path for fsp. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->GetRightsIdByPath(out, sf_path));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path) {
|
||||
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
|
||||
return fsGetRightsIdAndKeyGenerationByPath(path, out_key_generation, reinterpret_cast<::FsRightsId *>(out));
|
||||
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(out_key_generation != nullptr, fs::ResultNullptrArgument());
|
||||
AMS_FS_R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Convert the path for fsp. */
|
||||
fssrv::sf::FspPath sf_path;
|
||||
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->GetRightsIdAndKeyGenerationByPath(out, out_key_generation, sf_path));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -266,11 +266,11 @@ namespace ams::fs {
|
||||
RomFsDirectory(RomFsFileSystem *p, const FindPosition &f, fs::OpenDirectoryMode m) : m_parent(p), m_current_find(f), m_first_find(f), m_mode(m) { /* ... */ }
|
||||
virtual ~RomFsDirectory() override { /* ... */ }
|
||||
public:
|
||||
virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) {
|
||||
virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override {
|
||||
return this->ReadInternal(out_count, std::addressof(m_current_find), out_entries, max_entries);
|
||||
}
|
||||
|
||||
virtual Result DoGetEntryCount(s64 *out) {
|
||||
virtual Result DoGetEntryCount(s64 *out) override {
|
||||
FindPosition find = m_first_find;
|
||||
return this->ReadInternal(out, std::addressof(find), nullptr, 0);
|
||||
}
|
||||
@@ -293,9 +293,10 @@ namespace ams::fs {
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
if (out_entries) {
|
||||
R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
|
||||
strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax);
|
||||
out_entries[i].name[fs::EntryNameLengthMax] = '\x00';
|
||||
const size_t name_len = util::Strnlen(name_buf, NameBufferSize);
|
||||
R_UNLESS(name_len < NameBufferSize, fs::ResultTooLongPath());
|
||||
std::memcpy(out_entries[i].name, name_buf, name_len);
|
||||
out_entries[i].name[name_len] = '\x00';
|
||||
out_entries[i].type = fs::DirectoryEntryType_Directory;
|
||||
out_entries[i].file_size = 0;
|
||||
}
|
||||
@@ -313,9 +314,10 @@ namespace ams::fs {
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
if (out_entries) {
|
||||
R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
|
||||
strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax);
|
||||
out_entries[i].name[fs::EntryNameLengthMax] = '\x00';
|
||||
const size_t name_len = util::Strnlen(name_buf, NameBufferSize);
|
||||
R_UNLESS(name_len < NameBufferSize, fs::ResultTooLongPath());
|
||||
std::memcpy(out_entries[i].name, name_buf, name_len);
|
||||
out_entries[i].name[name_len] = '\x00';
|
||||
out_entries[i].type = fs::DirectoryEntryType_File;
|
||||
|
||||
RomFsFileSystem::RomFileTable::FileInfo file_info;
|
||||
@@ -443,48 +445,48 @@ namespace ams::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoCreateFile(const char *path, s64 size, int flags) {
|
||||
Result RomFsFileSystem::DoCreateFile(const fs::Path &path, s64 size, int flags) {
|
||||
AMS_UNUSED(path, size, flags);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoDeleteFile(const char *path) {
|
||||
Result RomFsFileSystem::DoDeleteFile(const fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoCreateDirectory(const char *path) {
|
||||
Result RomFsFileSystem::DoCreateDirectory(const fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoDeleteDirectory(const char *path) {
|
||||
Result RomFsFileSystem::DoDeleteDirectory(const fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoDeleteDirectoryRecursively(const char *path) {
|
||||
Result RomFsFileSystem::DoDeleteDirectoryRecursively(const fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoRenameFile(const char *old_path, const char *new_path) {
|
||||
Result RomFsFileSystem::DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoRenameDirectory(const char *old_path, const char *new_path) {
|
||||
Result RomFsFileSystem::DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const char *path) {
|
||||
Result RomFsFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) {
|
||||
RomDirectoryInfo dir_info;
|
||||
R_TRY_CATCH(m_rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) {
|
||||
R_TRY_CATCH(m_rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path.GetString())) {
|
||||
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
|
||||
R_CATCH(fs::ResultDbmInvalidOperation) {
|
||||
RomFileTable::FileInfo file_info;
|
||||
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
|
||||
R_TRY(this->GetFileInfo(std::addressof(file_info), path.GetString()));
|
||||
*out = fs::DirectoryEntryType_File;
|
||||
return ResultSuccess();
|
||||
}
|
||||
@@ -494,14 +496,13 @@ namespace ams::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
|
||||
Result RomFsFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) {
|
||||
AMS_ASSERT(out_file != nullptr);
|
||||
AMS_ASSERT(path != nullptr);
|
||||
|
||||
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
|
||||
|
||||
RomFileTable::FileInfo file_info;
|
||||
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
|
||||
R_TRY(this->GetFileInfo(std::addressof(file_info), path.GetString()));
|
||||
|
||||
auto file = std::make_unique<RomFsFile>(this, m_entry_size + file_info.offset.Get(), m_entry_size + file_info.offset.Get() + file_info.size.Get());
|
||||
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB());
|
||||
@@ -510,12 +511,11 @@ namespace ams::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
|
||||
Result RomFsFileSystem::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) {
|
||||
AMS_ASSERT(out_dir != nullptr);
|
||||
AMS_ASSERT(path != nullptr);
|
||||
|
||||
RomFileTable::FindPosition find;
|
||||
R_TRY_CATCH(m_rom_file_table.FindOpen(std::addressof(find), path)) {
|
||||
R_TRY_CATCH(m_rom_file_table.FindOpen(std::addressof(find), path.GetString())) {
|
||||
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
|
||||
R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultPathNotFound())
|
||||
} R_END_TRY_CATCH;
|
||||
@@ -531,19 +531,19 @@ namespace ams::fs {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoGetFreeSpaceSize(s64 *out, const char *path) {
|
||||
Result RomFsFileSystem::DoGetFreeSpaceSize(s64 *out, const fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
|
||||
*out = 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoGetTotalSpaceSize(s64 *out, const char *path) {
|
||||
Result RomFsFileSystem::DoGetTotalSpaceSize(s64 *out, const fs::Path &path) {
|
||||
AMS_UNUSED(out, path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemC();
|
||||
}
|
||||
|
||||
Result RomFsFileSystem::DoCleanDirectoryRecursively(const char *path) {
|
||||
Result RomFsFileSystem::DoCleanDirectoryRecursively(const fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
|
||||
}
|
||||
|
||||
@@ -15,45 +15,56 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace impl {
|
||||
|
||||
Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataId id) {
|
||||
return fsReadSaveDataFileSystemExtraData(out, sizeof(*out), id);
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->ReadSaveDataFileSystemExtraData(sf::OutBuffer(out, sizeof(*out)), id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataSpaceId space_id, SaveDataId id) {
|
||||
return fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(out, sizeof(*out), static_cast<::FsSaveDataSpaceId>(space_id), id);
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(sf::OutBuffer(out, sizeof(*out)), static_cast<u8>(space_id), id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId space_id, SaveDataId id, const SaveDataExtraData &extra_data) {
|
||||
return fsWriteSaveDataFileSystemExtraData(std::addressof(extra_data), sizeof(extra_data), static_cast<::FsSaveDataSpaceId>(space_id), id);
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_TRY(fsp->WriteSaveDataFileSystemExtraData(id, static_cast<u8>(space_id), sf::InBuffer(std::addressof(extra_data), sizeof(extra_data))));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DisableAutoSaveDataCreation() {
|
||||
/* Use libnx binding. */
|
||||
R_ABORT_UNLESS(fsDisableAutoSaveDataCreation());
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
AMS_FS_R_ABORT_UNLESS(fsp->DisableAutoSaveDataCreation());
|
||||
}
|
||||
|
||||
Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) {
|
||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, save_id);
|
||||
const SaveDataCreationInfo info = {
|
||||
.size = size,
|
||||
.journal_size = journal_size,
|
||||
.block_size = DefaultSaveDataBlockSize,
|
||||
.owner_id = owner_id,
|
||||
.flags = flags,
|
||||
.space_id = space_id,
|
||||
.pseudo = false,
|
||||
auto create_impl = [=]() -> Result {
|
||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, save_id);
|
||||
const SaveDataCreationInfo info = {
|
||||
.size = size,
|
||||
.journal_size = journal_size,
|
||||
.block_size = DefaultSaveDataBlockSize,
|
||||
.owner_id = owner_id,
|
||||
.flags = flags,
|
||||
.space_id = space_id,
|
||||
.pseudo = false,
|
||||
};
|
||||
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
R_RETURN(fsp->CreateSaveDataFileSystemBySystemSaveDataId(attribute, info));
|
||||
};
|
||||
|
||||
static_assert(sizeof(SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
|
||||
static_assert(sizeof(SaveDataCreationInfo) == sizeof(::FsSaveDataCreationInfo));
|
||||
return fsCreateSaveDataFileSystemBySystemSaveDataId(reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)), reinterpret_cast<const ::FsSaveDataCreationInfo *>(std::addressof(info)));
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(create_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_CREATE_SYSTEM_SAVE_DATA(space_id, save_id, user_id, owner_id, size, journal_size, flags)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags) {
|
||||
@@ -77,20 +88,35 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result DeleteSaveData(SaveDataId id) {
|
||||
/* TODO: Libnx binding for DeleteSaveDataFileSystem */
|
||||
AMS_UNUSED(id);
|
||||
AMS_ABORT();
|
||||
auto delete_impl = [=]() -> Result {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
R_RETURN(fsp->DeleteSaveDataFileSystem(id));
|
||||
};
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(delete_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_SAVE_DATA_ID, id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DeleteSaveData(SaveDataSpaceId space_id, SaveDataId id) {
|
||||
return fsDeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<::FsSaveDataSpaceId>(space_id), id);
|
||||
auto delete_impl = [=]() -> Result {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
R_RETURN(fsp->DeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<u8>(space_id), id));
|
||||
};
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(delete_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_DELETE_SAVE_DATA(space_id, id)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
|
||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
||||
auto delete_impl = [=]() -> Result {
|
||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
||||
|
||||
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
|
||||
return fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)));
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
R_RETURN(fsp->DeleteSaveDataFileSystemBySaveDataAttribute(static_cast<u8>(space_id), attribute));
|
||||
};
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(delete_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_DELETE_SYSTEM_SAVE_DATA(space_id, id, user_id)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSaveDataFlags(u32 *out, SaveDataId id) {
|
||||
@@ -133,7 +159,13 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result ExtendSaveData(SaveDataSpaceId space_id, SaveDataId id, s64 available_size, s64 journal_size) {
|
||||
return ::fsExtendSaveDataFileSystem(static_cast<::FsSaveDataSpaceId>(space_id), static_cast<u64>(id), available_size, journal_size);
|
||||
auto extend_impl = [=]() -> Result {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
R_RETURN(fsp->ExtendSaveDataFileSystem(static_cast<u8>(space_id), id, available_size, journal_size));
|
||||
};
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(extend_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_EXTEND_SAVE_DATA(space_id, id, available_size, journal_size)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_event_notifier_object_adapter.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
#include "impl/fs_event_notifier_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -31,7 +33,7 @@ namespace ams::fs {
|
||||
|
||||
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
|
||||
/* Determine how much space we need. */
|
||||
const size_t needed_size = strnlen(impl::SdCardFileSystemMountName, MountNameLengthMax) + 2;
|
||||
const size_t needed_size = util::Strnlen(impl::SdCardFileSystemMountName, MountNameLengthMax) + 2;
|
||||
AMS_ABORT_UNLESS(dst_size >= needed_size);
|
||||
|
||||
/* Generate the name. */
|
||||
@@ -47,14 +49,15 @@ namespace ams::fs {
|
||||
|
||||
Result MountSdCard(const char *name) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountNameAllowingReserved(name));
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(impl::CheckMountNameAllowingReserved(name), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, name));
|
||||
|
||||
/* Open the SD card. This uses libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
|
||||
/* Open the SD card filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenSdCardFileSystem(std::addressof(fs)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
|
||||
|
||||
/* Allocate a new mountname generator. */
|
||||
@@ -70,47 +73,53 @@ namespace ams::fs {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Open the SD card. This uses libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
|
||||
/* Open the SD card filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenSdCardFileSystem(std::addressof(fs)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
std::unique_ptr<fsa::IFileSystem> fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
auto fsa = std::make_shared<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
|
||||
|
||||
/* Ensure that the error report directory exists. */
|
||||
R_TRY(fssystem::EnsureDirectoryRecursively(fsa.get(), AtmosphereErrorReportDirectory));
|
||||
constexpr fs::Path fs_path = fs::MakeConstantPath(AtmosphereErrorReportDirectory);
|
||||
R_TRY(fssystem::EnsureDirectory(fsa.get(), fs_path));
|
||||
|
||||
/* Create a subdirectory filesystem. */
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(fsa), AtmosphereErrorReportDirectory);
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(fsa));
|
||||
R_UNLESS(subdir_fs != nullptr, fs::ResultAllocationFailureInSdCardA());
|
||||
R_TRY(subdir_fs->Initialize(fs_path));
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(subdir_fs));
|
||||
}
|
||||
|
||||
Result OpenSdCardDetectionEventNotifier(std::unique_ptr<IEventNotifier> *out) {
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Try to open an event notifier. */
|
||||
FsEventNotifier notifier;
|
||||
AMS_FS_R_TRY(fsOpenSdCardDetectionEventNotifier(std::addressof(notifier)));
|
||||
sf::SharedPointer<fssrv::sf::IEventNotifier> notifier;
|
||||
AMS_FS_R_TRY(fsp->OpenSdCardDetectionEventNotifier(std::addressof(notifier)));
|
||||
|
||||
/* Create an event notifier adapter. */
|
||||
auto adapter = std::make_unique<impl::RemoteEventNotifierObjectAdapter>(notifier);
|
||||
R_UNLESS(adapter != nullptr, fs::ResultAllocationFailureInSdCardB());
|
||||
auto adapter = std::make_unique<impl::EventNotifierObjectAdapter>(std::move(notifier));
|
||||
AMS_FS_R_UNLESS(adapter != nullptr, fs::ResultAllocationFailureInSdCardB());
|
||||
|
||||
*out = std::move(adapter);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool IsSdCardInserted() {
|
||||
/* Open device operator. */
|
||||
FsDeviceOperator device_operator;
|
||||
AMS_FS_R_ABORT_UNLESS(::fsOpenDeviceOperator(std::addressof(device_operator)));
|
||||
ON_SCOPE_EXIT { ::fsDeviceOperatorClose(std::addressof(device_operator)); };
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
|
||||
/* Get SD card inserted. */
|
||||
/* Open a device operator. */
|
||||
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
|
||||
AMS_FS_R_ABORT_UNLESS(fsp->OpenDeviceOperator(std::addressof(device_operator)));
|
||||
|
||||
/* Get insertion status. */
|
||||
bool inserted;
|
||||
AMS_FS_R_ABORT_UNLESS(::fsDeviceOperatorIsSdCardInserted(std::addressof(device_operator), std::addressof(inserted)));
|
||||
AMS_FS_R_ABORT_UNLESS(device_operator->IsSdCardInserted(std::addressof(inserted)));
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
@@ -16,21 +16,30 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_filesystem_accessor.hpp"
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
Result GetSignedSystemPartitionOnSdCardValid(char *out, impl::FileSystemAccessor *accessor) {
|
||||
R_TRY_CATCH(accessor->QueryEntry(out, sizeof(*out), nullptr, 0, fsa::QueryId::IsSignedSystemPartitionOnSdCardValid, "/")) {
|
||||
/* If querying isn't supported, then the partition isn't valid. */
|
||||
R_CATCH(fs::ResultUnsupportedOperation) { *out = false; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsSignedSystemPartitionOnSdCardValid(const char *system_root_path) {
|
||||
/* Get the accessor for the system filesystem. */
|
||||
impl::FileSystemAccessor *accessor;
|
||||
const char *sub_path;
|
||||
R_ABORT_UNLESS(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), system_root_path));
|
||||
AMS_FS_R_ABORT_UNLESS(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), system_root_path));
|
||||
|
||||
char is_valid;
|
||||
R_TRY_CATCH(accessor->QueryEntry(std::addressof(is_valid), 1, nullptr, 0, fsa::QueryId::IsSignedSystemPartitionOnSdCardValid, "/")) {
|
||||
/* If querying isn't supported, then the partition isn't valid. */
|
||||
R_CATCH(fs::ResultUnsupportedOperation) { is_valid = false; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
AMS_FS_R_ABORT_UNLESS(GetSignedSystemPartitionOnSdCardValid(std::addressof(is_valid), accessor));
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
@@ -40,8 +49,9 @@ namespace ams::fs {
|
||||
AMS_ABORT_UNLESS(hos::Version_4_0_0 <= version && version < hos::Version_8_0_0);
|
||||
|
||||
/* Check that the partition is valid. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
bool is_valid;
|
||||
R_ABORT_UNLESS(fsIsSignedSystemPartitionOnSdCardValid(std::addressof(is_valid)));
|
||||
AMS_FS_R_ABORT_UNLESS(fsp->IsSignedSystemPartitionOnSdCardValid(std::addressof(is_valid)));
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,20 @@
|
||||
namespace ams::fs {
|
||||
|
||||
Result QueryMountSystemDataCacheSize(size_t *out, ncm::SystemDataId data_id) {
|
||||
return impl::QueryMountDataCacheSize(out, data_id, ncm::StorageId::BuiltInSystem);
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(impl::QueryMountDataCacheSize(out, data_id, ncm::StorageId::BuiltInSystem), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_QUERY_MOUNT_SYSTEM_DATA_CACHE_SIZE(data_id, out)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountSystemData(const char *name, ncm::SystemDataId data_id) {
|
||||
return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem);
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_SYSTEM_DATA(name, data_id)));
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountSystemData(const char *name, ncm::SystemDataId data_id, void *cache_buffer, size_t cache_size) {
|
||||
return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem, cache_buffer, cache_size);
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem, cache_buffer, cache_size), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_SYSTEM_DATA(name, data_id)));
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,33 +15,11 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsa/fs_mount_utils.hpp"
|
||||
#include "impl/fs_file_system_proxy_service_object.hpp"
|
||||
#include "impl/fs_file_system_service_object_adapter.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
Result MountSystemSaveDataImpl(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id, SaveDataType type) {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Create the attribute. */
|
||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, type, user_id, id);
|
||||
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
|
||||
|
||||
/* Open the filesystem, use libnx bindings. */
|
||||
::FsFileSystem fs;
|
||||
R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSystemSaveDataA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result MountSystemSaveData(const char *name, SystemSaveDataId id) {
|
||||
return MountSystemSaveData(name, id, InvalidUserId);
|
||||
}
|
||||
@@ -55,7 +33,29 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
|
||||
return MountSystemSaveDataImpl(name, space_id, id, user_id, SaveDataType::System);
|
||||
auto mount_impl = [=]() -> Result {
|
||||
/* Validate the mount name. */
|
||||
R_TRY(impl::CheckMountName(name));
|
||||
|
||||
/* Create the attribute. */
|
||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
||||
|
||||
/* Open the filesystem. */
|
||||
auto fsp = impl::GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
|
||||
R_TRY(fsp->OpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<u8>(space_id), attribute));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSystemSaveDataA());
|
||||
|
||||
/* Register. */
|
||||
return fsa::Register(name, std::move(fsa));
|
||||
};
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_SYSTEM_SAVE_DATA(name, space_id, id, user_id)));
|
||||
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace ams::fs::impl {
|
||||
|
||||
template<typename List, typename Iter>
|
||||
void Remove(List &list, Iter *desired) {
|
||||
for (auto it = list.cbegin(); it != list.cend(); it++) {
|
||||
for (auto it = list.cbegin(); it != list.cend(); ++it) {
|
||||
if (it.operator->() == desired) {
|
||||
list.erase(it);
|
||||
return;
|
||||
@@ -35,18 +35,13 @@ namespace ams::fs::impl {
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result ValidatePath(const char *mount_name, const char *path) {
|
||||
const size_t mount_name_len = strnlen(mount_name, MountNameLengthMax);
|
||||
const size_t path_len = strnlen(path, EntryNameLengthMax);
|
||||
R_UNLESS(mount_name_len + 1 + path_len <= EntryNameLengthMax, fs::ResultTooLongPath());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ValidateMountName(const char *name) {
|
||||
Result SetMountName(char *dst, const char *name) {
|
||||
R_UNLESS(name[0] != 0, fs::ResultInvalidMountName());
|
||||
R_UNLESS(strnlen(name, sizeof(MountName)) < sizeof(MountName), fs::ResultInvalidMountName());
|
||||
return ResultSuccess();
|
||||
|
||||
const size_t n_len = util::Strlcpy<char>(dst, name, MountNameLengthMax + 1);
|
||||
R_UNLESS(n_len <= MountNameLengthMax, fs::ResultInvalidMountName());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename List>
|
||||
@@ -54,18 +49,21 @@ namespace ams::fs::impl {
|
||||
for (auto it = list.cbegin(); it != list.cend(); it++) {
|
||||
R_UNLESS((it->GetOpenMode() & OpenMode_Write) == 0, fs::ResultWriteModeFileNotClosed());
|
||||
}
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator)
|
||||
: m_impl(std::move(fs)), m_open_list_lock(), m_mount_name_generator(std::move(generator)),
|
||||
m_access_log_enabled(false), m_data_cache_attachable(false), m_path_cache_attachable(false), m_path_cache_attached(false), m_multi_commit_supported(false)
|
||||
m_access_log_enabled(false), m_data_cache_attachable(false), m_path_cache_attachable(false), m_path_cache_attached(false), m_multi_commit_supported(false),
|
||||
m_path_flags()
|
||||
{
|
||||
R_ABORT_UNLESS(ValidateMountName(n));
|
||||
std::strncpy(m_name.str, n, MountNameLengthMax);
|
||||
m_name.str[MountNameLengthMax] = 0;
|
||||
R_ABORT_UNLESS(SetMountName(m_name.str, n));
|
||||
|
||||
if (std::strcmp(m_name.str, AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME) == 0) {
|
||||
m_path_flags.AllowWindowsPath();
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemAccessor::~FileSystemAccessor() {
|
||||
@@ -83,13 +81,12 @@ namespace ams::fs::impl {
|
||||
|
||||
Result FileSystemAccessor::GetCommonMountName(char *dst, size_t dst_size) const {
|
||||
R_UNLESS(m_mount_name_generator != nullptr, fs::ResultPreconditionViolation());
|
||||
return m_mount_name_generator->GenerateCommonMountName(dst, dst_size);
|
||||
R_RETURN(m_mount_name_generator->GenerateCommonMountName(dst, dst_size));
|
||||
}
|
||||
|
||||
std::shared_ptr<fssrv::impl::FileSystemInterfaceAdapter> FileSystemAccessor::GetMultiCommitTarget() {
|
||||
if (m_multi_commit_supported) {
|
||||
/* TODO: Support multi commit. */
|
||||
AMS_ABORT();
|
||||
AMS_ABORT("TODO: Support multi commit");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -104,71 +101,126 @@ namespace ams::fs::impl {
|
||||
Remove(m_open_dir_list, d);
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::SetUpPath(fs::Path *out, const char *p) {
|
||||
/* Initialize the path appropriately. */
|
||||
bool normalized;
|
||||
size_t len;
|
||||
if (R_SUCCEEDED(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(len), p, m_path_flags)) && normalized) {
|
||||
/* We can use the input buffer directly. */
|
||||
out->SetShallowBuffer(p);
|
||||
} else {
|
||||
/* Initialize with appropriate slash replacement. */
|
||||
if (m_path_flags.IsWindowsPathAllowed()) {
|
||||
R_TRY(out->InitializeWithReplaceForwardSlashes(p));
|
||||
} else {
|
||||
R_TRY(out->InitializeWithReplaceBackslash(p));
|
||||
}
|
||||
|
||||
/* Ensure we're normalized. */
|
||||
R_TRY(out->Normalize(m_path_flags));
|
||||
}
|
||||
|
||||
/* Check the path isn't too long. */
|
||||
R_UNLESS(out->GetLength() <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::CreateFile(const char *path, s64 size, int option) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
if (m_path_cache_attached) {
|
||||
/* TODO: Path cache */
|
||||
R_TRY(m_impl->CreateFile(path, size, option));
|
||||
R_TRY(m_impl->CreateFile(normalized_path, size, option));
|
||||
} else {
|
||||
R_TRY(m_impl->CreateFile(path, size, option));
|
||||
R_TRY(m_impl->CreateFile(normalized_path, size, option));
|
||||
}
|
||||
return ResultSuccess();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::DeleteFile(const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->DeleteFile(path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->DeleteFile(normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::CreateDirectory(const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->CreateDirectory(path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->CreateDirectory(normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::DeleteDirectory(const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->DeleteDirectory(path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->DeleteDirectory(normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::DeleteDirectoryRecursively(const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->DeleteDirectoryRecursively(path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->DeleteDirectoryRecursively(normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::RenameFile(const char *old_path, const char *new_path) {
|
||||
R_TRY(ValidatePath(m_name.str, old_path));
|
||||
R_TRY(ValidatePath(m_name.str, new_path));
|
||||
/* Create path. */
|
||||
fs::Path normalized_old_path;
|
||||
fs::Path normalized_new_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_old_path), old_path));
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_new_path), new_path));
|
||||
|
||||
if (m_path_cache_attached) {
|
||||
/* TODO: Path cache */
|
||||
R_TRY(m_impl->RenameFile(old_path, new_path));
|
||||
R_TRY(m_impl->RenameFile(normalized_old_path, normalized_new_path));
|
||||
} else {
|
||||
R_TRY(m_impl->RenameFile(old_path, new_path));
|
||||
R_TRY(m_impl->RenameFile(normalized_old_path, normalized_new_path));
|
||||
}
|
||||
return ResultSuccess();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::RenameDirectory(const char *old_path, const char *new_path) {
|
||||
R_TRY(ValidatePath(m_name.str, old_path));
|
||||
R_TRY(ValidatePath(m_name.str, new_path));
|
||||
/* Create path. */
|
||||
fs::Path normalized_old_path;
|
||||
fs::Path normalized_new_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_old_path), old_path));
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_new_path), new_path));
|
||||
|
||||
if (m_path_cache_attached) {
|
||||
/* TODO: Path cache */
|
||||
R_TRY(m_impl->RenameDirectory(old_path, new_path));
|
||||
R_TRY(m_impl->RenameDirectory(normalized_old_path, normalized_new_path));
|
||||
} else {
|
||||
R_TRY(m_impl->RenameDirectory(old_path, new_path));
|
||||
R_TRY(m_impl->RenameDirectory(normalized_old_path, normalized_new_path));
|
||||
}
|
||||
return ResultSuccess();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::GetEntryType(DirectoryEntryType *out, const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->GetEntryType(out, path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->GetEntryType(out, normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::OpenFile(std::unique_ptr<FileAccessor> *out_file, const char *path, OpenMode mode) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
std::unique_ptr<fsa::IFile> file;
|
||||
R_TRY(m_impl->OpenFile(std::addressof(file), path, mode));
|
||||
R_TRY(m_impl->OpenFile(std::addressof(file), normalized_path, mode));
|
||||
|
||||
auto accessor = new FileAccessor(std::move(file), this, mode);
|
||||
R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInFileSystemAccessorA());
|
||||
@@ -187,14 +239,16 @@ namespace ams::fs::impl {
|
||||
}
|
||||
|
||||
out_file->reset(accessor);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::OpenDirectory(std::unique_ptr<DirectoryAccessor> *out_dir, const char *path, OpenDirectoryMode mode) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
std::unique_ptr<fsa::IDirectory> dir;
|
||||
R_TRY(m_impl->OpenDirectory(std::addressof(dir), path, mode));
|
||||
R_TRY(m_impl->OpenDirectory(std::addressof(dir), normalized_path, mode));
|
||||
|
||||
auto accessor = new DirectoryAccessor(std::move(dir), *this);
|
||||
R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInFileSystemAccessorB());
|
||||
@@ -205,7 +259,7 @@ namespace ams::fs::impl {
|
||||
}
|
||||
|
||||
out_dir->reset(accessor);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::Commit() {
|
||||
@@ -213,30 +267,47 @@ namespace ams::fs::impl {
|
||||
std::scoped_lock lk(m_open_list_lock);
|
||||
R_ABORT_UNLESS(ValidateNoOpenWriteModeFiles(m_open_file_list));
|
||||
}
|
||||
return m_impl->Commit();
|
||||
R_RETURN(m_impl->Commit());
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::GetFreeSpaceSize(s64 *out, const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->GetFreeSpaceSize(out, path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->GetFreeSpaceSize(out, normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::GetTotalSpaceSize(s64 *out, const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->GetTotalSpaceSize(out, path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->GetTotalSpaceSize(out, normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::CleanDirectoryRecursively(const char *path) {
|
||||
R_TRY(ValidatePath(m_name.str, path));
|
||||
return m_impl->CleanDirectoryRecursively(path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->CleanDirectoryRecursively(normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) {
|
||||
return m_impl->GetFileTimeStampRaw(out, path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->GetFileTimeStampRaw(out, normalized_path));
|
||||
}
|
||||
|
||||
Result FileSystemAccessor::QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
|
||||
return m_impl->QueryEntry(dst, dst_size, src, src_size, query, path);
|
||||
/* Create path. */
|
||||
fs::Path normalized_path;
|
||||
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
|
||||
|
||||
R_RETURN(m_impl->QueryEntry(dst, dst_size, src, src_size, query, normalized_path));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace ams::fs::impl {
|
||||
bool m_path_cache_attachable;
|
||||
bool m_path_cache_attached;
|
||||
bool m_multi_commit_supported;
|
||||
PathFlags m_path_flags;
|
||||
public:
|
||||
FileSystemAccessor(const char *name, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator = nullptr);
|
||||
virtual ~FileSystemAccessor();
|
||||
@@ -93,6 +94,8 @@ namespace ams::fs::impl {
|
||||
private:
|
||||
void NotifyCloseFile(FileAccessor *f);
|
||||
void NotifyCloseDirectory(DirectoryAccessor *d);
|
||||
public:
|
||||
Result SetUpPath(fs::Path *out, const char *p);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -23,22 +23,32 @@ namespace ams::fs::impl {
|
||||
namespace {
|
||||
|
||||
const char *FindMountNameDriveSeparator(const char *path) {
|
||||
for (const char *cur = path; cur < path + MountNameLengthMax + 1; cur++) {
|
||||
for (const char *cur = path; cur < path + MountNameLengthMax + 1 && *cur != StringTraits::NullTerminator; ++cur) {
|
||||
if (*cur == StringTraits::DriveSeparator) {
|
||||
return cur;
|
||||
} else if (PathNormalizer::IsNullTerminator(*cur)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
constexpr bool IsHostRootPath(const char *path) {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON) || defined(ATMOSPHERE_OS_WINDOWS)
|
||||
return fs::IsWindowsDrive(path) || fs::IsUncPath(path);
|
||||
#elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
|
||||
return fs::IsPathAbsolute(path);
|
||||
#else
|
||||
#error "Unknown OS for host path identification"
|
||||
#endif
|
||||
}
|
||||
|
||||
Result GetMountNameAndSubPath(MountName *out_mount_name, const char **out_sub_path, const char *path) {
|
||||
/* Handle the Host-path case. */
|
||||
if (fs::IsWindowsDrive(path) || fs::IsUnc(path)) {
|
||||
if (IsHostRootPath(path)) {
|
||||
std::strncpy(out_mount_name->str, HostRootFileSystemMountName, MountNameLengthMax);
|
||||
out_mount_name->str[MountNameLengthMax] = '\x00';
|
||||
return ResultSuccess();
|
||||
|
||||
*out_sub_path = path;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Locate the drive separator. */
|
||||
@@ -47,34 +57,37 @@ namespace ams::fs::impl {
|
||||
|
||||
/* Ensure the mount name isn't too long. */
|
||||
const size_t len = drive_separator - path;
|
||||
R_UNLESS(0 < len, fs::ResultInvalidMountName());
|
||||
R_UNLESS(len <= MountNameLengthMax, fs::ResultInvalidMountName());
|
||||
|
||||
/* Ensure the result sub-path is valid. */
|
||||
const char *sub_path = drive_separator + 1;
|
||||
R_UNLESS(!PathNormalizer::IsNullTerminator(sub_path[0]), fs::ResultInvalidMountName());
|
||||
R_UNLESS(PathNormalizer::IsAnySeparator(sub_path[0]), fs::ResultInvalidPathFormat());
|
||||
|
||||
const auto starts_with_dir = (sub_path[0] == StringTraits::DirectorySeparator) || (sub_path[0] == StringTraits::AlternateDirectorySeparator);
|
||||
R_UNLESS(starts_with_dir, fs::ResultInvalidPathFormat());
|
||||
|
||||
/* Set output. */
|
||||
std::memcpy(out_mount_name->str, path, len);
|
||||
out_mount_name->str[len] = StringTraits::NullTerminator;
|
||||
|
||||
*out_sub_path = sub_path;
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsValidMountName(const char *name) {
|
||||
if (PathNormalizer::IsNullTerminator(name[0])) {
|
||||
if (name[0] == StringTraits::NullTerminator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((('a' <= name[0] && name[0] <= 'z') || ('A' <= name[0] && name[0] <= 'Z')) && PathNormalizer::IsNullTerminator(name[1])) {
|
||||
if ((('a' <= name[0] && name[0] <= 'z') || ('A' <= name[0] && name[0] <= 'Z')) && name[1] == StringTraits::NullTerminator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t len = 0;
|
||||
for (const char *cur = name; !PathNormalizer::IsNullTerminator(*cur); cur++) {
|
||||
if (*cur == StringTraits::DriveSeparator || PathNormalizer::IsSeparator(*cur)) {
|
||||
for (const char *cur = name; *cur != StringTraits::NullTerminator; ++cur) {
|
||||
if (*cur == StringTraits::DriveSeparator || *cur == StringTraits::DirectorySeparator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -83,8 +96,7 @@ namespace ams::fs::impl {
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: N validates that the mount name decodes via utf-8 here. */
|
||||
return true;
|
||||
return util::VerifyUtf8String(name, len);
|
||||
}
|
||||
|
||||
bool IsReservedMountName(const char *name) {
|
||||
@@ -108,7 +120,7 @@ namespace ams::fs::impl {
|
||||
R_UNLESS(out_sub_path != nullptr, fs::ResultUnexpectedInFindFileSystemA());
|
||||
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
R_UNLESS(strncmp(path, HostRootFileSystemMountName, strnlen(HostRootFileSystemMountName, sizeof(MountName))) != 0, fs::ResultNotMounted());
|
||||
R_UNLESS(strncmp(path, HostRootFileSystemMountName, util::Strnlen(HostRootFileSystemMountName, sizeof(MountName))) != 0, fs::ResultNotMounted());
|
||||
|
||||
MountName mount_name;
|
||||
R_TRY(GetMountNameAndSubPath(std::addressof(mount_name), out_sub_path, path));
|
||||
@@ -132,10 +144,6 @@ namespace ams::fs::impl {
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
}
|
||||
|
||||
Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *src) {
|
||||
/* Ensure neither argument is nullptr. */
|
||||
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
@@ -150,7 +158,7 @@ namespace ams::fs {
|
||||
AMS_FS_R_TRY(impl::Find(std::addressof(accessor), mount_name.str));
|
||||
AMS_FS_R_TRY(accessor->GetCommonMountName(dst, dst_size));
|
||||
|
||||
const auto mount_name_len = strnlen(dst, dst_size);
|
||||
const auto mount_name_len = util::Strnlen(dst, dst_size);
|
||||
const auto common_path_len = util::SNPrintf(dst + mount_name_len, dst_size - mount_name_len, "%s", sub_path);
|
||||
|
||||
AMS_FS_R_UNLESS(static_cast<size_t>(common_path_len) < dst_size - mount_name_len, fs::ResultTooLongPath());
|
||||
|
||||
@@ -164,62 +164,27 @@ namespace ams::fs {
|
||||
}
|
||||
|
||||
Result GetFreeSpaceSize(s64 *out, const char *path) {
|
||||
/* Find the filesystem without access logging. */
|
||||
impl::FileSystemAccessor *accessor;
|
||||
const char *sub_path = nullptr;
|
||||
const char *sub_path;
|
||||
AMS_FS_R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
|
||||
/* Get the accessor. */
|
||||
auto find_impl = [&]() -> Result {
|
||||
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||
if (impl::IsValidMountName(path)) {
|
||||
R_TRY(impl::Find(std::addressof(accessor), path));
|
||||
} else {
|
||||
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
}
|
||||
return ResultSuccess();
|
||||
};
|
||||
/* Get the total space size. */
|
||||
AMS_FS_R_TRY(accessor->GetFreeSpaceSize(out, sub_path));
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_UNLESS_R_SUCCEEDED(find_impl(), AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
|
||||
|
||||
/* Get the space size. */
|
||||
auto get_size_impl = [&]() -> Result {
|
||||
R_UNLESS(sub_path == nullptr || std::strcmp(sub_path, "/") == 0, fs::ResultInvalidMountName());
|
||||
R_TRY(accessor->GetFreeSpaceSize(out, "/"));
|
||||
return ResultSuccess();
|
||||
};
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_FILESYSTEM(get_size_impl(), nullptr, accessor, AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetTotalSpaceSize(s64 *out, const char *path) {
|
||||
/* NOTE: Nintendo does not do access logging here, and does not support mount-name instead of path. */
|
||||
/* Find the filesystem without access logging. */
|
||||
impl::FileSystemAccessor *accessor;
|
||||
const char *sub_path = nullptr;
|
||||
const char *sub_path;
|
||||
AMS_FS_R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
|
||||
/* Get the accessor. */
|
||||
auto find_impl = [&]() -> Result {
|
||||
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||
if (impl::IsValidMountName(path)) {
|
||||
R_TRY(impl::Find(std::addressof(accessor), path));
|
||||
} else {
|
||||
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
|
||||
}
|
||||
return ResultSuccess();
|
||||
};
|
||||
/* Get the total space size. */
|
||||
AMS_FS_R_TRY(accessor->GetTotalSpaceSize(out, sub_path));
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_UNLESS_R_SUCCEEDED(find_impl(), AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
|
||||
|
||||
/* Get the space size. */
|
||||
auto get_size_impl = [&]() -> Result {
|
||||
R_UNLESS(sub_path == nullptr || std::strcmp(sub_path, "/") == 0, fs::ResultInvalidMountName());
|
||||
R_TRY(accessor->GetTotalSpaceSize(out, "/"));
|
||||
return ResultSuccess();
|
||||
};
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_FILESYSTEM(get_size_impl(), nullptr, accessor, AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetConcatenationFileAttribute(const char *path) {
|
||||
@@ -229,7 +194,7 @@ namespace ams::fs {
|
||||
|
||||
AMS_FS_R_TRY(accessor->QueryEntry(nullptr, 0, nullptr, 0, fsa::QueryId::SetConcatenationFileAttribute, sub_path));
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenFile(FileHandle *out, std::unique_ptr<fsa::IFile> &&file, int mode) {
|
||||
@@ -239,7 +204,7 @@ namespace ams::fs {
|
||||
AMS_FS_R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInNew());
|
||||
out->handle = file_accessor.release();
|
||||
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -249,7 +214,7 @@ namespace ams::fs {
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_UNLESS_R_SUCCEEDED(impl::Find(std::addressof(accessor), mount_name), AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, mount_name));
|
||||
|
||||
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_FILESYSTEM_WITH_NAME(accessor->Commit(), nullptr, accessor, func_name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, mount_name));
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,9 +36,13 @@ namespace ams::fs {
|
||||
|
||||
}
|
||||
|
||||
Result GetFileTimeStampRawForDebug(FileTimeStampRaw *out, const char *path) {
|
||||
AMS_FS_R_TRY(impl::GetFileTimeStampRawForDebug(out, path));
|
||||
return ResultSuccess();
|
||||
Result GetFileTimeStamp(FileTimeStamp *out, const char *path) {
|
||||
fs::FileTimeStampRaw raw;
|
||||
AMS_FS_R_TRY(impl::GetFileTimeStampRawForDebug(std::addressof(raw), path));
|
||||
|
||||
static_assert(sizeof(raw) == sizeof(*out));
|
||||
std::memcpy(out, std::addressof(raw), sizeof(raw));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,23 +38,4 @@ namespace ams::fs::impl {
|
||||
}
|
||||
};
|
||||
|
||||
class RemoteEventNotifierObjectAdapter final : public ::ams::fs::IEventNotifier, public ::ams::fs::impl::Newable {
|
||||
private:
|
||||
::FsEventNotifier m_notifier;
|
||||
public:
|
||||
RemoteEventNotifierObjectAdapter(::FsEventNotifier &n) : m_notifier(n) { /* ... */ }
|
||||
virtual ~RemoteEventNotifierObjectAdapter() { fsEventNotifierClose(std::addressof(m_notifier)); }
|
||||
private:
|
||||
virtual Result DoBindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) override {
|
||||
/* Get the handle. */
|
||||
::Event e;
|
||||
R_TRY(fsEventNotifierGetEventHandle(std::addressof(m_notifier), std::addressof(e), false));
|
||||
|
||||
/* Create the system event. */
|
||||
os::AttachReadableHandleToSystemEvent(out, e.revent, true, clear_mode);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxy> GetFileSystemProxyServiceObject();
|
||||
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> GetFileSystemProxyForLoaderServiceObject();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
class FileServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::fsa::IFile {
|
||||
NON_COPYABLE(FileServiceObjectAdapter);
|
||||
NON_MOVEABLE(FileServiceObjectAdapter);
|
||||
private:
|
||||
sf::SharedPointer<fssrv::sf::IFile> m_x;
|
||||
public:
|
||||
explicit FileServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IFile> &&o) : m_x(o) { /* ... */}
|
||||
virtual ~FileServiceObjectAdapter() { /* ... */ }
|
||||
public:
|
||||
virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
||||
s64 read_size = 0;
|
||||
R_TRY(m_x->Read(std::addressof(read_size), offset, sf::OutNonSecureBuffer(buffer, size), static_cast<s64>(size), option));
|
||||
|
||||
*out = static_cast<size_t>(read_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result DoGetSize(s64 *out) override final {
|
||||
R_RETURN(m_x->GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result DoFlush() override final {
|
||||
R_RETURN(m_x->Flush());
|
||||
}
|
||||
|
||||
virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||
R_RETURN(m_x->Write(offset, sf::InNonSecureBuffer(buffer, size), static_cast<s64>(size), option));
|
||||
}
|
||||
|
||||
virtual Result DoSetSize(s64 size) override final {
|
||||
R_RETURN(m_x->SetSize(size));
|
||||
}
|
||||
|
||||
virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||
switch (op_id) {
|
||||
case OperationId::Invalidate:
|
||||
{
|
||||
fs::QueryRangeInfo dummy_range_info;
|
||||
R_RETURN(m_x->OperateRange(std::addressof(dummy_range_info), static_cast<s32>(op_id), offset, size));
|
||||
}
|
||||
case OperationId::QueryRange:
|
||||
{
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize());
|
||||
|
||||
R_RETURN(m_x->OperateRange(reinterpret_cast<fs::QueryRangeInfo *>(dst), static_cast<s32>(op_id), offset, size));
|
||||
}
|
||||
default:
|
||||
{
|
||||
R_RETURN(m_x->OperateRangeWithBuffer(sf::OutNonSecureBuffer(dst, dst_size), sf::InNonSecureBuffer(src, src_size), static_cast<s32>(op_id), offset, size));
|
||||
}
|
||||
}
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override final {
|
||||
AMS_ABORT("Invalid GetDomainObjectId call");
|
||||
}
|
||||
};
|
||||
|
||||
class DirectoryServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::fsa::IDirectory {
|
||||
NON_COPYABLE(DirectoryServiceObjectAdapter);
|
||||
NON_MOVEABLE(DirectoryServiceObjectAdapter);
|
||||
private:
|
||||
sf::SharedPointer<fssrv::sf::IDirectory> m_x;
|
||||
public:
|
||||
explicit DirectoryServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IDirectory> &&o) : m_x(o) { /* ... */}
|
||||
virtual ~DirectoryServiceObjectAdapter() { /* ... */ }
|
||||
public:
|
||||
virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
|
||||
R_RETURN(m_x->Read(out_count, sf::OutBuffer(out_entries, max_entries * sizeof(*out_entries))));
|
||||
}
|
||||
|
||||
virtual Result DoGetEntryCount(s64 *out) override final {
|
||||
R_RETURN(m_x->GetEntryCount(out));
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override final {
|
||||
AMS_ABORT("Invalid GetDomainObjectId call");
|
||||
}
|
||||
};
|
||||
|
||||
class FileSystemServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::fsa::IFileSystem {
|
||||
NON_COPYABLE(FileSystemServiceObjectAdapter);
|
||||
NON_MOVEABLE(FileSystemServiceObjectAdapter);
|
||||
private:
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> m_x;
|
||||
public:
|
||||
explicit FileSystemServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IFileSystem> &&o) : m_x(o) { /* ... */}
|
||||
virtual ~FileSystemServiceObjectAdapter() { /* ... */ }
|
||||
|
||||
sf::SharedPointer<fssrv::sf::IFileSystem> GetFileSystem() const { return m_x; }
|
||||
private:
|
||||
static Result GetPathForServiceObject(fssrv::sf::Path *out, const fs::Path &path) {
|
||||
const size_t len = util::Strlcpy<char>(out->str, path.GetString(), sizeof(out->str));
|
||||
R_UNLESS(len < sizeof(out->str), fs::ResultTooLongPath());
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const fs::Path &path, OpenMode mode) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
/* Open the file. */
|
||||
sf::SharedPointer<fssrv::sf::IFile> file;
|
||||
R_TRY(m_x->OpenFile(std::addressof(file), fsp_path, mode));
|
||||
|
||||
/* Create the output fsa file. */
|
||||
out_file->reset(new FileServiceObjectAdapter(std::move(file)));
|
||||
R_UNLESS(out_file != nullptr, fs::ResultAllocationFailureInNew());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result DoOpenDirectory(std::unique_ptr<fsa::IDirectory> *out_dir, const fs::Path &path, OpenDirectoryMode mode) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
/* Open the directory. */
|
||||
sf::SharedPointer<fssrv::sf::IDirectory> dir;
|
||||
R_TRY(m_x->OpenDirectory(std::addressof(dir), fsp_path, mode));
|
||||
|
||||
/* Create the output fsa directory. */
|
||||
out_dir->reset(new DirectoryServiceObjectAdapter(std::move(dir)));
|
||||
R_UNLESS(out_dir != nullptr, fs::ResultAllocationFailureInNew());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result DoGetEntryType(DirectoryEntryType *out, const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->GetEntryType(out, fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoCommit() override final {
|
||||
R_RETURN(m_x->Commit());
|
||||
}
|
||||
|
||||
virtual Result DoCreateFile(const fs::Path &path, s64 size, int flags) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->CreateFile(fsp_path, size, flags));
|
||||
}
|
||||
|
||||
virtual Result DoDeleteFile(const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->DeleteFile(fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoCreateDirectory(const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->CreateDirectory(fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectory(const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->DeleteDirectory(fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->DeleteDirectoryRecursively(fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_old_path;
|
||||
fssrv::sf::Path fsp_new_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_old_path), old_path));
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_new_path), new_path));
|
||||
|
||||
R_RETURN(m_x->RenameFile(fsp_old_path, fsp_new_path));
|
||||
}
|
||||
|
||||
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_old_path;
|
||||
fssrv::sf::Path fsp_new_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_old_path), old_path));
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_new_path), new_path));
|
||||
|
||||
R_RETURN(m_x->RenameDirectory(fsp_old_path, fsp_new_path));
|
||||
}
|
||||
|
||||
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->CleanDirectoryRecursively(fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->GetFreeSpaceSize(out, fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->GetTotalSpaceSize(out, fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const fs::Path &path) override final {
|
||||
/* Convert the path. */
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->GetFileTimeStampRaw(out, fsp_path));
|
||||
}
|
||||
|
||||
virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const fs::Path &path) override {
|
||||
fssrv::sf::Path fsp_path;
|
||||
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
|
||||
|
||||
R_RETURN(m_x->QueryEntry(sf::OutBuffer(dst, dst_size), sf::InBuffer(src, src_size), static_cast<s32>(query), fsp_path));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
115
libraries/libstratosphere/source/fs/impl/fs_library.cpp
Normal file
115
libraries/libstratosphere/source/fs/impl/fs_library.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
#include "fs_library.hpp"
|
||||
#include "fs_file_system_service_object_adapter.hpp"
|
||||
#include "../../fssrv/impl/fssrv_allocator_for_service_framework.hpp"
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
#if !defined(ATMOSPHERE_OS_HORIZON)
|
||||
namespace {
|
||||
|
||||
constexpr size_t SystemHeapSize = 8_MB;
|
||||
alignas(os::MemoryPageSize) constinit u8 g_system_heap[SystemHeapSize];
|
||||
|
||||
ALWAYS_INLINE auto &GetSystemHeapAllocator() {
|
||||
AMS_FUNCTION_LOCAL_STATIC(mem::StandardAllocator, s_system_heap_allocator, g_system_heap, sizeof(g_system_heap));
|
||||
return s_system_heap_allocator;
|
||||
}
|
||||
|
||||
constinit util::optional<fssrv::MemoryResourceFromStandardAllocator> g_system_heap_memory_resource;
|
||||
|
||||
void *AllocateForSystem(size_t size) { return g_system_heap_memory_resource->Allocate(size); }
|
||||
void DeallocateForSystem(void *p, size_t size) { return g_system_heap_memory_resource->Deallocate(p, size); }
|
||||
|
||||
[[maybe_unused]] constexpr size_t BufferPoolSize = 6_MB;
|
||||
[[maybe_unused]] constexpr size_t DeviceBufferSize = 8_MB;
|
||||
[[maybe_unused]] constexpr size_t BufferManagerHeapSize = 14_MB;
|
||||
|
||||
static_assert(util::IsAligned(BufferManagerHeapSize, os::MemoryBlockUnitSize));
|
||||
|
||||
//alignas(os::MemoryPageSize) u8 g_buffer_pool[BufferPoolSize];
|
||||
//alignas(os::MemoryPageSize) u8 g_device_buffer[DeviceBufferSize];
|
||||
//alignas(os::MemoryPageSize) u8 g_buffer_manager_heap[BufferManagerHeapSize];
|
||||
//
|
||||
//alignas(os::MemoryPageSize) u8 g_buffer_manager_work_buffer[64_KB];
|
||||
/* TODO: Other work buffers. */
|
||||
|
||||
/* TODO: Implement pooled threads. */
|
||||
// constexpr int PooledThreadCount = 12;
|
||||
// constexpr size_t PooledThreadStackSize = 32_KB;
|
||||
// fssystem::PooledThread g_pooled_threads[PooledThreadCount];
|
||||
|
||||
/* FileSystem creators. */
|
||||
constinit util::optional<fssrv::fscreator::LocalFileSystemCreator> g_local_fs_creator;
|
||||
constinit util::optional<fssrv::fscreator::SubDirectoryFileSystemCreator> g_subdir_fs_creator;
|
||||
|
||||
constinit fssrv::fscreator::FileSystemCreatorInterfaces g_fs_creator_interfaces = {};
|
||||
|
||||
Result InitializeFileSystemLibraryImpl() {
|
||||
/* Set system allocator. */
|
||||
fssystem::InitializeAllocator(::ams::fs::impl::Allocate, ::ams::fs::impl::Deallocate);
|
||||
fssystem::InitializeAllocatorForSystem(::ams::fs::impl::AllocateForSystem, ::ams::fs::impl::DeallocateForSystem);
|
||||
|
||||
/* TODO: Many things. */
|
||||
g_system_heap_memory_resource.emplace(std::addressof(GetSystemHeapAllocator()));
|
||||
|
||||
/* Setup fscreators/interfaces. */
|
||||
g_local_fs_creator.emplace(true);
|
||||
g_subdir_fs_creator.emplace();
|
||||
|
||||
g_fs_creator_interfaces.local_fs_creator = std::addressof(*g_local_fs_creator);
|
||||
g_fs_creator_interfaces.subdir_fs_creator = std::addressof(*g_subdir_fs_creator);
|
||||
|
||||
/* Initialize fssrv. */
|
||||
const fssrv::FileSystemProxyConfiguration config = {
|
||||
.m_fs_creator_interfaces = std::addressof(g_fs_creator_interfaces),
|
||||
.m_base_storage_service_impl = nullptr /* TODO */,
|
||||
.m_base_file_system_service_impl = nullptr /* TODO */,
|
||||
.m_nca_file_system_service_impl = nullptr /* TODO */,
|
||||
.m_save_data_file_system_service_impl = nullptr /* TODO */,
|
||||
.m_access_failure_management_service_impl = nullptr /* TODO */,
|
||||
.m_time_service_impl = nullptr /* TODO */,
|
||||
.m_status_report_service_impl = nullptr /* TODO */,
|
||||
.m_program_registry_service_impl = nullptr /* TODO */,
|
||||
.m_access_log_service_impl = nullptr /* TODO */,
|
||||
.m_debug_configuration_service_impl = nullptr /* TODO */,
|
||||
};
|
||||
|
||||
fssrv::InitializeForFileSystemProxy(config);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
class FileSystemLibraryInitializer {
|
||||
public:
|
||||
FileSystemLibraryInitializer() {
|
||||
R_ABORT_UNLESS(InitializeFileSystemLibraryImpl());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void InitializeFileSystemLibrary() {
|
||||
#if !defined(ATMOSPHERE_OS_HORIZON)
|
||||
AMS_FUNCTION_LOCAL_STATIC(FileSystemLibraryInitializer, s_library_initializer);
|
||||
AMS_UNUSED(s_library_initializer);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
23
libraries/libstratosphere/source/fs/impl/fs_library.hpp
Normal file
23
libraries/libstratosphere/source/fs/impl/fs_library.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
void InitializeFileSystemLibrary();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
class RemoteDeviceOperator {
|
||||
private:
|
||||
::FsDeviceOperator m_operator;
|
||||
public:
|
||||
RemoteDeviceOperator(::FsDeviceOperator &o) : m_operator(o) { /* ... */ }
|
||||
|
||||
virtual ~RemoteDeviceOperator() {
|
||||
fsDeviceOperatorClose(std::addressof(m_operator));
|
||||
}
|
||||
public:
|
||||
Result IsSdCardInserted(ams::sf::Out<bool> out) {
|
||||
R_RETURN(fsDeviceOperatorIsSdCardInserted(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result IsGameCardInserted(ams::sf::Out<bool> out) {
|
||||
R_RETURN(fsDeviceOperatorIsGameCardInserted(std::addressof(m_operator), out.GetPointer()));
|
||||
}
|
||||
|
||||
Result GetGameCardHandle(ams::sf::Out<u32> out) {
|
||||
static_assert(sizeof(::FsGameCardHandle) == sizeof(u32));
|
||||
R_RETURN(fsDeviceOperatorGetGameCardHandle(std::addressof(m_operator), reinterpret_cast<::FsGameCardHandle *>(out.GetPointer())));
|
||||
}
|
||||
};
|
||||
static_assert(fssrv::sf::IsIDeviceOperator<RemoteDeviceOperator>);
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
class RemoteEventNotifier {
|
||||
private:
|
||||
::FsEventNotifier m_notifier;
|
||||
public:
|
||||
RemoteEventNotifier(::FsEventNotifier &n) : m_notifier(n) { /* ... */ }
|
||||
|
||||
virtual ~RemoteEventNotifier() {
|
||||
fsEventNotifierClose(std::addressof(m_notifier));
|
||||
}
|
||||
public:
|
||||
Result GetEventHandle(ams::sf::OutCopyHandle out) {
|
||||
::Event e;
|
||||
R_TRY(fsEventNotifierGetEventHandle(std::addressof(m_notifier), std::addressof(e), false));
|
||||
|
||||
out.SetValue(e.revent, true);
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
static_assert(fssrv::sf::IsIEventNotifier<RemoteEventNotifier>);
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs::impl {
|
||||
|
||||
class StorageServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::IStorage {
|
||||
NON_COPYABLE(StorageServiceObjectAdapter);
|
||||
NON_MOVEABLE(StorageServiceObjectAdapter);
|
||||
private:
|
||||
sf::SharedPointer<fssrv::sf::IStorage> m_x;
|
||||
public:
|
||||
explicit StorageServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IStorage> &&o) : m_x(o) { /* ... */}
|
||||
virtual ~StorageServiceObjectAdapter() { /* ... */ }
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override final {
|
||||
R_RETURN(m_x->Read(offset, sf::OutNonSecureBuffer(buffer, size), static_cast<s64>(size)));
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override final {
|
||||
R_RETURN(m_x->GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override final {
|
||||
R_RETURN(m_x->Flush());
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override final {
|
||||
R_RETURN(m_x->Write(offset, sf::InNonSecureBuffer(buffer, size), static_cast<s64>(size)));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override final {
|
||||
R_RETURN(m_x->SetSize(size));
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||
AMS_UNUSED(src, src_size);
|
||||
switch (op_id) {
|
||||
case OperationId::Invalidate:
|
||||
{
|
||||
fs::QueryRangeInfo dummy_range_info;
|
||||
R_RETURN(m_x->OperateRange(std::addressof(dummy_range_info), static_cast<s32>(op_id), offset, size));
|
||||
}
|
||||
case OperationId::QueryRange:
|
||||
{
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize());
|
||||
|
||||
R_RETURN(m_x->OperateRange(reinterpret_cast<fs::QueryRangeInfo *>(dst), static_cast<s32>(op_id), offset, size));
|
||||
}
|
||||
default:
|
||||
{
|
||||
R_THROW(fs::ResultUnsupportedOperationInStorageServiceObjectAdapterA());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t DefaultPathBufferSize = fs::EntryNameLengthMax + 1;
|
||||
|
||||
consteval PathFlags DecodeFlags(const char *desc) {
|
||||
PathFlags flags{};
|
||||
|
||||
while (*desc) {
|
||||
switch (*(desc++)) {
|
||||
case 'B':
|
||||
flags.AllowBackslash();
|
||||
break;
|
||||
case 'E':
|
||||
flags.AllowEmptyPath();
|
||||
break;
|
||||
case 'M':
|
||||
flags.AllowMountName();
|
||||
break;
|
||||
case 'R':
|
||||
flags.AllowRelativePath();
|
||||
break;
|
||||
case 'W':
|
||||
flags.AllowWindowsPath();
|
||||
break;
|
||||
case 'C':
|
||||
flags.AllowAllCharacters();
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
consteval size_t Strlen(const char *p) {
|
||||
size_t len = 0;
|
||||
|
||||
while (p[len] != StringTraits::NullTerminator) {
|
||||
++len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
//#define ENABLE_PRINT_FORMAT_TEST_DEBUGGING
|
||||
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
template<u32 Expected, u32 Actual>
|
||||
struct Print {
|
||||
constexpr Print() {
|
||||
if (std::is_constant_evaluated()) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<u32 E = 0, u32 A = 0, size_t N = 0>
|
||||
consteval void PrintResultMismatchImpl(u32 e, u32 a) {
|
||||
if constexpr (N == 32) {
|
||||
Print<E, A>{};
|
||||
} else {
|
||||
const bool is_e = (e & (1 << N)) != 0;
|
||||
const bool is_a = (a & (1 << N)) != 0;
|
||||
if (is_e) {
|
||||
if (is_a) {
|
||||
PrintResultMismatchImpl<E | (1 << N), A | (1 << N), N + 1>(e, a);
|
||||
} else {
|
||||
PrintResultMismatchImpl<E | (1 << N), A | (0 << N), N + 1>(e, a);
|
||||
}
|
||||
} else {
|
||||
if (is_a) {
|
||||
PrintResultMismatchImpl<E | (0 << N), A | (1 << N), N + 1>(e, a);
|
||||
} else {
|
||||
PrintResultMismatchImpl<E | (0 << N), A | (0 << N), N + 1>(e, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
consteval void PrintResultMismatch(const Result &lhs, const Result &rhs) {
|
||||
PrintResultMismatchImpl(lhs.GetDescription(), rhs.GetDescription());
|
||||
}
|
||||
|
||||
template<size_t Index, char Expected, char Actual>
|
||||
struct PrintMismatchChar {
|
||||
constexpr PrintMismatchChar() {
|
||||
if (std::is_constant_evaluated()) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t Ix, char Expected, size_t C = 0>
|
||||
consteval void PrintCharacterMismatch(char c) {
|
||||
if (c == static_cast<char>(C)) {
|
||||
PrintMismatchChar<Ix, Expected, static_cast<char>(C)>{};
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
|
||||
PrintCharacterMismatch<Ix, Expected, C + 1>(c);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Ix, char C = 0>
|
||||
consteval void PrintCharacterMismatch(char c, char c2) {
|
||||
if (c == static_cast<char>(C)) {
|
||||
PrintCharacterMismatch<Ix, static_cast<char>(C)>(c2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
|
||||
PrintCharacterMismatch<Ix, C + 1>(c, c2);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Ix = 0>
|
||||
consteval void PrintCharacterMismatch(size_t ix, char c, char c2) {
|
||||
if (Ix == ix) {
|
||||
PrintCharacterMismatch<Ix>(c, c2);
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (Ix <= DefaultPathBufferSize) {
|
||||
PrintCharacterMismatch<Ix + 1>(ix, c, c2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
consteval bool TestNormalizedImpl(const char *path, const PathFlags &flags, size_t buffer_size, const char *normalized, Result expected_result) {
|
||||
/* Allocate a buffer to normalize into. */
|
||||
char *buffer = new char[buffer_size];
|
||||
ON_SCOPE_EXIT { delete[] buffer; };
|
||||
buffer[buffer_size - 1] = '\xcc';
|
||||
|
||||
/* Perform normalization. */
|
||||
const Result actual_result = PathFormatter::Normalize(buffer, buffer_size, path, Strlen(path) + 1, flags);
|
||||
|
||||
/* Check that the expected result matches the actual. */
|
||||
if (actual_result.GetValue() != expected_result.GetValue()) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the expected string matches the actual. */
|
||||
for (size_t i = 0; i < buffer_size; ++i) {
|
||||
if (normalized[i] != StringTraits::NullTerminator || R_SUCCEEDED(expected_result)) {
|
||||
if (buffer[i] != normalized[i]) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintCharacterMismatch(i, normalized[i], buffer[i]);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (normalized[i] == StringTraits::NullTerminator || buffer[i] == StringTraits::NullTerminator) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct NormalizeTestData {
|
||||
const char *path;
|
||||
const char *flag_desc;
|
||||
const char *normalized;
|
||||
Result result;
|
||||
size_t buffer_size = DefaultPathBufferSize;
|
||||
};
|
||||
|
||||
template<size_t N, size_t Ix = 0>
|
||||
consteval bool DoNormalizeTests(const NormalizeTestData (&tests)[N]) {
|
||||
if constexpr (Ix >= N) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &test = tests[Ix];
|
||||
if (!TestNormalizedImpl(test.path, DecodeFlags(test.flag_desc), test.buffer_size, test.normalized, test.result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (Ix < N) {
|
||||
return DoNormalizeTests<N, Ix + 1>(tests);
|
||||
} else {
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
}
|
||||
|
||||
consteval bool TestNormalizeEmptyPath() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "", "", "", fs::ResultInvalidPathFormat() },
|
||||
{ "", "E", "", ResultSuccess() },
|
||||
{ "/aa/bb/../cc", "E", "/aa/cc", ResultSuccess() },
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeEmptyPath());
|
||||
|
||||
consteval bool TestNormalizeMountName() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "mount:/aa/bb", "", "", fs::ResultInvalidPathFormat() },
|
||||
{ "mount:/aa/bb", "W", "", fs::ResultInvalidPathFormat() },
|
||||
{ "mount:/aa/bb", "M", "mount:/aa/bb", ResultSuccess() },
|
||||
{ "mount:/aa/./bb", "M", "mount:/aa/bb", ResultSuccess() },
|
||||
{ "mount:\\aa\\bb", "M", "mount:", fs::ResultInvalidPathFormat() },
|
||||
{ "m:/aa/bb", "M", "", fs::ResultInvalidPathFormat() },
|
||||
{ "mo>unt:/aa/bb", "M", "", fs::ResultInvalidCharacter() },
|
||||
{ "moun?t:/aa/bb", "M", "", fs::ResultInvalidCharacter() },
|
||||
{ "mo&unt:/aa/bb", "M", "mo&unt:/aa/bb", ResultSuccess() },
|
||||
{ "/aa/./bb", "M", "/aa/bb", ResultSuccess() },
|
||||
{ "mount/aa/./bb", "M", "", fs::ResultInvalidPathFormat() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeMountName());
|
||||
|
||||
consteval bool TestNormalizeWindowsPath() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "c:/aa/bb", "", "", fs::ResultInvalidPathFormat() },
|
||||
{ "c:\\aa\\bb", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\host\\share", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\.\\c:\\", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\.\\c:/aa/bb/.", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\?\\c:\\", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "mount:\\\\host\\share\\aa\\bb", "M", "mount:", fs::ResultInvalidCharacter() },
|
||||
{ "mount:\\\\host/share\\aa\\bb", "M", "mount:", fs::ResultInvalidCharacter() },
|
||||
{ "c:\\aa\\..\\..\\..\\bb", "W", "c:/bb", ResultSuccess() },
|
||||
{ "mount:/\\\\aa\\..\\bb", "MW", "mount:", fs::ResultInvalidPathFormat() },
|
||||
{ "mount:/c:\\aa\\..\\bb", "MW", "mount:c:/bb", ResultSuccess() },
|
||||
{ "mount:/aa/bb", "MW", "mount:/aa/bb", ResultSuccess() },
|
||||
{ "/mount:/aa/bb", "MW", "/", fs::ResultInvalidCharacter() },
|
||||
{ "/mount:/aa/bb", "W", "/", fs::ResultInvalidCharacter() },
|
||||
{ "a:aa/../bb", "MW", "a:aa/bb", ResultSuccess() },
|
||||
{ "a:aa\\..\\bb", "MW", "a:aa/bb", ResultSuccess() },
|
||||
{ "/a:aa\\..\\bb", "W", "/", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\?\\c:\\.\\aa", "W", "\\\\?\\c:/aa", ResultSuccess() },
|
||||
{ "\\\\.\\c:\\.\\aa", "W", "\\\\.\\c:/aa", ResultSuccess() },
|
||||
{ "\\\\.\\mount:\\.\\aa", "W", "\\\\./", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\./.\\aa", "W", "\\\\./aa", ResultSuccess() },
|
||||
{ "\\\\/aa", "W", "", fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\\\aa", "W", "", fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\", "W", "/", ResultSuccess() },
|
||||
{ "\\\\host\\share", "W", "\\\\host\\share/", ResultSuccess() },
|
||||
{ "\\\\host\\share\\path", "W", "\\\\host\\share/path", ResultSuccess() },
|
||||
{ "\\\\host\\share\\path\\aa\\bb\\..\\cc\\.", "W", "\\\\host\\share/path/aa/cc", ResultSuccess() },
|
||||
{ "\\\\host\\", "W", "", fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\ho$st\\share\\path", "W", "", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\host:\\share\\path", "W", "", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\..\\share\\path", "W", "", fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\host\\s:hare\\path", "W", "", fs::ResultInvalidCharacter() },
|
||||
{ "\\\\host\\.\\path", "W", "", fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\host\\..\\path", "W", "", fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\host\\sha:re", "W", "", fs::ResultInvalidCharacter() },
|
||||
{ ".\\\\host\\share", "RW", "..\\\\host\\share/", ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeWindowsPath());
|
||||
|
||||
consteval bool TestNormalizeRelativePath() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "./aa/bb", "", "", fs::ResultInvalidPathFormat() },
|
||||
{ "./aa/bb/../cc", "R", "./aa/cc", ResultSuccess() },
|
||||
{ ".\\aa/bb/../cc", "R", "..", fs::ResultInvalidCharacter() },
|
||||
{ ".", "R", ".", ResultSuccess() },
|
||||
{ "../aa/bb", "R", "", fs::ResultDirectoryUnobtainable() },
|
||||
{ "/aa/./bb", "R", "/aa/bb", ResultSuccess() },
|
||||
{ "mount:./aa/bb", "MR", "mount:./aa/bb", ResultSuccess() },
|
||||
{ "mount:./aa/./bb", "MR", "mount:./aa/bb", ResultSuccess() },
|
||||
{ "mount:./aa/bb", "M", "mount:", fs::ResultInvalidPathFormat() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeRelativePath());
|
||||
|
||||
consteval bool TestNormalizeBackslash() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "\\aa\\bb\\..\\cc", "", "", fs::ResultInvalidPathFormat() },
|
||||
{ "\\aa\\bb\\..\\cc", "B", "", fs::ResultInvalidPathFormat() },
|
||||
{ "/aa\\bb\\..\\cc", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "/aa\\bb\\..\\cc", "B", "/cc", ResultSuccess() },
|
||||
{ "/aa\\bb\\cc", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "/aa\\bb\\cc", "B", "/aa\\bb\\cc", ResultSuccess() },
|
||||
{ "\\\\host\\share\\path\\aa\\bb\\cc", "W", "\\\\host\\share/path/aa/bb/cc", ResultSuccess() },
|
||||
{ "\\\\host\\share\\path\\aa\\bb\\cc", "WB", "\\\\host\\share/path/aa/bb/cc", ResultSuccess() },
|
||||
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "", "", fs::ResultInvalidCharacter() },
|
||||
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "B", "/aa", ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeBackslash());
|
||||
|
||||
consteval bool TestNormalizeAllowAllChars() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "/aa/b:b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b*b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b?b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b<b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b>b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b|b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b:b/cc", "C", "/aa/b:b/cc", ResultSuccess() },
|
||||
{ "/aa/b*b/cc", "C", "/aa/b*b/cc", ResultSuccess() },
|
||||
{ "/aa/b?b/cc", "C", "/aa/b?b/cc", ResultSuccess() },
|
||||
{ "/aa/b<b/cc", "C", "/aa/b<b/cc", ResultSuccess() },
|
||||
{ "/aa/b>b/cc", "C", "/aa/b>b/cc", ResultSuccess() },
|
||||
{ "/aa/b|b/cc", "C", "/aa/b|b/cc", ResultSuccess() },
|
||||
{ "/aa/b'b/cc", "", "/aa/b'b/cc", ResultSuccess() },
|
||||
{ "/aa/b\"b/cc", "", "/aa/b\"b/cc", ResultSuccess() },
|
||||
{ "/aa/b(b/cc", "", "/aa/b(b/cc", ResultSuccess() },
|
||||
{ "/aa/b)b/cc", "", "/aa/b)b/cc", ResultSuccess() },
|
||||
{ "/aa/b'b/cc", "C", "/aa/b'b/cc", ResultSuccess() },
|
||||
{ "/aa/b\"b/cc", "C", "/aa/b\"b/cc", ResultSuccess() },
|
||||
{ "/aa/b(b/cc", "C", "/aa/b(b/cc", ResultSuccess() },
|
||||
{ "/aa/b)b/cc", "C", "/aa/b)b/cc", ResultSuccess() },
|
||||
{ "mount:/aa/b<b/cc", "MC", "mount:/aa/b<b/cc", ResultSuccess() },
|
||||
{ "mo>unt:/aa/bb/cc", "MC", "", fs::ResultInvalidCharacter() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeAllowAllChars());
|
||||
|
||||
consteval bool TestNormalizeAll() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "mount:./aa/bb", "WRM", "mount:./aa/bb", ResultSuccess() },
|
||||
{ "mount:./aa/bb\\cc/dd", "WRM", "mount:./aa/bb/cc/dd", ResultSuccess() },
|
||||
{ "mount:./aa/bb\\cc/dd", "WRMB", "mount:./aa/bb/cc/dd", ResultSuccess() },
|
||||
{ "mount:./.c:/aa/bb", "RM", "mount:./", fs::ResultInvalidCharacter() },
|
||||
{ "mount:.c:/aa/bb", "WRM", "mount:./", fs::ResultInvalidCharacter() },
|
||||
{ "mount:./cc:/aa/bb", "WRM", "mount:./", fs::ResultInvalidCharacter() },
|
||||
{ "mount:./\\\\host\\share/aa/bb", "MW", "mount:", fs::ResultInvalidPathFormat() },
|
||||
{ "mount:./\\\\host\\share/aa/bb", "WRM", "mount:.\\\\host\\share/aa/bb", ResultSuccess() },
|
||||
{ "mount:.\\\\host\\share/aa/bb", "WRM", "mount:..\\\\host\\share/aa/bb", ResultSuccess() },
|
||||
{ "mount:..\\\\host\\share/aa/bb", "WRM", "mount:.", fs::ResultDirectoryUnobtainable() },
|
||||
{ ".\\\\host\\share/aa/bb", "WRM", "..\\\\host\\share/aa/bb", ResultSuccess() },
|
||||
{ "..\\\\host\\share/aa/bb", "WRM", ".", fs::ResultDirectoryUnobtainable() },
|
||||
{ "mount:\\\\host\\share/aa/bb", "MW", "mount:\\\\host\\share/aa/bb", ResultSuccess() },
|
||||
{ "mount:\\aa\\bb", "BM", "mount:", fs::ResultInvalidPathFormat() },
|
||||
{ "mount:/aa\\bb", "BM", "mount:/aa\\bb", ResultSuccess() },
|
||||
{ ".//aa/bb", "RW", "./aa/bb", ResultSuccess() },
|
||||
{ "./aa/bb", "R", "./aa/bb", ResultSuccess() },
|
||||
{ "./c:/aa/bb", "RW", "./", fs::ResultInvalidCharacter() },
|
||||
{ "mount:./aa/b:b\\cc/dd", "WRMBC", "mount:./aa/b:b/cc/dd", ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeAll());
|
||||
|
||||
consteval bool TestNormalizeSmallBuffer() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "/aa/bb", "M", "", fs::ResultTooLongPath(), 1},
|
||||
{ "mount:/aa/bb", "MR", "", fs::ResultTooLongPath(), 6 },
|
||||
{ "mount:/aa/bb", "MR", "mount:", fs::ResultTooLongPath(), 7 },
|
||||
{ "aa/bb", "MR", "./", fs::ResultTooLongPath(), 3 },
|
||||
{ "\\\\host\\share", "W", "\\\\host\\share", fs::ResultTooLongPath(), 13 }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizeSmallBuffer());
|
||||
|
||||
consteval bool TestIsNormalizedImpl(const char *path, const PathFlags &flags, bool expected_normalized, size_t expected_size, Result expected_result) {
|
||||
/* Perform normalization checking. */
|
||||
bool actual_normalized;
|
||||
size_t actual_size;
|
||||
const Result actual_result = PathFormatter::IsNormalized(std::addressof(actual_normalized), std::addressof(actual_size), path, flags);
|
||||
|
||||
/* Check that the expected result matches the actual. */
|
||||
if (actual_result.GetValue() != expected_result.GetValue()) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the expected output matches the actual. */
|
||||
if (R_SUCCEEDED(expected_result)) {
|
||||
if (expected_normalized != actual_normalized) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatchImpl(static_cast<u32>(expected_normalized), static_cast<u32>(actual_normalized));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_normalized) {
|
||||
if (expected_size != actual_size) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatchImpl(static_cast<u32>(expected_size), static_cast<u32>(actual_size));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct IsNormalizedTestData {
|
||||
const char *path;
|
||||
const char *flag_desc;
|
||||
bool normalized;
|
||||
size_t len;
|
||||
Result result;
|
||||
};
|
||||
|
||||
template<size_t N, size_t Ix = 0>
|
||||
consteval bool DoIsNormalizedTests(const IsNormalizedTestData (&tests)[N]) {
|
||||
if constexpr (Ix >= N) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &test = tests[Ix];
|
||||
if (!TestIsNormalizedImpl(test.path, DecodeFlags(test.flag_desc), test.normalized, test.len, test.result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (Ix < N) {
|
||||
return DoIsNormalizedTests<N, Ix + 1>(tests);
|
||||
} else {
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
}
|
||||
|
||||
consteval bool TestIsNormalizedEmptyPath() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "", "E", true, 0, ResultSuccess() },
|
||||
{ "/aa/bb/../cc", "E", false, 0, ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalizedEmptyPath());
|
||||
|
||||
consteval bool TestIsNormalizedMountName() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "mount:/aa/bb", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:/aa/bb", "W", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:/aa/bb", "M", true, 12, ResultSuccess() },
|
||||
{ "mount:/aa/./bb", "M", false, 6, ResultSuccess() },
|
||||
{ "mount:\\aa\\bb", "M", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "m:/aa/bb", "M", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mo>unt:/aa/bb", "M", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "moun?t:/aa/bb", "M", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "mo&unt:/aa/bb", "M", true, 13, ResultSuccess() },
|
||||
{ "/aa/./bb", "M", false, 0, ResultSuccess() },
|
||||
{ "mount/aa/./bb", "M", false, 0, fs::ResultInvalidPathFormat() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalizedMountName());
|
||||
|
||||
consteval bool TestIsNormalizedWindowsPath() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "c:/aa/bb", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "c:\\aa\\bb", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\host\\share", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\.\\c:\\", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\.\\c:/aa/bb/.", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\?\\c:\\", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:\\\\host\\share\\aa\\bb", "M", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:\\\\host/share\\aa\\bb", "M", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "c:\\aa\\..\\..\\..\\bb", "W", false, 0, ResultSuccess() },
|
||||
{ "mount:/\\\\aa\\..\\bb", "MW", false, 0, ResultSuccess() },
|
||||
{ "mount:/c:\\aa\\..\\bb", "MW", false, 0, ResultSuccess() },
|
||||
{ "mount:/aa/bb", "MW", true, 12, ResultSuccess() },
|
||||
{ "/mount:/aa/bb", "MW", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/mount:/aa/bb", "W", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "a:aa/../bb", "MW", false, 8, ResultSuccess() },
|
||||
{ "a:aa\\..\\bb", "MW", false, 0, ResultSuccess() },
|
||||
{ "/a:aa\\..\\bb", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\?\\c:\\.\\aa", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\.\\c:\\.\\aa", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\.\\mount:\\.\\aa", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\./.\\aa", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\/aa", "W", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\\\aa", "W", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\host\\share", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\host\\share\\path", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\host\\share\\path\\aa\\bb\\..\\cc\\.", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\host\\", "W", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\ho$st\\share\\path", "W", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "\\\\host:\\share\\path", "W", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "\\\\..\\share\\path", "W", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\host\\s:hare\\path", "W", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "\\\\host\\.\\path", "W", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\host\\..\\path", "W", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\host\\sha:re", "W", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ ".\\\\host\\share", "RW", false, 0, ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalizedWindowsPath());
|
||||
|
||||
consteval bool TestIsNormalizedRelativePath() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "./aa/bb", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "./aa/bb/../cc", "R", false, 1, ResultSuccess() },
|
||||
{ ".\\aa/bb/../cc", "R", false, 0, ResultSuccess() },
|
||||
{ ".", "R", true, 1, ResultSuccess() },
|
||||
{ "../aa/bb", "R", false, 0, fs::ResultDirectoryUnobtainable() },
|
||||
{ "/aa/./bb", "R", false, 0, ResultSuccess() },
|
||||
{ "mount:./aa/bb", "MR", true, 13, ResultSuccess() },
|
||||
{ "mount:./aa/./bb", "MR", false, 7, ResultSuccess() },
|
||||
{ "mount:./aa/bb", "M", false, 0, fs::ResultInvalidPathFormat() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalizedRelativePath());
|
||||
|
||||
consteval bool TestIsNormalizedBackslash() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "\\aa\\bb\\..\\cc", "", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\aa\\bb\\..\\cc", "B", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "/aa\\bb\\..\\cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa\\bb\\..\\cc", "B", false, 0, ResultSuccess() },
|
||||
{ "/aa\\bb\\cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa\\bb\\cc", "B", true, 9, ResultSuccess() },
|
||||
{ "\\\\host\\share\\path\\aa\\bb\\cc", "W", false, 0, ResultSuccess() },
|
||||
{ "\\\\host\\share\\path\\aa\\bb\\cc", "WB", false, 0, ResultSuccess() },
|
||||
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "B", false, 0, ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalizedBackslash());
|
||||
|
||||
consteval bool TestIsNormalizedAllowAllCharacters() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "/aa/b:b/cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b*b/cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b?b/cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b<b/cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b>b/cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b|b/cc", "", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/b:b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b*b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b?b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b<b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b>b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b|b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b'b/cc", "", true, 10, ResultSuccess() },
|
||||
{ "/aa/b\"b/cc", "", true, 10, ResultSuccess() },
|
||||
{ "/aa/b(b/cc", "", true, 10, ResultSuccess() },
|
||||
{ "/aa/b)b/cc", "", true, 10, ResultSuccess() },
|
||||
{ "/aa/b'b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b\"b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b(b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "/aa/b)b/cc", "C", true, 10, ResultSuccess() },
|
||||
{ "mount:/aa/b<b/cc", "MC", true, 16, ResultSuccess() },
|
||||
{ "mo>unt:/aa/bb/cc", "MC", false, 0, fs::ResultInvalidCharacter() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalizedAllowAllCharacters());
|
||||
|
||||
consteval bool TestIsNormalizedAll() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "mount:./aa/bb", "WRM", true, 13, ResultSuccess() },
|
||||
{ "mount:./aa/bb\\cc/dd", "WRM", false, 0, ResultSuccess() },
|
||||
{ "mount:./aa/bb\\cc/dd", "WRMB", true, 19, ResultSuccess() },
|
||||
{ "mount:./.c:/aa/bb", "RM", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "mount:.c:/aa/bb", "WRM", false, 0, ResultSuccess() },
|
||||
{ "mount:./cc:/aa/bb", "WRM", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "mount:./\\\\host\\share/aa/bb", "MW", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:./\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
|
||||
{ "mount:.\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
|
||||
{ "mount:..\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
|
||||
{ ".\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
|
||||
{ "..\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
|
||||
{ "mount:\\\\host\\share/aa/bb", "MW", true, 24, ResultSuccess() },
|
||||
{ "mount:\\aa\\bb", "BM", false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:/aa\\bb", "BM", true, 12, ResultSuccess() },
|
||||
{ ".//aa/bb", "RW", false, 1, ResultSuccess() },
|
||||
{ "./aa/bb", "R", true, 7, ResultSuccess() },
|
||||
{ "./c:/aa/bb", "RW", false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "mount:./aa/b:b\\cc/dd", "WRMBC", true, 20, ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalizedAll());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t DefaultPathBufferSize = fs::EntryNameLengthMax + 1;
|
||||
|
||||
//#define ENABLE_PRINT_FORMAT_TEST_DEBUGGING
|
||||
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
template<u32 Expected, u32 Actual>
|
||||
struct Print {
|
||||
constexpr Print() {
|
||||
if (std::is_constant_evaluated()) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<u32 E = 0, u32 A = 0, size_t N = 0>
|
||||
consteval void PrintResultMismatchImpl(u32 e, u32 a) {
|
||||
if constexpr (N == 32) {
|
||||
Print<E, A>{};
|
||||
} else {
|
||||
const bool is_e = (e & (1 << N)) != 0;
|
||||
const bool is_a = (a & (1 << N)) != 0;
|
||||
if (is_e) {
|
||||
if (is_a) {
|
||||
PrintResultMismatchImpl<E | (1 << N), A | (1 << N), N + 1>(e, a);
|
||||
} else {
|
||||
PrintResultMismatchImpl<E | (1 << N), A | (0 << N), N + 1>(e, a);
|
||||
}
|
||||
} else {
|
||||
if (is_a) {
|
||||
PrintResultMismatchImpl<E | (0 << N), A | (1 << N), N + 1>(e, a);
|
||||
} else {
|
||||
PrintResultMismatchImpl<E | (0 << N), A | (0 << N), N + 1>(e, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
consteval void PrintResultMismatch(const Result &lhs, const Result &rhs) {
|
||||
PrintResultMismatchImpl(lhs.GetDescription(), rhs.GetDescription());
|
||||
}
|
||||
|
||||
template<size_t Index, char Expected, char Actual>
|
||||
struct PrintMismatchChar {
|
||||
constexpr PrintMismatchChar() {
|
||||
if (std::is_constant_evaluated()) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t Ix, char Expected, size_t C = 0>
|
||||
consteval void PrintCharacterMismatch(char c) {
|
||||
if (c == static_cast<char>(C)) {
|
||||
PrintMismatchChar<Ix, Expected, static_cast<char>(C)>{};
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
|
||||
PrintCharacterMismatch<Ix, Expected, C + 1>(c);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Ix, char C = 0>
|
||||
consteval void PrintCharacterMismatch(char c, char c2) {
|
||||
if (c == static_cast<char>(C)) {
|
||||
PrintCharacterMismatch<Ix, static_cast<char>(C)>(c2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
|
||||
PrintCharacterMismatch<Ix, C + 1>(c, c2);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Ix = 0>
|
||||
consteval void PrintCharacterMismatch(size_t ix, char c, char c2) {
|
||||
if (Ix == ix) {
|
||||
PrintCharacterMismatch<Ix>(c, c2);
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (Ix <= DefaultPathBufferSize) {
|
||||
PrintCharacterMismatch<Ix + 1>(ix, c, c2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
consteval bool TestNormalizeImpl(const char *path, size_t buffer_size, const char *normalized, bool windows_path, bool drive_relative, bool all_chars, size_t expected_length, Result expected_result) {
|
||||
/* Allocate a buffer to normalize into. */
|
||||
char *buffer = new char[buffer_size];
|
||||
ON_SCOPE_EXIT { delete[] buffer; };
|
||||
buffer[buffer_size - 1] = '\xcc';
|
||||
|
||||
/* Perform normalization. */
|
||||
size_t actual_length;
|
||||
const Result actual_result = PathNormalizer::Normalize(buffer, std::addressof(actual_length), path, buffer_size, windows_path, drive_relative, all_chars);
|
||||
|
||||
/* Check that the expected result matches the actual. */
|
||||
if (actual_result.GetValue() != expected_result.GetValue()) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the expected string matches the actual. */
|
||||
for (size_t i = 0; i < buffer_size; ++i) {
|
||||
if (normalized[i] != StringTraits::NullTerminator || R_SUCCEEDED(expected_result)) {
|
||||
if (buffer[i] != normalized[i]) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintCharacterMismatch(i, normalized[i], buffer[i]);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (normalized[i] == StringTraits::NullTerminator || buffer[i] == StringTraits::NullTerminator) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that the expected length matches the actual. */
|
||||
if (R_SUCCEEDED(expected_result) || fs::ResultTooLongPath::Includes(expected_result)) {
|
||||
if (expected_length != actual_length) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatchImpl(static_cast<u32>(expected_length), static_cast<u32>(actual_length));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct NormalizeTestData {
|
||||
const char *path;
|
||||
bool windows;
|
||||
bool rel;
|
||||
bool allow_all;
|
||||
const char *normalized;
|
||||
size_t len;
|
||||
Result result;
|
||||
};
|
||||
|
||||
template<size_t N, size_t Ix = 0>
|
||||
consteval bool DoNormalizeTests(const NormalizeTestData (&tests)[N]) {
|
||||
if constexpr (Ix >= N) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &test = tests[Ix];
|
||||
if (!TestNormalizeImpl(test.path, DefaultPathBufferSize, test.normalized, test.windows, test.rel, test.allow_all, test.len, test.result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (Ix < N) {
|
||||
return DoNormalizeTests<N, Ix + 1>(tests);
|
||||
} else {
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
}
|
||||
|
||||
consteval bool TestNormalized() {
|
||||
constexpr NormalizeTestData Tests[] = {
|
||||
{ "/aa/bb/c/", false, true, false, "/aa/bb/c", 8, ResultSuccess() },
|
||||
{ "aa/bb/c/", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
|
||||
{ "aa/bb/c/", false, true, false, "/aa/bb/c", 8, ResultSuccess() },
|
||||
{ "mount:a/b", false, true, false, "/", 0, fs::ResultInvalidCharacter() },
|
||||
{ "mo|unt:a/b", false, true, true, "/mo|unt:a/b", 11, ResultSuccess() },
|
||||
{ "/aa/bb/../..", true, false, false, "/", 1, ResultSuccess() },
|
||||
{ "/aa/bb/../../..", true, false, false, "/", 1, ResultSuccess() },
|
||||
{ "/aa/bb/../../..", false, false, false, "/aa/bb/", 0, fs::ResultDirectoryUnobtainable() },
|
||||
{ "aa/bb/../../..", true, true, false, "/", 1, ResultSuccess() },
|
||||
{ "aa/bb/../../..", false, true, false, "/aa/bb/", 0, fs::ResultDirectoryUnobtainable() },
|
||||
{ "mount:a/b", false, true, true, "/mount:a/b", 10, ResultSuccess() },
|
||||
{ "/a|/bb/cc", false, false, true, "/a|/bb/cc", 9, ResultSuccess() },
|
||||
{ "/>a/bb/cc", false, false, true, "/>a/bb/cc", 9, ResultSuccess() },
|
||||
{ "/aa/.</cc", false, false, true, "/aa/.</cc", 9, ResultSuccess() },
|
||||
{ "/aa/..</cc", false, false, true, "/aa/..</cc", 10, ResultSuccess() },
|
||||
{ "", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
|
||||
{ "/", false, false, false, "/", 1, ResultSuccess() },
|
||||
{ "/.", false, false, false, "/", 1, ResultSuccess() },
|
||||
{ "/./", false, false, false, "/", 1, ResultSuccess() },
|
||||
{ "/..", false, false, false, "/", 0, fs::ResultDirectoryUnobtainable() },
|
||||
{ "//.", false, false, false, "/", 1, ResultSuccess() },
|
||||
{ "/ ..", false, false, false, "/ ..", 4, ResultSuccess() },
|
||||
{ "/.. /", false, false, false, "/.. ", 4, ResultSuccess() },
|
||||
{ "/. /.", false, false, false, "/. ", 3, ResultSuccess() },
|
||||
{ "/aa/bb/cc/dd/./.././../..", false, false, false, "/aa", 3, ResultSuccess() },
|
||||
{ "/aa/bb/cc/dd/./.././../../..", false, false, false, "/", 1, ResultSuccess() },
|
||||
{ "/./aa/./bb/./cc/./dd/.", false, false, false, "/aa/bb/cc/dd", 12, ResultSuccess() },
|
||||
{ "/aa\\bb/cc", false, false, false, "/aa\\bb/cc", 9, ResultSuccess() },
|
||||
{ "/aa\\bb/cc", false, false, false, "/aa\\bb/cc", 9, ResultSuccess() },
|
||||
{ "/a|/bb/cc", false, false, false, "/", 0, fs::ResultInvalidCharacter() },
|
||||
{ "/>a/bb/cc", false, false, false, "/", 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/.</cc", false, false, false, "/aa/", 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/..</cc", false, false, false, "/aa/", 0, fs::ResultInvalidCharacter() },
|
||||
{ "\\\\aa/bb/cc", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\aa\\bb\\cc", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
|
||||
{ "/aa/bb/..\\cc", false, false, false, "/aa/cc", 6, ResultSuccess() },
|
||||
{ "/aa/bb\\..\\cc", false, false, false, "/aa/cc", 6, ResultSuccess() },
|
||||
{ "/aa/bb\\..", false, false, false, "/aa", 3, ResultSuccess() },
|
||||
{ "/aa\\bb/../cc", false, false, false, "/cc", 3, ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalized());
|
||||
|
||||
struct NormalizeTestDataSmallBuffer {
|
||||
const char *path;
|
||||
size_t buffer_size;
|
||||
const char *normalized;
|
||||
size_t len;
|
||||
Result result;
|
||||
};
|
||||
|
||||
template<size_t N, size_t Ix = 0>
|
||||
consteval bool DoNormalizeTests(const NormalizeTestDataSmallBuffer (&tests)[N]) {
|
||||
if constexpr (Ix >= N) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &test = tests[Ix];
|
||||
if (!TestNormalizeImpl(test.path, test.buffer_size, test.normalized, false, false, false, test.len, test.result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (Ix < N) {
|
||||
return DoNormalizeTests<N, Ix + 1>(tests);
|
||||
} else {
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
}
|
||||
|
||||
consteval bool TestNormalizedSmallBuffer() {
|
||||
constexpr NormalizeTestDataSmallBuffer Tests[] = {
|
||||
{ "/aa/bb/cc/", 7, "/aa/bb", 6, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/cc/", 8, "/aa/bb/", 7, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/cc/", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/cc/", 10, "/aa/bb/cc", 9, ResultSuccess() },
|
||||
{ "/aa/bb/cc", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/cc", 10, "/aa/bb/cc", 9, ResultSuccess() },
|
||||
{ "/./aa/./bb/./cc", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
|
||||
{ "/./aa/./bb/./cc", 10, "/aa/bb/cc", 9, ResultSuccess() },
|
||||
{ "/aa/bb/cc/../../..", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/cc/../../..", 10, "/aa/bb/cc", 9, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/.", 7, "/aa/bb", 6, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/./", 7, "/aa/bb", 6, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb/..", 8, "/aa", 3, ResultSuccess() },
|
||||
{ "/aa/bb", 1, "", 0, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb", 2, "/", 1, fs::ResultTooLongPath() },
|
||||
{ "/aa/bb", 3, "/a", 2, fs::ResultTooLongPath() },
|
||||
{ "aa/bb", 1, "", 0, fs::ResultInvalidPathFormat() }
|
||||
};
|
||||
|
||||
return DoNormalizeTests(Tests);
|
||||
}
|
||||
static_assert(TestNormalizedSmallBuffer());
|
||||
|
||||
consteval bool TestIsNormalizedImpl(const char *path, bool allow_all, bool expected_normalized, size_t expected_size, Result expected_result) {
|
||||
/* Perform normalization checking. */
|
||||
bool actual_normalized;
|
||||
size_t actual_size = 0;
|
||||
const Result actual_result = PathNormalizer::IsNormalized(std::addressof(actual_normalized), std::addressof(actual_size), path, allow_all);
|
||||
|
||||
/* Check that the expected result matches the actual. */
|
||||
if (actual_result.GetValue() != expected_result.GetValue()) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_size != actual_size) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatchImpl(static_cast<u32>(expected_size), static_cast<u32>(actual_size));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the expected output matches the actual. */
|
||||
if (R_SUCCEEDED(expected_result)) {
|
||||
if (expected_normalized != actual_normalized) {
|
||||
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
|
||||
PrintResultMismatchImpl(static_cast<u32>(expected_normalized), static_cast<u32>(actual_normalized));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct IsNormalizedTestData {
|
||||
const char *path;
|
||||
bool allow_all;
|
||||
bool normalized;
|
||||
size_t len;
|
||||
Result result;
|
||||
};
|
||||
|
||||
template<size_t N, size_t Ix = 0>
|
||||
consteval bool DoIsNormalizedTests(const IsNormalizedTestData (&tests)[N]) {
|
||||
if constexpr (Ix >= N) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &test = tests[Ix];
|
||||
if (!TestIsNormalizedImpl(test.path, test.allow_all, test.normalized, test.len, test.result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (Ix < N) {
|
||||
return DoIsNormalizedTests<N, Ix + 1>(tests);
|
||||
} else {
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
}
|
||||
|
||||
consteval bool TestIsNormalized() {
|
||||
constexpr IsNormalizedTestData Tests[] = {
|
||||
{ "/aa/bb/c/", false, false, 9, ResultSuccess() },
|
||||
{ "aa/bb/c/", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "aa/bb/c/", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:a/b", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mo|unt:a/b", true, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "/aa/bb/../..", false, false, 0, ResultSuccess() },
|
||||
{ "/aa/bb/../../..", false, false, 0, ResultSuccess() },
|
||||
{ "/aa/bb/../../..", false, false, 0, ResultSuccess() },
|
||||
{ "aa/bb/../../..", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "aa/bb/../../..", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "mount:a/b", true, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "/a|/bb/cc", true, true, 9, ResultSuccess() },
|
||||
{ "/>a/bb/cc", true, true, 9, ResultSuccess() },
|
||||
{ "/aa/.</cc", true, true, 9, ResultSuccess() },
|
||||
{ "/aa/..</cc", true, true, 10, ResultSuccess() },
|
||||
{ "", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "/", false, true, 1, ResultSuccess() },
|
||||
{ "/.", false, false, 2, ResultSuccess() },
|
||||
{ "/./", false, false, 0, ResultSuccess() },
|
||||
{ "/..", false, false, 3, ResultSuccess() },
|
||||
{ "//.", false, false, 0, ResultSuccess() },
|
||||
{ "/ ..", false, true, 4, ResultSuccess() },
|
||||
{ "/.. /", false, false, 5, ResultSuccess() },
|
||||
{ "/. /.", false, false, 5, ResultSuccess() },
|
||||
{ "/aa/bb/cc/dd/./.././../..", false, false, 0, ResultSuccess() },
|
||||
{ "/aa/bb/cc/dd/./.././../../..", false, false, 0, ResultSuccess() },
|
||||
{ "/./aa/./bb/./cc/./dd/.", false, false, 0, ResultSuccess() },
|
||||
{ "/aa\\bb/cc", false, true, 9, ResultSuccess() },
|
||||
{ "/aa\\bb/cc", false, true, 9, ResultSuccess() },
|
||||
{ "/a|/bb/cc", false, false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/>a/bb/cc", false, false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/.</cc", false, false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "/aa/..</cc", false, false, 0, fs::ResultInvalidCharacter() },
|
||||
{ "\\\\aa/bb/cc", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "\\\\aa\\bb\\cc", false, false, 0, fs::ResultInvalidPathFormat() },
|
||||
{ "/aa/bb/..\\cc", false, true, 12, ResultSuccess() },
|
||||
{ "/aa/bb\\..\\cc", false, true, 12, ResultSuccess() },
|
||||
{ "/aa/bb\\..", false, true, 9, ResultSuccess() },
|
||||
{ "/aa\\bb/../cc", false, false, 0, ResultSuccess() }
|
||||
};
|
||||
|
||||
return DoIsNormalizedTests(Tests);
|
||||
}
|
||||
static_assert(TestIsNormalized());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
struct IsSubPathTestData {
|
||||
const char *lhs;
|
||||
const char *rhs;
|
||||
bool is_sub_path;
|
||||
};
|
||||
|
||||
template<size_t N, size_t Ix = 0>
|
||||
consteval bool DoIsSubPathTests(const IsSubPathTestData (&tests)[N]) {
|
||||
if constexpr (Ix >= N) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &test = tests[Ix];
|
||||
if (::ams::fs::IsSubPath(test.lhs, test.rhs) != test.is_sub_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (Ix < N) {
|
||||
return DoIsSubPathTests<N, Ix + 1>(tests);
|
||||
} else {
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
}
|
||||
|
||||
consteval bool TestIsSubPath() {
|
||||
constexpr IsSubPathTestData Tests[] = {
|
||||
{ "//a/b", "/a", false },
|
||||
{ "/a", "//a/b", false },
|
||||
{ "//a/b", "\\\\a", false },
|
||||
{ "//a/b", "//a", true },
|
||||
{ "/", "/a", true },
|
||||
{ "/a", "/", true },
|
||||
{ "/", "/", false },
|
||||
{ "", "", false },
|
||||
{ "/", "", true },
|
||||
{ "/", "mount:/a", false },
|
||||
{ "mount:/", "mount:/", false },
|
||||
{ "mount:/a/b", "mount:/a/b", false },
|
||||
{ "mount:/a/b", "mount:/a/b/c", true },
|
||||
{ "/a/b", "/a/b/c", true },
|
||||
{ "/a/b/c", "/a/b", true },
|
||||
{ "/a/b", "/a/b", false },
|
||||
{ "/a/b", "/a/b\\c", false }
|
||||
};
|
||||
|
||||
return DoIsSubPathTests(Tests);
|
||||
}
|
||||
static_assert(TestIsSubPath());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user