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