| |
| |
|
|
| #include "common/fs/file.h" |
| #include "common/fs/fs.h" |
| #ifdef ANDROID |
| #include "common/fs/fs_android.h" |
| #endif |
| #include "common/fs/path_util.h" |
| #include "common/logging/log.h" |
|
|
| namespace Common::FS { |
|
|
| namespace fs = std::filesystem; |
|
|
| |
|
|
| 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 (Exists(path) && !IsFile(path)) { |
| LOG_ERROR(Common_Filesystem, |
| "Filesystem object at path={} exists and is not a regular 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; |
| } |
|
|
| |
|
|
| 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::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 (entry.status().type() == fs::file_type::directory) { |
| return RemoveDirContentsRecursively(entry.path()); |
| } |
| } |
|
|
| 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)) { |
| callback_error = true; |
| break; |
| } |
| } |
|
|
| if (True(filter & DirEntryFilter::Directory) && |
| entry.status().type() == fs::file_type::directory) { |
| if (!callback(entry)) { |
| 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::directory_iterator(path, ec)) { |
| if (ec) { |
| break; |
| } |
|
|
| if (True(filter & DirEntryFilter::File) && |
| entry.status().type() == fs::file_type::regular) { |
| if (!callback(entry)) { |
| callback_error = true; |
| break; |
| } |
| } |
|
|
| if (True(filter & DirEntryFilter::Directory) && |
| entry.status().type() == fs::file_type::directory) { |
| if (!callback(entry)) { |
| callback_error = true; |
| break; |
| } |
| } |
|
|
| |
| |
| if (entry.status().type() == fs::file_type::directory) { |
| IterateDirEntriesRecursively(entry.path(), callback, filter); |
| } |
| } |
|
|
| 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)); |
| } |
|
|
| |
|
|
| bool Exists(const fs::path& path) { |
| std::error_code ec; |
| #ifdef ANDROID |
| if (Android::IsContentUri(path)) { |
| return Android::Exists(path); |
| } else { |
| return fs::exists(path, ec); |
| } |
| #else |
| return fs::exists(path, ec); |
| #endif |
| } |
|
|
| bool IsFile(const fs::path& path) { |
| std::error_code ec; |
| #ifdef ANDROID |
| if (Android::IsContentUri(path)) { |
| return !Android::IsDirectory(path); |
| } else { |
| return fs::is_regular_file(path, ec); |
| } |
| #else |
| return fs::is_regular_file(path, ec); |
| #endif |
| } |
|
|
| bool IsDir(const fs::path& path) { |
| std::error_code ec; |
| #ifdef ANDROID |
| if (Android::IsContentUri(path)) { |
| return Android::IsDirectory(path); |
| } else { |
| return fs::is_directory(path, ec); |
| } |
| #else |
| return fs::is_directory(path, ec); |
| #endif |
| } |
|
|
| 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) { |
| #ifdef ANDROID |
| if (Android::IsContentUri(path)) { |
| return Android::GetSize(path); |
| } |
| #endif |
|
|
| 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; |
| } |
|
|
| } |
|
|