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