#include <unzip.h>
#include <zlib.h>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
-#include <boost/system/error_code.hpp>
-
#include <manifest_parser/utils/logging.h>
#include <algorithm>
+#include <filesystem>
+#include <random>
+#include <regex>
#include <string>
+#include <system_error>
#include <vector>
#include "common/utils/byte_size_literals.h"
+#include "common/utils/paths.h"
-namespace ba = boost::algorithm;
-namespace bs = boost::system;
-namespace bf = boost::filesystem;
+namespace fs = std::filesystem;
namespace {
unsigned kZipBufSize = 8_kB;
unsigned kZipMaxPath = PATH_MAX;
-int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
+int64_t GetBlockSizeForPath(const fs::path& path_in_partition) {
struct stat stats;
- if (stat(path_in_partition.string().c_str(), &stats)) {
- LOG(ERROR) << "stat(" << path_in_partition.string()
+ if (stat(path_in_partition.c_str(), &stats)) {
+ LOG(ERROR) << "stat(" << path_in_partition
<< ") failed - error code: " << errno;
return -1;
}
return static_cast<FSFlag>(static_cast<int>(a) | static_cast<int>(b));
}
-bool SetOwnership(const bf::path& path, uid_t uid, gid_t gid) {
- int fd = open(path.c_str(), O_RDONLY);
- if (fd < 0) {
- LOG(ERROR) << "Can't open directory : " << path;
- return false;
- }
- int ret = fchown(fd, uid, gid);
- close(fd);
+bool SetOwnership(const fs::path& path, uid_t uid, gid_t gid) {
+ int ret = lchown(path.c_str(), uid, gid);
if (ret != 0) {
LOG(ERROR) << "Failed to change owner of: " << path;
return false;
return true;
}
-bool SetOwnershipAll(const bf::path& path, uid_t uid, gid_t gid) {
+bool SetOwnershipAll(const fs::path& path, uid_t uid, gid_t gid) {
if (!SetOwnership(path, uid, gid))
return false;
- if (!bf::is_directory(path))
+ if (!fs::is_directory(path))
return true;
- for (bf::recursive_directory_iterator iter(path);
- iter != bf::recursive_directory_iterator();
+ for (fs::recursive_directory_iterator iter(path);
+ iter != fs::recursive_directory_iterator();
++iter) {
- bf::path current(iter->path());
- if (bf::is_symlink(symlink_status(current)))
- continue;
+ fs::path current(iter->path());
if (!SetOwnership(current, uid, gid))
return false;
}
return true;
}
-bool CreateDir(const bf::path& path) {
- if (bf::exists(path))
+bool CreateDir(const fs::path& path) {
+ if (fs::exists(path))
return true;
- boost::system::error_code error;
- bf::create_directories(path, error);
+ std::error_code error;
+ fs::create_directories(path, error);
if (error) {
- LOG(ERROR) << "Failed to create directory: "
- << boost::system::system_error(error).what();
+ LOG(ERROR) << "Failed to create directory: " << error.message();
return false;
}
return true;
}
-bool SetDirPermissions(const boost::filesystem::path& path,
- boost::filesystem::perms permissions) {
- boost::system::error_code error;
- bf::permissions(path, permissions, error);
+bool SetDirPermissions(const fs::path& path,
+ fs::perms permissions, bool add_perms) {
+ std::error_code error;
+ fs::permissions(path, permissions,
+ add_perms ? fs::perm_options::add : fs::perm_options::replace, error);
if (error) {
LOG(ERROR) << "Failed to set permissions for directory: " << path
- << boost::system::system_error(error).what();
+ << error.message();
return false;
}
return true;
}
-bool SetDirOwnershipAndPermissions(const boost::filesystem::path& path,
- boost::filesystem::perms permissions, uid_t uid,
+bool SetDirOwnershipAndPermissions(const fs::path& path,
+ fs::perms permissions, uid_t uid,
gid_t gid) {
if (!SetOwnership(path, uid, gid)) {
LOG(ERROR) << "Failed to change owner: " << path
return false;
}
if (!SetDirPermissions(path, permissions)) {
- LOG(ERROR) << "Failed to change permission: " << path
- << std::oct << permissions;
+ LOG(ERROR) << "Failed to change permission: " << path;
return false;
}
return true;
}
-bool CopyDir(const bf::path& src, const bf::path& dst,
+bool CopyOwnershipAndPermissions(const fs::path& src, const fs::path& dst) {
+ if (!fs::exists(src)) {
+ LOG(ERROR) << "Failed to copy ownership and permissions"
+ << " from " << src << " to " << dst;
+ return false;
+ }
+ fs::perms permissions = fs::status(src).permissions();
+ struct stat stats;
+ if (stat(src.c_str(), &stats) != 0)
+ return false;
+ if (!SetDirOwnershipAndPermissions(dst, permissions, stats.st_uid,
+ stats.st_gid)) {
+ LOG(ERROR) << "Failed to copy ownership and permissions"
+ << " from " << src << " to " << dst;
+ return false;
+ }
+ return true;
+}
+
+bool CopyDir(const fs::path& src, const fs::path& dst,
FSFlag flags, bool skip_symlink) {
try {
// Check whether the function call is valid
- if (!bf::exists(src) || !bf::is_directory(src)) {
+ if (!fs::exists(src) || !fs::is_directory(src)) {
LOG(ERROR) << "Source directory " << src
<< " does not exist or is not a directory.";
return false;
}
- if (!bf::exists(dst)) {
+ if (!fs::exists(dst)) {
// Create the destination directory
if (!CreateDir(dst)) {
LOG(ERROR) << "Unable to create destination directory" << dst;
return false;
}
+ if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
+ CopyOwnershipAndPermissions(src, dst);
} else {
if (!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
LOG(ERROR) << "Destination directory " << dst.string()
<< " already exists.";
return false;
}
+ if (flags & (FS_MERGE_OVERWRITE | FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS))
+ CopyOwnershipAndPermissions(src, dst);
}
- } catch (const bf::filesystem_error& error) {
+ } catch (const fs::filesystem_error& error) {
LOG(ERROR) << "Failed to copy directory: " << error.what();
return false;
}
// Iterate through the source directory
- for (bf::directory_iterator file(src);
- file != bf::directory_iterator();
- ++file) {
+ for (const auto& current : fs::directory_iterator(src)) {
try {
- bf::path current(file->path());
- bf::path target = dst / current.filename();
+ fs::path target = dst / current.path().filename();
- if (bf::is_symlink(symlink_status(current))) {
+ if (fs::is_symlink(symlink_status(current))) {
if (skip_symlink)
continue;
if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) &&
- bf::exists(target))
+ fs::exists(target))
continue;
- bf::copy_symlink(current, target);
- } else if (bf::is_directory(current)) {
+ std::error_code error;
+ fs::copy_symlink(current, target, error);
+ if (error) {
+ LOG(ERROR) << "Failed to copy symlink: " << current << ", "
+ << error.message();
+ return false;
+ }
+ } else if (fs::is_directory(current)) {
// Found directory: Recursion
if (!CopyDir(current, target, flags, skip_symlink)) {
return false;
}
} else {
- if ((flags & FS_MERGE_SKIP) && bf::exists(target))
+ if ((flags & FS_MERGE_SKIP) && fs::exists(target))
continue;
- bf::path destination = target;
+ fs::path destination = target;
if (flags & FS_COMMIT_COPY_FILE)
- destination =
- bf::unique_path(target.parent_path() / "%%%%-%%%%-%%%%-%%%%");
+ destination = target.parent_path() /
+ GenerateUniquePathString("%%%%-%%%%-%%%%-%%%%");
if (flags & FS_MERGE_OVERWRITE)
- bf::copy_file(current, destination,
- bf::copy_option::overwrite_if_exists);
+ fs::copy_file(current, destination,
+ fs::copy_options::overwrite_existing);
else
- bf::copy_file(current, destination);
+ fs::copy_file(current, destination);
+
+ if (flags & FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)
+ CopyOwnershipAndPermissions(current, destination);
if (flags & FS_COMMIT_COPY_FILE) {
if (flags & FS_MERGE_OVERWRITE)
- bf::remove(target);
- bf::rename(destination, target);
+ fs::remove(target);
+ fs::rename(destination, target);
}
}
- } catch (const bf::filesystem_error& error) {
+ } catch (const fs::filesystem_error& error) {
LOG(ERROR) << "Failed to copy directory: " << error.what();
return false;
}
return true;
}
-bool CopyFile(const bf::path& src, const bf::path& dst) {
- bs::error_code error;
+bool CopyFile(const fs::path& src, const fs::path& dst) {
+ std::error_code error;
- bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
+ fs::copy_file(src, dst, fs::copy_options::overwrite_existing, error);
if (error) {
LOG(WARNING) << "copy file " << src << " due to error [" << error << "]";
return false;
return true;
}
-bool MoveDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
- if (bf::exists(dst) &&
+bool RestoreBackup(const fs::path& path) {
+ fs::path backup_path = GetBackupPathForPackagePath(path);
+ if (!fs::exists(backup_path) &&
+ !fs::is_symlink(fs::symlink_status(backup_path))) {
+ LOG(WARNING) << backup_path << " does not exist";
+ return true;
+ }
+ return MoveDir(backup_path, path);
+}
+
+bool MakeBackup(const fs::path& path) {
+ if (!fs::exists(path) && !fs::is_symlink(fs::symlink_status(path))) {
+ LOG(WARNING) << path << " does not exist";
+ return true;
+ }
+ fs::path backup_path = GetBackupPathForPackagePath(path);
+ return MoveDir(path, backup_path);
+}
+
+bool RemoveBackup(const fs::path& path) {
+ fs::path backup_path = GetBackupPathForPackagePath(path);
+ if (!fs::exists(backup_path) &&
+ !fs::is_symlink(fs::symlink_status(backup_path))) {
+ LOG(WARNING) << backup_path << " does not exist";
+ return true;
+ }
+ return RemoveAll(backup_path);
+}
+
+bool RemoveAll(const fs::path& path) {
+ if (!fs::exists(path) && !fs::is_symlink(fs::symlink_status(path)))
+ return true;
+
+ std::error_code error;
+ fs::remove_all(path, error);
+
+ if (error) {
+ LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
+ return false;
+ }
+
+ return true;
+}
+
+bool Remove(const fs::path& path) {
+ std::error_code error;
+ if (!fs::exists(fs::symlink_status(path, error)))
+ return true;
+
+ fs::remove(path, error);
+ if (error) {
+ LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
+ return false;
+ }
+ return true;
+}
+
+bool MoveDir(const fs::path& src, const fs::path& dst, FSFlag flags) {
+ if (fs::exists(dst) &&
!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
LOG(ERROR) << "Destination directory does exist: " << dst;
return false;
}
- bs::error_code error;
- bf::rename(src, dst, error);
+ std::error_code error;
+ fs::rename(src, dst, error);
if (error) {
LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
if (!CopyDir(src, dst, flags, false)) {
LOG(ERROR) << "Cannot copy directory: " << src;
return false;
}
- bf::remove_all(src, error);
+ fs::remove_all(src, error);
if (error) {
LOG(ERROR) << "Cannot remove old directory when coping: " << src;
return false;
return true;
}
-bool MoveFile(const bf::path& src, const bf::path& dst, bool force) {
- if (!force && bf::exists(dst))
+bool MoveFile(const fs::path& src, const fs::path& dst, bool force) {
+ if (!force && fs::exists(dst))
return false;
- bs::error_code error;
- bf::rename(src, dst, error);
+ std::error_code error;
+ fs::rename(src, dst, error);
if (error) {
LOG(WARNING) << "Cannot move file: " << src <<
". Will copy/remove... with error [" << error << "]";
- bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error);
+ fs::copy_file(src, dst, fs::copy_options::overwrite_existing, error);
if (error) {
LOG(WARNING) << "Cannot copy file " << src <<
" due to error [" << error << "]";
return false;
}
- bf::remove_all(src, error);
+ fs::remove_all(src, error);
if (error) {
LOG(ERROR) << "Cannot remove old file when coping: " << src <<
"with error [" << error << "]";
return true;
}
-int64_t GetUnpackedPackageSize(const bf::path& path) {
+bool BackupDir(const fs::path& src,
+ const fs::path& dst, const std::string& entry) {
+ if (!fs::exists(src / entry))
+ return true;
+
+ if (!MoveDir(src / entry, dst / entry,
+ FS_MERGE_OVERWRITE |
+ FS_COMMIT_COPY_FILE |
+ FS_PRESERVE_OWNERSHIP_AND_PERMISSIONS)) {
+ LOG(ERROR) << "Failed to backup file";
+ return false;
+ }
+
+ return true;
+}
+
+int64_t GetUnpackedPackageSize(const fs::path& path) {
int64_t size = 0;
int64_t block_size = GetBlockSizeForPath(path);
- // if failed to stat path
if (block_size == -1)
return -1;
unz_file_info64 raw_file_info;
char raw_file_name_in_zip[kZipMaxPath];
- unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.string().c_str()));
+ unzFile* zip_file = static_cast<unzFile*>(unzOpen(path.c_str()));
if (zip_file == nullptr) {
LOG(ERROR) << "Failed to open the source dir: " << path.string();
return -1;
return size;
}
-int64_t GetDirectorySize(const boost::filesystem::path& path) {
+int64_t GetDirectorySize(const fs::path& path) {
int64_t block_size = GetBlockSizeForPath(path);
- // if failed to stat path
if (block_size == -1)
return -1;
int64_t size = 0;
- for (bf::recursive_directory_iterator iter(path);
- iter != bf::recursive_directory_iterator(); ++iter) {
+ for (fs::recursive_directory_iterator iter(path);
+ iter != fs::recursive_directory_iterator(); ++iter) {
struct stat buf;
if (lstat(iter->path().c_str(), &buf) == -1) {
LOG(ERROR) << "lstat() failed for: " << iter->path();
return size;
}
-boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
- boost::filesystem::path install_tmp_dir;
- boost::filesystem::path tmp_dir(app_path);
+bool CheckFreeSpaceAtPath(int64_t required_size,
+ const fs::path& target_location) {
+ std::error_code error;
+ fs::path root = target_location;
+
+ while (!fs::exists(root) && root != root.root_path())
+ root = root.parent_path();
+
+ if (!fs::exists(root)) {
+ LOG(ERROR) << "No mount point for path: " << target_location;
+ return false;
+ }
+
+ fs::space_info space_info = fs::space(root, error);
+ if (error) {
+ LOG(ERROR) << "Failed to get space_info: " << error.message();
+ return false;
+ }
+
+ return (space_info.free >= static_cast<uint64_t>(required_size));
+}
+
+fs::path GenerateTmpDir(const fs::path &app_path) {
+ fs::path install_tmp_dir;
+ fs::path tmp_dir(app_path);
do {
- boost::filesystem::path model;
- boost::filesystem::path unique_dir =
- boost::filesystem::unique_path(model = "unpack-%%%%%%");
+ fs::path unique_dir = GenerateUniquePathString("unpack-%%%%%%");
install_tmp_dir = tmp_dir /= unique_dir;
- } while (boost::filesystem::exists(install_tmp_dir) &&
- boost::filesystem::is_directory(install_tmp_dir));
+ } while (fs::exists(install_tmp_dir) &&
+ fs::is_directory(install_tmp_dir));
return install_tmp_dir;
}
-boost::filesystem::path GenerateTemporaryPath(
- const boost::filesystem::path& path) {
- bf::path pattern = path;
+fs::path GenerateTemporaryPath(const fs::path& path) {
+ fs::path pattern = path;
pattern += "-%%%%%%";
- bf::path tmp_path;
+ fs::path tmp_path;
+
do {
- tmp_path = boost::filesystem::unique_path(pattern);
- } while (boost::filesystem::exists(tmp_path));
+ tmp_path = GenerateUniquePathString(pattern);
+ } while (fs::exists(tmp_path));
+
return tmp_path;
}
bool ExtractToTmpDir(const char* zip_path,
- const boost::filesystem::path& tmp_dir) {
+ const fs::path& tmp_dir) {
return ExtractToTmpDir(zip_path, tmp_dir, "");
}
-bool ExtractToTmpDir(const char* zip_path, const bf::path& tmp_dir,
+bool ExtractToTmpDir(const char* zip_path, const fs::path& tmp_dir,
const std::string& filter_prefix) {
unz_global_info info;
char read_buffer[kZipBufSize];
// unpack if filter is empty or path is matched
if (filter_prefix.empty() ||
std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
- bf::path filename_in_zip_path(raw_file_name_in_zip);
+ fs::path filename_in_zip_path(raw_file_name_in_zip);
- // prevent "directory climbing" attack
if (HasDirectoryClimbing(filename_in_zip_path)) {
LOG(ERROR) << "Relative path in widget in malformed";
return false;
}
if (!is_directory(filename_in_zip_path)) {
- FILE *out = fopen(raw_file_name_in_zip, "wb");
+ FILE* out = fopen(raw_file_name_in_zip, "wbx");
if (!out) {
LOG(ERROR) << "Failed to open destination ";
return false;
ret = unzReadCurrentFile(zip_file.Get(), read_buffer, kZipBufSize);
if (ret < 0) {
LOG(ERROR) << "Failed to read data: " << ret;
+ fclose(out);
return false;
} else {
fwrite(read_buffer, sizeof(char), ret, out);
}
bool CheckPathInZipArchive(const char* zip_archive_path,
- const boost::filesystem::path& relative_zip_path,
+ const fs::path& relative_zip_path,
bool* found) {
*found = false;
UnzFilePointer zip_file;
return true;
}
-bool HasDirectoryClimbing(const boost::filesystem::path& path) {
- std::vector<std::string> segments;
- ba::split(segments, path.string(), ba::is_any_of("/\\"));
- return std::any_of(segments.begin(), segments.end(),
- [](const std::string& segment) {
- return segment == "..";
- });
+bool HasDirectoryClimbing(const fs::path& path) {
+ std::regex re("[\\/\\\\]");
+ std::string path_str = path.string();
+ std::sregex_token_iterator first(path_str.begin(), path_str.end(), re, -1);
+ std::sregex_token_iterator last;
+ std::vector<std::string> segments(first, last);
+
+ return std::find(segments.begin(), segments.end(), "..") != segments.end();
}
-boost::filesystem::path MakeRelativePath(const boost::filesystem::path& input,
- const boost::filesystem::path& base) {
- bf::path input_absolute = bf::absolute(input);
- bf::path base_absolute = bf::absolute(base);
- return input_absolute.string().substr(base_absolute.string().length() + 1);
+fs::path MakeRelativePath(const fs::path& input,
+ const fs::path& base) {
+ if (input.string().find(base.string()) == std::string::npos) {
+ LOG(ERROR) << base.string() << " is not base path for " << input.string();
+ return input;
+ }
+
+ return input.string().substr(base.string().length() + 1);
}
-bool IsSubDir(const boost::filesystem::path& path,
- const boost::filesystem::path& root) {
- boost::filesystem::path p = path;
- while (p != boost::filesystem::path()) {
- if (bf::equivalent(p, root))
- return true;
- else
- p = p.parent_path();
+bool IsSubDir(const fs::path& path, const fs::path& root) {
+ std::string relative = fs::relative(path, root);
+ return relative.size() == 1 || (relative[0] != '.' && relative[1] != '.');
+}
+
+std::vector<std::string> GetDirectoryList(const fs::path& cwd) {
+ if (!fs::exists(cwd))
+ return {};
+
+ std::vector<std::string> list;
+ for (fs::directory_iterator file(cwd); file != fs::directory_iterator();
+ ++file) {
+ if (!fs::is_directory(file->path()))
+ continue;
+ list.emplace_back(file->path().filename().string());
}
- return false;
+
+ return list;
+}
+
+// To replace with std::filesystem::unique_path()
+fs::path GenerateUniquePathString(const std::string& format) {
+ static constexpr auto chars =
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ auto dist = std::uniform_int_distribution{{}, std::strlen(chars) - 1};
+ size_t len = std::count_if(format.begin(), format.end(),
+ [](char c) { return c == '%'; });
+
+ auto random_string = std::string(len, '\0');
+ std::generate_n(begin(random_string), len,
+ [&]() {
+ return chars[dist(gen)];
+ });
+
+ auto iter = random_string.begin();
+ std::string result(format);
+ std::transform(result.begin(), result.end(), result.begin(),
+ [&](char c) {
+ return c == '%' ? *iter++ : c;
+ });
+
+ return result;
+}
+
+bool SyncFile(const fs::path& path) {
+ if (!fs::exists(path)) {
+ LOG(ERROR) << "File does not exist: " << path;
+ return false;
+ }
+
+ int fd = open(path.c_str(), O_WRONLY);
+ if (fd == -1) {
+ LOG(ERROR) << "Failed to open file: " << path << ", errno: " << errno;
+ return false;
+ }
+
+ int ret = fsync(fd);
+ if (ret == -1) {
+ LOG(ERROR) << "Failed to fsync() file: " << path << ", errno: " << errno;
+ close(fd);
+ return false;
+ }
+
+ close(fd);
+ return true;
}
} // namespace common_installer