#include "UpdaterState.h" #include "common.h" #include "utils/logger.h" #include "utils/utils.h" #include bool CheckTempFilesValid(const std::vector &filesToCheck, const std::string &base_path = SD_PATH, const std::string &suffix = UPDATE_TEMP_SUFFIX); bool CopyFilesFinal(const std::vector &filesToCheck, const std::string &base_path = SD_PATH, const std::string &suffix_temp = UPDATE_TEMP_SUFFIX, const std::string &suffix_old = UPDATE_OLD_SUFFIX); bool RenameCurrentToOld(const std::vector &filesToCheck, const std::string &base_path = SD_PATH, const std::string &suffix_old = UPDATE_OLD_SUFFIX); bool RestoreOldToCurrent(const std::vector &filesToCheck, const std::string &base_path = SD_PATH, const std::string &suffix_old = UPDATE_OLD_SUFFIX); bool RemoveTempFiles(const std::vector &filesToCheck, const std::string &base_path = SD_PATH, const std::string &suffix_temp = UPDATE_TEMP_SUFFIX); bool CheckTempFilesValid(const std::vector &filesToCheck, const std::string &base_path, const std::string &suffix) { bool allValid = true; // Make sure the downloaded files have all the correct hash. for (auto &file : filesToCheck) { auto pathOnSDCardTemp = string_format("%s%s%s", base_path.c_str(), file.getPath().c_str(), suffix.c_str()); auto hashOfFileOnSDCard = hashFile(pathOnSDCardTemp); if (hashOfFileOnSDCard.has_value()) { if (hashOfFileOnSDCard != file.getSha1()) { DEBUG_FUNCTION_LINE_WARN("File %s has a unexpected hash", pathOnSDCardTemp.c_str()); allValid = false; break; } else { DEBUG_FUNCTION_LINE("File %s is valid", pathOnSDCardTemp.c_str()); } } else { DEBUG_FUNCTION_LINE_WARN("File %s doesn't exist", pathOnSDCardTemp.c_str()); allValid = false; break; } } return allValid; } bool CopyFilesFinal(const std::vector &filesToCheck, const std::string &base_path, const std::string &suffix_temp, const std::string &suffix_old) { for (auto &file : filesToCheck) { auto pathOnSDCard = string_format("%s%s", base_path.c_str(), file.getPath().c_str()); auto pathOnSDCardTemp = string_format("%s%s%s", base_path.c_str(), file.getPath().c_str(), suffix_temp.c_str()); auto pathOnSDCardOld = string_format("%s%s%s", base_path.c_str(), file.getPath().c_str(), suffix_old.c_str()); struct stat stBuf = {}; // Remove existing files. (Shouldn't happen) if (stat(pathOnSDCard.c_str(), &stBuf) >= 0 && S_ISDIR(stBuf.st_mode)) { DEBUG_FUNCTION_LINE("Remove %s", pathOnSDCard.c_str()); if (remove(pathOnSDCard.c_str()) < 0) { DEBUG_FUNCTION_LINE_ERR("(Should not happen) Failed to remove %s", pathOnSDCard.c_str()); } } DEBUG_FUNCTION_LINE("Rename %s to %s", pathOnSDCardTemp.c_str(), pathOnSDCard.c_str()); if (rename(pathOnSDCardTemp.c_str(), pathOnSDCard.c_str()) < 0) { DEBUG_FUNCTION_LINE_ERR("Failed to rename %s to %s", pathOnSDCardTemp.c_str(), pathOnSDCard.c_str()); return false; } if (file.getStatus() != VersionCheck::FileStatus::Missing) { DEBUG_FUNCTION_LINE("Remove %s", pathOnSDCardOld.c_str()); if (remove(pathOnSDCardOld.c_str()) < 0) { DEBUG_FUNCTION_LINE_ERR("Failed to remove %s", pathOnSDCardOld.c_str()); } } } return true; } bool RenameCurrentToOld(const std::vector &filesToCheck, const std::string &base_path, const std::string &suffix_old) { // Rename the target files to .update.old if it exists. bool allSuccessful = true; for (auto &file : filesToCheck) { auto pathOnSDCard = string_format("%s%s", base_path.c_str(), file.getPath().c_str()); auto pathOnSDCardOld = string_format("%s%s%s", base_path.c_str(), file.getPath().c_str(), suffix_old.c_str()); // backup the files that actually exist. if (file.getStatus() != VersionCheck::FileStatus::Missing) { struct stat stBuf = {}; // Remove any old ".temp.old" files if (stat(pathOnSDCardOld.c_str(), &stBuf) >= 0 && S_ISREG(stBuf.st_mode)) { DEBUG_FUNCTION_LINE("Remove %s", pathOnSDCardOld.c_str()); if (remove(pathOnSDCardOld.c_str()) < 0) { DEBUG_FUNCTION_LINE_ERR("Failed to remove %s", pathOnSDCardOld.c_str()); } } // rename file to ".temp.old" DEBUG_FUNCTION_LINE("Rename %s to %s", pathOnSDCard.c_str(), pathOnSDCardOld.c_str()); if (rename(pathOnSDCard.c_str(), pathOnSDCardOld.c_str()) < 0) { allSuccessful = false; break; } } } return allSuccessful; } bool RestoreOldToCurrent(const std::vector &filesToCheck, const std::string &base_path, const std::string &suffix_old) { DEBUG_FUNCTION_LINE("Try to restore files"); bool allSuccess = true; // If renaming existing files to ".temp.old" failed, try to restore for (auto &file : filesToCheck) { if (file.getStatus() != VersionCheck::FileStatus::Missing) { auto pathOnSDCard = string_format("%s%s", base_path.c_str(), file.getPath().c_str()); auto pathOnSDCardOld = string_format("%s%s%s", base_path.c_str(), file.getPath().c_str(), suffix_old.c_str()); struct stat stBuf = {}; if (stat(pathOnSDCardOld.c_str(), &stBuf) >= 0 && S_ISREG(stBuf.st_mode) && stat(pathOnSDCard.c_str(), &stBuf) < 0) { DEBUG_FUNCTION_LINE("Try to restore %s", pathOnSDCard.c_str()); // rename file from ".temp.old" DEBUG_FUNCTION_LINE("Rename %s to %s", pathOnSDCardOld.c_str(), pathOnSDCard.c_str()); if (rename(pathOnSDCardOld.c_str(), pathOnSDCard.c_str()) < 0) { allSuccess = false; DEBUG_FUNCTION_LINE_ERR("Failed to rename %s to %s", pathOnSDCardOld.c_str(), pathOnSDCard.c_str()); } } } } return allSuccess; } bool RemoveTempFiles(const std::vector &filesToCheck, const std::string &base_path, const std::string &suffix_temp) { bool allSuccess = true; // remove all temp files on error. for (auto &file : filesToCheck) { auto pathOnSDCardTemp = string_format("%s%s%s", base_path.c_str(), file.getPath().c_str(), suffix_temp.c_str()); DEBUG_FUNCTION_LINE("Remove %s", pathOnSDCardTemp.c_str()); if (remove(pathOnSDCardTemp.c_str()) < 0) { DEBUG_FUNCTION_LINE_WARN("Failed to remove %s", pathOnSDCardTemp.c_str()); allSuccess = false; } } return allSuccess; } ApplicationState::eSubState UpdaterState::UpdateDownloadFilesFinished(Input *pInput) { if (!CheckTempFilesValid(mFilesToUpdateConfirmed)) { RemoveTempFiles(mFilesToUpdateConfirmed); setError(ERROR_DOWNLOADED_FILES_INVALID); return SUBSTATE_RUNNING; } if (!RenameCurrentToOld(mFilesToUpdateConfirmed)) { RestoreOldToCurrent(mFilesToUpdateConfirmed); RemoveTempFiles(mFilesToUpdateConfirmed); setError(ERROR_FAILED_COPY_FILES); return SUBSTATE_RUNNING; } if (!CopyFilesFinal(mFilesToUpdateConfirmed)) { RestoreOldToCurrent(mFilesToUpdateConfirmed); RemoveTempFiles(mFilesToUpdateConfirmed); setError(ERROR_FAILED_COPY_FILES); return SUBSTATE_RUNNING; } mState = STATE_UPDATE_SUCCESS; return SUBSTATE_RUNNING; }