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"
25 namespace ba = boost::algorithm;
26 namespace bs = boost::system;
27 namespace bf = boost::filesystem;
31 unsigned kZipBufSize = 8_kB;
32 unsigned kZipMaxPath = PATH_MAX;
34 int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
36 if (stat(path_in_partition.string().c_str(), &stats)) {
37 LOG(ERROR) << "stat(" << path_in_partition.string()
38 << ") failed - error code: " << errno;
41 return stats.st_blksize;
44 int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
45 return ((size + block_size - 1) / block_size) * block_size;
48 class UnzFilePointer {
53 currentFileOpened_(false) { }
56 if (currentFileOpened_)
57 unzCloseCurrentFile(zipFile_);
62 bool Open(const char* zip_path) {
63 zipFile_ = static_cast<unzFile*>(unzOpen(zip_path));
71 if (unzOpenCurrentFile(zipFile_) != UNZ_OK)
73 currentFileOpened_ = true;
78 if (currentFileOpened_)
79 unzCloseCurrentFile(zipFile_);
80 currentFileOpened_ = false;
83 unzFile* Get() { return zipFile_; }
88 bool currentFileOpened_;
93 namespace common_installer {
95 bool SetOwnership(const bf::path& path, uid_t uid, gid_t gid) {
96 int fd = open(path.c_str(), O_RDONLY);
98 LOG(ERROR) << "Can't open directory : " << path;
101 int ret = fchown(fd, uid, gid);
104 LOG(ERROR) << "Failed to change owner of: " << path;
110 bool SetOwnershipAll(const bf::path& path, uid_t uid, gid_t gid) {
111 if (!SetOwnership(path, uid, gid))
113 if (!bf::is_directory(path))
115 for (bf::recursive_directory_iterator iter(path);
116 iter != bf::recursive_directory_iterator();
118 bf::path current(iter->path());
119 if (bf::is_symlink(symlink_status(current)))
121 if (!SetOwnership(current, uid, gid))
127 bool CreateDir(const bf::path& path) {
128 if (bf::exists(path))
131 boost::system::error_code error;
132 bf::create_directories(path, error);
135 LOG(ERROR) << "Failed to create directory: "
136 << boost::system::system_error(error).what();
142 bool SetDirPermissions(const boost::filesystem::path& path,
143 boost::filesystem::perms permissions) {
144 boost::system::error_code error;
145 bf::permissions(path, permissions, error);
148 LOG(ERROR) << "Failed to set permissions for directory: " << path
149 << boost::system::system_error(error).what();
155 bool CopyDir(const bf::path& src, const bf::path& dst,
156 FSFlag flags, bool skip_symlink) {
158 // Check whether the function call is valid
159 if (!bf::exists(src) || !bf::is_directory(src)) {
160 LOG(ERROR) << "Source directory " << src
161 << " does not exist or is not a directory.";
164 if (!bf::exists(dst)) {
165 // Create the destination directory
166 if (!CreateDir(dst)) {
167 LOG(ERROR) << "Unable to create destination directory" << dst;
171 if (!(flags & FS_MERGE_DIRECTORIES)) {
172 LOG(ERROR) << "Destination directory " << dst.string()
173 << " already exists.";
177 } catch (const bf::filesystem_error& error) {
178 LOG(ERROR) << "Failed to copy directory: " << error.what();
182 // Iterate through the source directory
183 for (bf::directory_iterator file(src);
184 file != bf::directory_iterator();
187 bf::path current(file->path());
188 bf::path target = dst / current.filename();
190 if (bf::is_symlink(symlink_status(current))) {
193 if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
195 bf::copy_symlink(current, target);
196 } else if (bf::is_directory(current)) {
197 // Found directory: Recursion
198 if (!CopyDir(current, target, flags, skip_symlink)) {
202 if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
204 bf::copy_file(current, target);
206 } catch (const bf::filesystem_error& error) {
207 LOG(ERROR) << "Failed to copy directory: " << error.what();
214 bool CopyFile(const bf::path& src, const bf::path& dst) {
215 bs::error_code error;
217 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
219 LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
225 bool MoveDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
226 if (bf::exists(dst) && !(flags & FS_MERGE_DIRECTORIES)) {
227 LOG(ERROR) << "Destination directory does exist: " << dst;
231 bs::error_code error;
232 bf::rename(src, dst, error);
234 LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
235 if (!CopyDir(src, dst, flags, false)) {
236 LOG(ERROR) << "Cannot copy directory: " << src;
239 bf::remove_all(src, error);
241 LOG(ERROR) << "Cannot remove old directory when coping: " << src;
248 bool MoveFile(const bf::path& src, const bf::path& dst) {
251 bs::error_code error;
252 bf::rename(src, dst, error);
254 LOG(WARNING) << "Cannot move file: " << src <<
255 ". Will copy/remove... with error [" << error << "]";
256 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
258 LOG(WARNING) << "Cannot copy file " << src <<
259 " due to error [" << error << "]";
262 bf::remove_all(src, error);
264 LOG(ERROR) << "Cannot remove old file when coping: " << src <<
265 "with error [" << error << "]";
271 int64_t GetUnpackedPackageSize(const bf::path& path) {
273 int64_t block_size = GetBlockSizeForPath(path);
275 // if failed to stat path
276 if (block_size == -1)
279 unz_global_info info;
280 unz_file_info64 raw_file_info;
281 char raw_file_name_in_zip[kZipMaxPath];
283 unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
284 if (zip_file == nullptr) {
285 LOG(ERROR) << "Failed to open the source dir: " << path.string();
289 if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
290 LOG(ERROR) << "Failed to read global info";
295 for (uLong i = 0; i < info.number_entry; i++) {
296 if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
297 sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
298 LOG(ERROR) << "Failed to read file info";
302 size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
303 unzGoToNextFile(zip_file);
306 // FIXME: calculate space needed for directories
311 int64_t GetDirectorySize(const boost::filesystem::path& path) {
312 int64_t block_size = GetBlockSizeForPath(path);
314 // if failed to stat path
315 if (block_size == -1)
319 for (bf::recursive_directory_iterator iter(path);
320 iter != bf::recursive_directory_iterator(); ++iter) {
322 if (lstat(iter->path().c_str(), &buf) == -1) {
323 LOG(ERROR) << "lstat() failed for: " << iter->path();
326 size += RoundUpToBlockSizeOf(buf.st_size, block_size);
329 // FIXME: block size for external device may differ...
333 boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
334 boost::filesystem::path install_tmp_dir;
335 boost::filesystem::path tmp_dir(app_path);
338 boost::filesystem::path model;
339 boost::filesystem::path unique_dir =
340 boost::filesystem::unique_path(model = "unpack-%%%%%%");
342 install_tmp_dir = tmp_dir /= unique_dir;
343 } while (boost::filesystem::exists(install_tmp_dir) &&
344 boost::filesystem::is_directory(install_tmp_dir));
346 return install_tmp_dir;
349 boost::filesystem::path GenerateTemporaryPath(
350 const boost::filesystem::path& path) {
351 bf::path pattern = path;
352 pattern += "-%%%%%%";
355 tmp_path = boost::filesystem::unique_path(pattern);
356 } while (boost::filesystem::exists(tmp_path));
360 bool ExtractToTmpDir(const char* zip_path,
361 const boost::filesystem::path& tmp_dir) {
362 return ExtractToTmpDir(zip_path, tmp_dir, "");
365 bool ExtractToTmpDir(const char* zip_path, const bf::path& tmp_dir,
366 const std::string& filter_prefix) {
367 unz_global_info info;
368 char read_buffer[kZipBufSize];
369 unz_file_info raw_file_info;
370 char raw_file_name_in_zip[kZipMaxPath];
372 current_path(tmp_dir);
374 UnzFilePointer zip_file;
375 if (!zip_file.Open(zip_path)) {
376 LOG(ERROR) << "Failed to open the source dir: " << zip_path;
380 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
381 LOG(ERROR) << "Failed to read global info";
385 for (uLong i = 0; i < info.number_entry; i++) {
386 if (unzGetCurrentFileInfo(zip_file.Get(),
388 raw_file_name_in_zip,
389 sizeof(raw_file_name_in_zip),
394 LOG(ERROR) << "Failed to read file info";
398 if (raw_file_name_in_zip[0] == '\0')
401 // unpack if filter is empty or path is matched
402 if (filter_prefix.empty() ||
403 std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
404 bf::path filename_in_zip_path(raw_file_name_in_zip);
406 // prevent "directory climbing" attack
407 if (HasDirectoryClimbing(filename_in_zip_path)) {
408 LOG(ERROR) << "Relative path in widget in malformed";
412 if (!filename_in_zip_path.parent_path().empty()) {
413 if (!CreateDir(filename_in_zip_path.parent_path())) {
414 LOG(ERROR) << "Failed to create directory: "
415 << filename_in_zip_path.parent_path();
420 if (!zip_file.OpenCurrent()) {
421 LOG(ERROR) << "Failed to open file";
425 if (!is_directory(filename_in_zip_path)) {
426 FILE *out = fopen(raw_file_name_in_zip, "wb");
428 LOG(ERROR) << "Failed to open destination ";
434 ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
436 LOG(ERROR) << "Failed to read data: " << ret;
439 fwrite(read_buffer, sizeof(char), ret, out);
446 zip_file.CloseCurrent();
449 if ((i+1) < info.number_entry) {
450 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
451 LOG(ERROR) << "Failed to read next file";
460 bool CheckPathInZipArchive(const char* zip_archive_path,
461 const boost::filesystem::path& relative_zip_path,
464 UnzFilePointer zip_file;
465 if (!zip_file.Open(zip_archive_path)) {
466 LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
470 unz_global_info info;
471 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
472 LOG(ERROR) << "Failed to read global info";
476 char raw_file_name_in_zip[kZipMaxPath];
477 unz_file_info raw_file_info;
478 for (uLong i = 0; i < info.number_entry; i++) {
479 if (unzGetCurrentFileInfo(zip_file.Get(),
481 raw_file_name_in_zip,
482 sizeof(raw_file_name_in_zip),
487 LOG(ERROR) << "Failed to read file info";
491 if (raw_file_name_in_zip[0] == '\0')
494 if (relative_zip_path.string() == raw_file_name_in_zip) {
499 if ((i + 1) < info.number_entry) {
500 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
501 LOG(ERROR) << "Failed to read next file";
510 bool HasDirectoryClimbing(const boost::filesystem::path& path) {
511 std::vector<std::string> segments;
512 ba::split(segments, path.string(), ba::is_any_of("/\\"));
513 return std::any_of(segments.begin(), segments.end(),
514 [](const std::string& segment) {
515 return segment == "..";
519 boost::filesystem::path MakeRelativePath(const boost::filesystem::path& input,
520 const boost::filesystem::path& base) {
521 bf::path input_absolute = bf::absolute(input);
522 bf::path base_absolute = bf::absolute(base);
523 return input_absolute.string().substr(base_absolute.string().length() + 1);
526 bool IsSubDir(const boost::filesystem::path& path,
527 const boost::filesystem::path& root) {
528 boost::filesystem::path p = path;
529 while (p != boost::filesystem::path()) {
530 if (bf::equivalent(p, root))
538 } // namespace common_installer