1 /* 2014, Copyright © Intel Coporation, license APACHE-2.0, see LICENSE file */
3 #include "common/utils/file_util.h"
7 #include <linux/limits.h>
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>
19 #include <manifest_parser/utils/logging.h>
25 #include "common/utils/byte_size_literals.h"
26 #include "common/utils/paths.h"
28 namespace ba = boost::algorithm;
29 namespace bs = boost::system;
30 namespace bf = boost::filesystem;
34 unsigned kZipBufSize = 8_kB;
35 unsigned kZipMaxPath = PATH_MAX;
37 int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
39 if (stat(path_in_partition.string().c_str(), &stats)) {
40 LOG(ERROR) << "stat(" << path_in_partition.string()
41 << ") failed - error code: " << errno;
44 return stats.st_blksize;
47 int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
48 return ((size + block_size - 1) / block_size) * block_size;
51 class UnzFilePointer {
56 currentFileOpened_(false) { }
59 if (currentFileOpened_)
60 unzCloseCurrentFile(zipFile_);
65 bool Open(const char* zip_path) {
66 zipFile_ = static_cast<unzFile*>(unzOpen(zip_path));
74 if (unzOpenCurrentFile(zipFile_) != UNZ_OK)
76 currentFileOpened_ = true;
81 if (currentFileOpened_)
82 unzCloseCurrentFile(zipFile_);
83 currentFileOpened_ = false;
86 unzFile* Get() { return zipFile_; }
91 bool currentFileOpened_;
96 namespace common_installer {
98 FSFlag operator|(FSFlag a, FSFlag b) {
99 return static_cast<FSFlag>(static_cast<int>(a) | static_cast<int>(b));
102 bool SetOwnership(const bf::path& path, uid_t uid, gid_t gid) {
103 int ret = lchown(path.c_str(), uid, gid);
105 LOG(ERROR) << "Failed to change owner of: " << path;
111 bool SetOwnershipAll(const bf::path& path, uid_t uid, gid_t gid) {
112 if (!SetOwnership(path, uid, gid))
114 if (!bf::is_directory(path))
116 for (bf::recursive_directory_iterator iter(path);
117 iter != bf::recursive_directory_iterator();
119 bf::path current(iter->path());
120 if (!SetOwnership(current, uid, gid))
126 bool CreateDir(const bf::path& path) {
127 if (bf::exists(path))
130 boost::system::error_code error;
131 bf::create_directories(path, error);
134 LOG(ERROR) << "Failed to create directory: "
135 << boost::system::system_error(error).what();
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);
147 LOG(ERROR) << "Failed to set permissions for directory: " << path
148 << boost::system::system_error(error).what();
154 bool SetDirOwnershipAndPermissions(const boost::filesystem::path& path,
155 boost::filesystem::perms permissions, uid_t uid,
157 if (!SetOwnership(path, uid, gid)) {
158 LOG(ERROR) << "Failed to change owner: " << path
159 << "(" << uid << ", " << gid << ")";
162 if (!SetDirPermissions(path, permissions)) {
163 LOG(ERROR) << "Failed to change permission: " << path
164 << std::oct << permissions;
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;
178 bf::perms permissions = bf::status(src).permissions();
180 if (stat(src.c_str(), &stats) != 0)
182 if (!SetDirOwnershipAndPermissions(dst, permissions, stats.st_uid,
184 LOG(ERROR) << "Failed to copy ownership and permissions"
185 << " from " << src << " to " << dst;
191 bool CopyDir(const bf::path& src, const bf::path& dst,
192 FSFlag flags, bool skip_symlink) {
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.";
200 if (!bf::exists(dst)) {
201 // Create the destination directory
202 if (!CreateDir(dst)) {
203 LOG(ERROR) << "Unable to create destination directory" << dst;
206 if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
207 CopyOwnershipAndPermissions(src, dst);
209 if (!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
210 LOG(ERROR) << "Destination directory " << dst.string()
211 << " already exists.";
214 if (flags & (FS_MERGE_OVERWRITE | FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS))
215 CopyOwnershipAndPermissions(src, dst);
217 } catch (const bf::filesystem_error& error) {
218 LOG(ERROR) << "Failed to copy directory: " << error.what();
222 // Iterate through the source directory
223 for (bf::directory_iterator file(src);
224 file != bf::directory_iterator();
227 bf::path current(file->path());
228 bf::path target = dst / current.filename();
230 if (bf::is_symlink(symlink_status(current))) {
233 if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) &&
236 bs::error_code error;
237 bf::copy_symlink(current, target, error);
239 LOG(ERROR) << "Failed to copy symlink: " << current << ", "
243 } else if (bf::is_directory(current)) {
244 // Found directory: Recursion
245 if (!CopyDir(current, target, flags, skip_symlink)) {
249 if ((flags & FS_MERGE_SKIP) && bf::exists(target))
251 bf::path destination = target;
253 if (flags & FS_COMMIT_COPY_FILE)
255 bf::unique_path(target.parent_path() / "%%%%-%%%%-%%%%-%%%%");
257 if (flags & FS_MERGE_OVERWRITE)
258 bf::copy_file(current, destination,
259 bf::copy_option::overwrite_if_exists);
261 bf::copy_file(current, destination);
263 if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
264 CopyOwnershipAndPermissions(current, destination);
266 if (flags & FS_COMMIT_COPY_FILE) {
267 if (flags & FS_MERGE_OVERWRITE)
269 bf::rename(destination, target);
272 } catch (const bf::filesystem_error& error) {
273 LOG(ERROR) << "Failed to copy directory: " << error.what();
280 bool CopyFile(const bf::path& src, const bf::path& dst) {
281 bs::error_code error;
283 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
285 LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
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";
298 return MoveDir(backup_path, path);
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";
306 bf::path backup_path = GetBackupPathForPackagePath(path);
307 return MoveDir(path, backup_path);
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";
317 return RemoveAll(backup_path);
320 bool RemoveAll(const bf::path& path) {
321 if (!bf::exists(path) && !bf::is_symlink(bf::symlink_status(path)))
324 bs::error_code error;
325 bf::remove_all(path, error);
328 LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
335 bool Remove(const bf::path& path) {
336 bs::error_code error;
337 if (!bf::exists(bf::symlink_status(path, error)))
340 bf::remove(path, error);
342 LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
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;
355 bs::error_code error;
356 bf::rename(src, dst, 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;
363 bf::remove_all(src, error);
365 LOG(ERROR) << "Cannot remove old directory when coping: " << src;
372 bool MoveFile(const bf::path& src, const bf::path& dst, bool force) {
373 if (!force && bf::exists(dst))
375 bs::error_code error;
376 bf::rename(src, dst, 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);
382 LOG(WARNING) << "Cannot copy file " << src <<
383 " due to error [" << error << "]";
386 bf::remove_all(src, error);
388 LOG(ERROR) << "Cannot remove old file when coping: " << src <<
389 "with error [" << error << "]";
395 bool BackupDir(const boost::filesystem::path& src,
396 const boost::filesystem::path& dst, const std::string& entry) {
397 if (!bf::exists(src / entry))
400 if (!MoveDir(src / entry, dst / entry,
402 FS_COMMIT_COPY_FILE |
403 FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)) {
404 LOG(ERROR) << "Failed to backup file";
411 int64_t GetUnpackedPackageSize(const bf::path& path) {
413 int64_t block_size = GetBlockSizeForPath(path);
415 if (block_size == -1)
418 unz_global_info info;
419 unz_file_info64 raw_file_info;
420 char raw_file_name_in_zip[kZipMaxPath];
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();
428 if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
429 LOG(ERROR) << "Failed to read global info";
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";
441 size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
442 unzGoToNextFile(zip_file);
445 // FIXME: calculate space needed for directories
450 int64_t GetDirectorySize(const boost::filesystem::path& path) {
451 int64_t block_size = GetBlockSizeForPath(path);
453 if (block_size == -1)
457 for (bf::recursive_directory_iterator iter(path);
458 iter != bf::recursive_directory_iterator(); ++iter) {
460 if (lstat(iter->path().c_str(), &buf) == -1) {
461 LOG(ERROR) << "lstat() failed for: " << iter->path();
464 size += RoundUpToBlockSizeOf(buf.st_size, block_size);
467 // FIXME: block size for external device may differ...
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;
476 while (!bf::exists(root) && root != root.root_path())
477 root = root.parent_path();
479 if (!bf::exists(root)) {
480 LOG(ERROR) << "No mount point for path: " << target_location;
484 bf::space_info space_info = bf::space(root, error);
486 LOG(ERROR) << "Failed to get space_info: " << error.message();
490 return (space_info.free >= static_cast<uint64_t>(required_size));
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);
498 boost::filesystem::path model;
499 boost::filesystem::path unique_dir =
500 boost::filesystem::unique_path(model = "unpack-%%%%%%");
502 install_tmp_dir = tmp_dir /= unique_dir;
503 } while (boost::filesystem::exists(install_tmp_dir) &&
504 boost::filesystem::is_directory(install_tmp_dir));
506 return install_tmp_dir;
509 boost::filesystem::path GenerateTemporaryPath(
510 const boost::filesystem::path& path) {
511 bf::path pattern = path;
512 pattern += "-%%%%%%";
516 tmp_path = boost::filesystem::unique_path(pattern);
517 } while (boost::filesystem::exists(tmp_path));
522 bool ExtractToTmpDir(const char* zip_path,
523 const boost::filesystem::path& tmp_dir) {
524 return ExtractToTmpDir(zip_path, tmp_dir, "");
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];
534 current_path(tmp_dir);
536 UnzFilePointer zip_file;
537 if (!zip_file.Open(zip_path)) {
538 LOG(WARNING) << "Failed to open the source dir: " << zip_path;
542 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
543 LOG(ERROR) << "Failed to read global info";
547 for (uLong i = 0; i < info.number_entry; i++) {
548 if (unzGetCurrentFileInfo(zip_file.Get(),
550 raw_file_name_in_zip,
551 sizeof(raw_file_name_in_zip),
556 LOG(ERROR) << "Failed to read file info";
560 if (raw_file_name_in_zip[0] == '\0')
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);
568 if (HasDirectoryClimbing(filename_in_zip_path)) {
569 LOG(ERROR) << "Relative path in widget in malformed";
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();
581 if (!zip_file.OpenCurrent()) {
582 LOG(ERROR) << "Failed to open file";
586 if (!is_directory(filename_in_zip_path)) {
587 FILE* out = fopen(raw_file_name_in_zip, "wbx");
589 LOG(ERROR) << "Failed to open destination ";
595 ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
597 LOG(ERROR) << "Failed to read data: " << ret;
601 fwrite(read_buffer, sizeof(char), ret, out);
608 zip_file.CloseCurrent();
611 if ((i+1) < info.number_entry) {
612 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
613 LOG(ERROR) << "Failed to read next file";
622 bool CheckPathInZipArchive(const char* zip_archive_path,
623 const boost::filesystem::path& relative_zip_path,
626 UnzFilePointer zip_file;
627 if (!zip_file.Open(zip_archive_path)) {
628 LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
632 unz_global_info info;
633 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
634 LOG(ERROR) << "Failed to read global info";
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(),
643 raw_file_name_in_zip,
644 sizeof(raw_file_name_in_zip),
649 LOG(ERROR) << "Failed to read file info";
653 if (raw_file_name_in_zip[0] == '\0')
656 if (relative_zip_path.string() == raw_file_name_in_zip) {
661 if ((i + 1) < info.number_entry) {
662 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
663 LOG(ERROR) << "Failed to read next file";
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 == "..";
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();
688 return input.string().substr(base.string().length() + 1);
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))
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();
709 while (itr_path != from.end() && itr_relative_to != to.end() &&
710 *itr_path == *itr_relative_to) {
716 if (itr_relative_to != to.end()) {
718 while (itr_relative_to != to.end()) {
724 while (itr_path != from.end()) {
729 bs::error_code error;
730 bf::path resolved_path = bf::canonical(result, error);
732 LOG(ERROR) << "Failed to get canonical path";
736 if (from != resolved_path) {
737 LOG(ERROR) << "Failed to get right relative path :" << resolved_path;
744 std::vector<std::string> GetLightUserList(uid_t uid) {
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;
754 std::vector<std::string> result_list;
755 for (int i = 0; i < user_count; i++) {
756 if (strlen(user_list[i]) == 0)
759 result_list.emplace_back(user_list[i]);
762 std::free(user_list);
766 } // namespace common_installer