#include "common/installer_context.h"
#include "common/pkgmgr_interface.h"
#include "common/pkgmgr_signal.h"
+#include "common/recovery_file.h"
#include "common/step/configuration/step_fail.h"
#include "common/utils/file_logbackend.h"
+#include "common/utils/file_util.h"
#include "common/step/backup/step_backup_icons.h"
#include "common/step/backup/step_backup_manifest.h"
status_ = SafeExecute(*it_, &Step::process, "process");
if (status_ != Step::Status::OK) {
- if (status_ != Step::Status::RECOVERY_DONE) {
- LOG(ERROR) << "Error during processing(" << (*it_)->name() << ")";
- result_ = Result::ERROR;
+ if (context_->undoable.get()) {
+ if (status_ != Step::Status::RECOVERY_DONE) {
+ LOG(ERROR) << "Error during processing(" << (*it_)->name() << ")";
+ result_ = Result::ERROR;
+ }
+ break;
}
- break;
+
+ LOG(WARNING) << "Error during processing(" << (*it_)->name()
+ << ") but the installation can't undo, installation is ongoing";
+ const auto& recovery_file =
+ context_->recovery_info.get().recovery_file;
+ if (recovery_file)
+ recovery_file->set_keep_recovery_file(true);
}
if (!context_->pkgid.get().empty())
history_logger_.LogHistoryStart(context_->pkgid.get(),
}
AppInstaller::Result AppInstaller::Clean() {
+ RemoveUnnecessaryRecoveryFiles();
+
const auto& recovery_file =
context_->recovery_info.get().recovery_file;
if (recovery_file) {
AddStep<ci::security::StepUnregisterTrustAnchor>();
AddStep<ci::security::StepPrivacyPrivilege>(
ci::security::StepPrivacyPrivilege::ActionType::Uninstall);
+ // After StepRemoveFiles is performed, the installation can't undo
+ // So the installation proceeds as is, ignoring errors in later steps
AddStep<ci::filesystem::StepRemoveFiles>();
AddStep<ci::pkgmgr::StepRemovePrivSharedres>();
AddStep<ci::filesystem::StepRemoveZipImage>();
ci::security::StepRegisterTrustAnchor::RegisterType::UPDATE);
AddStep<ci::security::StepPrivacyPrivilege>(
ci::security::StepPrivacyPrivilege::ActionType::Update);
+ // After StepRemoveFiles is performed, the installation can't undo
+ // So the installation proceeds as is, ignoring errors in later steps
AddStep<ci::filesystem::StepRemoveFiles>();
AddStep<ci::pkgmgr::StepRemovePrivSharedres>();
AddStep<ci::filesystem::StepRemoveZipImage>();
return process_status;
}
+
+void AppInstaller::RemoveUnnecessaryRecoveryFiles() {
+ if (context_->pkgid.get().empty())
+ return;
+
+ const auto& own_recovery_file =
+ context_->recovery_info.get().recovery_file;
+ std::vector<RecoverEntry> entries = SearchRecoveryFiles(context_->uid.get());
+ for (const auto& it : entries) {
+ if (it.first == "unified")
+ continue;
+
+ if (own_recovery_file && own_recovery_file->path() == it.second)
+ continue;
+
+ auto recovery_file = recovery::RecoveryFile::OpenRecoveryFile(it.second);
+ if (!recovery_file) {
+ LOG(ERROR) << "Failed to open recovery file path : " << it.second;
+ continue;
+ }
+
+ if (context_->pkgid.get() == recovery_file->pkgid()) {
+ LOG(WARNING) << "The installation request for package["
+ << context_->pkgid.get()
+ << "] has been completed, so remove unnecessary recovery file["
+ << it.second << "]";
+ Remove(it.second);
+ }
+ }
+}
+
} // namespace common_installer
std::string name);
void HandleStepError(Step::Status result, const std::string& error);
std::string GetPackageVersion();
+ void RemoveUnnecessaryRecoveryFiles();
std::shared_ptr<utils::FileLogBackend> failure_logger_;
Step::Status status_;
partial_rw(false),
force_clean_from_db(false),
cross_app_rules(false),
- debug_mode(false) {}
+ debug_mode(false),
+ undoable(true) {}
InstallerContext::~InstallerContext() {
if (manifest_data.get())
* (needed for rollback operations)
*/
Property<std::vector<PkgQueryInterface::PluginInfo>> backup_plugin_info;
+
+ /**
+ * @brief Property of indicating if the installer can undo
+ */
+ Property<bool> undoable;
};
} // namespace common_installer
RecoveryFile::RecoveryFile(const fs::path& path, RequestType type, bool load)
: type_(type), path_(path), backup_done_(false), cleanup_(false),
- security_operation_done_(false) {
+ security_operation_done_(false), keep_recovery_file_(false) {
backup_path_ = path_.string() + ".bck";
if (load) {
if (!ReadFileContent()) {
}
RecoveryFile::~RecoveryFile() {
+ if (keep_recovery_file_ == true) {
+ LOG(WARNING) << "Keep recovery file";
+ return;
+ }
+
if (Remove(path_))
LOG(DEBUG) << "Recovery file " << path_ << " removed";
if (Remove(backup_path_))
security_operation_done_ = security_operation_done;
}
+void RecoveryFile::set_keep_recovery_file(bool keep_recovery_file) {
+ keep_recovery_file_ = keep_recovery_file;
+}
+
const std::filesystem::path& RecoveryFile::unpacked_dir() const {
return unpacked_dir_;
}
return security_operation_done_;
}
+bool RecoveryFile::keep_recovery_file() const {
+ return keep_recovery_file_;
+}
+
+const std::filesystem::path& RecoveryFile::path() const {
+ return path_;
+}
+
bool RecoveryFile::ReadFileContent() {
FILE* handle = fopen(path_.c_str(), "r");
if (!handle) {
*/
void set_security_operation_done(bool security_operation_done);
+ /**
+ * setter for keep recovery file
+ *
+ * \param keep_recovery_file boolean value of keep_recovery_file
+ */
+ void set_keep_recovery_file(bool keep_recovery_file);
+
/**
* getter for unpacked dir
*
*/
bool security_operation_done() const;
+ /**
+ * getter for recovery file path
+ *
+ * \return true if backup does exist
+ */
+ const std::filesystem::path& path() const;
+
+ /**
+ * getter for keep recovery file flag
+ *
+ * \return true if keep recovery file flag has set
+ */
+ bool keep_recovery_file() const;
+
/**
* Transaction of current RecoveryFile content into recovery file
*
bool backup_done_;
bool cleanup_;
bool security_operation_done_;
+ bool keep_recovery_file_;
};
} // namespace recovery
namespace common_installer {
namespace configuration {
+bool StepFail::is_executed_(false);
+
+bool StepFail::IsExecuted() {
+ return is_executed_;
+}
+
Step::Status StepFail::process() {
LOG(ERROR) << "Request was expected to fail";
+ is_executed_ = true;
return Status::ERROR;
}
public:
using Step::Step;
+ static bool IsExecuted();
+
/**
* \brief returns error
*
Status undo() override { return Status::OK; }
Status precheck() override { return Status::OK; }
+ private:
+ static bool is_executed_;
+
STEP_NAME(Fail)
};
<< context_->GetPkgPath()
<< ") path does not exist";
+ context_->undoable.set(false);
+
return Step::Status::OK;
}
RequestType::Uninstall)
return RecoveryUninstall();
+ if (context_->recovery_info.get().recovery_file->type() ==
+ RequestType::ReadonlyUpdateUninstall)
+ return RecoveryReadonlyUpdateUninstall();
+
if (context_->recovery_info.get().recovery_file->cleanup() ||
context_->recovery_info.get().cleanup)
return Cleanup();
#include "common/utils/byte_size_literals.h"
#include "common/utils/paths.h"
+#include "common/utils/request.h"
namespace fs = std::filesystem;
namespace {
-unsigned kZipBufSize = 8_kB;
-unsigned kZipMaxPath = PATH_MAX;
+constexpr unsigned kZipBufSize = 8_kB;
+constexpr unsigned kZipMaxPath = PATH_MAX;
+constexpr const char kRecoveryFilePattern[] = "^(.*)-recovery-(.){6}$";
+constexpr const char kBackupFilePattern[] = "^(.*)-recovery-(.){6}\\.bck$";
int64_t GetBlockSizeForPath(const fs::path& path_in_partition) {
struct stat stats;
return true;
}
+static void SearchBackupFiles(uid_t uid) {
+ const fs::path recovery_dir =
+ GetRecoveryFilePath(GetRootAppPath(false, uid));
+ if (!fs::exists(recovery_dir))
+ return;
+ try {
+ for (fs::directory_iterator iter(recovery_dir);
+ iter != fs::directory_iterator();
+ ++iter) {
+ std::string file = iter->path().filename().string();
+ std::regex backup_regex(kBackupFilePattern);
+ std::smatch match;
+ if (std::regex_search(file, match, backup_regex)) {
+ fs::path orig_file(iter->path().parent_path() / iter->path().stem());
+ if (fs::exists(orig_file))
+ fs::remove(orig_file);
+ fs::rename(iter->path(), orig_file);
+ }
+ }
+ } catch (const std::exception& e) {
+ LOG(WARNING) << "Exception occurred: "
+ << typeid(e).name() << ", " << e.what();
+ }
+}
+
+std::vector<RecoverEntry> SearchRecoveryFiles(uid_t uid) {
+ SearchBackupFiles(uid);
+
+ std::vector<RecoverEntry> list;
+ const fs::path recovery_dir =
+ GetRecoveryFilePath(GetRootAppPath(false, uid));
+ LOG(INFO) << "RootAppPath: " << recovery_dir;
+ if (!fs::exists(recovery_dir))
+ return list;
+
+ for (fs::directory_iterator iter(recovery_dir);
+ iter != fs::directory_iterator();
+ ++iter) {
+ try {
+ std::string file = iter->path().filename().string();
+ std::regex recovery_regex(kRecoveryFilePattern);
+ std::smatch match;
+ if (std::regex_search(file, match, recovery_regex)) {
+ LOG(INFO) << "Found recovery file: " << file;
+ std::string type(match[1]);
+ if (type == "unified")
+ list.emplace(list.begin(), type, iter->path().string());
+ else
+ list.emplace_back(type, iter->path().string());
+ }
+ } catch (const std::exception& e) {
+ LOG(WARNING) << "Exception occurred: "
+ << typeid(e).name() << ", " << e.what();
+ continue;
+ }
+ }
+
+ return list;
+}
+
} // namespace common_installer
namespace common_installer {
+typedef std::pair<std::string, std::filesystem::path> RecoverEntry;
+
enum FSFlag : int {
FS_NONE = 0,
FS_MERGE_SKIP = (1 << 0),
bool SyncFile(const std::filesystem::path& path);
+std::vector<RecoverEntry> SearchRecoveryFiles(uid_t uid);
+
} // namespace common_installer
#endif // COMMON_UTILS_FILE_UTIL_H_
namespace {
-typedef std::pair<std::string, std::string> RecoverEntry;
-
-const char kRecoveryFilePattern[] = "^(.*)-recovery-(.){6}$";
-const char kBackupFilePattern[] = "^(.*)-recovery-(.){6}\\.bck$";
const uid_t kGlobalUserUid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
std::string TruncateNewLine(const char* data) {
void Run();
private:
- void SearchBackupFiles(uid_t uid);
- std::vector<RecoverEntry> SearchRecoveryFiles(uid_t uid);
- void ProcessRecovery(uid_t uid, const std::vector<RecoverEntry>& entries);
+ void ProcessRecovery(uid_t uid, const std::vector<ci::RecoverEntry>& entries);
bool RunBackend(uid_t uid, const char* type, const char* file);
};
void PkgRecoveryService::Run() {
// recover global packages
- SearchBackupFiles(kGlobalUserUid);
LOG(INFO) << "Searching recovery files for user " << kGlobalUserUid;
- std::vector<RecoverEntry> globalentries = SearchRecoveryFiles(kGlobalUserUid);
+ std::vector<ci::RecoverEntry> globalentries = ci::SearchRecoveryFiles(kGlobalUserUid);
ProcessRecovery(kGlobalUserUid, globalentries);
// recover normal user packages
for (const auto& userinfo : list) {
uid_t uid = std::get<0>(userinfo);
LOG(INFO) << "Searching recovery files for user " << std::get<0>(userinfo);
- SearchBackupFiles(uid);
- std::vector<RecoverEntry> entries = SearchRecoveryFiles(uid);
+ std::vector<ci::RecoverEntry> entries = ci::SearchRecoveryFiles(uid);
ProcessRecovery(uid, entries);
}
}
-void PkgRecoveryService::SearchBackupFiles(uid_t uid) {
- const fs::path recovery_dir =
- ci::GetRecoveryFilePath(ci::GetRootAppPath(false, uid));
- if (!fs::exists(recovery_dir))
- return;
- try {
- for (fs::directory_iterator iter(recovery_dir);
- iter != fs::directory_iterator();
- ++iter) {
- std::string file = iter->path().filename().string();
- std::regex backup_regex(kBackupFilePattern);
- std::smatch match;
- if (std::regex_search(file, match, backup_regex)) {
- fs::path orig_file(iter->path().parent_path() / iter->path().stem());
- if (fs::exists(orig_file))
- fs::remove(orig_file);
- fs::rename(iter->path(), orig_file);
- }
- }
- } catch (const std::exception& e) {
- LOG(WARNING) << "Exception occurred: "
- << typeid(e).name() << ", " << e.what();
- }
-}
-
-std::vector<RecoverEntry> PkgRecoveryService::SearchRecoveryFiles(uid_t uid) {
- std::vector<RecoverEntry> list;
- const fs::path recovery_dir =
- ci::GetRecoveryFilePath(ci::GetRootAppPath(false, uid));
- LOG(INFO) << "RootAppPath: " << recovery_dir;
- if (!fs::exists(recovery_dir))
- return list;
-
- for (fs::directory_iterator iter(recovery_dir);
- iter != fs::directory_iterator();
- ++iter) {
- try {
- std::string file = iter->path().filename().string();
- std::regex recovery_regex(kRecoveryFilePattern);
- std::smatch match;
- if (std::regex_search(file, match, recovery_regex)) {
- LOG(INFO) << "Found recovery file: " << file;
- std::string type(match[1]);
- if (type == "unified")
- list.emplace(list.begin(), type, iter->path().string());
- else
- list.emplace_back(type, iter->path().string());
- }
- } catch (const std::exception& e) {
- LOG(WARNING) << "Exception occurred: "
- << typeid(e).name() << ", " << e.what();
- continue;
- }
- }
-
- return list;
-}
-
void PkgRecoveryService::ProcessRecovery(uid_t uid,
- const std::vector<RecoverEntry>& entries) {
+ const std::vector<ci::RecoverEntry>& entries) {
LOG(INFO) << "Process recovery for user " << uid;
for (const auto& entry : entries) {
const char* type = entry.first.c_str();
#include <gtest/gtest.h>
#include <common/installer/app_installer.h>
+#include <common/step/configuration/step_fail.h>
#include <common/utils/paths.h>
#include <common/pkgmgr_interface.h>
#include <common/utils/pkgmgr_query.h>
}
AppInstallerPtr backend;
unsigned int insert_idx = 0;
+ unsigned int step_count = 0;
do {
backend = CreateFailExpectedInstaller(pkgmgr, insert_idx);
LOG(DEBUG) << "StepFail is inserted at: " << insert_idx;
- ci::AppInstaller::Result ret = backend->Run();
- if (ret != ci::AppInstaller::Result::ERROR) {
+ backend->Run();
+ step_count = backend->StepCount();
+ backend.reset();
+ if (!ci::configuration::StepFail::IsExecuted()) {
LOG(ERROR) << "StepFail not executed";
return 1;
}
break;
}
insert_idx++;
- } while (insert_idx < backend->StepCount());
- if (insert_idx != backend->StepCount())
+ } while (insert_idx < step_count);
+ if (insert_idx != step_count)
return 1;
return 0;