Fixed listDirectory()
[platform/core/api/webapi-plugins.git] / src / filesystem / filesystem_utils.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include "filesystem_utils.h"
17 #include "common/logger.h"
18 #include "common/platform_exception.h"
19
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <ftw.h>
23 #include <glib.h>
24 #include <libgen.h>
25 #include <sys/sendfile.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 namespace FilesystemUtils {
31 using namespace std::string_literals;
32 using namespace common;
33 using common::tools::ReportError;
34 using common::tools::GetErrorString;
35
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);
39
40   if (0 != ret) {
41     throw std::system_error{errno, std::generic_category()};
42   }
43 }
44
45 void Mkdir(const std::string& path, bool parents) {
46   // ScopeLogger("%s, %d", path.c_str(), parents); // disabled in recursive function
47   if (!parents) {
48     Mkdir(path);
49     return;
50   }
51
52   struct ::stat buf;
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) {
57       stack.push_back(s);
58     } else {
59       throw std::system_error{errno, std::generic_category()};
60     }
61   }
62
63   if (stack.empty()) {  // this means, that path exists, let Mkdir handle error
64     Mkdir(path);
65     return;
66   }
67
68   // from top to bottom
69   for (auto rit = stack.rbegin(); rit != stack.rend(); ++rit) {
70     try {
71       Mkdir(*rit);
72     } catch (const std::system_error& error) {
73       if (stack.rbegin() != rit) {
74         try {
75           RemoveDirectoryRecursively(*stack.rbegin());
76         } catch (const std::system_error& removalError) {
77           LoggerW(
78               "Could not remove parent directories created so far: %s."
79               "Some of them might still exist",
80               removalError.what());
81         }
82       }
83       throw;
84     }
85   }
86 }
87
88 void Unlink(const std::string& path) {
89   ScopeLogger();
90   int ret = ::unlink(path.c_str());
91   if (0 != ret) {
92     throw std::system_error{errno, std::generic_category()};
93   }
94 }
95
96 std::string PosixBasename(const std::string& path) {
97   ScopeLogger();
98   char* s = ::strdup(path.c_str());
99   if (!s) {
100     throw std::system_error{errno, std::generic_category(), path};
101   }
102
103   // basename will never fail
104   std::string name{::basename(s)};
105   free(s);
106   return name;
107 }
108
109 std::string Dirname(const std::string& path) {
110   ScopeLogger();
111   char* s = ::strdup(path.c_str());
112   if (!s) {
113     throw std::system_error{errno, std::generic_category(), path};
114   }
115   // dirname will never fail
116   std::string dir{::dirname(s)};
117   free(s);
118   return dir;
119 }
120
121 void CopyFileOverExistingDirectory(const std::string& src, const std::string& dest,
122                                    bool overwrite) {
123   ScopeLogger("From: %s, To %s", src.c_str(), dest.c_str());
124   struct stat buf {};
125   if (CheckIfExists(dest, &buf) && CheckIfDir(buf)) {
126     if (overwrite) {
127       RemoveDirectoryRecursively(dest);
128     } else {
129       throw std::system_error{EIO, std::generic_category(),
130                               "Failed to copy file: overwrite is not allowed."};
131     }
132   }
133   CopyFile(src, dest, overwrite);
134 }
135
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());
138
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;
145   if (overwrite) {
146     flags |= G_FILE_COPY_OVERWRITE;
147   }
148
149   gboolean success =
150       g_file_copy(source_ptr.get(), dest_ptr.get(), static_cast<GFileCopyFlags>(flags), nullptr,
151                   nullptr, nullptr, &error);
152   if (!success) {
153     std::string why{};
154     if (error) {
155       why = error->message;
156       g_error_free(error);
157     }
158     throw std::system_error{EIO, std::generic_category(), "Failed to copy file: "s + why};
159   }
160 }
161
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;
167       struct stat buf {};
168       bool exists = CheckIfExists(dest_dir, &buf);
169       if (exists && !CheckIfDir(buf)) {
170         if (overwrite) {
171           Unlink(dest_dir);
172           Mkdir(dest_dir);
173         } else {
174           throw std::system_error{EIO, std::generic_category(),
175                                   "Failed to copy directory: overwriting is not allowed."};
176         }
177       } else if (!exists) {
178         Mkdir(dest_dir);
179       }
180
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);
184     }
185     // Set errno to 0 to prevent from reporting readdir error after successful iterating through
186     // directory.
187     errno = 0;
188   });
189 }
190
191 void ListDirectory(
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());
196   if (nullptr == d) {
197     throw std::system_error{errno, std::generic_category(),
198                             "Failed to open directory: "s + GetErrorString(errno)};
199   }
200
201   std::unique_ptr<DIR, void (*)(DIR*)> dir_ptr(d, [](DIR* d) {
202     if (::closedir(d)) {
203       LoggerW("closedir failed");
204     }
205   });
206
207   while (true) {
208     errno = 0;
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)};
214     }
215
216     if (entry == nullptr) {
217       break;
218     }
219
220     if (0 == std::strcmp(entry->d_name, ".") || 0 == std::strcmp(entry->d_name, "..")) {
221       continue;
222     }
223     next(std::string(entry->d_name), entry->d_type);
224   }
225 }
226
227 void RemoveDirectoryRecursively(const std::string& path) {
228   ScopeLogger("%s", path.c_str());
229   auto res =
230       nftw(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);
235
236              auto res = remove(fpath);
237              if (res) {
238                LoggerD("Failed to remove %s: %s", fpath, GetErrorString(errno).c_str());
239                return errno;
240              }
241              return 0;
242            },
243            128, FTW_DEPTH | FTW_PHYS);
244
245   if (res) {
246     if (-1 == res) {
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
249       res = EIO;
250     }
251     throw std::system_error{res, std::generic_category(),
252                             "Failed to remove directory recursively: "s + GetErrorString(res)};
253   }
254 }
255
256 void RemoveDirectory(const std::string& path) {
257   ScopeLogger();
258   if (rmdir(path.c_str())) {
259     throw std::system_error{errno, std::generic_category(), "Failed to remove directory"};
260   }
261 }
262
263 std::string RealPath(const std::string& path) {
264   ScopeLogger();
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."};
268   }
269   std::string s{real_path};
270   free(real_path);
271   return s;
272 }
273
274 bool CheckIfExists(const std::string& path, struct stat* buf) {
275   ScopeLogger();
276   if (stat(path.c_str(), buf)) {
277     if (ENOENT == errno) {
278       return false;
279     } else {
280       throw std::system_error{errno, std::generic_category(),
281                               "Unable to check file existence: "s + GetErrorString(errno)};
282     }
283   }
284   return true;
285 }
286
287 bool CheckIfDir(const struct stat& buf) {
288   ScopeLogger();
289   if (S_ISDIR(buf.st_mode)) {
290     return true;
291   }
292   return false;
293 }
294
295 bool CheckIfFile(const struct stat& buf) {
296   ScopeLogger();
297   if (S_ISREG(buf.st_mode)) {
298     return true;
299   }
300   return false;
301 }
302
303 void Rename(const std::string& path, const std::string& new_path) {
304   ScopeLogger();
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)};
308   }
309 }
310
311 void MoveFile(const std::string& path, const std::string& new_path, bool overwrite) {
312   ScopeLogger();
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;
319   if (overwrite) {
320     flags |= G_FILE_COPY_OVERWRITE;
321   }
322   gboolean success =
323       g_file_move(source_ptr.get(), dest_ptr.get(), static_cast<GFileCopyFlags>(flags), nullptr,
324                   nullptr, nullptr, &error);
325   if (!success) {
326     std::string why{};
327     if (error) {
328       why = error->message;
329       g_error_free(error);
330     }
331     throw std::system_error{EIO, std::generic_category(), "Failed to move file: "s + why};
332   }
333 }
334
335 void MoveDirectory(const std::string& src, const std::string& dest, bool overwrite) {
336   ScopeLogger("%s %s", src.c_str(), dest.c_str());
337   struct stat buf {};
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
341   // directories.
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());
345     if (!result) {
346       return;
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)};
351     }
352   }
353
354   // Move directory to other move point.
355   CopyDirectory(src, dest, overwrite);
356   RemoveDirectoryRecursively(src);
357 }
358
359 void TranslateException(const std::system_error& e, picojson::object& obj) {
360   ScopeLogger();
361   if (std::errc::no_such_file_or_directory == e.code()) {
362     LogAndReportError(NotFoundException(e.what()), obj);
363   } else {
364     LogAndReportError(IOException(e.what()), obj);
365   }
366 }
367
368 std::string GetDirname(const std::string& path) {
369   ScopeLogger();
370   char* dir = g_path_get_dirname(path.c_str());
371   if (dir) {
372     std::string dir_result(dir);
373     g_free(dir);
374     return dir_result;
375   } else {
376     return std::string(".");
377   }
378 }
379 }