2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #include "filesystem_utils.h"
17 #include "common/logger.h"
18 #include "common/platform_exception.h"
25 #include <sys/sendfile.h>
27 #include <sys/types.h>
30 namespace FilesystemUtils {
31 using namespace std::string_literals;
32 using namespace common;
33 using common::tools::ReportError;
34 using common::tools::GetErrorString;
36 void Mkdir(const std::string& path) {
37 ScopeLogger("%s", path.c_str());
38 int ret = ::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
41 throw std::system_error{errno, std::generic_category()};
45 void Mkdir(const std::string& path, bool parents) {
46 // ScopeLogger("%s, %d", path.c_str(), parents); // disabled in recursive function
53 std::vector<std::string> stack;
54 // iterate path up to first existing element
55 for (std::string s = path; 0 != ::stat(s.c_str(), &buf); s = Dirname(s)) {
56 if (ENOENT == errno) {
59 throw std::system_error{errno, std::generic_category()};
63 if (stack.empty()) { // this means, that path exists, let Mkdir handle error
69 for (auto rit = stack.rbegin(); rit != stack.rend(); ++rit) {
72 } catch (const std::system_error& error) {
73 if (stack.rbegin() != rit) {
75 RemoveDirectoryRecursively(*stack.rbegin());
76 } catch (const std::system_error& removalError) {
78 "Could not remove parent directories created so far: %s."
79 "Some of them might still exist",
88 void Unlink(const std::string& path) {
90 int ret = ::unlink(path.c_str());
92 throw std::system_error{errno, std::generic_category()};
96 std::string PosixBasename(const std::string& path) {
98 char* s = ::strdup(path.c_str());
100 throw std::system_error{errno, std::generic_category(), path};
103 // basename will never fail
104 std::string name{::basename(s)};
109 std::string Dirname(const std::string& path) {
111 char* s = ::strdup(path.c_str());
113 throw std::system_error{errno, std::generic_category(), path};
115 // dirname will never fail
116 std::string dir{::dirname(s)};
121 void CopyFileOverExistingDirectory(const std::string& src, const std::string& dest,
123 ScopeLogger("From: %s, To %s", src.c_str(), dest.c_str());
125 if (CheckIfExists(dest, &buf) && CheckIfDir(buf)) {
127 RemoveDirectoryRecursively(dest);
129 throw std::system_error{EIO, std::generic_category(),
130 "Failed to copy file: overwrite is not allowed."};
133 CopyFile(src, dest, overwrite);
136 void CopyFile(const std::string& src, const std::string& dest, bool overwrite) {
137 ScopeLogger("From: %s; To %s", src.c_str(), dest.c_str());
139 GError* error = nullptr;
140 auto source_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>(
141 g_file_new_for_path(src.c_str()), g_object_unref);
142 auto dest_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>(
143 g_file_new_for_path(dest.c_str()), g_object_unref);
144 int flags = G_FILE_COPY_ALL_METADATA;
146 flags |= G_FILE_COPY_OVERWRITE;
150 g_file_copy(source_ptr.get(), dest_ptr.get(), static_cast<GFileCopyFlags>(flags), nullptr,
151 nullptr, nullptr, &error);
155 why = error->message;
158 throw std::system_error{EIO, std::generic_category(), "Failed to copy file: "s + why};
162 void CopyDirectory(const std::string& src, const std::string& dest, bool overwrite) {
163 ScopeLogger("From: %s, To %s", src.c_str(), dest.c_str());
164 ListDirectory(src, [&](const std::string& name, unsigned char type) {
165 if (DT_DIR == type) {
166 std::string dest_dir = dest + '/' + name;
168 bool exists = CheckIfExists(dest_dir, &buf);
169 if (exists && !CheckIfDir(buf)) {
174 throw std::system_error{EIO, std::generic_category(),
175 "Failed to copy directory: overwriting is not allowed."};
177 } else if (!exists) {
181 CopyDirectory(src + '/' + name, dest_dir, overwrite);
182 } else { // copying of regular files as well as other types of items pointed by src
183 CopyFileOverExistingDirectory(src + '/' + name, dest + '/' + name, overwrite);
185 // Set errno to 0 to prevent from reporting readdir error after successful iterating through
192 const std::string& path,
193 std::function<void(const std::string&, unsigned char)> next) {
194 ScopeLogger("%s", path.c_str());
195 DIR* d = ::opendir(path.c_str());
197 throw std::system_error{errno, std::generic_category(),
198 "Failed to open directory: "s + GetErrorString(errno)};
201 std::unique_ptr<DIR, void (*)(DIR*)> dir_ptr(d, [](DIR* d) {
203 LoggerW("closedir failed");
209 dirent* entry = ::readdir(d);
210 int readdir_errno = errno;
211 if (0 != readdir_errno) {
212 throw std::system_error{errno, std::generic_category(),
213 "Failed to read directory: "s + GetErrorString(readdir_errno)};
216 if (entry == nullptr) {
220 if (0 == std::strcmp(entry->d_name, ".") || 0 == std::strcmp(entry->d_name, "..")) {
223 next(std::string(entry->d_name), entry->d_type);
227 void RemoveDirectoryRecursively(const std::string& path) {
228 ScopeLogger("%s", path.c_str());
231 [](const char* fpath, const struct stat* sb, int typeflag, struct FTW* ftwbuf) -> int {
232 // if number of nested directories is large
233 // below log could decrease readability
234 // ScopeLogger("%s", fpath);
236 auto res = remove(fpath);
238 LoggerD("Failed to remove %s: %s", fpath, GetErrorString(errno).c_str());
243 128, FTW_DEPTH | FTW_PHYS);
247 // -1 can be returned by nftw() function, to prevent invalid translation of error in
248 // std::system_error constructor, such situation will be treated as generic IOError
251 throw std::system_error{res, std::generic_category(),
252 "Failed to remove directory recursively: "s + GetErrorString(res)};
256 void RemoveDirectory(const std::string& path) {
258 if (rmdir(path.c_str())) {
259 throw std::system_error{errno, std::generic_category(), "Failed to remove directory"};
263 std::string RealPath(const std::string& path) {
265 char* real_path = realpath(path.c_str(), nullptr);
266 if (nullptr == real_path) {
267 throw std::system_error{errno, std::generic_category(), "Path is not valid."};
269 std::string s{real_path};
274 bool CheckIfExists(const std::string& path, struct stat* buf) {
276 if (stat(path.c_str(), buf)) {
277 if (ENOENT == errno) {
280 throw std::system_error{errno, std::generic_category(),
281 "Unable to check file existence: "s + GetErrorString(errno)};
287 bool CheckIfDir(const struct stat& buf) {
289 if (S_ISDIR(buf.st_mode)) {
295 bool CheckIfFile(const struct stat& buf) {
297 if (S_ISREG(buf.st_mode)) {
303 void Rename(const std::string& path, const std::string& new_path) {
305 if (::rename(path.c_str(), new_path.c_str())) {
306 throw std::system_error{errno, std::generic_category(),
307 "Unable to rename file or directory: "s + GetErrorString(errno)};
311 void MoveFile(const std::string& path, const std::string& new_path, bool overwrite) {
313 GError* error = nullptr;
314 auto source_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>(
315 g_file_new_for_path(path.c_str()), g_object_unref);
316 auto dest_ptr = std::unique_ptr<GFile, decltype(&g_object_unref)>(
317 g_file_new_for_path(new_path.c_str()), g_object_unref);
318 int flags = G_FILE_COPY_ALL_METADATA;
320 flags |= G_FILE_COPY_OVERWRITE;
323 g_file_move(source_ptr.get(), dest_ptr.get(), static_cast<GFileCopyFlags>(flags), nullptr,
324 nullptr, nullptr, &error);
328 why = error->message;
331 throw std::system_error{EIO, std::generic_category(), "Failed to move file: "s + why};
335 void MoveDirectory(const std::string& src, const std::string& dest, bool overwrite) {
336 ScopeLogger("%s %s", src.c_str(), dest.c_str());
338 const std::string& new_path = dest + '/' + PosixBasename(src);
339 // If directories are on the same mount point, we can simply try to rename them.
340 // However, it might be done only if new_path does not exist because move_directory should merge
342 if (!CheckIfExists(new_path, &buf)) {
343 LoggerD("new_path %s", new_path.c_str());
344 auto result = ::rename(src.c_str(), new_path.c_str());
347 } else if (EXDEV != errno) {
348 // The directories are in the same mount point, but the operation has just failed.
349 throw std::system_error{EIO, std::generic_category(),
350 "Unable to move directory: "s + GetErrorString(errno)};
354 // Move directory to other move point.
355 CopyDirectory(src, dest, overwrite);
356 RemoveDirectoryRecursively(src);
359 void TranslateException(const std::system_error& e, picojson::object& obj) {
361 if (std::errc::no_such_file_or_directory == e.code()) {
362 LogAndReportError(NotFoundException(e.what()), obj);
364 LogAndReportError(IOException(e.what()), obj);
368 std::string GetDirname(const std::string& path) {
370 char* dir = g_path_get_dirname(path.c_str());
372 std::string dir_result(dir);
376 return std::string(".");