From 61143b4d7d06bdd2f22824b2c9a67359427fed8f Mon Sep 17 00:00:00 2001 From: Arkadiusz Pietraszek Date: Wed, 18 Apr 2018 11:10:04 +0200 Subject: [PATCH] [Filesystem] Added utility functions to be used in filesystem module. Functions added: Mkdir Unlink PosixBasename Dirname CopyFile CopyDirectory ListDirectory RemoveDirectoryRecursively RemoveDirectory RealPath CheckIfExists CheckIfDir CheckIfFile Rename MoveFile MoveDirectory TranslateException ACR: http://suprem.sec.samsung.net/jira/browse/TWDAPI-121 Change-Id: I7ebaa5fbacc31c941f46fd9372a827c15b69a632 Signed-off-by: Szymon Jastrzebski Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Jakub Skowron Signed-off-by: Pawel Wasowski --- src/filesystem/filesystem_manager.cc | 2 +- src/filesystem/filesystem_utils.cc | 342 +++++++++++++++++++++++++-- src/filesystem/filesystem_utils.h | 120 +++++++++- 3 files changed, 436 insertions(+), 28 deletions(-) diff --git a/src/filesystem/filesystem_manager.cc b/src/filesystem/filesystem_manager.cc index 755defb5..a95f5b17 100644 --- a/src/filesystem/filesystem_manager.cc +++ b/src/filesystem/filesystem_manager.cc @@ -196,7 +196,7 @@ FilesystemError make_directory_worker(const std::string& path) { } } - std::string parent_path = FilesystemUtils::get_dirname(path); + std::string parent_path = FilesystemUtils::GetDirname(path); auto parent_result = make_directory_worker(parent_path); if (parent_result == FilesystemError::DirectoryExists) { diff --git a/src/filesystem/filesystem_utils.cc b/src/filesystem/filesystem_utils.cc index c6d04e5a..3ae85747 100644 --- a/src/filesystem/filesystem_utils.cc +++ b/src/filesystem/filesystem_utils.cc @@ -14,27 +14,338 @@ * limitations under the License. */ #include "filesystem_utils.h" +#include "common/logger.h" +#include "common/platform_exception.h" +#include +#include +#include #include #include -#include "common/logger.h" +#include +#include +#include +#include namespace FilesystemUtils { -std::string get_storage_dir_path(int id, storage_directory_e typeToCheck) { +using namespace std::string_literals; +using namespace common; +using common::tools::ReportError; + +void Mkdir(const std::string& path) { + ScopeLogger("%s", path.c_str()); + int ret = ::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); + + if (0 != ret) { + throw std::system_error{errno, std::generic_category()}; + } +} + +void Mkdir(const std::string& path, bool parents) { + // ScopeLogger("%s, %d", path.c_str(), parents); // disabled in recursive function + if (!parents) { + Mkdir(path); + return; + } + + struct ::stat buf; + std::vector stack; + // iterate path up to first existing element + for (std::string s = path; 0 != ::stat(s.c_str(), &buf); s = Dirname(s)) { + if (ENOENT == errno) { + stack.push_back(s); + } else { + throw std::system_error{errno, std::generic_category()}; + } + } + + if (stack.empty()) { // this means, that path exists, let Mkdir handle error + Mkdir(path); + return; + } + + // from top to bottom + for (auto rit = stack.rbegin(); rit != stack.rend(); ++rit) { + try { + Mkdir(*rit); + } catch (const std::system_error& error) { + if (stack.rbegin() != rit) { + try { + RemoveDirectoryRecursively(*stack.rbegin()); + } catch (const std::system_error& removalError) { + LoggerW( + "Could not remove parent directories created so far: %s." + "Some of them might still exist", + removalError.what()); + } + } + throw; + } + } +} + +void Unlink(const std::string& path) { + ScopeLogger(); + int ret = ::unlink(path.c_str()); + if (0 != ret) { + throw std::system_error{errno, std::generic_category()}; + } +} + +std::string PosixBasename(const std::string& path) { + ScopeLogger(); + char* s = ::strdup(path.c_str()); + if (!s) { + throw std::system_error{errno, std::generic_category(), path}; + } + + // basename will never fail + std::string name{::basename(s)}; + free(s); + return name; +} + +std::string Dirname(const std::string& path) { + ScopeLogger(); + char* s = ::strdup(path.c_str()); + if (!s) { + throw std::system_error{errno, std::generic_category(), path}; + } + // dirname will never fail + std::string dir{::dirname(s)}; + free(s); + return dir; +} + +void CopyFile(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("From: %s; To %s", src.c_str(), dest.c_str()); + struct stat buf {}; + if (CheckIfExists(dest, &buf) && CheckIfDir(buf)) { + if (overwrite) { + RemoveDirectoryRecursively(dest); + } else { + throw std::system_error{EIO, std::generic_category(), + "Failed to copy file: overwrite is not allowed."}; + } + } + + GError* error = nullptr; + auto source_ptr = std::unique_ptr( + g_file_new_for_path(src.c_str()), g_object_unref); + auto dest_ptr = std::unique_ptr( + g_file_new_for_path(dest.c_str()), g_object_unref); + int flags = G_FILE_COPY_ALL_METADATA; + if (overwrite) { + flags |= G_FILE_COPY_OVERWRITE; + } + + gboolean success = + g_file_copy(source_ptr.get(), dest_ptr.get(), static_cast(flags), nullptr, + nullptr, nullptr, &error); + if (!success) { + std::string why{}; + if (error) { + why = error->message; + g_error_free(error); + } + throw std::system_error{EIO, std::generic_category(), "Failed to copy file: "s + why}; + } +} + +void CopyDirectory(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("From: %s; To %s", src.c_str(), dest.c_str()); + std::string dest_dir = dest + '/' + PosixBasename(src); + struct stat buf {}; + bool exists = CheckIfExists(dest_dir, &buf); + if (exists && !CheckIfDir(buf)) { + if (overwrite) { + Unlink(dest_dir); + Mkdir(dest_dir); + } else { + throw std::system_error{EIO, std::generic_category(), + "Failed to copy directory: overwrite is not allowed."}; + } + } else if (!exists) { + Mkdir(dest_dir); + } + + ListDirectory(src, [&](const char* name, unsigned char type) { + if (DT_DIR == type) { + CopyDirectory(src + '/' + name, dest_dir, overwrite); + } else { // copying of regular files as well as other types of items pointed by src + CopyFile(src + '/' + name, dest_dir + '/' + name, overwrite); + } + // Set errno to 0 to prevent from reporting readdir error after successful iterating through + // directory. + errno = 0; + }); +} + +void ListDirectory(const std::string& path, std::function next) { + ScopeLogger("%s", path.c_str()); + DIR* d = ::opendir(path.c_str()); + if (nullptr == d) { + throw std::system_error{errno, std::generic_category(), + "Failed to open directory: "s + std::strerror(errno)}; + } + + std::unique_ptr dir_ptr(d, [](DIR * d) { + if (::closedir(d)) { + LoggerW("closedir failed"); + } + }); + errno = 0; + + for (dirent* entry; (entry = ::readdir(d));) { + if (0 == std::strcmp(entry->d_name, ".") || 0 == std::strcmp(entry->d_name, "..")) { + continue; + } + next(entry->d_name, entry->d_type); + } + + if (0 != errno) { + throw std::system_error{errno, std::generic_category(), + "Failed to read directory: "s + std::strerror(errno)}; + } +} + +void RemoveDirectoryRecursively(const std::string& path) { + ScopeLogger("%s", path); + auto res = + nftw(path.c_str(), + [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int { + // if number of nested directories is large + // below log could decrease readability + // ScopeLogger("%s", fpath); + + auto res = remove(fpath); + if (res) { + LoggerD("Failed to remove %s: %s", fpath, std::strerror(errno)); + return errno; + } + return 0; + }, + 128, FTW_DEPTH | FTW_PHYS); + + if (res) { + throw std::system_error{res, std::generic_category(), + "Failed to remove directory recursively: "s + std::strerror(res)}; + } +} + +void RemoveDirectory(const std::string& path) { + ScopeLogger(); + if (rmdir(path.c_str())) { + throw std::system_error{errno, std::generic_category(), "Failed to remove directory"}; + } +} + +std::string RealPath(const std::string& path) { ScopeLogger(); - char* platformPath = NULL; - int result = storage_get_directory(id, typeToCheck, &platformPath); - if (STORAGE_ERROR_NONE != result) { - LoggerD("Cannot retrieve path for type %i: %d (%s)", typeToCheck, result, - get_error_message(result)); - return std::string(); + char* real_path = realpath(path.c_str(), nullptr); + if (nullptr == real_path) { + throw std::system_error{errno, std::generic_category(), "Path is not valid."}; } - std::string path = std::string(platformPath); - free(platformPath); - return path; + std::string s{real_path}; + free(real_path); + return s; } -std::string get_dirname(const std::string& path) { +bool CheckIfExists(const std::string& path, struct stat* buf) { + ScopeLogger(); + if (stat(path.c_str(), buf)) { + if (ENOENT == errno) { + return false; + } else { + throw std::system_error{errno, std::generic_category(), + "Unable to check file existence: "s + std::strerror(errno)}; + } + } + return true; +} + +bool CheckIfDir(const struct stat& buf) { + ScopeLogger(); + if (S_ISDIR(buf.st_mode)) { + return true; + } + return false; +} + +bool CheckIfFile(const struct stat& buf) { + ScopeLogger(); + if (S_ISREG(buf.st_mode)) { + return true; + } + return false; +} + +void Rename(const std::string& path, const std::string& new_path) { + ScopeLogger(); + if (::rename(path.c_str(), new_path.c_str())) { + throw std::system_error{errno, std::generic_category(), + "Unable to rename file or directory: "s + std::strerror(errno)}; + } +} + +void MoveFile(const std::string& path, const std::string& new_path, bool overwrite) { + ScopeLogger(); + GError* error = nullptr; + auto source_ptr = std::unique_ptr( + g_file_new_for_path(path.c_str()), g_object_unref); + auto dest_ptr = std::unique_ptr( + g_file_new_for_path(new_path.c_str()), g_object_unref); + int flags = G_FILE_COPY_ALL_METADATA; + if (overwrite) { + flags |= G_FILE_COPY_OVERWRITE; + } + gboolean success = + g_file_move(source_ptr.get(), dest_ptr.get(), static_cast(flags), nullptr, + nullptr, nullptr, &error); + if (!success) { + std::string why{}; + if (error) { + why = error->message; + g_error_free(error); + } + throw std::system_error{EIO, std::generic_category(), "Failed to move file: "s + why}; + } +} + +void MoveDirectory(const std::string& src, const std::string& dest, bool overwrite) { + ScopeLogger("%s %s", src.c_str(), dest.c_str()); + struct stat buf {}; + const std::string& new_path = dest + '/' + PosixBasename(src); + // If directories are on the same mount point, we can simply try to rename them. + // However, it might be done only if new_path does not exist because move_directory should merge + // directories. + if (!CheckIfExists(new_path, &buf)) { + LoggerD("new_path %s", new_path.c_str()); + auto result = ::rename(src.c_str(), new_path.c_str()); + if (!result) { + return; + } else if (EXDEV != errno) { + // The directories are in the same mount point, but the operation has just failed. + throw std::system_error{EIO, std::generic_category(), + "Unable to move directory: "s + std::strerror(errno)}; + } + } + + // Move directory to other move point. + CopyDirectory(src, dest, overwrite); + RemoveDirectoryRecursively(src); +} + +void TranslateException(const std::system_error& e, picojson::object& obj) { + ScopeLogger(); + if (std::errc::no_such_file_or_directory == e.code()) { + LogAndReportError(NotFoundException(e.what()), obj); + } else { + LogAndReportError(IOException(e.what()), obj); + } +} + +std::string GetDirname(const std::string& path) { ScopeLogger(); char* dir = g_path_get_dirname(path.c_str()); if (dir) { @@ -45,11 +356,4 @@ std::string get_dirname(const std::string& path) { return std::string("."); } } - -std::string get_basename(const std::string& path) { - ScopeLogger(); - // basename will modify content: pass a copy - std::string buf = path.c_str(); - return std::string(basename(const_cast(buf.c_str()))); -} } diff --git a/src/filesystem/filesystem_utils.h b/src/filesystem/filesystem_utils.h index f59048e9..58882847 100644 --- a/src/filesystem/filesystem_utils.h +++ b/src/filesystem/filesystem_utils.h @@ -17,9 +17,16 @@ #ifndef FILESYSTEM_FILESYSTEM_UTILS_H #define FILESYSTEM_FILESYSTEM_UTILS_H -#include -#include #include "common/picojson.h" +#include "common/tools.h" + +#include +#include +#include +#include +#include +#include +#include namespace extension { namespace filesystem { @@ -40,14 +47,111 @@ enum class FilesystemError { namespace FilesystemUtils { /** - * @brief get_storage_dir_path attempts to get path from storage. - * If path cannot be retrieved then an empty string is returned. - * + * @brief Wrapper for POSIX mkdir function. + * @throw std::system_error + */ +void Mkdir(const std::string& path); + +/** + * @brief Make directory using mkdir. If 'parents' is true, make parent directories as needed + * @throw std::system_error */ -std::string get_storage_dir_path(int id, storage_directory_e typeToCheck); +void Mkdir(const std::string& path, bool parents); + +/** + * @brief Wrapper for POSIX unlink function + * @throw std::system_error + */ +void Unlink(const std::string& path); + +/** + * @brief Returns last element of path (wrapper for POSIX basename function) + * @throw std::system_error + */ +std::string PosixBasename(const std::string& path); + +/** + * @brief Returns parent directory of path (wrapper for POSIX dirname function) + * @throw std::system_error + */ +std::string Dirname(const std::string& path); + +/** + * @brief Wrapper for GLib g_file_copy function. + * @throw std::system_error + */ +void CopyFile(const std::string& src, const std::string& dest, bool overwrite); + +/** + * @brief Copies directory recursively + * @throw std::system_error + */ +void CopyDirectory(const std::string& src, const std::string& dest, bool overwrite); + +/** + * @brief Calls 'next' function with name for every entry in given directory + * @throw std::system_error + */ +void ListDirectory(const std::string& path, std::function next); + +/** + * @brief Removes directory recursively pointed by path. + * @throw std::system_error + */ +void RemoveDirectoryRecursively(const std::string& path); + +/** + * @brief Removes directory pointed by path. + * @throw std::system_error + */ +void RemoveDirectory(const std::string& path); + +/** + * @brief Returns the real path. + * @throw std::system_error + */ +std::string RealPath(const std::string& path); + +/** + * @brief Checks if path points to file or directory. + * @throw std::system_error + */ +bool CheckIfExists(const std::string& path, struct stat* buf); + +/** + * @brief Checks if path points to directory. + * @throw std::system_error + */ +bool CheckIfDir(const struct stat& buf); + +/** + * @brief Checks if path points to file. + * @throw std::system_error + */ +bool CheckIfFile(const struct stat& buf); + +/** + * @brief Renames file or directory. + * @throw std::system_error + */ +void Rename(const std::string& path, const std::string& new_path); + +/** + * @brief Wrapper for GLib g_file_move function. + * @throw std::system_error + */ +void MoveFile(const std::string& path, const std::string& new_path, bool overwrite); + +/** + * @brief Moves directory by recursively calling move_file. + * @throw std::system_error + */ +void MoveDirectory(const std::string& path, const std::string& new_path, bool overwrite); + +void TranslateException(const std::system_error& e, picojson::object& obj); -std::string get_dirname(const std::string& path); -std::string get_basename(const std::string& path); +// This function is left only for compatibility with previous implementation in FilesystemManager +std::string GetDirname(const std::string& path); } #endif // FILESYSTEM_FILESYSTEM_UTILS_H -- 2.34.1