Change the SetOwnership function behavior
[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 (!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   if (!exists(path) && !bf::is_symlink(bf::symlink_status(path)))
335     return true;
336
337   bs::error_code error;
338   bf::remove(path, error);
339
340   if (error) {
341     LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
342     return false;
343   }
344   return true;
345 }
346
347 bool MoveDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
348   if (bf::exists(dst) &&
349       !(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
350     LOG(ERROR) << "Destination directory does exist: " << dst;
351     return false;
352   }
353
354   bs::error_code error;
355   bf::rename(src, dst, error);
356   if (error) {
357     LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
358     if (!CopyDir(src, dst, flags, false)) {
359       LOG(ERROR) << "Cannot copy directory: " << src;
360       return false;
361     }
362     bf::remove_all(src, error);
363     if (error) {
364       LOG(ERROR) << "Cannot remove old directory when coping: " << src;
365       return false;
366     }
367   }
368   return true;
369 }
370
371 bool MoveFile(const bf::path& src, const bf::path& dst, bool force) {
372   if (!force && bf::exists(dst))
373     return false;
374   bs::error_code error;
375   bf::rename(src, dst, error);
376   if (error) {
377     LOG(WARNING) << "Cannot move file: " << src <<
378         ". Will copy/remove... with error [" << error << "]";
379     bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
380     if (error) {
381       LOG(WARNING) << "Cannot copy file " << src <<
382           " due to error [" << error << "]";
383       return false;
384     }
385     bf::remove_all(src, error);
386     if (error) {
387       LOG(ERROR) << "Cannot remove old file when coping: " << src <<
388           "with error [" << error << "]";
389     }
390   }
391   return true;
392 }
393
394 bool BackupDir(const boost::filesystem::path& src,
395     const boost::filesystem::path& dst, const std::string& entry) {
396   if (!bf::exists(src / entry))
397     return true;
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 failed to stat path
414   if (block_size == -1)
415     return -1;
416
417   unz_global_info info;
418   unz_file_info64 raw_file_info;
419   char raw_file_name_in_zip[kZipMaxPath];
420
421   unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
422   if (zip_file == nullptr) {
423     LOG(ERROR) << "Failed to open the source dir: " << path.string();
424     return -1;
425   }
426
427   if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
428     LOG(ERROR) << "Failed to read global info";
429     unzClose(zip_file);
430     return -1;
431   }
432
433   for (uLong i = 0; i < info.number_entry; i++) {
434     if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
435         sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
436       LOG(ERROR) << "Failed to read file info";
437       unzClose(zip_file);
438       return -1;
439     }
440     size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
441     unzGoToNextFile(zip_file);
442   }
443
444   // FIXME: calculate space needed for directories
445   unzClose(zip_file);
446   return size;
447 }
448
449 int64_t GetDirectorySize(const boost::filesystem::path& path) {
450   int64_t block_size = GetBlockSizeForPath(path);
451
452   // if failed to stat path
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   while (!bf::exists(root) && root != root.root_path())
476     root = root.parent_path();
477
478   if (!bf::exists(root)) {
479     LOG(ERROR) << "No mount point for path: " << target_location;
480     return false;
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   do {
513     tmp_path = boost::filesystem::unique_path(pattern);
514   } while (boost::filesystem::exists(tmp_path));
515   return tmp_path;
516 }
517
518 bool ExtractToTmpDir(const char* zip_path,
519                      const boost::filesystem::path& tmp_dir) {
520   return ExtractToTmpDir(zip_path, tmp_dir, "");
521 }
522
523 bool ExtractToTmpDir(const char* zip_path, const bf::path& tmp_dir,
524                      const std::string& filter_prefix) {
525   unz_global_info info;
526   char read_buffer[kZipBufSize];
527   unz_file_info raw_file_info;
528   char raw_file_name_in_zip[kZipMaxPath];
529
530   current_path(tmp_dir);
531
532   UnzFilePointer zip_file;
533   if (!zip_file.Open(zip_path)) {
534     LOG(WARNING) << "Failed to open the source dir: " << zip_path;
535     return false;
536   }
537
538   if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
539     LOG(ERROR) << "Failed to read global info";
540     return false;
541   }
542
543   for (uLong i = 0; i < info.number_entry; i++) {
544     if (unzGetCurrentFileInfo(zip_file.Get(),
545                               &raw_file_info,
546                               raw_file_name_in_zip,
547                               sizeof(raw_file_name_in_zip),
548                               nullptr,
549                               0,
550                               nullptr,
551                               0) != UNZ_OK) {
552       LOG(ERROR) << "Failed to read file info";
553       return false;
554     }
555
556     if (raw_file_name_in_zip[0] == '\0')
557       return false;
558
559     // unpack if filter is empty or path is matched
560     if (filter_prefix.empty() ||
561         std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
562       bf::path filename_in_zip_path(raw_file_name_in_zip);
563
564       // prevent "directory climbing" attack
565       if (HasDirectoryClimbing(filename_in_zip_path)) {
566         LOG(ERROR) << "Relative path in widget in malformed";
567         return false;
568       }
569
570       if (!filename_in_zip_path.parent_path().empty()) {
571         if (!CreateDir(filename_in_zip_path.parent_path())) {
572           LOG(ERROR) << "Failed to create directory: "
573               << filename_in_zip_path.parent_path();
574           return false;
575         }
576       }
577
578       if (!zip_file.OpenCurrent()) {
579         LOG(ERROR) << "Failed to open file";
580         return false;
581       }
582
583       if (!is_directory(filename_in_zip_path)) {
584         FILE* out = fopen(raw_file_name_in_zip, "wbx");
585         if (!out) {
586           LOG(ERROR) << "Failed to open destination ";
587           return false;
588         }
589
590         int ret = UNZ_OK;
591         do {
592           ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
593           if (ret < 0) {
594             LOG(ERROR) << "Failed to read data: " << ret;
595             fclose(out);
596             return false;
597           } else {
598             fwrite(read_buffer, sizeof(char), ret, out);
599           }
600         } while (ret > 0);
601
602         fclose(out);
603       }
604
605       zip_file.CloseCurrent();
606     }
607
608     if ((i+1) < info.number_entry) {
609       if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
610         LOG(ERROR) << "Failed to read next file";
611         return false;
612       }
613     }
614   }
615
616   return true;
617 }
618
619 bool CheckPathInZipArchive(const char* zip_archive_path,
620                            const boost::filesystem::path& relative_zip_path,
621                            bool* found) {
622   *found = false;
623   UnzFilePointer zip_file;
624   if (!zip_file.Open(zip_archive_path)) {
625     LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
626     return false;
627   }
628
629   unz_global_info info;
630   if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
631     LOG(ERROR) << "Failed to read global info";
632     return false;
633   }
634
635   char raw_file_name_in_zip[kZipMaxPath];
636   unz_file_info raw_file_info;
637   for (uLong i = 0; i < info.number_entry; i++) {
638     if (unzGetCurrentFileInfo(zip_file.Get(),
639                               &raw_file_info,
640                               raw_file_name_in_zip,
641                               sizeof(raw_file_name_in_zip),
642                               nullptr,
643                               0,
644                               nullptr,
645                               0) != UNZ_OK) {
646       LOG(ERROR) << "Failed to read file info";
647       return false;
648     }
649
650     if (raw_file_name_in_zip[0] == '\0')
651       return false;
652
653     if (relative_zip_path.string() == raw_file_name_in_zip) {
654       *found = true;
655       return true;
656     }
657
658     if ((i + 1) < info.number_entry) {
659       if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
660         LOG(ERROR) << "Failed to read next file";
661         return false;
662       }
663     }
664   }
665
666   return true;
667 }
668
669 bool HasDirectoryClimbing(const boost::filesystem::path& path) {
670   std::vector<std::string> segments;
671   ba::split(segments, path.string(), ba::is_any_of("/\\"));
672   return std::any_of(segments.begin(), segments.end(),
673                   [](const std::string& segment) {
674                     return segment == "..";
675                   });
676 }
677
678 boost::filesystem::path MakeRelativePath(const boost::filesystem::path& input,
679                                          const boost::filesystem::path& base) {
680   if (input.string().find(base.string()) == std::string::npos) {
681       LOG(ERROR) << base.string() << " is not base path for " << input.string();
682       return input;
683   }
684   return input.string().substr(base.string().length() + 1);
685 }
686
687 bool IsSubDir(const boost::filesystem::path& path,
688     const boost::filesystem::path& root) {
689   boost::filesystem::path p = path;
690   while (p != boost::filesystem::path()) {
691     if (bf::equivalent(p, root))
692       return true;
693     else
694       p = p.parent_path();
695   }
696   return false;
697 }
698
699 bf::path RelativePath(const bf::path& from,
700                                      const bf::path& to) {
701   bf::path::const_iterator itr_path = from.begin();
702   bf::path::const_iterator itr_relative_to = to.begin();
703   while (itr_path != from.end() && itr_relative_to != to.end() &&
704          *itr_path == *itr_relative_to) {
705     ++itr_path;
706     ++itr_relative_to;
707   }
708
709   bf::path result;
710   if (itr_relative_to != to.end()) {
711     ++itr_relative_to;
712     while (itr_relative_to != to.end()) {
713       result /= "..";
714       ++itr_relative_to;
715     }
716   }
717
718   while (itr_path != from.end()) {
719     result /= *itr_path;
720     ++itr_path;
721   }
722
723   bs::error_code error;
724   bf::path resolved_path = bf::canonical(result, error);
725   if (error) {
726     LOG(ERROR) << "Failed to get canonical path";
727     return {};
728   }
729
730   if (from != resolved_path) {
731     LOG(ERROR) << "Failed to get right relative path :" << resolved_path;
732     return {};
733   }
734
735   return result;
736 }
737
738 }  // namespace common_installer