Perform code style checking fixes
[platform/core/appfw/app-installers.git] / src / common / utils / file_util.cc
1 /* 2014, Copyright © Intel Coporation, license APACHE-2.0, see LICENSE file */
2
3 #include "common/utils/file_util.h"
4
5 #include <fcntl.h>
6 #include <sys/stat.h>
7 #include <linux/limits.h>
8 #include <unzip.h>
9 #include <zlib.h>
10
11 #include <boost/algorithm/string/classification.hpp>
12 #include <boost/algorithm/string/split.hpp>
13 #include <boost/filesystem/operations.hpp>
14 #include <boost/filesystem/path.hpp>
15 #include <boost/system/error_code.hpp>
16
17 #include <manifest_parser/utils/logging.h>
18
19 #include <algorithm>
20 #include <string>
21 #include <vector>
22
23 #include "common/utils/byte_size_literals.h"
24
25 namespace ba = boost::algorithm;
26 namespace bs = boost::system;
27 namespace bf = boost::filesystem;
28
29 namespace {
30
31 unsigned kZipBufSize = 8_kB;
32 unsigned kZipMaxPath = PATH_MAX;
33
34 int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
35   struct stat stats;
36   if (stat(path_in_partition.string().c_str(), &stats)) {
37     LOG(ERROR) << "stat(" << path_in_partition.string()
38                << ") failed - error code: " << errno;
39     return -1;
40   }
41   return stats.st_blksize;
42 }
43
44 int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
45   return ((size + block_size - 1) / block_size) * block_size;
46 }
47
48 class UnzFilePointer {
49  public:
50   UnzFilePointer()
51     : zipFile_(nullptr),
52       fileOpened_(false),
53       currentFileOpened_(false) { }
54
55   ~UnzFilePointer() {
56     if (currentFileOpened_)
57       unzCloseCurrentFile(zipFile_);
58     if (fileOpened_)
59       unzClose(zipFile_);
60   }
61
62   bool Open(const char* zip_path) {
63     zipFile_ = static_cast<unzFile*>(unzOpen(zip_path));
64     if (!zipFile_)
65        return false;
66     fileOpened_ = true;
67     return true;
68   }
69
70   bool OpenCurrent() {
71     if (unzOpenCurrentFile(zipFile_) != UNZ_OK)
72       return false;
73     currentFileOpened_ = true;
74     return true;
75   }
76
77   void CloseCurrent() {
78     if (currentFileOpened_)
79       unzCloseCurrentFile(zipFile_);
80     currentFileOpened_ = false;
81   }
82
83   unzFile* Get() { return zipFile_; }
84
85  private:
86   unzFile* zipFile_;
87   bool fileOpened_;
88   bool currentFileOpened_;
89 };
90
91 }  // namespace
92
93 namespace common_installer {
94
95 FSFlag operator|(FSFlag a, FSFlag b) {
96   return static_cast<FSFlag>(static_cast<int>(a) | static_cast<int>(b));
97 }
98
99 bool SetOwnership(const bf::path& path, uid_t uid, gid_t gid) {
100   int fd = open(path.c_str(), O_RDONLY);
101   if (fd < 0) {
102     LOG(ERROR) << "Can't open directory : " << path;
103     return false;
104   }
105   int ret = fchown(fd, uid, gid);
106   close(fd);
107   if (ret != 0) {
108     LOG(ERROR) << "Failed to change owner of: " << path;
109     return false;
110   }
111   return true;
112 }
113
114 bool SetOwnershipAll(const bf::path& path, uid_t uid, gid_t gid) {
115   if (!SetOwnership(path, uid, gid))
116     return false;
117   if (!bf::is_directory(path))
118     return true;
119   for (bf::recursive_directory_iterator iter(path);
120     iter != bf::recursive_directory_iterator();
121     ++iter) {
122     bf::path current(iter->path());
123     if (bf::is_symlink(symlink_status(current)))
124       continue;
125     if (!SetOwnership(current, uid, gid))
126       return false;
127   }
128   return true;
129 }
130
131 bool CreateDir(const bf::path& path) {
132   if (bf::exists(path))
133     return true;
134
135   boost::system::error_code error;
136   bf::create_directories(path, error);
137
138   if (error) {
139     LOG(ERROR) << "Failed to create directory: "
140                << boost::system::system_error(error).what();
141     return false;
142   }
143   return true;
144 }
145
146 bool SetDirPermissions(const boost::filesystem::path& path,
147                       boost::filesystem::perms permissions) {
148   boost::system::error_code error;
149   bf::permissions(path, permissions, error);
150
151   if (error) {
152     LOG(ERROR) << "Failed to set permissions for directory: " << path
153                << boost::system::system_error(error).what();
154     return false;
155   }
156   return true;
157 }
158
159 bool SetDirOwnershipAndPermissions(const boost::filesystem::path& path,
160                       boost::filesystem::perms permissions, uid_t uid,
161                       gid_t gid) {
162   if (!SetOwnership(path, uid, gid)) {
163     LOG(ERROR) << "Failed to change owner: " << path
164                << "(" << uid << ", " << gid << ")";
165     return false;
166   }
167   if (!SetDirPermissions(path, permissions)) {
168     LOG(ERROR) << "Failed to change permission: " << path
169                << std::oct << permissions;
170     return false;
171   }
172
173   return true;
174 }
175
176 bool CopyOwnershipAndPermissions(const boost::filesystem::path& src,
177                                  const boost::filesystem::path& dst) {
178   if (!bf::exists(src)) {
179     LOG(ERROR) << "Failed to copy ownership and permissions"
180                << " from " << src << " to " << dst;
181     return false;
182   }
183   bf::perms permissions = bf::status(src).permissions();
184   struct stat stats;
185   stat(src.c_str(), &stats);
186   if (!SetDirOwnershipAndPermissions(dst, permissions, stats.st_uid,
187                                     stats.st_gid)) {
188     LOG(ERROR) << "Failed to copy ownership and permissions"
189                << " from " << src << " to " << dst;
190     return false;
191   }
192   return true;
193 }
194
195 bool CopyDir(const bf::path& src, const bf::path& dst,
196              FSFlag flags, bool skip_symlink) {
197   try {
198     // Check whether the function call is valid
199     if (!bf::exists(src) || !bf::is_directory(src)) {
200       LOG(ERROR) << "Source directory " << src
201                  << " does not exist or is not a directory.";
202       return false;
203     }
204     if (!bf::exists(dst)) {
205       // Create the destination directory
206       if (!CreateDir(dst)) {
207         LOG(ERROR) << "Unable to create destination directory" << dst;
208         return false;
209       }
210       if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
211         CopyOwnershipAndPermissions(src, dst);
212     } else {
213       if (!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
214         LOG(ERROR) << "Destination directory " << dst.string()
215                    << " already exists.";
216         return false;
217       }
218       if (flags & (FS_MERGE_OVERWRITE | FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS))
219         CopyOwnershipAndPermissions(src, dst);
220     }
221   } catch (const bf::filesystem_error& error) {
222     LOG(ERROR) << "Failed to copy directory: " << error.what();
223     return false;
224   }
225
226   // Iterate through the source directory
227   for (bf::directory_iterator file(src);
228       file != bf::directory_iterator();
229       ++file) {
230     try {
231       bf::path current(file->path());
232       bf::path target = dst / current.filename();
233
234       if (bf::is_symlink(symlink_status(current))) {
235         if (skip_symlink)
236           continue;
237         if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) &&
238             bf::exists(target))
239           continue;
240         bs::error_code error;
241         bf::copy_symlink(current, target, error);
242         if (error) {
243           LOG(ERROR) << "Failed to copy symlink: " << current << ", "
244                      << error.message();
245           return false;
246         }
247       } else if (bf::is_directory(current)) {
248         // Found directory: Recursion
249         if (!CopyDir(current, target, flags, skip_symlink)) {
250           return false;
251         }
252       } else {
253         if ((flags & FS_MERGE_SKIP) && bf::exists(target))
254           continue;
255         bf::path destination = target;
256
257         if (flags & FS_COMMIT_COPY_FILE)
258           destination =
259               bf::unique_path(target.parent_path() / "%%%%-%%%%-%%%%-%%%%");
260
261         if (flags & FS_MERGE_OVERWRITE)
262           bf::copy_file(current, destination,
263                         bf::copy_option::overwrite_if_exists);
264         else
265           bf::copy_file(current, destination);
266
267         if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
268           CopyOwnershipAndPermissions(current, destination);
269
270         if (flags & FS_COMMIT_COPY_FILE) {
271           if (flags & FS_MERGE_OVERWRITE)
272             bf::remove(target);
273           bf::rename(destination, target);
274         }
275       }
276     } catch (const bf::filesystem_error& error) {
277       LOG(ERROR) << "Failed to copy directory: " << error.what();
278       return false;
279     }
280   }
281   return true;
282 }
283
284 bool CopyFile(const bf::path& src, const bf::path& dst) {
285   bs::error_code error;
286
287   bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
288   if (error) {
289     LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
290     return false;
291   }
292   return true;
293 }
294
295 bool RemoveAll(const bf::path& path) {
296   if (!exists(path))
297     return true;
298
299   bs::error_code error;
300   bf::remove_all(path, error);
301
302   if (error) {
303     LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
304     return false;
305   }
306
307   return true;
308 }
309
310 bool Remove(const bf::path& path) {
311   if (!exists(path))
312     return true;
313
314   bs::error_code error;
315   bf::remove(path, error);
316
317   if (error) {
318     LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
319     return false;
320   }
321   return true;
322 }
323
324 bool MoveDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
325   if (bf::exists(dst) &&
326       !(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
327     LOG(ERROR) << "Destination directory does exist: " << dst;
328     return false;
329   }
330
331   bs::error_code error;
332   bf::rename(src, dst, error);
333   if (error) {
334     LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
335     if (!CopyDir(src, dst, flags, false)) {
336       LOG(ERROR) << "Cannot copy directory: " << src;
337       return false;
338     }
339     bf::remove_all(src, error);
340     if (error) {
341       LOG(ERROR) << "Cannot remove old directory when coping: " << src;
342       return false;
343     }
344   }
345   return true;
346 }
347
348 bool MoveFile(const bf::path& src, const bf::path& dst, bool force) {
349   if (!force && bf::exists(dst))
350     return false;
351   bs::error_code error;
352   bf::rename(src, dst, error);
353   if (error) {
354     LOG(WARNING) << "Cannot move file: " << src <<
355         ". Will copy/remove... with error [" << error << "]";
356     bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
357     if (error) {
358       LOG(WARNING) << "Cannot copy file " << src <<
359           " due to error [" << error << "]";
360       return false;
361     }
362     bf::remove_all(src, error);
363     if (error) {
364       LOG(ERROR) << "Cannot remove old file when coping: " << src <<
365           "with error [" << error << "]";
366     }
367   }
368   return true;
369 }
370
371 int64_t GetUnpackedPackageSize(const bf::path& path) {
372   int64_t size = 0;
373   int64_t block_size = GetBlockSizeForPath(path);
374
375   // if failed to stat path
376   if (block_size == -1)
377     return -1;
378
379   unz_global_info info;
380   unz_file_info64 raw_file_info;
381   char raw_file_name_in_zip[kZipMaxPath];
382
383   unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
384   if (zip_file == nullptr) {
385     LOG(ERROR) << "Failed to open the source dir: " << path.string();
386     return -1;
387   }
388
389   if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
390     LOG(ERROR) << "Failed to read global info";
391     unzClose(zip_file);
392     return -1;
393   }
394
395   for (uLong i = 0; i < info.number_entry; i++) {
396     if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
397         sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
398       LOG(ERROR) << "Failed to read file info";
399       unzClose(zip_file);
400       return -1;
401     }
402     size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
403     unzGoToNextFile(zip_file);
404   }
405
406   // FIXME: calculate space needed for directories
407   unzClose(zip_file);
408   return size;
409 }
410
411 int64_t GetDirectorySize(const boost::filesystem::path& path) {
412   int64_t block_size = GetBlockSizeForPath(path);
413
414   // if failed to stat path
415   if (block_size == -1)
416     return -1;
417
418   int64_t size = 0;
419   for (bf::recursive_directory_iterator iter(path);
420       iter != bf::recursive_directory_iterator(); ++iter) {
421       struct stat buf;
422       if (lstat(iter->path().c_str(), &buf) == -1) {
423         LOG(ERROR) << "lstat() failed for: " << iter->path();
424         return -1;
425       }
426       size += RoundUpToBlockSizeOf(buf.st_size, block_size);
427   }
428
429   // FIXME: block size for external device may differ...
430   return size;
431 }
432
433 boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
434   boost::filesystem::path install_tmp_dir;
435   boost::filesystem::path tmp_dir(app_path);
436
437   do {
438     boost::filesystem::path model;
439     boost::filesystem::path unique_dir =
440         boost::filesystem::unique_path(model = "unpack-%%%%%%");
441
442     install_tmp_dir = tmp_dir /= unique_dir;
443   } while (boost::filesystem::exists(install_tmp_dir) &&
444            boost::filesystem::is_directory(install_tmp_dir));
445
446   return install_tmp_dir;
447 }
448
449 boost::filesystem::path GenerateTemporaryPath(
450     const boost::filesystem::path& path) {
451   bf::path pattern = path;
452   pattern += "-%%%%%%";
453   bf::path tmp_path;
454   do {
455     tmp_path = boost::filesystem::unique_path(pattern);
456   } while (boost::filesystem::exists(tmp_path));
457   return tmp_path;
458 }
459
460 bool ExtractToTmpDir(const char* zip_path,
461                      const boost::filesystem::path& tmp_dir) {
462   return ExtractToTmpDir(zip_path, tmp_dir, "");
463 }
464
465 bool ExtractToTmpDir(const char* zip_path, const bf::path& tmp_dir,
466                      const std::string& filter_prefix) {
467   unz_global_info info;
468   char read_buffer[kZipBufSize];
469   unz_file_info raw_file_info;
470   char raw_file_name_in_zip[kZipMaxPath];
471
472   current_path(tmp_dir);
473
474   UnzFilePointer zip_file;
475   if (!zip_file.Open(zip_path)) {
476     LOG(WARNING) << "Failed to open the source dir: " << zip_path;
477     return false;
478   }
479
480   if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
481     LOG(ERROR) << "Failed to read global info";
482     return false;
483   }
484
485   for (uLong i = 0; i < info.number_entry; i++) {
486     if (unzGetCurrentFileInfo(zip_file.Get(),
487                               &raw_file_info,
488                               raw_file_name_in_zip,
489                               sizeof(raw_file_name_in_zip),
490                               nullptr,
491                               0,
492                               nullptr,
493                               0) != UNZ_OK) {
494       LOG(ERROR) << "Failed to read file info";
495       return false;
496     }
497
498     if (raw_file_name_in_zip[0] == '\0')
499       return false;
500
501     // unpack if filter is empty or path is matched
502     if (filter_prefix.empty() ||
503         std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
504       bf::path filename_in_zip_path(raw_file_name_in_zip);
505
506       // prevent "directory climbing" attack
507       if (HasDirectoryClimbing(filename_in_zip_path)) {
508         LOG(ERROR) << "Relative path in widget in malformed";
509         return false;
510       }
511
512       if (!filename_in_zip_path.parent_path().empty()) {
513         if (!CreateDir(filename_in_zip_path.parent_path())) {
514           LOG(ERROR) << "Failed to create directory: "
515               << filename_in_zip_path.parent_path();
516           return false;
517         }
518       }
519
520       if (!zip_file.OpenCurrent()) {
521         LOG(ERROR) << "Failed to open file";
522         return false;
523       }
524
525       if (!is_directory(filename_in_zip_path)) {
526         FILE* out = fopen(raw_file_name_in_zip, "wbx");
527         if (!out) {
528           LOG(ERROR) << "Failed to open destination ";
529           return false;
530         }
531
532         int ret = UNZ_OK;
533         do {
534           ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
535           if (ret < 0) {
536             LOG(ERROR) << "Failed to read data: " << ret;
537             fclose(out);
538             return false;
539           } else {
540             fwrite(read_buffer, sizeof(char), ret, out);
541           }
542         } while (ret > 0);
543
544         fclose(out);
545       }
546
547       zip_file.CloseCurrent();
548     }
549
550     if ((i+1) < info.number_entry) {
551       if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
552         LOG(ERROR) << "Failed to read next file";
553         return false;
554       }
555     }
556   }
557
558   return true;
559 }
560
561 bool CheckPathInZipArchive(const char* zip_archive_path,
562                            const boost::filesystem::path& relative_zip_path,
563                            bool* found) {
564   *found = false;
565   UnzFilePointer zip_file;
566   if (!zip_file.Open(zip_archive_path)) {
567     LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
568     return false;
569   }
570
571   unz_global_info info;
572   if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
573     LOG(ERROR) << "Failed to read global info";
574     return false;
575   }
576
577   char raw_file_name_in_zip[kZipMaxPath];
578   unz_file_info raw_file_info;
579   for (uLong i = 0; i < info.number_entry; i++) {
580     if (unzGetCurrentFileInfo(zip_file.Get(),
581                               &raw_file_info,
582                               raw_file_name_in_zip,
583                               sizeof(raw_file_name_in_zip),
584                               nullptr,
585                               0,
586                               nullptr,
587                               0) != UNZ_OK) {
588       LOG(ERROR) << "Failed to read file info";
589       return false;
590     }
591
592     if (raw_file_name_in_zip[0] == '\0')
593       return false;
594
595     if (relative_zip_path.string() == raw_file_name_in_zip) {
596       *found = true;
597       return true;
598     }
599
600     if ((i + 1) < info.number_entry) {
601       if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
602         LOG(ERROR) << "Failed to read next file";
603         return false;
604       }
605     }
606   }
607
608   return true;
609 }
610
611 bool HasDirectoryClimbing(const boost::filesystem::path& path) {
612   std::vector<std::string> segments;
613   ba::split(segments, path.string(), ba::is_any_of("/\\"));
614   return std::any_of(segments.begin(), segments.end(),
615                   [](const std::string& segment) {
616                     return segment == "..";
617                   });
618 }
619
620 boost::filesystem::path MakeRelativePath(const boost::filesystem::path& input,
621                                          const boost::filesystem::path& base) {
622   if (input.string().find(base.string()) == std::string::npos) {
623       LOG(ERROR) << base.string() << " is not base path for " << input.string();
624       return input;
625   }
626   return input.string().substr(base.string().length() + 1);
627 }
628
629 bool IsSubDir(const boost::filesystem::path& path,
630     const boost::filesystem::path& root) {
631   boost::filesystem::path p = path;
632   while (p != boost::filesystem::path()) {
633     if (bf::equivalent(p, root))
634       return true;
635     else
636       p = p.parent_path();
637   }
638   return false;
639 }
640
641 }  // namespace common_installer