Remove boost dependency
[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 <manifest_parser/utils/logging.h>
12
13 #include <algorithm>
14 #include <filesystem>
15 #include <random>
16 #include <regex>
17 #include <string>
18 #include <system_error>
19 #include <vector>
20
21 #include "common/utils/byte_size_literals.h"
22 #include "common/utils/paths.h"
23
24 namespace fs = std::filesystem;
25
26 namespace {
27
28 unsigned kZipBufSize = 8_kB;
29 unsigned kZipMaxPath = PATH_MAX;
30
31 int64_t GetBlockSizeForPath(const fs::path& path_in_partition) {
32   struct stat stats;
33   if (stat(path_in_partition.c_str(), &stats)) {
34     LOG(ERROR) << "stat(" << path_in_partition
35                << ") failed - error code: " << errno;
36     return -1;
37   }
38   return stats.st_blksize;
39 }
40
41 int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
42   return ((size + block_size - 1) / block_size) * block_size;
43 }
44
45 class UnzFilePointer {
46  public:
47   UnzFilePointer()
48     : zipFile_(nullptr),
49       fileOpened_(false),
50       currentFileOpened_(false) { }
51
52   ~UnzFilePointer() {
53     if (currentFileOpened_)
54       unzCloseCurrentFile(zipFile_);
55     if (fileOpened_)
56       unzClose(zipFile_);
57   }
58
59   bool Open(const char* zip_path) {
60     zipFile_ = static_cast<unzFile*>(unzOpen(zip_path));
61     if (!zipFile_)
62        return false;
63     fileOpened_ = true;
64     return true;
65   }
66
67   bool OpenCurrent() {
68     if (unzOpenCurrentFile(zipFile_) != UNZ_OK)
69       return false;
70     currentFileOpened_ = true;
71     return true;
72   }
73
74   void CloseCurrent() {
75     if (currentFileOpened_)
76       unzCloseCurrentFile(zipFile_);
77     currentFileOpened_ = false;
78   }
79
80   unzFile* Get() { return zipFile_; }
81
82  private:
83   unzFile* zipFile_;
84   bool fileOpened_;
85   bool currentFileOpened_;
86 };
87
88 }  // namespace
89
90 namespace common_installer {
91
92 FSFlag operator|(FSFlag a, FSFlag b) {
93   return static_cast<FSFlag>(static_cast<int>(a) | static_cast<int>(b));
94 }
95
96 bool SetOwnership(const fs::path& path, uid_t uid, gid_t gid) {
97   int ret = lchown(path.c_str(), uid, gid);
98   if (ret != 0) {
99     LOG(ERROR) << "Failed to change owner of: " << path;
100     return false;
101   }
102   return true;
103 }
104
105 bool SetOwnershipAll(const fs::path& path, uid_t uid, gid_t gid) {
106   if (!SetOwnership(path, uid, gid))
107     return false;
108   if (!fs::is_directory(path))
109     return true;
110   for (fs::recursive_directory_iterator iter(path);
111     iter != fs::recursive_directory_iterator();
112     ++iter) {
113     fs::path current(iter->path());
114     if (!SetOwnership(current, uid, gid))
115       return false;
116   }
117   return true;
118 }
119
120 bool CreateDir(const fs::path& path) {
121   if (fs::exists(path))
122     return true;
123
124   std::error_code error;
125   fs::create_directories(path, error);
126
127   if (error) {
128     LOG(ERROR) << "Failed to create directory: " << error.message();
129     return false;
130   }
131   return true;
132 }
133
134 bool SetDirPermissions(const fs::path& path,
135                       fs::perms permissions, bool add_perms) {
136   std::error_code error;
137   fs::permissions(path, permissions,
138       add_perms ? fs::perm_options::add : fs::perm_options::replace, error);
139
140   if (error) {
141     LOG(ERROR) << "Failed to set permissions for directory: " << path
142                << error.message();
143     return false;
144   }
145   return true;
146 }
147
148 bool SetDirOwnershipAndPermissions(const fs::path& path,
149                       fs::perms permissions, uid_t uid,
150                       gid_t gid) {
151   if (!SetOwnership(path, uid, gid)) {
152     LOG(ERROR) << "Failed to change owner: " << path
153                << "(" << uid << ", " << gid << ")";
154     return false;
155   }
156   if (!SetDirPermissions(path, permissions)) {
157     LOG(ERROR) << "Failed to change permission: " << path;
158     return false;
159   }
160
161   return true;
162 }
163
164 bool CopyOwnershipAndPermissions(const fs::path& src, const fs::path& dst) {
165   if (!fs::exists(src)) {
166     LOG(ERROR) << "Failed to copy ownership and permissions"
167                << " from " << src << " to " << dst;
168     return false;
169   }
170   fs::perms permissions = fs::status(src).permissions();
171   struct stat stats;
172   if (stat(src.c_str(), &stats) != 0)
173     return false;
174   if (!SetDirOwnershipAndPermissions(dst, permissions, stats.st_uid,
175                                     stats.st_gid)) {
176     LOG(ERROR) << "Failed to copy ownership and permissions"
177                << " from " << src << " to " << dst;
178     return false;
179   }
180   return true;
181 }
182
183 bool CopyDir(const fs::path& src, const fs::path& dst,
184              FSFlag flags, bool skip_symlink) {
185   try {
186     // Check whether the function call is valid
187     if (!fs::exists(src) || !fs::is_directory(src)) {
188       LOG(ERROR) << "Source directory " << src
189                  << " does not exist or is not a directory.";
190       return false;
191     }
192     if (!fs::exists(dst)) {
193       // Create the destination directory
194       if (!CreateDir(dst)) {
195         LOG(ERROR) << "Unable to create destination directory" << dst;
196         return false;
197       }
198       if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
199         CopyOwnershipAndPermissions(src, dst);
200     } else {
201       if (!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
202         LOG(ERROR) << "Destination directory " << dst.string()
203                    << " already exists.";
204         return false;
205       }
206       if (flags & (FS_MERGE_OVERWRITE | FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS))
207         CopyOwnershipAndPermissions(src, dst);
208     }
209   } catch (const fs::filesystem_error& error) {
210     LOG(ERROR) << "Failed to copy directory: " << error.what();
211     return false;
212   }
213
214   // Iterate through the source directory
215   for (const auto& current : fs::directory_iterator(src)) {
216     try {
217       fs::path target = dst / current.path().filename();
218
219       if (fs::is_symlink(symlink_status(current))) {
220         if (skip_symlink)
221           continue;
222         if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) &&
223             fs::exists(target))
224           continue;
225         std::error_code error;
226         fs::copy_symlink(current, target, error);
227         if (error) {
228           LOG(ERROR) << "Failed to copy symlink: " << current << ", "
229                      << error.message();
230           return false;
231         }
232       } else if (fs::is_directory(current)) {
233         // Found directory: Recursion
234         if (!CopyDir(current, target, flags, skip_symlink)) {
235           return false;
236         }
237       } else {
238         if ((flags & FS_MERGE_SKIP) && fs::exists(target))
239           continue;
240         fs::path destination = target;
241
242         if (flags & FS_COMMIT_COPY_FILE)
243           destination = target.parent_path() /
244               GenerateUniquePathString("%%%%-%%%%-%%%%-%%%%");
245
246         if (flags & FS_MERGE_OVERWRITE)
247           fs::copy_file(current, destination,
248                         fs::copy_options::overwrite_existing);
249         else
250           fs::copy_file(current, destination);
251
252         if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
253           CopyOwnershipAndPermissions(current, destination);
254
255         if (flags & FS_COMMIT_COPY_FILE) {
256           if (flags & FS_MERGE_OVERWRITE)
257             fs::remove(target);
258           fs::rename(destination, target);
259         }
260       }
261     } catch (const fs::filesystem_error& error) {
262       LOG(ERROR) << "Failed to copy directory: " << error.what();
263       return false;
264     }
265   }
266   return true;
267 }
268
269 bool CopyFile(const fs::path& src, const fs::path& dst) {
270   std::error_code error;
271
272   fs::copy_file(src, dst, fs::copy_options::overwrite_existing, error);
273   if (error) {
274     LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
275     return false;
276   }
277   return true;
278 }
279
280 bool RestoreBackup(const fs::path& path) {
281   fs::path backup_path = GetBackupPathForPackagePath(path);
282   if (!fs::exists(backup_path) &&
283       !fs::is_symlink(fs::symlink_status(backup_path))) {
284     LOG(WARNING) << backup_path << " does not exist";
285     return true;
286   }
287   return MoveDir(backup_path, path);
288 }
289
290 bool MakeBackup(const fs::path& path) {
291   if (!fs::exists(path) && !fs::is_symlink(fs::symlink_status(path))) {
292     LOG(WARNING) << path << " does not exist";
293     return true;
294   }
295   fs::path backup_path = GetBackupPathForPackagePath(path);
296   return MoveDir(path, backup_path);
297 }
298
299 bool RemoveBackup(const fs::path& path) {
300   fs::path backup_path = GetBackupPathForPackagePath(path);
301   if (!fs::exists(backup_path) &&
302       !fs::is_symlink(fs::symlink_status(backup_path))) {
303     LOG(WARNING) << backup_path << " does not exist";
304     return true;
305   }
306   return RemoveAll(backup_path);
307 }
308
309 bool RemoveAll(const fs::path& path) {
310   if (!fs::exists(path) && !fs::is_symlink(fs::symlink_status(path)))
311     return true;
312
313   std::error_code error;
314   fs::remove_all(path, error);
315
316   if (error) {
317     LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
318     return false;
319   }
320
321   return true;
322 }
323
324 bool Remove(const fs::path& path) {
325   std::error_code error;
326   if (!fs::exists(fs::symlink_status(path, error)))
327     return true;
328
329   fs::remove(path, error);
330   if (error) {
331     LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
332     return false;
333   }
334   return true;
335 }
336
337 bool MoveDir(const fs::path& src, const fs::path& dst, FSFlag flags) {
338   if (fs::exists(dst) &&
339       !(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
340     LOG(ERROR) << "Destination directory does exist: " << dst;
341     return false;
342   }
343
344   std::error_code error;
345   fs::rename(src, dst, error);
346   if (error) {
347     LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
348     if (!CopyDir(src, dst, flags, false)) {
349       LOG(ERROR) << "Cannot copy directory: " << src;
350       return false;
351     }
352     fs::remove_all(src, error);
353     if (error) {
354       LOG(ERROR) << "Cannot remove old directory when coping: " << src;
355       return false;
356     }
357   }
358   return true;
359 }
360
361 bool MoveFile(const fs::path& src, const fs::path& dst, bool force) {
362   if (!force && fs::exists(dst))
363     return false;
364   std::error_code error;
365   fs::rename(src, dst, error);
366   if (error) {
367     LOG(WARNING) << "Cannot move file: " << src <<
368         ". Will copy/remove... with error [" << error << "]";
369     fs::copy_file(src, dst, fs::copy_options::overwrite_existing, error);
370     if (error) {
371       LOG(WARNING) << "Cannot copy file " << src <<
372           " due to error [" << error << "]";
373       return false;
374     }
375     fs::remove_all(src, error);
376     if (error) {
377       LOG(ERROR) << "Cannot remove old file when coping: " << src <<
378           "with error [" << error << "]";
379     }
380   }
381   return true;
382 }
383
384 bool BackupDir(const fs::path& src,
385     const fs::path& dst, const std::string& entry) {
386   if (!fs::exists(src / entry))
387     return true;
388
389   if (!MoveDir(src / entry, dst / entry,
390       FS_MERGE_OVERWRITE |
391       FS_COMMIT_COPY_FILE |
392       FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)) {
393     LOG(ERROR) << "Failed to backup file";
394     return false;
395   }
396
397   return true;
398 }
399
400 int64_t GetUnpackedPackageSize(const fs::path& path) {
401   int64_t size = 0;
402   int64_t block_size = GetBlockSizeForPath(path);
403
404   if (block_size == -1)
405     return -1;
406
407   unz_global_info info;
408   unz_file_info64 raw_file_info;
409   char raw_file_name_in_zip[kZipMaxPath];
410
411   unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.c_str()));
412   if (zip_file == nullptr) {
413     LOG(ERROR) << "Failed to open the source dir: " << path.string();
414     return -1;
415   }
416
417   if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
418     LOG(ERROR) << "Failed to read global info";
419     unzClose(zip_file);
420     return -1;
421   }
422
423   for (uLong i = 0; i < info.number_entry; i++) {
424     if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
425         sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
426       LOG(ERROR) << "Failed to read file info";
427       unzClose(zip_file);
428       return -1;
429     }
430     size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
431     unzGoToNextFile(zip_file);
432   }
433
434   // FIXME: calculate space needed for directories
435   unzClose(zip_file);
436   return size;
437 }
438
439 int64_t GetDirectorySize(const fs::path& path) {
440   int64_t block_size = GetBlockSizeForPath(path);
441
442   if (block_size == -1)
443     return -1;
444
445   int64_t size = 0;
446   for (fs::recursive_directory_iterator iter(path);
447       iter != fs::recursive_directory_iterator(); ++iter) {
448       struct stat buf;
449       if (lstat(iter->path().c_str(), &buf) == -1) {
450         LOG(ERROR) << "lstat() failed for: " << iter->path();
451         return -1;
452       }
453       size += RoundUpToBlockSizeOf(buf.st_size, block_size);
454   }
455
456   // FIXME: block size for external device may differ...
457   return size;
458 }
459
460 bool CheckFreeSpaceAtPath(int64_t required_size,
461     const fs::path& target_location) {
462   std::error_code error;
463   fs::path root = target_location;
464
465   while (!fs::exists(root) && root != root.root_path())
466     root = root.parent_path();
467
468   if (!fs::exists(root)) {
469     LOG(ERROR) << "No mount point for path: " << target_location;
470     return false;
471   }
472
473   fs::space_info space_info = fs::space(root, error);
474   if (error) {
475     LOG(ERROR) << "Failed to get space_info: " << error.message();
476     return false;
477   }
478
479   return (space_info.free >= static_cast<uint64_t>(required_size));
480 }
481
482 fs::path GenerateTmpDir(const fs::path &app_path) {
483   fs::path install_tmp_dir;
484   fs::path tmp_dir(app_path);
485
486   do {
487     fs::path unique_dir = GenerateUniquePathString("unpack-%%%%%%");
488
489     install_tmp_dir = tmp_dir /= unique_dir;
490   } while (fs::exists(install_tmp_dir) &&
491            fs::is_directory(install_tmp_dir));
492
493   return install_tmp_dir;
494 }
495
496 fs::path GenerateTemporaryPath(const fs::path& path) {
497   fs::path pattern = path;
498   pattern += "-%%%%%%";
499   fs::path tmp_path;
500
501   do {
502     tmp_path = GenerateUniquePathString(pattern);
503   } while (fs::exists(tmp_path));
504
505   return tmp_path;
506 }
507
508 bool ExtractToTmpDir(const char* zip_path,
509                      const fs::path& tmp_dir) {
510   return ExtractToTmpDir(zip_path, tmp_dir, "");
511 }
512
513 bool ExtractToTmpDir(const char* zip_path, const fs::path& tmp_dir,
514                      const std::string& filter_prefix) {
515   unz_global_info info;
516   char read_buffer[kZipBufSize];
517   unz_file_info raw_file_info;
518   char raw_file_name_in_zip[kZipMaxPath];
519
520   current_path(tmp_dir);
521
522   UnzFilePointer zip_file;
523   if (!zip_file.Open(zip_path)) {
524     LOG(WARNING) << "Failed to open the source dir: " << zip_path;
525     return false;
526   }
527
528   if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
529     LOG(ERROR) << "Failed to read global info";
530     return false;
531   }
532
533   for (uLong i = 0; i < info.number_entry; i++) {
534     if (unzGetCurrentFileInfo(zip_file.Get(),
535                               &raw_file_info,
536                               raw_file_name_in_zip,
537                               sizeof(raw_file_name_in_zip),
538                               nullptr,
539                               0,
540                               nullptr,
541                               0) != UNZ_OK) {
542       LOG(ERROR) << "Failed to read file info";
543       return false;
544     }
545
546     if (raw_file_name_in_zip[0] == '\0')
547       return false;
548
549     // unpack if filter is empty or path is matched
550     if (filter_prefix.empty() ||
551         std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
552       fs::path filename_in_zip_path(raw_file_name_in_zip);
553
554       if (HasDirectoryClimbing(filename_in_zip_path)) {
555         LOG(ERROR) << "Relative path in widget in malformed";
556         return false;
557       }
558
559       if (!filename_in_zip_path.parent_path().empty()) {
560         if (!CreateDir(filename_in_zip_path.parent_path())) {
561           LOG(ERROR) << "Failed to create directory: "
562               << filename_in_zip_path.parent_path();
563           return false;
564         }
565       }
566
567       if (!zip_file.OpenCurrent()) {
568         LOG(ERROR) << "Failed to open file";
569         return false;
570       }
571
572       if (!is_directory(filename_in_zip_path)) {
573         FILE* out = fopen(raw_file_name_in_zip, "wbx");
574         if (!out) {
575           LOG(ERROR) << "Failed to open destination ";
576           return false;
577         }
578
579         int ret = UNZ_OK;
580         do {
581           ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
582           if (ret < 0) {
583             LOG(ERROR) << "Failed to read data: " << ret;
584             fclose(out);
585             return false;
586           } else {
587             fwrite(read_buffer, sizeof(char), ret, out);
588           }
589         } while (ret > 0);
590
591         fclose(out);
592       }
593
594       zip_file.CloseCurrent();
595     }
596
597     if ((i+1) < info.number_entry) {
598       if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
599         LOG(ERROR) << "Failed to read next file";
600         return false;
601       }
602     }
603   }
604
605   return true;
606 }
607
608 bool CheckPathInZipArchive(const char* zip_archive_path,
609                            const fs::path& relative_zip_path,
610                            bool* found) {
611   *found = false;
612   UnzFilePointer zip_file;
613   if (!zip_file.Open(zip_archive_path)) {
614     LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
615     return false;
616   }
617
618   unz_global_info info;
619   if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
620     LOG(ERROR) << "Failed to read global info";
621     return false;
622   }
623
624   char raw_file_name_in_zip[kZipMaxPath];
625   unz_file_info raw_file_info;
626   for (uLong i = 0; i < info.number_entry; i++) {
627     if (unzGetCurrentFileInfo(zip_file.Get(),
628                               &raw_file_info,
629                               raw_file_name_in_zip,
630                               sizeof(raw_file_name_in_zip),
631                               nullptr,
632                               0,
633                               nullptr,
634                               0) != UNZ_OK) {
635       LOG(ERROR) << "Failed to read file info";
636       return false;
637     }
638
639     if (raw_file_name_in_zip[0] == '\0')
640       return false;
641
642     if (relative_zip_path.string() == raw_file_name_in_zip) {
643       *found = true;
644       return true;
645     }
646
647     if ((i + 1) < info.number_entry) {
648       if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
649         LOG(ERROR) << "Failed to read next file";
650         return false;
651       }
652     }
653   }
654
655   return true;
656 }
657
658 bool HasDirectoryClimbing(const fs::path& path) {
659   std::regex re("[\\/\\\\]");
660   std::string path_str = path.string();
661   std::sregex_token_iterator first(path_str.begin(), path_str.end(), re, -1);
662   std::sregex_token_iterator last;
663   std::vector<std::string> segments(first, last);
664
665   return std::find(segments.begin(), segments.end(), "..") != segments.end();
666 }
667
668 fs::path MakeRelativePath(const fs::path& input,
669                                          const fs::path& base) {
670   if (input.string().find(base.string()) == std::string::npos) {
671       LOG(ERROR) << base.string() << " is not base path for " << input.string();
672       return input;
673   }
674
675   return input.string().substr(base.string().length() + 1);
676 }
677
678 bool IsSubDir(const fs::path& path, const fs::path& root) {
679   std::string relative = fs::relative(path, root);
680   return relative.size() == 1 || (relative[0] != '.' && relative[1] != '.');
681 }
682
683 std::vector<std::string> GetDirectoryList(const fs::path& cwd) {
684   if (!fs::exists(cwd))
685     return {};
686
687   std::vector<std::string> list;
688   for (fs::directory_iterator file(cwd); file != fs::directory_iterator();
689       ++file) {
690     if (!fs::is_directory(file->path()))
691       continue;
692     list.emplace_back(file->path().filename().string());
693   }
694
695   return list;
696 }
697
698 // To replace with std::filesystem::unique_path()
699 fs::path GenerateUniquePathString(const std::string& format) {
700   static constexpr auto chars =
701       "0123456789"
702       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
703       "abcdefghijklmnopqrstuvwxyz";
704   std::random_device rd;
705   std::mt19937 gen(rd());
706   auto dist = std::uniform_int_distribution{{}, std::strlen(chars) - 1};
707   size_t len = std::count_if(format.begin(), format.end(),
708       [](char c) { return c == '%'; });
709
710   auto random_string = std::string(len, '\0');
711   std::generate_n(begin(random_string), len,
712       [&]() {
713         return chars[dist(gen)];
714       });
715
716   auto iter = random_string.begin();
717   std::string result(format);
718   std::transform(result.begin(), result.end(), result.begin(),
719       [&](char c) {
720         return c == '%' ? *iter++ : c;
721       });
722
723   return result;
724 }
725
726 bool SyncFile(const fs::path& path) {
727   if (!fs::exists(path)) {
728     LOG(ERROR) << "File does not exist: " << path;
729     return false;
730   }
731
732   int fd = open(path.c_str(), O_WRONLY);
733   if (fd == -1) {
734     LOG(ERROR) << "Failed to open file: " << path << ", errno: " << errno;
735     return false;
736   }
737
738   int ret = fsync(fd);
739   if (ret == -1) {
740     LOG(ERROR) << "Failed to fsync() file: " << path << ", errno: " << errno;
741     close(fd);
742     return false;
743   }
744
745   close(fd);
746   return true;
747 }
748
749 }  // namespace common_installer