[Filesystem] Added utility functions to be used in filesystem module. 13/176313/13
authorArkadiusz Pietraszek <a.pietraszek@partner.samsung.com>
Wed, 18 Apr 2018 09:10:04 +0000 (11:10 +0200)
committerSzymon Jastrzebski <s.jastrzebsk@partner.samsung.com>
Fri, 11 May 2018 07:41:35 +0000 (09:41 +0200)
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 <s.jastrzebsk@partner.samsung.com>
Signed-off-by: Arkadiusz Pietraszek <a.pietraszek@partner.samsung.com>
Signed-off-by: Jakub Skowron <j.skowron@samsung.com>
Signed-off-by: Pawel Wasowski <p.wasowski2@partner.samsung.com>
src/filesystem/filesystem_manager.cc
src/filesystem/filesystem_utils.cc
src/filesystem/filesystem_utils.h

index 755defb57cb4e7707165219e5a6d7f5cff4e0b02..a95f5b171efb2997794d1d736c9d3e41b77ad811 100644 (file)
@@ -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) {
index c6d04e5ae63ae71b9301d157027bf38d2ca70d4a..3ae857471f91c94c7de07ed1598b766e7083413c 100644 (file)
  *    limitations under the License.
  */
 #include "filesystem_utils.h"
+#include "common/logger.h"
+#include "common/platform_exception.h"
 
+#include <dirent.h>
+#include <fcntl.h>
+#include <ftw.h>
 #include <glib.h>
 #include <libgen.h>
-#include "common/logger.h"
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 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<std::string> 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<GFile, decltype(&g_object_unref)>(
+      g_file_new_for_path(src.c_str()), g_object_unref);
+  auto dest_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>(
+      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<GFileCopyFlags>(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<void(const char*, unsigned char)> 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, void (*)(DIR*)> 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<GFile, decltype(&g_object_unref)>(
+      g_file_new_for_path(path.c_str()), g_object_unref);
+  auto dest_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>(
+      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<GFileCopyFlags>(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<char*>(buf.c_str())));
-}
 }
index f59048e9cf0bd26b77d5a586e8a7a851b6b87a93..58882847d498760ba6dc38f76541f8586f65ab02 100644 (file)
 #ifndef FILESYSTEM_FILESYSTEM_UTILS_H
 #define FILESYSTEM_FILESYSTEM_UTILS_H
 
-#include <storage-expand.h>
-#include <string>
 #include "common/picojson.h"
+#include "common/tools.h"
+
+#include <gio/gio.h>
+#include <glib-object.h>
+#include <sys/stat.h>
+#include <functional>
+#include <memory>
+#include <string>
+#include <system_error>
 
 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<void(const char*, unsigned char)> 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