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 <manifest_parser/utils/logging.h>
18 #include <system_error>
21 #include "common/utils/byte_size_literals.h"
22 #include "common/utils/paths.h"
24 namespace fs = std::filesystem;
28 unsigned kZipBufSize = 8_kB;
29 unsigned kZipMaxPath = PATH_MAX;
31 int64_t GetBlockSizeForPath(const fs::path& path_in_partition) {
33 if (stat(path_in_partition.c_str(), &stats)) {
34 LOG(ERROR) << "stat(" << path_in_partition
35 << ") failed - error code: " << errno;
38 return stats.st_blksize;
41 int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
42 return ((size + block_size - 1) / block_size) * block_size;
45 class UnzFilePointer {
50 currentFileOpened_(false) { }
53 if (currentFileOpened_)
54 unzCloseCurrentFile(zipFile_);
59 bool Open(const char* zip_path) {
60 zipFile_ = static_cast<unzFile*>(unzOpen(zip_path));
68 if (unzOpenCurrentFile(zipFile_) != UNZ_OK)
70 currentFileOpened_ = true;
75 if (currentFileOpened_)
76 unzCloseCurrentFile(zipFile_);
77 currentFileOpened_ = false;
80 unzFile* Get() { return zipFile_; }
85 bool currentFileOpened_;
90 namespace common_installer {
92 FSFlag operator|(FSFlag a, FSFlag b) {
93 return static_cast<FSFlag>(static_cast<int>(a) | static_cast<int>(b));
96 bool SetOwnership(const fs::path& path, uid_t uid, gid_t gid) {
97 int ret = lchown(path.c_str(), uid, gid);
99 LOG(ERROR) << "Failed to change owner of: " << path;
105 bool SetOwnershipAll(const fs::path& path, uid_t uid, gid_t gid) {
106 if (!SetOwnership(path, uid, gid))
108 if (!fs::is_directory(path))
110 for (fs::recursive_directory_iterator iter(path);
111 iter != fs::recursive_directory_iterator();
113 fs::path current(iter->path());
114 if (!SetOwnership(current, uid, gid))
120 bool CreateDir(const fs::path& path) {
121 if (fs::exists(path))
124 std::error_code error;
125 fs::create_directories(path, error);
128 LOG(ERROR) << "Failed to create directory: " << error.message();
134 bool SetDirPermissions(const fs::path& path,
135 fs::perms permissions, bool add_perms) {
136 std::error_code error;
137 fs::permissions(path, permissions,
138 add_perms ? fs::perm_options::add : fs::perm_options::replace, error);
141 LOG(ERROR) << "Failed to set permissions for directory: " << path
148 bool SetDirOwnershipAndPermissions(const fs::path& path,
149 fs::perms permissions, uid_t uid,
151 if (!SetOwnership(path, uid, gid)) {
152 LOG(ERROR) << "Failed to change owner: " << path
153 << "(" << uid << ", " << gid << ")";
156 if (!SetDirPermissions(path, permissions)) {
157 LOG(ERROR) << "Failed to change permission: " << path;
164 bool CopyOwnershipAndPermissions(const fs::path& src, const fs::path& dst) {
165 if (!fs::exists(src)) {
166 LOG(ERROR) << "Failed to copy ownership and permissions"
167 << " from " << src << " to " << dst;
170 fs::perms permissions = fs::status(src).permissions();
172 if (stat(src.c_str(), &stats) != 0)
174 if (!SetDirOwnershipAndPermissions(dst, permissions, stats.st_uid,
176 LOG(ERROR) << "Failed to copy ownership and permissions"
177 << " from " << src << " to " << dst;
183 bool CopyDir(const fs::path& src, const fs::path& dst,
184 FSFlag flags, bool skip_symlink) {
186 // Check whether the function call is valid
187 if (!fs::exists(src) || !fs::is_directory(src)) {
188 LOG(ERROR) << "Source directory " << src
189 << " does not exist or is not a directory.";
192 if (!fs::exists(dst)) {
193 // Create the destination directory
194 if (!CreateDir(dst)) {
195 LOG(ERROR) << "Unable to create destination directory" << dst;
198 if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
199 CopyOwnershipAndPermissions(src, dst);
201 if (!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
202 LOG(ERROR) << "Destination directory " << dst.string()
203 << " already exists.";
206 if (flags & (FS_MERGE_OVERWRITE | FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS))
207 CopyOwnershipAndPermissions(src, dst);
209 } catch (const fs::filesystem_error& error) {
210 LOG(ERROR) << "Failed to copy directory: " << error.what();
214 // Iterate through the source directory
215 for (const auto& current : fs::directory_iterator(src)) {
217 fs::path target = dst / current.path().filename();
219 if (fs::is_symlink(symlink_status(current))) {
222 if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) &&
225 std::error_code error;
226 fs::copy_symlink(current, target, error);
228 LOG(ERROR) << "Failed to copy symlink: " << current << ", "
232 } else if (fs::is_directory(current)) {
233 // Found directory: Recursion
234 if (!CopyDir(current, target, flags, skip_symlink)) {
238 if ((flags & FS_MERGE_SKIP) && fs::exists(target))
240 fs::path destination = target;
242 if (flags & FS_COMMIT_COPY_FILE)
243 destination = target.parent_path() /
244 GenerateUniquePathString("%%%%-%%%%-%%%%-%%%%");
246 if (flags & FS_MERGE_OVERWRITE)
247 fs::copy_file(current, destination,
248 fs::copy_options::overwrite_existing);
250 fs::copy_file(current, destination);
252 if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
253 CopyOwnershipAndPermissions(current, destination);
255 if (flags & FS_COMMIT_COPY_FILE) {
256 if (flags & FS_MERGE_OVERWRITE)
258 fs::rename(destination, target);
261 } catch (const fs::filesystem_error& error) {
262 LOG(ERROR) << "Failed to copy directory: " << error.what();
269 bool CopyFile(const fs::path& src, const fs::path& dst) {
270 std::error_code error;
272 fs::copy_file(src, dst, fs::copy_options::overwrite_existing, error);
274 LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
280 bool RestoreBackup(const fs::path& path) {
281 fs::path backup_path = GetBackupPathForPackagePath(path);
282 if (!fs::exists(backup_path) &&
283 !fs::is_symlink(fs::symlink_status(backup_path))) {
284 LOG(WARNING) << backup_path << " does not exist";
287 return MoveDir(backup_path, path);
290 bool MakeBackup(const fs::path& path) {
291 if (!fs::exists(path) && !fs::is_symlink(fs::symlink_status(path))) {
292 LOG(WARNING) << path << " does not exist";
295 fs::path backup_path = GetBackupPathForPackagePath(path);
296 return MoveDir(path, backup_path);
299 bool RemoveBackup(const fs::path& path) {
300 fs::path backup_path = GetBackupPathForPackagePath(path);
301 if (!fs::exists(backup_path) &&
302 !fs::is_symlink(fs::symlink_status(backup_path))) {
303 LOG(WARNING) << backup_path << " does not exist";
306 return RemoveAll(backup_path);
309 bool RemoveAll(const fs::path& path) {
310 if (!fs::exists(path) && !fs::is_symlink(fs::symlink_status(path)))
313 std::error_code error;
314 fs::remove_all(path, error);
317 LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
324 bool Remove(const fs::path& path) {
325 std::error_code error;
326 if (!fs::exists(fs::symlink_status(path, error)))
329 fs::remove(path, error);
331 LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
337 bool MoveDir(const fs::path& src, const fs::path& dst, FSFlag flags) {
338 if (fs::exists(dst) &&
339 !(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
340 LOG(ERROR) << "Destination directory does exist: " << dst;
344 std::error_code error;
345 fs::rename(src, dst, error);
347 LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
348 if (!CopyDir(src, dst, flags, false)) {
349 LOG(ERROR) << "Cannot copy directory: " << src;
352 fs::remove_all(src, error);
354 LOG(ERROR) << "Cannot remove old directory when coping: " << src;
361 bool MoveFile(const fs::path& src, const fs::path& dst, bool force) {
362 if (!force && fs::exists(dst))
364 std::error_code error;
365 fs::rename(src, dst, error);
367 LOG(WARNING) << "Cannot move file: " << src <<
368 ". Will copy/remove... with error [" << error << "]";
369 fs::copy_file(src, dst, fs::copy_options::overwrite_existing, error);
371 LOG(WARNING) << "Cannot copy file " << src <<
372 " due to error [" << error << "]";
375 fs::remove_all(src, error);
377 LOG(ERROR) << "Cannot remove old file when coping: " << src <<
378 "with error [" << error << "]";
384 bool BackupDir(const fs::path& src,
385 const fs::path& dst, const std::string& entry) {
386 if (!fs::exists(src / entry))
389 if (!MoveDir(src / entry, dst / entry,
391 FS_COMMIT_COPY_FILE |
392 FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)) {
393 LOG(ERROR) << "Failed to backup file";
400 int64_t GetUnpackedPackageSize(const fs::path& path) {
402 int64_t block_size = GetBlockSizeForPath(path);
404 if (block_size == -1)
407 unz_global_info info;
408 unz_file_info64 raw_file_info;
409 char raw_file_name_in_zip[kZipMaxPath];
411 unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.c_str()));
412 if (zip_file == nullptr) {
413 LOG(ERROR) << "Failed to open the source dir: " << path.string();
417 if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
418 LOG(ERROR) << "Failed to read global info";
423 for (uLong i = 0; i < info.number_entry; i++) {
424 if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
425 sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
426 LOG(ERROR) << "Failed to read file info";
430 size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
431 unzGoToNextFile(zip_file);
434 // FIXME: calculate space needed for directories
439 int64_t GetDirectorySize(const fs::path& path) {
440 int64_t block_size = GetBlockSizeForPath(path);
442 if (block_size == -1)
446 for (fs::recursive_directory_iterator iter(path);
447 iter != fs::recursive_directory_iterator(); ++iter) {
449 if (lstat(iter->path().c_str(), &buf) == -1) {
450 LOG(ERROR) << "lstat() failed for: " << iter->path();
453 size += RoundUpToBlockSizeOf(buf.st_size, block_size);
456 // FIXME: block size for external device may differ...
460 bool CheckFreeSpaceAtPath(int64_t required_size,
461 const fs::path& target_location) {
462 std::error_code error;
463 fs::path root = target_location;
465 while (!fs::exists(root) && root != root.root_path())
466 root = root.parent_path();
468 if (!fs::exists(root)) {
469 LOG(ERROR) << "No mount point for path: " << target_location;
473 fs::space_info space_info = fs::space(root, error);
475 LOG(ERROR) << "Failed to get space_info: " << error.message();
479 return (space_info.free >= static_cast<uint64_t>(required_size));
482 fs::path GenerateTmpDir(const fs::path &app_path) {
483 fs::path install_tmp_dir;
484 fs::path tmp_dir(app_path);
487 fs::path unique_dir = GenerateUniquePathString("unpack-%%%%%%");
489 install_tmp_dir = tmp_dir /= unique_dir;
490 } while (fs::exists(install_tmp_dir) &&
491 fs::is_directory(install_tmp_dir));
493 return install_tmp_dir;
496 fs::path GenerateTemporaryPath(const fs::path& path) {
497 fs::path pattern = path;
498 pattern += "-%%%%%%";
502 tmp_path = GenerateUniquePathString(pattern);
503 } while (fs::exists(tmp_path));
508 bool ExtractToTmpDir(const char* zip_path,
509 const fs::path& tmp_dir) {
510 return ExtractToTmpDir(zip_path, tmp_dir, "");
513 bool ExtractToTmpDir(const char* zip_path, const fs::path& tmp_dir,
514 const std::string& filter_prefix) {
515 unz_global_info info;
516 char read_buffer[kZipBufSize];
517 unz_file_info raw_file_info;
518 char raw_file_name_in_zip[kZipMaxPath];
520 current_path(tmp_dir);
522 UnzFilePointer zip_file;
523 if (!zip_file.Open(zip_path)) {
524 LOG(WARNING) << "Failed to open the source dir: " << zip_path;
528 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
529 LOG(ERROR) << "Failed to read global info";
533 for (uLong i = 0; i < info.number_entry; i++) {
534 if (unzGetCurrentFileInfo(zip_file.Get(),
536 raw_file_name_in_zip,
537 sizeof(raw_file_name_in_zip),
542 LOG(ERROR) << "Failed to read file info";
546 if (raw_file_name_in_zip[0] == '\0')
549 // unpack if filter is empty or path is matched
550 if (filter_prefix.empty() ||
551 std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
552 fs::path filename_in_zip_path(raw_file_name_in_zip);
554 if (HasDirectoryClimbing(filename_in_zip_path)) {
555 LOG(ERROR) << "Relative path in widget in malformed";
559 if (!filename_in_zip_path.parent_path().empty()) {
560 if (!CreateDir(filename_in_zip_path.parent_path())) {
561 LOG(ERROR) << "Failed to create directory: "
562 << filename_in_zip_path.parent_path();
567 if (!zip_file.OpenCurrent()) {
568 LOG(ERROR) << "Failed to open file";
572 if (!is_directory(filename_in_zip_path)) {
573 FILE* out = fopen(raw_file_name_in_zip, "wbx");
575 LOG(ERROR) << "Failed to open destination ";
581 ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
583 LOG(ERROR) << "Failed to read data: " << ret;
587 fwrite(read_buffer, sizeof(char), ret, out);
594 zip_file.CloseCurrent();
597 if ((i+1) < info.number_entry) {
598 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
599 LOG(ERROR) << "Failed to read next file";
608 bool CheckPathInZipArchive(const char* zip_archive_path,
609 const fs::path& relative_zip_path,
612 UnzFilePointer zip_file;
613 if (!zip_file.Open(zip_archive_path)) {
614 LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
618 unz_global_info info;
619 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
620 LOG(ERROR) << "Failed to read global info";
624 char raw_file_name_in_zip[kZipMaxPath];
625 unz_file_info raw_file_info;
626 for (uLong i = 0; i < info.number_entry; i++) {
627 if (unzGetCurrentFileInfo(zip_file.Get(),
629 raw_file_name_in_zip,
630 sizeof(raw_file_name_in_zip),
635 LOG(ERROR) << "Failed to read file info";
639 if (raw_file_name_in_zip[0] == '\0')
642 if (relative_zip_path.string() == raw_file_name_in_zip) {
647 if ((i + 1) < info.number_entry) {
648 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
649 LOG(ERROR) << "Failed to read next file";
658 bool HasDirectoryClimbing(const fs::path& path) {
659 std::regex re("[\\/\\\\]");
660 std::string path_str = path.string();
661 std::sregex_token_iterator first(path_str.begin(), path_str.end(), re, -1);
662 std::sregex_token_iterator last;
663 std::vector<std::string> segments(first, last);
665 return std::find(segments.begin(), segments.end(), "..") != segments.end();
668 fs::path MakeRelativePath(const fs::path& input,
669 const fs::path& base) {
670 if (input.string().find(base.string()) == std::string::npos) {
671 LOG(ERROR) << base.string() << " is not base path for " << input.string();
675 return input.string().substr(base.string().length() + 1);
678 bool IsSubDir(const fs::path& path, const fs::path& root) {
679 std::string relative = fs::relative(path, root);
680 return relative.size() == 1 || (relative[0] != '.' && relative[1] != '.');
683 std::vector<std::string> GetDirectoryList(const fs::path& cwd) {
684 if (!fs::exists(cwd))
687 std::vector<std::string> list;
688 for (fs::directory_iterator file(cwd); file != fs::directory_iterator();
690 if (!fs::is_directory(file->path()))
692 list.emplace_back(file->path().filename().string());
698 // To replace with std::filesystem::unique_path()
699 fs::path GenerateUniquePathString(const std::string& format) {
700 static constexpr auto chars =
702 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
703 "abcdefghijklmnopqrstuvwxyz";
704 std::random_device rd;
705 std::mt19937 gen(rd());
706 auto dist = std::uniform_int_distribution{{}, std::strlen(chars) - 1};
707 size_t len = std::count_if(format.begin(), format.end(),
708 [](char c) { return c == '%'; });
710 auto random_string = std::string(len, '\0');
711 std::generate_n(begin(random_string), len,
713 return chars[dist(gen)];
716 auto iter = random_string.begin();
717 std::string result(format);
718 std::transform(result.begin(), result.end(), result.begin(),
720 return c == '%' ? *iter++ : c;
726 bool SyncFile(const fs::path& path) {
727 if (!fs::exists(path)) {
728 LOG(ERROR) << "File does not exist: " << path;
732 int fd = open(path.c_str(), O_WRONLY);
734 LOG(ERROR) << "Failed to open file: " << path << ", errno: " << errno;
740 LOG(ERROR) << "Failed to fsync() file: " << path << ", errno: " << errno;
749 } // namespace common_installer