1 /* 2014, Copyright © Intel Coporation, license APACHE-2.0, see LICENSE file */
3 #include "common/utils/file_util.h"
7 #include <linux/limits.h>
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>
17 #include <manifest_parser/utils/logging.h>
23 #include "common/utils/byte_size_literals.h"
24 #include "common/utils/paths.h"
26 namespace ba = boost::algorithm;
27 namespace bs = boost::system;
28 namespace bf = boost::filesystem;
32 unsigned kZipBufSize = 8_kB;
33 unsigned kZipMaxPath = PATH_MAX;
35 int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
37 if (stat(path_in_partition.string().c_str(), &stats)) {
38 LOG(ERROR) << "stat(" << path_in_partition.string()
39 << ") failed - error code: " << errno;
42 return stats.st_blksize;
45 int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
46 return ((size + block_size - 1) / block_size) * block_size;
49 class UnzFilePointer {
54 currentFileOpened_(false) { }
57 if (currentFileOpened_)
58 unzCloseCurrentFile(zipFile_);
63 bool Open(const char* zip_path) {
64 zipFile_ = static_cast<unzFile*>(unzOpen(zip_path));
72 if (unzOpenCurrentFile(zipFile_) != UNZ_OK)
74 currentFileOpened_ = true;
79 if (currentFileOpened_)
80 unzCloseCurrentFile(zipFile_);
81 currentFileOpened_ = false;
84 unzFile* Get() { return zipFile_; }
89 bool currentFileOpened_;
94 namespace common_installer {
96 FSFlag operator|(FSFlag a, FSFlag b) {
97 return static_cast<FSFlag>(static_cast<int>(a) | static_cast<int>(b));
100 bool SetOwnership(const bf::path& path, uid_t uid, gid_t gid) {
101 int ret = lchown(path.c_str(), uid, gid);
103 LOG(ERROR) << "Failed to change owner of: " << path;
109 bool SetOwnershipAll(const bf::path& path, uid_t uid, gid_t gid) {
110 if (!SetOwnership(path, uid, gid))
112 if (!bf::is_directory(path))
114 for (bf::recursive_directory_iterator iter(path);
115 iter != bf::recursive_directory_iterator();
117 bf::path current(iter->path());
118 if (!SetOwnership(current, uid, gid))
124 bool CreateDir(const bf::path& path) {
125 if (bf::exists(path))
128 boost::system::error_code error;
129 bf::create_directories(path, error);
132 LOG(ERROR) << "Failed to create directory: "
133 << boost::system::system_error(error).what();
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);
145 LOG(ERROR) << "Failed to set permissions for directory: " << path
146 << boost::system::system_error(error).what();
152 bool SetDirOwnershipAndPermissions(const boost::filesystem::path& path,
153 boost::filesystem::perms permissions, uid_t uid,
155 if (!SetOwnership(path, uid, gid)) {
156 LOG(ERROR) << "Failed to change owner: " << path
157 << "(" << uid << ", " << gid << ")";
160 if (!SetDirPermissions(path, permissions)) {
161 LOG(ERROR) << "Failed to change permission: " << path
162 << std::oct << permissions;
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;
176 bf::perms permissions = bf::status(src).permissions();
178 if (stat(src.c_str(), &stats) != 0)
180 if (!SetDirOwnershipAndPermissions(dst, permissions, stats.st_uid,
182 LOG(ERROR) << "Failed to copy ownership and permissions"
183 << " from " << src << " to " << dst;
189 bool CopyDir(const bf::path& src, const bf::path& dst,
190 FSFlag flags, bool skip_symlink) {
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.";
198 if (!bf::exists(dst)) {
199 // Create the destination directory
200 if (!CreateDir(dst)) {
201 LOG(ERROR) << "Unable to create destination directory" << dst;
204 if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
205 CopyOwnershipAndPermissions(src, dst);
207 if (!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
208 LOG(ERROR) << "Destination directory " << dst.string()
209 << " already exists.";
212 if (flags & (FS_MERGE_OVERWRITE | FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS))
213 CopyOwnershipAndPermissions(src, dst);
215 } catch (const bf::filesystem_error& error) {
216 LOG(ERROR) << "Failed to copy directory: " << error.what();
220 // Iterate through the source directory
221 for (bf::directory_iterator file(src);
222 file != bf::directory_iterator();
225 bf::path current(file->path());
226 bf::path target = dst / current.filename();
228 if (bf::is_symlink(symlink_status(current))) {
231 if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) &&
234 bs::error_code error;
235 bf::copy_symlink(current, target, error);
237 LOG(ERROR) << "Failed to copy symlink: " << current << ", "
241 } else if (bf::is_directory(current)) {
242 // Found directory: Recursion
243 if (!CopyDir(current, target, flags, skip_symlink)) {
247 if ((flags & FS_MERGE_SKIP) && bf::exists(target))
249 bf::path destination = target;
251 if (flags & FS_COMMIT_COPY_FILE)
253 bf::unique_path(target.parent_path() / "%%%%-%%%%-%%%%-%%%%");
255 if (flags & FS_MERGE_OVERWRITE)
256 bf::copy_file(current, destination,
257 bf::copy_option::overwrite_if_exists);
259 bf::copy_file(current, destination);
261 if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
262 CopyOwnershipAndPermissions(current, destination);
264 if (flags & FS_COMMIT_COPY_FILE) {
265 if (flags & FS_MERGE_OVERWRITE)
267 bf::rename(destination, target);
270 } catch (const bf::filesystem_error& error) {
271 LOG(ERROR) << "Failed to copy directory: " << error.what();
278 bool CopyFile(const bf::path& src, const bf::path& dst) {
279 bs::error_code error;
281 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
283 LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
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";
296 return MoveDir(backup_path, path);
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";
304 bf::path backup_path = GetBackupPathForPackagePath(path);
305 return MoveDir(path, backup_path);
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";
315 return RemoveAll(backup_path);
318 bool RemoveAll(const bf::path& path) {
319 if (!bf::exists(path) && !bf::is_symlink(bf::symlink_status(path)))
322 bs::error_code error;
323 bf::remove_all(path, error);
326 LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
333 bool Remove(const bf::path& path) {
334 bs::error_code error;
335 if (!bf::exists(bf::symlink_status(path, error)))
338 bf::remove(path, error);
340 LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
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;
353 bs::error_code error;
354 bf::rename(src, dst, 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;
361 bf::remove_all(src, error);
363 LOG(ERROR) << "Cannot remove old directory when coping: " << src;
370 bool MoveFile(const bf::path& src, const bf::path& dst, bool force) {
371 if (!force && bf::exists(dst))
373 bs::error_code error;
374 bf::rename(src, dst, 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);
380 LOG(WARNING) << "Cannot copy file " << src <<
381 " due to error [" << error << "]";
384 bf::remove_all(src, error);
386 LOG(ERROR) << "Cannot remove old file when coping: " << src <<
387 "with error [" << error << "]";
393 bool BackupDir(const boost::filesystem::path& src,
394 const boost::filesystem::path& dst, const std::string& entry) {
395 if (!bf::exists(src / entry))
398 if (!MoveDir(src / entry, dst / entry,
400 FS_COMMIT_COPY_FILE |
401 FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)) {
402 LOG(ERROR) << "Failed to backup file";
409 int64_t GetUnpackedPackageSize(const bf::path& path) {
411 int64_t block_size = GetBlockSizeForPath(path);
413 if (block_size == -1)
416 unz_global_info info;
417 unz_file_info64 raw_file_info;
418 char raw_file_name_in_zip[kZipMaxPath];
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();
426 if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
427 LOG(ERROR) << "Failed to read global info";
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";
439 size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
440 unzGoToNextFile(zip_file);
443 // FIXME: calculate space needed for directories
448 int64_t GetDirectorySize(const boost::filesystem::path& path) {
449 int64_t block_size = GetBlockSizeForPath(path);
451 if (block_size == -1)
455 for (bf::recursive_directory_iterator iter(path);
456 iter != bf::recursive_directory_iterator(); ++iter) {
458 if (lstat(iter->path().c_str(), &buf) == -1) {
459 LOG(ERROR) << "lstat() failed for: " << iter->path();
462 size += RoundUpToBlockSizeOf(buf.st_size, block_size);
465 // FIXME: block size for external device may differ...
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;
474 while (!bf::exists(root) && root != root.root_path())
475 root = root.parent_path();
477 if (!bf::exists(root)) {
478 LOG(ERROR) << "No mount point for path: " << target_location;
482 bf::space_info space_info = bf::space(root, error);
484 LOG(ERROR) << "Failed to get space_info: " << error.message();
488 return (space_info.free >= static_cast<uint64_t>(required_size));
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);
496 boost::filesystem::path model;
497 boost::filesystem::path unique_dir =
498 boost::filesystem::unique_path(model = "unpack-%%%%%%");
500 install_tmp_dir = tmp_dir /= unique_dir;
501 } while (boost::filesystem::exists(install_tmp_dir) &&
502 boost::filesystem::is_directory(install_tmp_dir));
504 return install_tmp_dir;
507 boost::filesystem::path GenerateTemporaryPath(
508 const boost::filesystem::path& path) {
509 bf::path pattern = path;
510 pattern += "-%%%%%%";
514 tmp_path = boost::filesystem::unique_path(pattern);
515 } while (boost::filesystem::exists(tmp_path));
520 bool ExtractToTmpDir(const char* zip_path,
521 const boost::filesystem::path& tmp_dir) {
522 return ExtractToTmpDir(zip_path, tmp_dir, "");
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];
532 current_path(tmp_dir);
534 UnzFilePointer zip_file;
535 if (!zip_file.Open(zip_path)) {
536 LOG(WARNING) << "Failed to open the source dir: " << zip_path;
540 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
541 LOG(ERROR) << "Failed to read global info";
545 for (uLong i = 0; i < info.number_entry; i++) {
546 if (unzGetCurrentFileInfo(zip_file.Get(),
548 raw_file_name_in_zip,
549 sizeof(raw_file_name_in_zip),
554 LOG(ERROR) << "Failed to read file info";
558 if (raw_file_name_in_zip[0] == '\0')
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);
566 if (HasDirectoryClimbing(filename_in_zip_path)) {
567 LOG(ERROR) << "Relative path in widget in malformed";
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();
579 if (!zip_file.OpenCurrent()) {
580 LOG(ERROR) << "Failed to open file";
584 if (!is_directory(filename_in_zip_path)) {
585 FILE* out = fopen(raw_file_name_in_zip, "wbx");
587 LOG(ERROR) << "Failed to open destination ";
593 ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
595 LOG(ERROR) << "Failed to read data: " << ret;
599 fwrite(read_buffer, sizeof(char), ret, out);
606 zip_file.CloseCurrent();
609 if ((i+1) < info.number_entry) {
610 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
611 LOG(ERROR) << "Failed to read next file";
620 bool CheckPathInZipArchive(const char* zip_archive_path,
621 const boost::filesystem::path& relative_zip_path,
624 UnzFilePointer zip_file;
625 if (!zip_file.Open(zip_archive_path)) {
626 LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
630 unz_global_info info;
631 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
632 LOG(ERROR) << "Failed to read global info";
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(),
641 raw_file_name_in_zip,
642 sizeof(raw_file_name_in_zip),
647 LOG(ERROR) << "Failed to read file info";
651 if (raw_file_name_in_zip[0] == '\0')
654 if (relative_zip_path.string() == raw_file_name_in_zip) {
659 if ((i + 1) < info.number_entry) {
660 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
661 LOG(ERROR) << "Failed to read next file";
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 == "..";
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();
686 return input.string().substr(base.string().length() + 1);
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))
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();
707 while (itr_path != from.end() && itr_relative_to != to.end() &&
708 *itr_path == *itr_relative_to) {
714 if (itr_relative_to != to.end()) {
716 while (itr_relative_to != to.end()) {
722 while (itr_path != from.end()) {
727 bs::error_code error;
728 bf::path resolved_path = bf::canonical(result, error);
730 LOG(ERROR) << "Failed to get canonical path";
734 if (from != resolved_path) {
735 LOG(ERROR) << "Failed to get right relative path :" << resolved_path;
742 std::vector<std::string> GetDirectoryList(const boost::filesystem::path& cwd) {
743 if (!bf::exists(cwd))
746 std::vector<std::string> list;
747 for (bf::directory_iterator file(cwd); file != bf::directory_iterator();
749 if (!bf::is_directory(file->path()))
751 list.emplace_back(file->path().filename().string());
757 } // namespace common_installer