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 CreateDir(const bf::path& path) {
99 boost::system::error_code error;
100 bf::create_directories(path, error);
103 LOG(ERROR) << "Failed to create directory: "
104 << boost::system::system_error(error).what();
110 bool SetDirPermissions(const boost::filesystem::path& path,
111 boost::filesystem::perms permissions) {
112 boost::system::error_code error;
113 bf::permissions(path, permissions, error);
116 LOG(ERROR) << "Failed to set permissions for directory: " << path
117 << boost::system::system_error(error).what();
123 bool CopyDir(const bf::path& src, const bf::path& dst,
124 FSFlag flags, bool skip_symlink) {
126 // Check whether the function call is valid
127 if (!bf::exists(src) || !bf::is_directory(src)) {
128 LOG(ERROR) << "Source directory " << src
129 << " does not exist or is not a directory.";
132 if (!bf::exists(dst)) {
133 // Create the destination directory
134 if (!CreateDir(dst)) {
135 LOG(ERROR) << "Unable to create destination directory" << dst;
139 if (!(flags & FS_MERGE_DIRECTORIES)) {
140 LOG(ERROR) << "Destination directory " << dst.string()
141 << " already exists.";
145 } catch (const bf::filesystem_error& error) {
146 LOG(ERROR) << "Failed to copy directory: " << error.what();
150 // Iterate through the source directory
151 for (bf::directory_iterator file(src);
152 file != bf::directory_iterator();
155 bf::path current(file->path());
156 bf::path target = dst / current.filename();
158 if (bf::is_symlink(symlink_status(current))) {
161 if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
163 bf::copy_symlink(current, target);
164 } else if (bf::is_directory(current)) {
165 // Found directory: Recursion
166 if (!CopyDir(current, target, flags, skip_symlink)) {
170 if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
172 bf::copy_file(current, target);
174 } catch (const bf::filesystem_error& error) {
175 LOG(ERROR) << "Failed to copy directory: " << error.what();
182 bool CopyFile(const bf::path& src, const bf::path& dst) {
183 bs::error_code error;
185 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
187 LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
193 bool MoveDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
194 if (bf::exists(dst) && !(flags & FS_MERGE_DIRECTORIES)) {
195 LOG(ERROR) << "Destination directory does exist: " << dst;
199 bs::error_code error;
200 bf::rename(src, dst, error);
202 LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
203 if (!CopyDir(src, dst, flags, false)) {
204 LOG(ERROR) << "Cannot copy directory: " << src;
207 bf::remove_all(src, error);
209 LOG(ERROR) << "Cannot remove old directory when coping: " << src;
216 bool MoveFile(const bf::path& src, const bf::path& dst) {
219 bs::error_code error;
220 bf::rename(src, dst, error);
222 LOG(WARNING) << "Cannot move file: " << src <<
223 ". Will copy/remove... with error [" << error << "]";
224 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
226 LOG(WARNING) << "Cannot copy file " << src <<
227 " due to error [" << error << "]";
230 bf::remove_all(src, error);
232 LOG(ERROR) << "Cannot remove old file when coping: " << src <<
233 "with error [" << error << "]";
239 int64_t GetUnpackedPackageSize(const bf::path& path) {
241 int64_t block_size = GetBlockSizeForPath(path);
243 // if failed to stat path
244 if (block_size == -1)
247 unz_global_info info;
248 unz_file_info64 raw_file_info;
249 char raw_file_name_in_zip[kZipMaxPath];
251 unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
252 if (zip_file == nullptr) {
253 LOG(ERROR) << "Failed to open the source dir: " << path.string();
257 if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
258 LOG(ERROR) << "Failed to read global info";
263 for (uLong i = 0; i < info.number_entry; i++) {
264 if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
265 sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
266 LOG(ERROR) << "Failed to read file info";
270 size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
271 unzGoToNextFile(zip_file);
274 // FIXME: calculate space needed for directories
279 int64_t GetDirectorySize(const boost::filesystem::path& path) {
280 int64_t block_size = GetBlockSizeForPath(path);
282 // if failed to stat path
283 if (block_size == -1)
287 for (bf::recursive_directory_iterator iter(path);
288 iter != bf::recursive_directory_iterator(); ++iter) {
290 if (lstat(iter->path().c_str(), &buf) == -1) {
291 LOG(ERROR) << "lstat() failed for: " << iter->path();
294 size += RoundUpToBlockSizeOf(buf.st_size, block_size);
297 // FIXME: block size for external device may differ...
301 boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
302 boost::filesystem::path install_tmp_dir;
303 boost::filesystem::path tmp_dir(app_path);
306 boost::filesystem::path model;
307 boost::filesystem::path unique_dir =
308 boost::filesystem::unique_path(model = "unpack-%%%%%%");
310 install_tmp_dir = tmp_dir /= unique_dir;
311 } while (boost::filesystem::exists(install_tmp_dir) &&
312 boost::filesystem::is_directory(install_tmp_dir));
314 return install_tmp_dir;
317 boost::filesystem::path GenerateTemporaryPath(
318 const boost::filesystem::path& path) {
319 bf::path pattern = path;
320 pattern += "-%%%%%%";
323 tmp_path = boost::filesystem::unique_path(pattern);
324 } while (boost::filesystem::exists(tmp_path));
328 bool ExtractToTmpDir(const char* zip_path,
329 const boost::filesystem::path& tmp_dir) {
330 return ExtractToTmpDir(zip_path, tmp_dir, "");
333 bool ExtractToTmpDir(const char* zip_path, const bf::path& tmp_dir,
334 const std::string& filter_prefix) {
335 unz_global_info info;
336 char read_buffer[kZipBufSize];
337 unz_file_info raw_file_info;
338 char raw_file_name_in_zip[kZipMaxPath];
340 current_path(tmp_dir);
342 UnzFilePointer zip_file;
343 if (!zip_file.Open(zip_path)) {
344 LOG(ERROR) << "Failed to open the source dir: " << zip_path;
348 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
349 LOG(ERROR) << "Failed to read global info";
353 for (uLong i = 0; i < info.number_entry; i++) {
354 if (unzGetCurrentFileInfo(zip_file.Get(),
356 raw_file_name_in_zip,
357 sizeof(raw_file_name_in_zip),
362 LOG(ERROR) << "Failed to read file info";
366 if (raw_file_name_in_zip[0] == '\0')
369 // unpack if filter is empty or path is matched
370 if (filter_prefix.empty() ||
371 std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
372 bf::path filename_in_zip_path(raw_file_name_in_zip);
374 // prevent "directory climbing" attack
375 if (HasDirectoryClimbing(filename_in_zip_path)) {
376 LOG(ERROR) << "Relative path in widget in malformed";
380 if (!filename_in_zip_path.parent_path().empty()) {
381 if (!CreateDir(filename_in_zip_path.parent_path())) {
382 LOG(ERROR) << "Failed to create directory: "
383 << filename_in_zip_path.parent_path();
388 if (!zip_file.OpenCurrent()) {
389 LOG(ERROR) << "Failed to open file";
393 if (!is_directory(filename_in_zip_path)) {
394 FILE *out = fopen(raw_file_name_in_zip, "wb");
396 LOG(ERROR) << "Failed to open destination ";
402 ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
404 LOG(ERROR) << "Failed to read data: " << ret;
407 fwrite(read_buffer, sizeof(char), ret, out);
414 zip_file.CloseCurrent();
417 if ((i+1) < info.number_entry) {
418 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
419 LOG(ERROR) << "Failed to read next file";
428 bool CheckPathInZipArchive(const char* zip_archive_path,
429 const boost::filesystem::path& relative_zip_path,
432 UnzFilePointer zip_file;
433 if (!zip_file.Open(zip_archive_path)) {
434 LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
438 unz_global_info info;
439 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
440 LOG(ERROR) << "Failed to read global info";
444 char raw_file_name_in_zip[kZipMaxPath];
445 unz_file_info raw_file_info;
446 for (uLong i = 0; i < info.number_entry; i++) {
447 if (unzGetCurrentFileInfo(zip_file.Get(),
449 raw_file_name_in_zip,
450 sizeof(raw_file_name_in_zip),
455 LOG(ERROR) << "Failed to read file info";
459 if (raw_file_name_in_zip[0] == '\0')
462 if (relative_zip_path.string() == raw_file_name_in_zip) {
467 if ((i + 1) < info.number_entry) {
468 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
469 LOG(ERROR) << "Failed to read next file";
478 bool HasDirectoryClimbing(const boost::filesystem::path& path) {
479 std::vector<std::string> segments;
480 ba::split(segments, path.string(), ba::is_any_of("/\\"));
481 return std::any_of(segments.begin(), segments.end(),
482 [](const std::string& segment) {
483 return segment == "..";
487 boost::filesystem::path MakeRelativePath(const boost::filesystem::path& input,
488 const boost::filesystem::path& base) {
489 bf::path input_absolute = bf::absolute(input);
490 bf::path base_absolute = bf::absolute(base);
491 return input_absolute.string().substr(base_absolute.string().length() + 1);
494 bool IsSubDir(const boost::filesystem::path& path,
495 const boost::filesystem::path& root) {
496 boost::filesystem::path p = path;
497 while (p != boost::filesystem::path()) {
498 if (bf::equivalent(p, root))
506 } // namespace common_installer