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>
19 #include "common/utils/byte_size_literals.h"
20 #include "common/utils/logging.h"
22 namespace ba = boost::algorithm;
23 namespace bs = boost::system;
24 namespace bf = boost::filesystem;
28 unsigned kZipBufSize = 8_kB;
29 unsigned kZipMaxPath = 256;
31 int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
33 if (stat(path_in_partition.string().c_str(), &stats)) {
34 LOG(ERROR) << "stat(" << path_in_partition.string()
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 bool CreateDir(const bf::path& path) {
96 boost::system::error_code error;
97 bf::create_directories(path, error);
100 LOG(ERROR) << "Failed to create directory: "
101 << boost::system::system_error(error).what();
107 bool SetDirPermissions(const boost::filesystem::path& path,
108 boost::filesystem::perms permissions) {
109 boost::system::error_code error;
110 bf::permissions(path, permissions, error);
113 LOG(ERROR) << "Failed to set permissions for directory: " << path
114 << boost::system::system_error(error).what();
120 bool CopyDir(const bf::path& src, const bf::path& dst) {
122 // Check whether the function call is valid
123 if (!bf::exists(src) || !bf::is_directory(src)) {
124 LOG(ERROR) << "Source directory " << src.string()
125 << " does not exist or is not a directory.";
128 if (bf::exists(dst)) {
129 LOG(ERROR) << "Destination directory " << dst.string()
130 << " already exists.";
133 // Create the destination directory
134 if (!CreateDir(dst)) {
135 LOG(ERROR) << "Unable to create destination directory" << dst.string();
139 if (!SetDirPermissions(dst,
140 bf::owner_all | bf::group_read | bf::others_read)) {
142 "Could not set permissions for dir:" << dst;
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 if (bf::is_directory(current)) {
157 // Found directory: Recursion
158 if (!CopyDir(current, dst / current.filename())) {
161 } else if (bf::is_symlink(current)) {
163 bf::copy_symlink(current, dst / current.filename());
166 bf::copy_file(current, dst / current.filename());
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) {
190 bs::error_code error;
191 bf::rename(src, dst, error);
193 LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
194 if (!CopyDir(src, dst)) {
195 LOG(ERROR) << "Cannot copy directory: " << src;
198 bf::remove_all(src, error);
200 LOG(ERROR) << "Cannot remove old directory when coping: " << src;
207 bool MoveFile(const bf::path& src, const bf::path& dst) {
210 bs::error_code error;
211 bf::rename(src, dst, error);
213 LOG(WARNING) << "Cannot move file: " << src <<
214 ". Will copy/remove... with error [" << error << "]";
215 bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
217 LOG(WARNING) << "Cannot copy file " << src <<
218 " due to error [" << error << "]";
221 bf::remove_all(src, error);
223 LOG(ERROR) << "Cannot remove old file when coping: " << src <<
224 "with error [" << error << "]";
230 int64_t GetUnpackedPackageSize(const bf::path& path) {
232 int64_t block_size = GetBlockSizeForPath(path);
234 // if failed to stat path
235 if (block_size == -1)
238 unz_global_info info;
239 unz_file_info64 raw_file_info;
240 char raw_file_name_in_zip[kZipMaxPath];
242 unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
243 if (zip_file == nullptr) {
244 LOG(ERROR) << "Failed to open the source dir: " << path.string();
248 if (unzGetGlobalInfo(zip_file, &info) != UNZ_OK) {
249 LOG(ERROR) << "Failed to read global info";
254 for (uLong i = 0; i < info.number_entry; i++) {
255 if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
256 sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
257 LOG(ERROR) << "Failed to read file info";
260 size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
263 // FIXME: calculate space needed for directories
268 boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
269 boost::filesystem::path install_tmp_dir;
270 boost::filesystem::path tmp_dir(app_path);
273 boost::filesystem::path model;
274 boost::filesystem::path unique_dir =
275 boost::filesystem::unique_path(model = "unpack-%%%%%%");
277 install_tmp_dir = tmp_dir /= unique_dir;
278 } while (boost::filesystem::exists(install_tmp_dir) &&
279 boost::filesystem::is_directory(install_tmp_dir));
281 return install_tmp_dir;
284 boost::filesystem::path GenerateTemporaryPath(
285 const boost::filesystem::path& path) {
286 bf::path pattern = path;
287 pattern += "-%%%%%%";
290 tmp_path = boost::filesystem::unique_path(pattern);
291 } while (boost::filesystem::exists(tmp_path));
295 bool ExtractToTmpDir(const char* zip_path,
296 const boost::filesystem::path& tmp_dir) {
297 return ExtractToTmpDir(zip_path, tmp_dir, "");
300 bool ExtractToTmpDir(const char* zip_path, const bf::path& tmp_dir,
301 const std::string& filter_prefix) {
302 unz_global_info info;
303 char read_buffer[kZipBufSize];
304 unz_file_info raw_file_info;
305 char raw_file_name_in_zip[kZipMaxPath];
307 current_path(tmp_dir);
309 UnzFilePointer zip_file;
310 if (!zip_file.Open(zip_path)) {
311 LOG(ERROR) << "Failed to open the source dir: " << zip_path;
315 if (unzGetGlobalInfo(zip_file.Get(), &info) != UNZ_OK) {
316 LOG(ERROR) << "Failed to read global info";
320 for (uLong i = 0; i < info.number_entry; i++) {
321 if (unzGetCurrentFileInfo(zip_file.Get(),
323 raw_file_name_in_zip,
324 sizeof(raw_file_name_in_zip),
329 LOG(ERROR) << "Failed to read file info";
333 if (raw_file_name_in_zip[0] == '\0')
336 // unpack if filter is empty or path is matched
337 if (filter_prefix.empty() ||
338 std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
339 bf::path filename_in_zip_path(raw_file_name_in_zip);
341 // prevent "directory climbing" attack
342 if (HasDirectoryClimbing(filename_in_zip_path)) {
343 LOG(ERROR) << "Relative path in widget in malformed";
347 if (!filename_in_zip_path.parent_path().empty()) {
348 if (!CreateDir(filename_in_zip_path.parent_path())) {
349 LOG(ERROR) << "Failed to create directory: "
350 << filename_in_zip_path.parent_path();
353 if (!SetDirPermissions(
354 filename_in_zip_path.parent_path(),
355 bf::owner_all | bf::group_read | bf::others_read)) {
357 "Could not set permissions for dir:" <<
358 filename_in_zip_path.parent_path();
363 if (!zip_file.OpenCurrent()) {
364 LOG(ERROR) << "Failed to open file";
368 if (!is_directory(filename_in_zip_path)) {
369 FILE *out = fopen(raw_file_name_in_zip, "wb");
371 LOG(ERROR) << "Failed to open destination ";
377 ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
379 LOG(ERROR) << "Failed to read data: " << ret;
382 fwrite(read_buffer, sizeof(char), ret, out);
389 zip_file.CloseCurrent();
392 if ((i+1) < info.number_entry) {
393 if (unzGoToNextFile(zip_file.Get()) != UNZ_OK) {
394 LOG(ERROR) << "Failed to read next file";
403 bool HasDirectoryClimbing(const boost::filesystem::path& path) {
404 std::vector<std::string> segments;
405 ba::split(segments, path.string(), ba::is_any_of("/\\"));
406 return std::any_of(segments.begin(), segments.end(),
407 [](const std::string& segment) {
408 return segment == "..";
412 boost::filesystem::path MakeRelativePath(const boost::filesystem::path& input,
413 const boost::filesystem::path& base) {
414 bf::path input_absolute = bf::absolute(input);
415 bf::path base_absolute = bf::absolute(base);
416 return input_absolute.string().substr(base_absolute.string().length() + 1);
419 } // namespace common_installer