1 /* 2014, Copyright © Intel Coporation, license APACHE-2.0, see LICENSE file */
3 #include "common/utils/file_util.h"
10 #include <boost/algorithm/string/classification.hpp>
11 #include <boost/algorithm/string/split.hpp>
12 #include <boost/filesystem/path.hpp>
13 #include <boost/system/error_code.hpp>
15 #include <manifest_parser/utils/logging.h>
21 #include "common/utils/byte_size_literals.h"
23 namespace ba = boost::algorithm;
24 namespace bs = boost::system;
25 namespace bf = boost::filesystem;
29 unsigned kZipBufSize = 8_kB;
30 unsigned kZipMaxPath = 256;
32 int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
34 if (stat(path_in_partition.string().c_str(), &stats)) {
35 LOG(ERROR) << "stat(" << path_in_partition.string()
36 << ") failed - error code: " << errno;
39 return stats.st_blksize;
42 int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) {
43 return ((size + block_size - 1) / block_size) * block_size;
46 class UnzFilePointer {
51 currentFileOpened_(false) { }
54 if (currentFileOpened_)
55 unzCloseCurrentFile(zipFile_);
60 bool Open(const char* zip_path) {
61 zipFile_ = static_cast<unzFile*>(unzOpen(zip_path));
69 if (unzOpenCurrentFile(zipFile_) != UNZ_OK)
71 currentFileOpened_ = true;
76 if (currentFileOpened_)
77 unzCloseCurrentFile(zipFile_);
78 currentFileOpened_ = false;
81 unzFile* Get() { return zipFile_; }
86 bool currentFileOpened_;
91 namespace common_installer {
93 bool CreateDir(const bf::path& path) {
97 boost::system::error_code error;
98 bf::create_directories(path, error);
101 LOG(ERROR) << "Failed to create directory: "
102 << boost::system::system_error(error).what();
108 bool SetDirPermissions(const boost::filesystem::path& path,
109 boost::filesystem::perms permissions) {
110 boost::system::error_code error;
111 bf::permissions(path, permissions, error);
114 LOG(ERROR) << "Failed to set permissions for directory: " << path
115 << boost::system::system_error(error).what();
121 bool CopyDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
123 // Check whether the function call is valid
124 if (!bf::exists(src) || !bf::is_directory(src)) {
125 LOG(ERROR) << "Source directory " << src
126 << " does not exist or is not a directory.";
129 if (!bf::exists(dst)) {
130 // Create the destination directory
131 if (!CreateDir(dst)) {
132 LOG(ERROR) << "Unable to create destination directory" << dst;
136 if (!(flags & FS_MERGE_DIRECTORIES)) {
137 LOG(ERROR) << "Destination directory " << dst.string()
138 << " already exists.";
142 } catch (const bf::filesystem_error& error) {
143 LOG(ERROR) << "Failed to copy directory: " << error.what();
147 // Iterate through the source directory
148 for (bf::directory_iterator file(src);
149 file != bf::directory_iterator();
152 bf::path current(file->path());
153 bf::path target = dst / current.filename();
154 if (bf::is_directory(current)) {
155 // Found directory: Recursion
156 if (!CopyDir(current, target, flags)) {
159 } else if (bf::is_symlink(current)) {
160 if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
162 bf::copy_symlink(current, target);
164 if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
166 bf::copy_file(current, target);
168 } catch (const bf::filesystem_error& error) {
169 LOG(ERROR) << "Failed to copy directory: " << error.what();
176 bool CopyFile(const bf::path& src, const bf::path& dst) {
177 bs::error_code error;
179 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
181 LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
187 bool MoveDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
188 if (bf::exists(dst) && !(flags & FS_MERGE_DIRECTORIES)) {
189 LOG(ERROR) << "Destination directory does exist: " << dst;
193 bs::error_code error;
194 bf::rename(src, dst, error);
196 LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
197 if (!CopyDir(src, dst, flags)) {
198 LOG(ERROR) << "Cannot copy directory: " << src;
201 bf::remove_all(src, error);
203 LOG(ERROR) << "Cannot remove old directory when coping: " << src;
210 bool MoveFile(const bf::path& src, const bf::path& dst) {
213 bs::error_code error;
214 bf::rename(src, dst, error);
216 LOG(WARNING) << "Cannot move file: " << src <<
217 ". Will copy/remove... with error [" << error << "]";
218 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
220 LOG(WARNING) << "Cannot copy file " << src <<
221 " due to error [" << error << "]";
224 bf::remove_all(src, error);
226 LOG(ERROR) << "Cannot remove old file when coping: " << src <<
227 "with error [" << error << "]";
233 int64_t GetUnpackedPackageSize(const bf::path& path) {
235 int64_t block_size = GetBlockSizeForPath(path);
237 // if failed to stat path
238 if (block_size == -1)
241 unz_global_info info;
242 unz_file_info64 raw_file_info;
243 char raw_file_name_in_zip[kZipMaxPath];
245 unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
246 if (zip_file == nullptr) {
247 LOG(ERROR) << "Failed to open the source dir: " << path.string();
251 if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
252 LOG(ERROR) << "Failed to read global info";
257 for (uLong i = 0; i < info.number_entry; i++) {
258 if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
259 sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
260 LOG(ERROR) << "Failed to read file info";
264 size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
265 unzGoToNextFile(zip_file);
268 // FIXME: calculate space needed for directories
273 boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
274 boost::filesystem::path install_tmp_dir;
275 boost::filesystem::path tmp_dir(app_path);
278 boost::filesystem::path model;
279 boost::filesystem::path unique_dir =
280 boost::filesystem::unique_path(model = "unpack-%%%%%%");
282 install_tmp_dir = tmp_dir /= unique_dir;
283 } while (boost::filesystem::exists(install_tmp_dir) &&
284 boost::filesystem::is_directory(install_tmp_dir));
286 return install_tmp_dir;
289 boost::filesystem::path GenerateTemporaryPath(
290 const boost::filesystem::path& path) {
291 bf::path pattern = path;
292 pattern += "-%%%%%%";
295 tmp_path = boost::filesystem::unique_path(pattern);
296 } while (boost::filesystem::exists(tmp_path));
300 bool ExtractToTmpDir(const char* zip_path,
301 const boost::filesystem::path& tmp_dir) {
302 return ExtractToTmpDir(zip_path, tmp_dir, "");
305 bool ExtractToTmpDir(const char* zip_path, const bf::path& tmp_dir,
306 const std::string& filter_prefix) {
307 unz_global_info info;
308 char read_buffer[kZipBufSize];
309 unz_file_info raw_file_info;
310 char raw_file_name_in_zip[kZipMaxPath];
312 current_path(tmp_dir);
314 UnzFilePointer zip_file;
315 if (!zip_file.Open(zip_path)) {
316 LOG(ERROR) << "Failed to open the source dir: " << zip_path;
320 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
321 LOG(ERROR) << "Failed to read global info";
325 for (uLong i = 0; i < info.number_entry; i++) {
326 if (unzGetCurrentFileInfo(zip_file.Get(),
328 raw_file_name_in_zip,
329 sizeof(raw_file_name_in_zip),
334 LOG(ERROR) << "Failed to read file info";
338 if (raw_file_name_in_zip[0] == '\0')
341 // unpack if filter is empty or path is matched
342 if (filter_prefix.empty() ||
343 std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
344 bf::path filename_in_zip_path(raw_file_name_in_zip);
346 // prevent "directory climbing" attack
347 if (HasDirectoryClimbing(filename_in_zip_path)) {
348 LOG(ERROR) << "Relative path in widget in malformed";
352 if (!filename_in_zip_path.parent_path().empty()) {
353 if (!CreateDir(filename_in_zip_path.parent_path())) {
354 LOG(ERROR) << "Failed to create directory: "
355 << filename_in_zip_path.parent_path();
360 if (!zip_file.OpenCurrent()) {
361 LOG(ERROR) << "Failed to open file";
365 if (!is_directory(filename_in_zip_path)) {
366 FILE *out = fopen(raw_file_name_in_zip, "wb");
368 LOG(ERROR) << "Failed to open destination ";
374 ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
376 LOG(ERROR) << "Failed to read data: " << ret;
379 fwrite(read_buffer, sizeof(char), ret, out);
386 zip_file.CloseCurrent();
389 if ((i+1) < info.number_entry) {
390 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
391 LOG(ERROR) << "Failed to read next file";
400 bool CheckPathInZipArchive(const char* zip_archive_path,
401 const boost::filesystem::path& relative_zip_path,
404 UnzFilePointer zip_file;
405 if (!zip_file.Open(zip_archive_path)) {
406 LOG(ERROR) << "Failed to open the source dir: " << zip_archive_path;
410 unz_global_info info;
411 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
412 LOG(ERROR) << "Failed to read global info";
416 char raw_file_name_in_zip[kZipMaxPath];
417 unz_file_info raw_file_info;
418 for (uLong i = 0; i < info.number_entry; i++) {
419 if (unzGetCurrentFileInfo(zip_file.Get(),
421 raw_file_name_in_zip,
422 sizeof(raw_file_name_in_zip),
427 LOG(ERROR) << "Failed to read file info";
431 if (raw_file_name_in_zip[0] == '\0')
434 if (relative_zip_path.string() == raw_file_name_in_zip) {
439 if ((i + 1) < info.number_entry) {
440 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
441 LOG(ERROR) << "Failed to read next file";
450 bool HasDirectoryClimbing(const boost::filesystem::path& path) {
451 std::vector<std::string> segments;
452 ba::split(segments, path.string(), ba::is_any_of("/\\"));
453 return std::any_of(segments.begin(), segments.end(),
454 [](const std::string& segment) {
455 return segment == "..";
459 boost::filesystem::path MakeRelativePath(const boost::filesystem::path& input,
460 const boost::filesystem::path& base) {
461 bf::path input_absolute = bf::absolute(input);
462 bf::path base_absolute = bf::absolute(base);
463 return input_absolute.string().substr(base_absolute.string().length() + 1);
466 } // namespace common_installer