diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp index 8eb601cb0..37f2c5b5d 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp @@ -26,21 +26,25 @@ namespace ams::fs { constexpr inline char Dot = '.'; constexpr inline char NullTerminator = '\x00'; - constexpr inline char UnsupportedDirectorySeparator = '\\'; + constexpr inline char AlternateDirectorySeparator = '\\'; } class PathTool { public: static constexpr const char RootPath[] = "/"; public: - static constexpr inline bool IsUnsupportedSeparator(char c) { - return c == StringTraits::UnsupportedDirectorySeparator; + static constexpr inline bool IsAlternateSeparator(char c) { + return c == StringTraits::AlternateDirectorySeparator; } static constexpr inline bool IsSeparator(char c) { return c == StringTraits::DirectorySeparator; } + static constexpr inline bool IsAnySeparator(char c) { + return IsSeparator(c) || IsAlternateSeparator(c); + } + static constexpr inline bool IsNullTerminator(char c) { return c == StringTraits::NullTerminator; } @@ -61,6 +65,10 @@ namespace ams::fs { return IsWindowsDriveCharacter(p[0]) && IsDriveSeparator(p[1]); } + static constexpr inline bool IsUnc(const char *p) { + return (IsSeparator(p[0]) && IsSeparator(p[1])) || (IsAlternateSeparator(p[0]) && IsAlternateSeparator(p[1])); + } + static constexpr inline bool IsCurrentDirectory(const char *p) { return IsDot(p[0]) && (IsSeparator(p[1]) || IsNullTerminator(p[1])); } diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp index d75fff734..8f94eb750 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp @@ -33,47 +33,36 @@ namespace ams::fs::impl { return nullptr; } - Result GetMountNameImpl(MountName &out, const char *path) { + Result GetMountNameAndSubPath(MountName *out_mount_name, const char **out_sub_path, const char *path) { + /* Handle the Host-path case. */ + if (PathTool::IsWindowsAbsolutePath(path) || PathTool::IsUnc(path)) { + std::strncpy(out_mount_name->str, HostRootFileSystemMountName, MountNameLengthMax); + out_mount_name->str[MountNameLengthMax] = '\x00'; + return ResultSuccess(); + } + + /* Locate the drive separator. */ const char *drive_separator = FindMountNameDriveSeparator(path); R_UNLESS(drive_separator != nullptr, fs::ResultInvalidMountName()); + /* Ensure the mount name isn't too long. */ const size_t len = drive_separator - path; - R_UNLESS(len + 1 <= sizeof(MountName), fs::ResultInvalidMountName()); + R_UNLESS(len <= MountNameLengthMax, fs::ResultInvalidMountName()); - std::memcpy(out.str, path, len); - out.str[len] = StringTraits::NullTerminator; + /* Ensure the result sub-path is valid. */ + const char *sub_path = drive_separator + 1; + R_UNLESS(!PathTool::IsNullTerminator(sub_path[0]), fs::ResultInvalidMountName()); + R_UNLESS(PathTool::IsAnySeparator(sub_path[0]), 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(); } } - MountName GetMountName(const char *path) { - MountName mount_name; - - if (IsWindowsDrive(path)) { - std::strncpy(mount_name.str, HostRootFileSystemMountName, MountNameLengthMax); - mount_name.str[MountNameLengthMax] = StringTraits::NullTerminator; - } else { - R_ABORT_UNLESS(GetMountNameImpl(mount_name, path)); - } - - return mount_name; - } - - const char *GetSubPath(const char *path) { - if (IsWindowsDrive(path)) { - return path; - } - - const char *sub_path = path; - while (!PathTool::IsDriveSeparator(*sub_path)) { - sub_path++; - } - - AMS_ABORT_UNLESS(PathTool::IsSeparator(sub_path[1]) || PathTool::IsUnsupportedSeparator(sub_path[1])); - return sub_path + 1; - } - bool IsValidMountName(const char *name) { if (PathTool::IsNullTerminator(*name)) { return false; @@ -118,12 +107,17 @@ namespace ams::fs::impl { return ResultSuccess(); } - Result FindFileSystem(FileSystemAccessor **out, const char *path) { - R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + Result FindFileSystem(FileSystemAccessor **out_accessor, const char **out_sub_path, const char *path) { + R_UNLESS(out_accessor != nullptr, fs::ResultUnexpectedInFindFileSystemA()); + 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::ResultInvalidPathFormat()); + R_UNLESS(strncmp(path, HostRootFileSystemMountName, strnlen(HostRootFileSystemMountName, sizeof(MountName))) != 0, fs::ResultNotMounted()); - return impl::Find(out, GetMountName(path).str); + MountName mount_name; + R_TRY(GetMountNameAndSubPath(std::addressof(mount_name), out_sub_path, path)); + + return impl::Find(out_accessor, mount_name.str); } } @@ -147,15 +141,21 @@ namespace ams::fs { } Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *src) { - /* Get the mount name for the path. */ - MountName mount_name = impl::GetMountName(src); + /* Ensure neither argument is nullptr. */ + R_UNLESS(dst != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(src != nullptr, fs::ResultNullptrArgument()); + + /* Get the mount name and sub path for the path. */ + MountName mount_name; + const char *sub_path; + R_TRY(impl::GetMountNameAndSubPath(std::addressof(mount_name), std::addressof(sub_path), src)); impl::FileSystemAccessor *accessor; R_TRY(impl::Find(std::addressof(accessor), mount_name.str)); R_TRY(accessor->GetCommonMountName(dst, dst_size)); const auto mount_name_len = strnlen(dst, dst_size); - const auto common_path_len = std::snprintf(dst + mount_name_len, dst_size - mount_name_len, "%s", impl::GetSubPath(src)); + const auto common_path_len = std::snprintf(dst + mount_name_len, dst_size - mount_name_len, "%s", sub_path); R_UNLESS(static_cast(common_path_len) < dst_size - mount_name_len, fs::ResultTooLongPath()); return ResultSuccess(); diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp index b4c191aa2..1b174f5c0 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.hpp @@ -20,9 +20,8 @@ namespace ams::fs::impl { class FileSystemAccessor; - Result FindFileSystem(FileSystemAccessor **out, const char *path); + Result FindFileSystem(FileSystemAccessor **out_accessor, const char **out_sub_path, const char *path); - const char *GetSubPath(const char *path); bool IsWindowsDrive(const char *name); bool IsReservedMountName(const char *name); Result CheckMountName(const char *name); diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp index 3e6165987..3294c16d8 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp @@ -34,74 +34,85 @@ namespace ams::fs { Result CreateFile(const char* path, s64 size, int option) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->CreateFile(impl::GetSubPath(path), size, option); + return accessor->CreateFile(sub_path, size, option); } Result DeleteFile(const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->DeleteFile(impl::GetSubPath(path)); + return accessor->DeleteFile(sub_path); } Result CreateDirectory(const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->CreateDirectory(impl::GetSubPath(path)); + return accessor->CreateDirectory(sub_path); } Result DeleteDirectory(const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->DeleteDirectory(impl::GetSubPath(path)); + return accessor->DeleteDirectory(sub_path); } Result DeleteDirectoryRecursively(const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->DeleteDirectoryRecursively(impl::GetSubPath(path)); + return accessor->DeleteDirectoryRecursively(sub_path); } Result RenameFile(const char *old_path, const char *new_path) { impl::FileSystemAccessor *old_accessor; impl::FileSystemAccessor *new_accessor; - R_TRY(impl::FindFileSystem(std::addressof(old_accessor), old_path)); - R_TRY(impl::FindFileSystem(std::addressof(new_accessor), new_path)); + const char *old_sub_path; + const char *new_sub_path; + R_TRY(impl::FindFileSystem(std::addressof(old_accessor), std::addressof(old_sub_path), old_path)); + R_TRY(impl::FindFileSystem(std::addressof(new_accessor), std::addressof(new_sub_path), new_path)); R_UNLESS(old_accessor == new_accessor, fs::ResultRenameToOtherFileSystem()); - return old_accessor->RenameFile(impl::GetSubPath(old_path), impl::GetSubPath(new_path)); + return old_accessor->RenameFile(old_sub_path, new_sub_path); } Result RenameDirectory(const char *old_path, const char *new_path) { impl::FileSystemAccessor *old_accessor; impl::FileSystemAccessor *new_accessor; - R_TRY(impl::FindFileSystem(std::addressof(old_accessor), old_path)); - R_TRY(impl::FindFileSystem(std::addressof(new_accessor), new_path)); + const char *old_sub_path; + const char *new_sub_path; + R_TRY(impl::FindFileSystem(std::addressof(old_accessor), std::addressof(old_sub_path), old_path)); + R_TRY(impl::FindFileSystem(std::addressof(new_accessor), std::addressof(new_sub_path), new_path)); R_UNLESS(old_accessor == new_accessor, fs::ResultRenameToOtherFileSystem()); - return old_accessor->RenameDirectory(impl::GetSubPath(old_path), impl::GetSubPath(new_path)); + return old_accessor->RenameDirectory(old_sub_path, new_sub_path); } Result GetEntryType(DirectoryEntryType *out, const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->GetEntryType(out, impl::GetSubPath(path)); + return accessor->GetEntryType(out, sub_path); } Result OpenFile(FileHandle *out_file, const char *path, int mode) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); R_UNLESS(out_file != nullptr, fs::ResultNullptrArgument()); std::unique_ptr file_accessor; - R_TRY(accessor->OpenFile(std::addressof(file_accessor), impl::GetSubPath(path), static_cast(mode))); + R_TRY(accessor->OpenFile(std::addressof(file_accessor), sub_path, static_cast(mode))); out_file->handle = file_accessor.release(); return ResultSuccess(); @@ -109,12 +120,13 @@ namespace ams::fs { Result OpenDirectory(DirectoryHandle *out_dir, const char *path, int mode) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument()); std::unique_ptr dir_accessor; - R_TRY(accessor->OpenDirectory(std::addressof(dir_accessor), impl::GetSubPath(path), static_cast(mode))); + R_TRY(accessor->OpenDirectory(std::addressof(dir_accessor), sub_path, static_cast(mode))); out_dir->handle = dir_accessor.release(); return ResultSuccess(); @@ -122,38 +134,43 @@ namespace ams::fs { Result CleanDirectoryRecursively(const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->CleanDirectoryRecursively(impl::GetSubPath(path)); + return accessor->CleanDirectoryRecursively(sub_path); } Result GetFreeSpaceSize(s64 *out, const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->GetFreeSpaceSize(out, impl::GetSubPath(path)); + return accessor->GetFreeSpaceSize(out, sub_path); } Result GetTotalSpaceSize(s64 *out, const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->GetTotalSpaceSize(out, impl::GetSubPath(path)); + return accessor->GetTotalSpaceSize(out, sub_path); } Result SetConcatenationFileAttribute(const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->QueryEntry(nullptr, 0, nullptr, 0, fsa::QueryId::SetConcatenationFileAttribute, impl::GetSubPath(path)); + return accessor->QueryEntry(nullptr, 0, nullptr, 0, fsa::QueryId::SetConcatenationFileAttribute, sub_path); } Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) { impl::FileSystemAccessor *accessor; - R_TRY(impl::FindFileSystem(std::addressof(accessor), path)); + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); - return accessor->GetFileTimeStampRaw(out, impl::GetSubPath(path)); + return accessor->GetFileTimeStampRaw(out, sub_path); } Result OpenFile(FileHandle *out, std::unique_ptr &&file, int mode) { diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index eaa659a44..c94ceab7f 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -198,6 +198,7 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(GameCardLogoDataCorrupted, 4781); R_DEFINE_ERROR_RANGE(Unexpected, 5000, 5999); + R_DEFINE_ERROR_RESULT(UnexpectedInFindFileSystemA, 5319); R_DEFINE_ERROR_RANGE(PreconditionViolation, 6000, 6499); R_DEFINE_ERROR_RANGE(InvalidArgument, 6001, 6199);