#include <fcntl.h>
#include <sys/stat.h>
+#include <linux/limits.h>
#include <unzip.h>
#include <zlib.h>
+#include <sessiond.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 <vector>
#include "common/utils/byte_size_literals.h"
+#include "common/utils/paths.h"
namespace ba = boost::algorithm;
namespace bs = boost::system;
namespace {
unsigned kZipBufSize = 8_kB;
-unsigned kZipMaxPath = 256;
+unsigned kZipMaxPath = PATH_MAX;
int64_t GetBlockSizeForPath(const bf::path& path_in_partition) {
struct stat stats;
namespace common_installer {
+FSFlag operator|(FSFlag a, FSFlag b) {
+ 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 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) {
+ if (!SetOwnership(path, uid, gid))
+ return false;
+ if (!bf::is_directory(path))
+ return true;
+ for (bf::recursive_directory_iterator iter(path);
+ iter != bf::recursive_directory_iterator();
+ ++iter) {
+ bf::path current(iter->path());
+ if (!SetOwnership(current, uid, gid))
+ return false;
+ }
+ return true;
+}
+
bool CreateDir(const bf::path& path) {
if (bf::exists(path))
return true;
return true;
}
-bool CopyDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
+bool SetDirOwnershipAndPermissions(const boost::filesystem::path& path,
+ boost::filesystem::perms permissions, uid_t uid,
+ gid_t gid) {
+ if (!SetOwnership(path, uid, gid)) {
+ LOG(ERROR) << "Failed to change owner: " << path
+ << "(" << uid << ", " << gid << ")";
+ return false;
+ }
+ if (!SetDirPermissions(path, permissions)) {
+ LOG(ERROR) << "Failed to change permission: " << path
+ << std::oct << permissions;
+ return false;
+ }
+
+ return true;
+}
+
+bool CopyOwnershipAndPermissions(const boost::filesystem::path& src,
+ const boost::filesystem::path& dst) {
+ if (!bf::exists(src)) {
+ LOG(ERROR) << "Failed to copy ownership and permissions"
+ << " from " << src << " to " << dst;
+ return false;
+ }
+ bf::perms permissions = bf::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 bf::path& src, const bf::path& dst,
+ FSFlag flags, bool skip_symlink) {
try {
// Check whether the function call is valid
if (!bf::exists(src) || !bf::is_directory(src)) {
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_DIRECTORIES)) {
+ 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) {
LOG(ERROR) << "Failed to copy directory: " << error.what();
try {
bf::path current(file->path());
bf::path target = dst / current.filename();
- if (bf::is_directory(current)) {
+
+ if (bf::is_symlink(symlink_status(current))) {
+ if (skip_symlink)
+ continue;
+ if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) &&
+ bf::exists(target))
+ continue;
+ bs::error_code error;
+ bf::copy_symlink(current, target, error);
+ if (error) {
+ LOG(ERROR) << "Failed to copy symlink: " << current << ", "
+ << error.message();
+ return false;
+ }
+ } else if (bf::is_directory(current)) {
// Found directory: Recursion
- if (!CopyDir(current, target, flags)) {
+ if (!CopyDir(current, target, flags, skip_symlink)) {
return false;
}
- } else if (bf::is_symlink(current)) {
- if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
- continue;
- bf::copy_symlink(current, target);
} else {
- if ((flags & FS_MERGE_DIRECTORIES) && bf::exists(target))
+ if ((flags & FS_MERGE_SKIP) && bf::exists(target))
continue;
- bf::copy_file(current, target);
+ bf::path destination = target;
+
+ if (flags & FS_COMMIT_COPY_FILE)
+ destination =
+ bf::unique_path(target.parent_path() / "%%%%-%%%%-%%%%-%%%%");
+
+ if (flags & FS_MERGE_OVERWRITE)
+ bf::copy_file(current, destination,
+ bf::copy_option::overwrite_if_exists);
+ else
+ bf::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);
+ }
}
} catch (const bf::filesystem_error& error) {
LOG(ERROR) << "Failed to copy directory: " << error.what();
return true;
}
+bool RestoreBackup(const bf::path& path) {
+ bf::path backup_path = GetBackupPathForPackagePath(path);
+ if (!bf::exists(backup_path) &&
+ !bf::is_symlink(bf::symlink_status(backup_path))) {
+ LOG(WARNING) << backup_path << " does not exist";
+ return true;
+ }
+ return MoveDir(backup_path, path);
+}
+
+bool MakeBackup(const bf::path& path) {
+ if (!bf::exists(path) && !bf::is_symlink(bf::symlink_status(path))) {
+ LOG(WARNING) << path << " does not exist";
+ return true;
+ }
+ bf::path backup_path = GetBackupPathForPackagePath(path);
+ return MoveDir(path, backup_path);
+}
+
+bool RemoveBackup(const bf::path& path) {
+ bf::path backup_path = GetBackupPathForPackagePath(path);
+ if (!bf::exists(backup_path) &&
+ !bf::is_symlink(bf::symlink_status(backup_path))) {
+ LOG(WARNING) << backup_path << " does not exist";
+ return true;
+ }
+ return RemoveAll(backup_path);
+}
+
+bool RemoveAll(const bf::path& path) {
+ if (!bf::exists(path) && !bf::is_symlink(bf::symlink_status(path)))
+ return true;
+
+ bs::error_code error;
+ bf::remove_all(path, error);
+
+ if (error) {
+ LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
+ return false;
+ }
+
+ return true;
+}
+
+bool Remove(const bf::path& path) {
+ bs::error_code error;
+ if (!bf::exists(bf::symlink_status(path, error)))
+ return true;
+
+ bf::remove(path, error);
+ if (error) {
+ LOG(ERROR) << "Cannot remove: " << path << ", " << error.message();
+ return false;
+ }
+ return true;
+}
+
bool MoveDir(const bf::path& src, const bf::path& dst, FSFlag flags) {
- if (bf::exists(dst) && !(flags & FS_MERGE_DIRECTORIES)) {
+ if (bf::exists(dst) &&
+ !(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) {
LOG(ERROR) << "Destination directory does exist: " << dst;
return false;
}
bf::rename(src, dst, error);
if (error) {
LOG(WARNING) << "Cannot move directory: " << src << ". Will copy/remove...";
- if (!CopyDir(src, dst, flags)) {
+ if (!CopyDir(src, dst, flags, false)) {
LOG(ERROR) << "Cannot copy directory: " << src;
return false;
}
return true;
}
-bool MoveFile(const bf::path& src, const bf::path& dst) {
- if (bf::exists(dst))
+bool MoveFile(const bf::path& src, const bf::path& dst, bool force) {
+ if (!force && bf::exists(dst))
return false;
bs::error_code error;
bf::rename(src, dst, error);
return true;
}
+bool BackupDir(const boost::filesystem::path& src,
+ const boost::filesystem::path& dst, const std::string& entry) {
+ if (!bf::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 bf::path& path) {
int64_t size = 0;
int64_t block_size = GetBlockSizeForPath(path);
- // if failed to stat path
if (block_size == -1)
return -1;
if (unzGetCurrentFileInfo64(zip_file, &raw_file_info, raw_file_name_in_zip,
sizeof(raw_file_name_in_zip), nullptr, 0, nullptr, 0) != UNZ_OK) {
LOG(ERROR) << "Failed to read file info";
+ unzClose(zip_file);
return -1;
}
size += RoundUpToBlockSizeOf(raw_file_info.uncompressed_size, block_size);
+ unzGoToNextFile(zip_file);
}
// FIXME: calculate space needed for directories
return size;
}
+int64_t GetDirectorySize(const boost::filesystem::path& path) {
+ int64_t block_size = GetBlockSizeForPath(path);
+
+ if (block_size == -1)
+ return -1;
+
+ int64_t size = 0;
+ for (bf::recursive_directory_iterator iter(path);
+ iter != bf::recursive_directory_iterator(); ++iter) {
+ struct stat buf;
+ if (lstat(iter->path().c_str(), &buf) == -1) {
+ LOG(ERROR) << "lstat() failed for: " << iter->path();
+ return -1;
+ }
+ size += RoundUpToBlockSizeOf(buf.st_size, block_size);
+ }
+
+ // FIXME: block size for external device may differ...
+ return size;
+}
+
+bool CheckFreeSpaceAtPath(int64_t required_size,
+ const boost::filesystem::path& target_location) {
+ bs::error_code error;
+ boost::filesystem::path root = target_location;
+
+ while (!bf::exists(root) && root != root.root_path())
+ root = root.parent_path();
+
+ if (!bf::exists(root)) {
+ LOG(ERROR) << "No mount point for path: " << target_location;
+ return false;
+ }
+
+ bf::space_info space_info = bf::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));
+}
+
boost::filesystem::path GenerateTmpDir(const bf::path &app_path) {
boost::filesystem::path install_tmp_dir;
boost::filesystem::path tmp_dir(app_path);
bf::path pattern = path;
pattern += "-%%%%%%";
bf::path tmp_path;
+
do {
tmp_path = boost::filesystem::unique_path(pattern);
} while (boost::filesystem::exists(tmp_path));
+
return tmp_path;
}
UnzFilePointer zip_file;
if (!zip_file.Open(zip_path)) {
- LOG(ERROR) << "Failed to open the source dir: " << zip_path;
+ LOG(WARNING) << "Failed to open the source dir: " << zip_path;
return false;
}
std::string(raw_file_name_in_zip).find(filter_prefix) == 0) {
bf::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);
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);
+ 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();
+ }
+
+ return false;
+}
+
+bf::path RelativePath(const bf::path& from,
+ const bf::path& to) {
+ bf::path::const_iterator itr_path = from.begin();
+ bf::path::const_iterator itr_relative_to = to.begin();
+
+ while (itr_path != from.end() && itr_relative_to != to.end() &&
+ *itr_path == *itr_relative_to) {
+ ++itr_path;
+ ++itr_relative_to;
+ }
+
+ bf::path result;
+ if (itr_relative_to != to.end()) {
+ ++itr_relative_to;
+ while (itr_relative_to != to.end()) {
+ result /= "..";
+ ++itr_relative_to;
+ }
+ }
+
+ while (itr_path != from.end()) {
+ result /= *itr_path;
+ ++itr_path;
+ }
+
+ bs::error_code error;
+ bf::path resolved_path = bf::canonical(result, error);
+ if (error) {
+ LOG(ERROR) << "Failed to get canonical path";
+ return {};
+ }
+
+ if (from != resolved_path) {
+ LOG(ERROR) << "Failed to get right relative path :" << resolved_path;
+ return {};
+ }
+
+ return result;
+}
+
+std::vector<std::string> GetLightUserList(uid_t uid) {
+ int user_count = 0;
+ subsession_user_t* user_list = nullptr;
+ int ret = subsession_get_user_list(
+ static_cast<int>(uid), &user_list, &user_count);
+ if (ret != TIZEN_ERROR_NONE) {
+ LOG(ERROR) << "Failed to get light user list : " << ret;
+ return {};
+ }
+
+ std::vector<std::string> result_list;
+ for (int i = 0; i < user_count; i++) {
+ if (strlen(user_list[i]) == 0)
+ continue;
+
+ result_list.emplace_back(user_list[i]);
+ }
+
+ std::free(user_list);
+ return result_list;
}
} // namespace common_installer