diff options
Diffstat (limited to 'src/common/fs/fs.cpp')
-rw-r--r-- | src/common/fs/fs.cpp | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp new file mode 100644 index 000000000..d492480d9 --- /dev/null +++ b/src/common/fs/fs.cpp @@ -0,0 +1,610 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" + +namespace Common::FS { + +namespace fs = std::filesystem; + +// File Operations + +bool NewFile(const fs::path& path, u64 size) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return false; + } + + if (!Exists(path.parent_path())) { + LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist", + PathToUTF8String(path)); + return false; + } + + if (Exists(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} exists", PathToUTF8String(path)); + return false; + } + + IOFile io_file{path, FileAccessMode::Write}; + + if (!io_file.IsOpen()) { + LOG_ERROR(Common_Filesystem, "Failed to create a file at path={}", PathToUTF8String(path)); + return false; + } + + if (!io_file.SetSize(size)) { + LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={} to size={}", + PathToUTF8String(path), size); + return false; + } + + io_file.Close(); + + LOG_DEBUG(Common_Filesystem, "Successfully created a file at path={} with size={}", + PathToUTF8String(path), size); + + return true; +} + +bool RemoveFile(const fs::path& path) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return false; + } + + if (!Exists(path)) { + LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", + PathToUTF8String(path)); + return true; + } + + if (!IsFile(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file", + PathToUTF8String(path)); + return false; + } + + std::error_code ec; + + fs::remove(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to remove the file at path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, "Successfully removed the file at path={}", + PathToUTF8String(path)); + + return true; +} + +bool RenameFile(const fs::path& old_path, const fs::path& new_path) { + if (!ValidatePath(old_path) || !ValidatePath(new_path)) { + LOG_ERROR(Common_Filesystem, + "One or both input path(s) is not valid, old_path={}, new_path={}", + PathToUTF8String(old_path), PathToUTF8String(new_path)); + return false; + } + + if (!Exists(old_path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist", + PathToUTF8String(old_path)); + return false; + } + + if (!IsFile(old_path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a file", + PathToUTF8String(old_path)); + return false; + } + + if (Exists(new_path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists", + PathToUTF8String(new_path)); + return false; + } + + std::error_code ec; + + fs::rename(old_path, new_path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to rename the file from old_path={} to new_path={}, ec_message={}", + PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}", + PathToUTF8String(old_path), PathToUTF8String(new_path)); + + return true; +} + +std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, FileType type, + FileShareFlag flag) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return nullptr; + } + + if (!IsFile(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file", + PathToUTF8String(path)); + return nullptr; + } + + auto io_file = std::make_shared<IOFile>(path, mode, type, flag); + + if (!io_file->IsOpen()) { + io_file.reset(); + + LOG_ERROR(Common_Filesystem, + "Failed to open the file at path={} with mode={}, type={}, flag={}", + PathToUTF8String(path), mode, type, flag); + + return nullptr; + } + + LOG_DEBUG(Common_Filesystem, + "Successfully opened the file at path={} with mode={}, type={}, flag={}", + PathToUTF8String(path), mode, type, flag); + + return io_file; +} + +// Directory Operations + +bool CreateDir(const fs::path& path) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return false; + } + + if (!Exists(path.parent_path())) { + LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist", + PathToUTF8String(path)); + return false; + } + + if (IsDir(path)) { + LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory", + PathToUTF8String(path)); + return true; + } + + std::error_code ec; + + fs::create_directory(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to create the directory at path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, "Successfully created the directory at path={}", + PathToUTF8String(path)); + + return true; +} + +bool CreateDirs(const fs::path& path) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return false; + } + + if (IsDir(path)) { + LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory", + PathToUTF8String(path)); + return true; + } + + std::error_code ec; + + fs::create_directories(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to create the directories at path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, "Successfully created the directories at path={}", + PathToUTF8String(path)); + + return true; +} + +bool CreateParentDir(const fs::path& path) { + return CreateDir(path.parent_path()); +} + +bool CreateParentDirs(const fs::path& path) { + return CreateDirs(path.parent_path()); +} + +bool RemoveDir(const fs::path& path) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return false; + } + + if (!Exists(path)) { + LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", + PathToUTF8String(path)); + return true; + } + + if (!IsDir(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", + PathToUTF8String(path)); + return false; + } + + std::error_code ec; + + fs::remove(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to remove the directory at path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, "Successfully removed the directory at path={}", + PathToUTF8String(path)); + + return true; +} + +bool RemoveDirRecursively(const fs::path& path) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return false; + } + + if (!Exists(path)) { + LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", + PathToUTF8String(path)); + return true; + } + + if (!IsDir(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", + PathToUTF8String(path)); + return false; + } + + std::error_code ec; + + fs::remove_all(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to remove the directory and its contents at path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, "Successfully removed the directory and its contents at path={}", + PathToUTF8String(path)); + + return true; +} + +bool RemoveDirContentsRecursively(const fs::path& path) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return false; + } + + if (!Exists(path)) { + LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", + PathToUTF8String(path)); + return true; + } + + if (!IsDir(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", + PathToUTF8String(path)); + return false; + } + + std::error_code ec; + + for (const auto& entry : fs::recursive_directory_iterator(path, ec)) { + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to completely enumerate the directory at path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + break; + } + + fs::remove(entry.path(), ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to remove the filesystem object at path={}, ec_message={}", + PathToUTF8String(entry.path()), ec.message()); + break; + } + } + + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to remove all the contents of the directory at path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, + "Successfully removed all the contents of the directory at path={}", + PathToUTF8String(path)); + + return true; +} + +bool RenameDir(const fs::path& old_path, const fs::path& new_path) { + if (!ValidatePath(old_path) || !ValidatePath(new_path)) { + LOG_ERROR(Common_Filesystem, + "One or both input path(s) is not valid, old_path={}, new_path={}", + PathToUTF8String(old_path), PathToUTF8String(new_path)); + return false; + } + + if (!Exists(old_path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist", + PathToUTF8String(old_path)); + return false; + } + + if (!IsDir(old_path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a directory", + PathToUTF8String(old_path)); + return false; + } + + if (Exists(new_path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists", + PathToUTF8String(new_path)); + return false; + } + + std::error_code ec; + + fs::rename(old_path, new_path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to rename the file from old_path={} to new_path={}, ec_message={}", + PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message()); + return false; + } + + LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}", + PathToUTF8String(old_path), PathToUTF8String(new_path)); + + return true; +} + +void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback, + DirEntryFilter filter) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return; + } + + if (!Exists(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", + PathToUTF8String(path)); + return; + } + + if (!IsDir(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", + PathToUTF8String(path)); + return; + } + + bool callback_error = false; + + std::error_code ec; + + for (const auto& entry : fs::directory_iterator(path, ec)) { + if (ec) { + break; + } + + if (True(filter & DirEntryFilter::File) && + entry.status().type() == fs::file_type::regular) { + if (!callback(entry.path())) { + callback_error = true; + break; + } + } + + if (True(filter & DirEntryFilter::Directory) && + entry.status().type() == fs::file_type::directory) { + if (!callback(entry.path())) { + callback_error = true; + break; + } + } + } + + if (callback_error || ec) { + LOG_ERROR(Common_Filesystem, + "Failed to visit all the directory entries of path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return; + } + + LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}", + PathToUTF8String(path)); +} + +void IterateDirEntriesRecursively(const std::filesystem::path& path, + const DirEntryCallable& callback, DirEntryFilter filter) { + if (!ValidatePath(path)) { + LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); + return; + } + + if (!Exists(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", + PathToUTF8String(path)); + return; + } + + if (!IsDir(path)) { + LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", + PathToUTF8String(path)); + return; + } + + bool callback_error = false; + + std::error_code ec; + + for (const auto& entry : fs::recursive_directory_iterator(path, ec)) { + if (ec) { + break; + } + + if (True(filter & DirEntryFilter::File) && + entry.status().type() == fs::file_type::regular) { + if (!callback(entry.path())) { + callback_error = true; + break; + } + } + + if (True(filter & DirEntryFilter::Directory) && + entry.status().type() == fs::file_type::directory) { + if (!callback(entry.path())) { + callback_error = true; + break; + } + } + } + + if (callback_error || ec) { + LOG_ERROR(Common_Filesystem, + "Failed to visit all the directory entries of path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return; + } + + LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}", + PathToUTF8String(path)); +} + +// Generic Filesystem Operations + +bool Exists(const fs::path& path) { + return fs::exists(path); +} + +bool IsFile(const fs::path& path) { + return fs::is_regular_file(path); +} + +bool IsDir(const fs::path& path) { + return fs::is_directory(path); +} + +fs::path GetCurrentDir() { + std::error_code ec; + + const auto current_path = fs::current_path(ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to get the current path, ec_message={}", ec.message()); + return {}; + } + + return current_path; +} + +bool SetCurrentDir(const fs::path& path) { + std::error_code ec; + + fs::current_path(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to set the current path to path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return false; + } + + return true; +} + +fs::file_type GetEntryType(const fs::path& path) { + std::error_code ec; + + const auto file_status = fs::status(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to retrieve the entry type of path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return fs::file_type::not_found; + } + + return file_status.type(); +} + +u64 GetSize(const fs::path& path) { + std::error_code ec; + + const auto file_size = fs::file_size(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return 0; + } + + return file_size; +} + +u64 GetFreeSpaceSize(const fs::path& path) { + std::error_code ec; + + const auto space_info = fs::space(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to retrieve the available free space of path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return 0; + } + + return space_info.free; +} + +u64 GetTotalSpaceSize(const fs::path& path) { + std::error_code ec; + + const auto space_info = fs::space(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, + "Failed to retrieve the total capacity of path={}, ec_message={}", + PathToUTF8String(path), ec.message()); + return 0; + } + + return space_info.capacity; +} + +} // namespace Common::FS |