diff --git a/README.md b/README.md new file mode 100755 index 0000000..93c2b19 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Kylin Backup Tools + +1、对备份还原工具4.0.13版本进行了重构 +2、界面和流程对比4.0.13版本全都发送了变化 +3、适配UKui3.1界面风格 +4、新增了一些功能或对原功能进行了优化 + diff --git a/backup-daemon/backup-daemon.pro b/backup-daemon/backup-daemon.pro new file mode 100755 index 0000000..61a9c0d --- /dev/null +++ b/backup-daemon/backup-daemon.pro @@ -0,0 +1,97 @@ +QT += core +QT -= gui +QT += dbus xml + +CONFIG += c++11 console +CONFIG -= app_bundle + +CONFIG += link_pkgconfig + +LIBS += -lblkid \ + -lkysec + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +QMAKE_CXXFLAGS += -Wno-implicit-fallthrough + +HEADERS += \ + ../common/mydefine.h \ + ../common/mydusizetool.h \ + ../common/mylittleparse.h \ + ../common/reflect.h \ + ../common/singleton.h \ + ../common/spinlock_mutex.h \ + ../common/utils.h \ + backupmanager_adaptor.h \ + customizedatabackupproxy.h \ + customizeghostImageproxy.h \ + customizesystembackupproxy.h \ + customizesystemrestoreproxy.h \ + databackupproxy.h \ + datarestoreproxy.h \ + deletebackupproxy.h \ + ghostimageproxy.h \ + mybackupmanager.h \ + mymountproxy.h \ + myprocess/calcbackupsize.h \ + myprocess/mksquashfsprocess.h \ + myprocess/mountbackupprocess.h \ + myprocess/rsyncpathtodirprocess.h \ + mythread.h \ + parsebackuplist.h \ + systembackupproxy.h \ + systemrestoreproxy.h \ + udiskdatabackupproxy.h \ + udiskdatarestoreproxy.h \ + udiskghostImageproxy.h \ + udisksystembackupproxy.h \ + udisksystemrestoreproxy.h \ + workerfactory.h + +SOURCES += \ + ../common/mydefine.cpp \ + ../common/mydusizetool.cpp \ + ../common/mylittleparse.cpp \ + ../common/reflect.cpp \ + ../common/utils.cpp \ + backupmanager_adaptor.cpp \ + customizedatabackupproxy.cpp \ + customizeghostImageproxy.cpp \ + customizesystembackupproxy.cpp \ + customizesystemrestoreproxy.cpp \ + databackupproxy.cpp \ + datarestoreproxy.cpp \ + deletebackupproxy.cpp \ + ghostimageproxy.cpp \ + main.cpp \ + mybackupmanager.cpp \ + mymountproxy.cpp \ + myprocess/calcbackupsize.cpp \ + myprocess/mksquashfsprocess.cpp \ + myprocess/mountbackupprocess.cpp \ + myprocess/rsyncpathtodirprocess.cpp \ + mythread.cpp \ + parsebackuplist.cpp \ + systembackupproxy.cpp \ + systemrestoreproxy.cpp \ + udiskdatabackupproxy.cpp \ + udiskdatarestoreproxy.cpp \ + udiskghostImageproxy.cpp \ + udisksystembackupproxy.cpp \ + udisksystemrestoreproxy.cpp \ + workerfactory.cpp + +# Default rules for deployment. +# qnx: target.path = /tmp/$${TARGET}/bin +# else: unix:!android: target.path = /usr/bin +# !isEmpty(target.path): INSTALLS += target + + diff --git a/backup-daemon/backupmanager_adaptor.cpp b/backup-daemon/backupmanager_adaptor.cpp new file mode 100755 index 0000000..061f8b1 --- /dev/null +++ b/backup-daemon/backupmanager_adaptor.cpp @@ -0,0 +1,131 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp com.kylin.backup.manager.xml -i mybackupmanager.h -a ManagerAdaptor + * + * qdbusxml2cpp is Copyright (C) 2020 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#include "backupmanager_adaptor.h" +#include +#include +#include +#include +#include +#include +#include + +/* + * Implementation of adaptor class ManagerAdaptor + */ + +ManagerAdaptor::ManagerAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ + // constructor + setAutoRelaySignals(true); +} + +ManagerAdaptor::~ManagerAdaptor() +{ + // destructor +} + +int ManagerAdaptor::Mount_backup_partition() +{ + // handle method call com.kylin.backup.manager.Mount_backup_partition + int out0; + QMetaObject::invokeMethod(parent(), "Mount_backup_partition", Q_RETURN_ARG(int, out0)); + return out0; +} + +bool ManagerAdaptor::umountBackupPartition() +{ + // handle method call com.kylin.backup.manager.umountBackupPartition + bool out0; + QMetaObject::invokeMethod(parent(), "umountBackupPartition", Q_RETURN_ARG(bool, out0)); + return out0; +} + +void ManagerAdaptor::autoBackUpForSystemUpdate_noreturn(const QString &autobackup_name, const QString &create_note, const QString &inc_note, const QString &frontUserName, int frontUid) +{ + // handle method call com.kylin.backup.manager.autoBackUpForSystemUpdate_noreturn + QMetaObject::invokeMethod(parent(), "autoBackUpForSystemUpdate_noreturn", Q_ARG(QString, autobackup_name), Q_ARG(QString, create_note), Q_ARG(QString, inc_note), Q_ARG(QString, frontUserName), Q_ARG(int, frontUid)); +} + +int ManagerAdaptor::checkEnv(BackupWrapper backupWrapper) +{ + // handle method call com.kylin.backup.manager.checkEnv + int out0; + QMetaObject::invokeMethod(parent(), "checkEnv", Q_RETURN_ARG(int, out0), Q_ARG(BackupWrapper, backupWrapper)); + return out0; +} + +int ManagerAdaptor::deleteBackupPoint(BackupWrapper backupWrapper) +{ + // handle method call com.kylin.backup.manager.deleteBackupPoint + int out0; + QMetaObject::invokeMethod(parent(), "deleteBackupPoint", Q_RETURN_ARG(int, out0), Q_ARG(BackupWrapper, backupWrapper)); + return out0; +} + +QString ManagerAdaptor::getBackupCommentForSystemUpdate(QString &state) +{ + // handle method call com.kylin.backup.manager.getBackupCommentForSystemUpdate + //return static_cast(parent())->getBackupCommentForSystemUpdate(state); + QString out0; + QMetaObject::invokeMethod(parent(), "getBackupCommentForSystemUpdate", Q_RETURN_ARG(QString, out0), Q_ARG(QString&, state)); + return out0; +} + +int ManagerAdaptor::getBackupState(bool &isActive) +{ + // handle method call com.kylin.backup.manager.getBackupState + //return static_cast(parent())->getBackupState(isActive); + int out0; + QMetaObject::invokeMethod(parent(), "getBackupState", Q_RETURN_ARG(int, out0), Q_ARG(bool&, isActive)); + return out0; +} + +int ManagerAdaptor::ghostBackup(BackupWrapper backupWrapper) +{ + // handle method call com.kylin.backup.manager.ghostBackup + int out0; + QMetaObject::invokeMethod(parent(), "ghostBackup", Q_RETURN_ARG(int, out0), Q_ARG(BackupWrapper, backupWrapper)); + return out0; +} + +int ManagerAdaptor::goBackup(BackupWrapper backupWrapper) +{ + // handle method call com.kylin.backup.manager.goBackup + int out0; + QMetaObject::invokeMethod(parent(), "goBackup", Q_RETURN_ARG(int, out0), Q_ARG(BackupWrapper, backupWrapper)); + return out0; +} + +int ManagerAdaptor::goRestore(BackupWrapper backupWrapper) +{ + // handle method call com.kylin.backup.manager.goRestore + int out0; + QMetaObject::invokeMethod(parent(), "goRestore", Q_RETURN_ARG(int, out0), Q_ARG(BackupWrapper, backupWrapper)); + return out0; +} + +int ManagerAdaptor::cancel() +{ + // handle method call com.kylin.backup.manager.cancel + int out0; + QMetaObject::invokeMethod(parent(), "cancel", Q_RETURN_ARG(int, out0)); + return out0; +} + +int ManagerAdaptor::reboot() +{ + // handle method call com.kylin.backup.manager.reboot + int out0; + QMetaObject::invokeMethod(parent(), "reboot", Q_RETURN_ARG(int, out0)); + return out0; +} + diff --git a/backup-daemon/backupmanager_adaptor.h b/backup-daemon/backupmanager_adaptor.h new file mode 100755 index 0000000..c800d57 --- /dev/null +++ b/backup-daemon/backupmanager_adaptor.h @@ -0,0 +1,146 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp com.kylin.backup.manager.xml -i mybackupmanager.h -a ManagerAdaptor + * + * qdbusxml2cpp is Copyright (C) 2020 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#ifndef MANAGERADAPTOR_H +#define MANAGERADAPTOR_H + +#include +#include +#include "mybackupmanager.h" +QT_BEGIN_NAMESPACE +class QByteArray; +template class QList; +template class QMap; +class QString; +class QStringList; +class QVariant; +QT_END_NAMESPACE + +/* + * Adaptor class for interface com.kylin.backup.manager + */ +class ManagerAdaptor: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.kylin.backup.manager") + Q_CLASSINFO("D-Bus Introspection", "" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" + "") +public: + ManagerAdaptor(QObject *parent); + virtual ~ManagerAdaptor(); + +public: // PROPERTIES +public Q_SLOTS: // METHODS + int Mount_backup_partition(); + bool umountBackupPartition(); + void autoBackUpForSystemUpdate_noreturn(const QString &autobackup_name, const QString &create_note, const QString &inc_note, const QString &frontUserName, int frontUid); + int checkEnv(BackupWrapper backupWrapper); + int deleteBackupPoint(BackupWrapper backupWrapper); + QString getBackupCommentForSystemUpdate(QString &state); + int getBackupState(bool &isActive); + int ghostBackup(BackupWrapper backupWrapper); + int goBackup(BackupWrapper backupWrapper); + int goRestore(BackupWrapper backupWrapper); + int cancel(); + int reboot(); +Q_SIGNALS: // SIGNALS + void backupFinished(bool result); + void progress(int in0, int in1); + void sendBackupResult(bool result); + void sendDeleteResult(bool result); + void sendEnvCheckResult(int result); + void sendGhostBackupResult(bool result); + void sendRate(int in0, int in1); + void sendRestoreResult(bool result); + void sendStartBackupResult(int result); +}; + +#endif diff --git a/backup-daemon/com.kylin.backup.manager.xml b/backup-daemon/com.kylin.backup.manager.xml new file mode 100755 index 0000000..181f0a8 --- /dev/null +++ b/backup-daemon/com.kylin.backup.manager.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backup-daemon/customizedatabackupproxy.cpp b/backup-daemon/customizedatabackupproxy.cpp new file mode 100755 index 0000000..0b8201f --- /dev/null +++ b/backup-daemon/customizedatabackupproxy.cpp @@ -0,0 +1,484 @@ +#include "customizedatabackupproxy.h" +#include +#include +#include +#include +#include "../common/utils.h" +#include "../common/mydusizetool.h" +#include "mymountproxy.h" +#include "myprocess/calcbackupsize.h" + +IMPLEMENT_DYNCREATE(CustomizeDataBackupProxy) + +CustomizeDataBackupProxy::CustomizeDataBackupProxy() +{ + m_isOnlyCheck = true; + m_bSuccess = false; + m_isFinished = false; + m_p = nullptr; + m_size = 0; + m_calc = new CalcBackupSize(this); + + connect(this, &CustomizeDataBackupProxy::cancel, this, &CustomizeDataBackupProxy::cancelEx); +} + +CustomizeDataBackupProxy::~CustomizeDataBackupProxy() +{ + delete m_p; + m_p = nullptr; + + delete m_calc; + m_calc = nullptr; +} + +/** + * @brief 环境检测 + * @return false,检测失败;true,检测成功 + */ +bool CustomizeDataBackupProxy::checkEnvEx() +{ + qDebug() << "CustomizeDataBackupProxy::checkEnv invoke begin"; + + // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载 + MyMountProxy mountProxy; + MountResult result = mountProxy.mountBackupPartition(); + // 无备份分区 + if (MountResult::CANNOT_GET_BACKUPUUID == result) { + qInfo() << "There is no backup partition!"; + + QString snapshotsPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH; + snapshotsPath.replace("//", "/"); + Utils::mkpath(snapshotsPath); + Utils::generateExcludePathsFile(); + } else if (MountResult::MOUNTED != result) { + emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL)); + return false; + } + + if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) { + qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); + Utils::mkpath(m_backupWrapper.m_prefixDestPath); + } + + // 因为有可能是选择的移动设备,故进行校验移动设备情况:空间大小、文件格式、挂载模式等 + QString backupPath(m_backupWrapper.m_prefixDestPath); + backupPath.replace("//", "/"); + QStorageInfo udisk(backupPath); + QString udisk_type = udisk.fileSystemType(); + qDebug() << "udisk's filesystemtype is " << udisk_type; + if (udisk_type == "vfat") { + qCritical() << m_backupWrapper.m_prefixDestPath + " udisk's filesystemtype is vfat"; + emit checkResult(int(BackupResult::UDISK_FILESYSTEM_TYPE_IS_VFAT)); + return false; + } + if (udisk.isReadOnly()) { + // 只读挂载的U盘 + qCritical() << QString("udisk(%s) is readonly filesystem").arg(m_backupWrapper.m_prefixDestPath); + emit checkResult(int(BackupResult::UDISK_FILESYSTEM_IS_READONLY)); + return false; + } + + // 2、判断备份是否增量备份 + isIncBackup(); + + // 3、检测空间是否满足备份 + calcSizeForBackup(); + + qDebug() << "CustomizeDataBackupProxy::checkEnv invoke end"; + return true; +} + +/** + * @brief 执行备份逻辑 + */ +void CustomizeDataBackupProxy::doWorkEx() +{ + qDebug() << "CustomizeDataBackupProxy::doWorkEx invoke begin"; + + m_isOnlyCheck = false; + // 环境检测 + checkEnvEx(); + + qDebug() << "CustomizeDataBackupProxy::doWorkEx invoke end"; +} + +/** + * @brief 取消操作 + */ +void CustomizeDataBackupProxy::cancelEx() +{ + qDebug() << "CustomizeDataBackupProxy::cancelEx invoke begin"; + + m_bCancel = true; + if (!m_isFinished) { + emit this->checkResult(int(BackupResult::START_CANCEL)); + + if (m_calc) + m_calc->stop(); + if (m_p) + m_p->stop(); + + QProcess::execute("sync"); + Utils::wait(5); + deleteFailedData(); + emit this->checkResult(int(BackupResult::CANCEL_SUCCESS)); + } + + qDebug() << "CustomizeDataBackupProxy::cancelEx invoke end"; +} + +/** + * @brief 失败则删除相应数据 + */ +void CustomizeDataBackupProxy::deleteFailedData() +{ + if (m_curUuid.isEmpty()) + return; + + // 1、删除备份目录 + QString destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid; + destPath.replace("//", "/"); + QStringList args; + args << "-rf"; + args << destPath; + QProcess::execute("rm", args); + + // 2、删除xml文件中的备份项 + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + parse.deleteItem(m_curUuid); +} + +/** + * @brief 判断是否增量备份 + * @return true,增量备份; false,全量备份 + */ +bool CustomizeDataBackupProxy::isIncBackup() +{ + QString backupPath; + ParseBackupList::BackupPoint point; + if (m_backupWrapper.m_uuid.isEmpty()) { + return false; + } else { + backupPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + } + + backupPath.replace("//", "/"); + if (Utils::isDirExist(backupPath)) { + m_backupWrapper.m_bIncrement = true; + m_backupWrapper.m_type = BackupType::INC_BACKUP_DATA; + return true; + } + return false; +} + +/** + * @brief 校验剩余空间是否满足备份 + */ +bool CustomizeDataBackupProxy::checkFreeCapacity(qint64 itotalSize) +{ + qDebug() << "CustomizeDataBackupProxy::checkFreeCapacity invoke begin"; + + // 如果是取消了操作,则不再发送其它信息 + if (m_bCancel) + return false; + + // 1、计算待备份数据的大小 + m_size = itotalSize; + // 备份过程中会有一些临时文件产生,会占用一部分空间,故我们预留5M的空间 + itotalSize += 5 * MB; + + // 2、计算备份分区剩余空间大小 + QString backupPath(m_backupWrapper.m_prefixDestPath); + backupPath.replace("//", "/"); + QStorageInfo backupDisk(backupPath); + qint64 freeSize = backupDisk.bytesAvailable(); + + // 3、校验空间是否足够 + bool result = true; + if (itotalSize > freeSize) { + emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH)); + result = false; + return result; + } else { + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + } + + if (m_isOnlyCheck) + return result; + + // 开始备份 + doBackup(); + + qDebug() << "CustomizeDataBackupProxy::checkFreeCapacity invoke end"; + return result; +} + +/** + * @brief 计算备份所需空间大小 + * @return 计算备份所需空间大小,单位字节 + */ +void CustomizeDataBackupProxy::calcSizeForBackup() +{ + QString destPath = Utils::getSysRootPath(); + + QStringList args; + if (m_backupWrapper.m_bIncrement) { + // 在原来的备份点上增量备份场景 + args = getRsyncArgs(CustomizeDataBackupScene::TRY_INC_DATA_BACKUP); + destPath = m_backupWrapper.m_prefixDestPath; + destPath += BACKUP_SNAPSHOTS_PATH; + destPath += "/"; + destPath += m_backupWrapper.m_uuid; + destPath += "/data/"; + } else { + // 全量备份场景 + args = getRsyncArgs(CustomizeDataBackupScene::TRY_DATA_BACKUP); + destPath += CHECK_PATH; + } + + // 拼接备份源路径和目标路径 + QString srcPath = Utils::getSysRootPath(); + srcPath += "/"; + srcPath.replace("//", "/"); + args << srcPath; + destPath.replace("//", "/"); + args << destPath; + Utils::mkpath(destPath); + + connect(m_calc, &CalcBackupSize::finished, this, &CustomizeDataBackupProxy::checkFreeCapacity); + m_calc->start(args, false); +} + +/** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return rsync的参数信息 + */ +QStringList CustomizeDataBackupProxy::getRsyncArgs(CustomizeDataBackupScene scene) +{ + QStringList args; + QString backupFile = "/tmp/.backup.user"; + Utils::writeFileByLines(backupFile, m_backupWrapper.m_backupPaths); + + switch (scene) { + case CustomizeDataBackupScene::DATA_BACKUP : + args << "-avAHXr"; + args << "--info=progress2"; + args << "--no-inc-recursive"; + args << "--ignore-missing-args"; + args << "--delete"; + args << "--files-from" << backupFile; + break ; + case CustomizeDataBackupScene::INC_DATA_BACKUP : + args << "-avAHXr"; + args << "--info=progress2"; + args << "--no-inc-recursive"; + args << "--ignore-missing-args"; + args << "--delete"; + args << "--files-from" << backupFile; + break ; + case CustomizeDataBackupScene::TRY_DATA_BACKUP : + args << "-aAHXrn"; + args << "--stats"; + args << "--ignore-missing-args"; + args << "--files-from" << backupFile; + break ; + case CustomizeDataBackupScene::TRY_INC_DATA_BACKUP : + args << "-aAHXrn"; + args << "--stats"; + args << "--ignore-missing-args"; + args << "--delete"; + args << "--files-from" << backupFile; + break ; + default: + return args; + } + + return args; +} + +/** + * @brief 备份 + */ +void CustomizeDataBackupProxy::doBackup() +{ + qDebug() << "CustomizeDataBackupProxy::doBackup invoke begin"; + + // 准备 + if (!doPrepare()) + return ; + + // 启动数据备份 + backupData(); + + qDebug() << "CustomizeDataBackupProxy::doBackup invoke end"; +} + +/** + * @brief 备份准备 + * @return true,准备成功;false,准备失败 + */ +bool CustomizeDataBackupProxy::doPrepare() +{ + qDebug() << "CustomizeDataBackupProxy::doPrepare invoke begin"; + + m_bSuccess = false; + + // 1、设置当前备份的Uuid + if (m_backupWrapper.m_uuid.isEmpty()) { + // 新增备份点,不指定uuid的场景 + m_curUuid += Utils::createUuid(); + } else { + // 指定uuid备份的场景 + m_curUuid = m_backupWrapper.m_uuid; + } + + // 2、准备备份目录及文件 + m_destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/data"; + m_destPath.replace("//", "/"); + if (!Utils::mkpath(m_destPath)) { + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + qCritical() << QString("mkdir %1 failed !").arg(m_destPath) ; + return false; + } + + m_userFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + PATHS_USER_FILE; + m_userFile.replace("//", "/"); + if (!Utils::writeFileByLines(m_userFile, m_backupWrapper.m_backupPaths)) { + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + qCritical() << QString("create file %1 failed !").arg(m_userFile) ; + return false; + } + + m_excludeUserFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + EXCLUDE_PATHS_USER_FILE; + m_excludeUserFile.replace("//", "/"); + if (!Utils::writeFileByLines(m_excludeUserFile, m_backupWrapper.m_backupExcludePaths)) { + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + qCritical() << QString("create file %1 failed !").arg(m_excludeUserFile) ; + return false; + } + + // 3、记录/backup/snapshots/backuplist.xml文件 + if (!recordBackupPoint()) { + qCritical() << "add or update item to backuplist.xml failed !"; + return false; + } + + qDebug() << "CustomizeDataBackupProxy::doPrepare invoke end"; + return true; +} + +/** + * @brief 记录/backup/snapshots/backuplist.xml文件 + * @return true-成功;false-失败 + */ +bool CustomizeDataBackupProxy::recordBackupPoint() +{ + m_backupPoint.m_backupName = m_backupWrapper.m_backupName; + m_backupPoint.m_uuid = m_curUuid; + m_backupPoint.m_iPosition = m_backupWrapper.m_iPosition; + m_backupPoint.m_type = m_backupWrapper.m_type; + m_backupPoint.m_size = Utils::StringBySize(m_size); + m_backupPoint.m_time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); + m_backupPoint.m_state = BACKUP_PARSE_STATE_FAIL_STRTING; + if (0 < m_backupWrapper.m_frontUid) + m_backupPoint.m_userId = QString::number(m_backupWrapper.m_frontUid); + m_backupPoint.m_os = SystemInfo::m_os; + m_backupPoint.m_arch = SystemInfo::m_arch; + m_backupPoint.m_archdetect = SystemInfo::m_archDetect; + m_backupPoint.m_path = m_backupWrapper.m_prefixDestPath; + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + if (m_backupWrapper.m_bIncrement) { + if (parse.updateItem(m_backupPoint) != ParseBackupList::SUCCESS) { + emit checkResult(int(BackupResult::WRITE_STORAGEINFO_UPDATE_ITEM_FAIL)); + return false; + } + } else { + if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) { + emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL)); + return false; + } + } + + return true; +} + +/** + * @brief 备份系统 + * @return true,备份成功;false,备份失败 + */ +bool CustomizeDataBackupProxy::backupData() +{ + qDebug() << "CustomizeDataBackupProxy::backupData invoke begin"; + + QStringList args; + if (m_backupWrapper.m_bIncrement) { + // 在原来的备份点上增量备份场景 + args = getRsyncArgs(CustomizeDataBackupScene::INC_DATA_BACKUP); + } else { + // 全量备份场景 + args = getRsyncArgs(CustomizeDataBackupScene::DATA_BACKUP); + } + + // 拼接备份源路径和目标路径 + QString srcPath = Utils::getSysRootPath(); + srcPath += "/"; + srcPath.replace("//", "/"); + args << srcPath; + QString destPath = m_destPath + "/"; + destPath.replace("//", "/"); + args << destPath; + + m_p = new RsyncPathToDirProcess(this); + connect(m_p, &RsyncPathToDirProcess::progress, this, &CustomizeDataBackupProxy::progress); + connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) { + // 如果是取消了操作,则不再发送其它信息 + if (m_bCancel) + return ; + + m_isFinished = true; + if (result) { + m_backupPoint.m_state = BACKUP_PARSE_STATE_SUCCESS_STRTING; + m_backupPoint.m_size = Utils::StringBySize(Utils::getDirOrFileSize(m_destPath)); + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + parse.updateItem(m_backupPoint); + + // Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ","+ m_backupWrapper.m_note + "," + m_backupPoint.m_size+ "," + QString::number(m_backupWrapper.m_frontUid)); + Utils::writeBackupLog(m_backupPoint.m_time + "," + + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + "," + + m_backupWrapper.m_note + "," + m_backupPoint.m_size + + "," + QString::number(m_backupWrapper.m_frontUid) + + "," + m_backupWrapper.m_backupName); + Utils::update_backup_unique_settings(m_curUuid, m_backupPoint.m_backupName); + m_bSuccess = true; + } + + emit this->workResult(result); + }); + m_p->start(args, false); + + do_kylin_security(m_destPath); + + qDebug() << "CustomizeDataBackupProxy::backupData invoke end"; + return true; +} + +void CustomizeDataBackupProxy::do_kylin_security(const QString& dstDir) +{ + int ret = 0; + ret = kysec_getstatus(); + if (ret > 0) { + QString seFilePath(dstDir + "/.exectl"); + QFile file(seFilePath); + file.open(QIODevice::WriteOnly); + file.close(); + } +} + + diff --git a/backup-daemon/customizedatabackupproxy.h b/backup-daemon/customizedatabackupproxy.h new file mode 100755 index 0000000..1698290 --- /dev/null +++ b/backup-daemon/customizedatabackupproxy.h @@ -0,0 +1,99 @@ +#ifndef CUSTOMIZEDATABACKUPPROXY_H +#define CUSTOMIZEDATABACKUPPROXY_H + +#include "workerfactory.h" +#include "myprocess/calcbackupsize.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class CustomizeDataBackupProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeDataBackupProxy) +public: + // 系统备份的几种场景 + enum CustomizeDataBackupScene { + DATA_BACKUP, // 系统备份 + INC_DATA_BACKUP, // 增量系统备份 + TRY_DATA_BACKUP, // 测试系统备份,可用于计算备份传输数据大小 + TRY_INC_DATA_BACKUP, // 测试增量系统备份,可用于计算备份传输数据大小 + }; + + explicit CustomizeDataBackupProxy(); + virtual ~CustomizeDataBackupProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + +public slots: + // 任务取消 + virtual void cancelEx(); + +private slots: + // 校验剩余空间是否满足备份 + bool checkFreeCapacity(qint64 itotalSize); + + // 备份 + void doBackup(); + +private: + // 计算备份所需空间大小 + void calcSizeForBackup(); + + /** + * @brief 记录/backup/snapshots/backuplist.xml文件 + * @return true-成功;false-失败 + */ + bool recordBackupPoint(); + + // 备份准备 + bool doPrepare(); + + // 备份系统 + bool backupData(); + +protected: + // 判断是否增量备份 + bool isIncBackup(); + + /** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ + QStringList getRsyncArgs(CustomizeDataBackupScene scene); + + void do_kylin_security(const QString& dstDir); + + // 失败则删除相应数据 + virtual void deleteFailedData(); + + // 计算备份空间大小的进程 + CalcBackupSize *m_calc; + // 是否只是检测 + bool m_isOnlyCheck; + // 是否完成 + bool m_isFinished; + // 是否成功 + bool m_bSuccess; + // 当前备份uuid + QString m_curUuid; + // 当前备份目标目录 + QString m_destPath; + // 当前备份所需空间大小 + qint64 m_size; + // 备份点用户备份路径文件 + QString m_userFile; + // 备份点排除备份路径文件 + QString m_excludeUserFile; + // 备份进程 + RsyncPathToDirProcess *m_p; + // 当前备份节点 + ParseBackupList::BackupPoint m_backupPoint; +}; + +#endif // CUSTOMIZEDATABACKUPPROXY_H diff --git a/backup-daemon/customizeghostImageproxy.cpp b/backup-daemon/customizeghostImageproxy.cpp new file mode 100755 index 0000000..375388e --- /dev/null +++ b/backup-daemon/customizeghostImageproxy.cpp @@ -0,0 +1,240 @@ +#include "customizeghostImageproxy.h" +#include +#include +#include +#include +#include +#include +#include +#include "../common/utils.h" +#include "../common/mydusizetool.h" +#include "mymountproxy.h" +#include "myprocess/calcbackupsize.h" + +IMPLEMENT_DYNCREATE(CustomizeGhostImageProxy) + +CustomizeGhostImageProxy::CustomizeGhostImageProxy() +{ + m_p = nullptr; + m_bSuccess = false; + m_isFinished = false; + m_isForce = false; + + connect(this, &CustomizeGhostImageProxy::cancel, this, &CustomizeGhostImageProxy::cancelEx); +} + +CustomizeGhostImageProxy::~CustomizeGhostImageProxy() +{ + // 不成功则删除中间文件 + if (!m_bSuccess) { + deleteFailedData(); + } +} + +/** + * @brief 环境检测 + */ +bool CustomizeGhostImageProxy::checkEnvEx() +{ + qDebug() << "CustomizeGhostImageProxy::checkEnvEx invoke begin"; + + // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载 + MyMountProxy mountProxy; + MountResult result = mountProxy.mountBackupPartition(); + // 无备份分区 + if (MountResult::CANNOT_GET_BACKUPUUID == result) { + qInfo() << "There is no backup partition!"; + + QString snapshotsPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH; + snapshotsPath.replace("//", "/"); + Utils::mkpath(snapshotsPath); + Utils::generateExcludePathsFile(); + } else if (MountResult::MOUNTED != result) { + emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL)); + return false; + } + + // 2、校验backuppoint.xml中相应的备份节点是否存在 + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList xmlParse(xmlPath); + m_backupPoint = xmlParse.findBackupPointByUuid(m_backupWrapper.m_uuid); + if (m_backupPoint.m_backupName.isEmpty()) { + emit checkResult(int(BackupResult::GHOST_CANNOT_FIND_BACKUPPOINT)); + return false; + } + + // 3、校验备份数据是否存在 + m_srcPath = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + m_srcPath.replace("//", "/"); + if (Utils::isDirEmpty(m_srcPath)) { + emit checkResult(int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST)); + return false; + } + m_imgDst = m_srcPath + "/" + UDISK_MKSQUASHFS_IMG_NAME; + if (!Utils::filsExists(m_imgDst)) { + emit checkResult(int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST)); + return false; + } + + // 4、校验空间是否充足 + qint64 itotalSize = Utils::getDirOrFileSize(m_srcPath); + m_destPath = m_backupWrapper.m_prefixDestPath + GHOST_PATH; + m_destPath.replace("//", "/"); + Utils::mkpath(m_destPath); + m_kyimg = m_destPath + "/" + m_backupWrapper.m_backupName; + m_kyimg.replace("//", "/"); + QFile kyimg(m_kyimg); + if (kyimg.exists()) + kyimg.remove(); + QStorageInfo storageInfo(m_destPath); + qint64 sizeAvailable = storageInfo.bytesAvailable(); + if (sizeAvailable <= itotalSize) { + emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH)); + return false; + } + + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + + qDebug() << "CustomizeGhostImageProxy::checkEnvEx invoke end"; + return true; +} + +/** + * @brief 任务处理 + */ +void CustomizeGhostImageProxy::doWorkEx() +{ + qDebug() << "CustomizeGhostImageProxy::doWorkEx invoke begin"; + + if (!checkEnvEx()) + return ; + + doGhostImage(); + + qDebug() << "CustomizeGhostImageProxy::doWorkEx invoke end"; +} + +/** + * @brief 任务取消 + */ +void CustomizeGhostImageProxy::cancelEx() +{ + qDebug() << "CustomizeGhostImageProxy::cancelEx invoke begin"; + + m_bCancel = true; + if (!m_isFinished) { + emit this->checkResult(int(BackupResult::START_CANCEL)); + + if (m_p) + m_p->stop(); + + QProcess::execute("sync"); + Utils::wait(5); + deleteFailedData(); + emit this->checkResult(int(BackupResult::CANCEL_SUCCESS)); + } + + qDebug() << "CustomizeGhostImageProxy::cancelEx invoke end"; +} + +/** + * @brief 失败则删除相应数据 + */ +void CustomizeGhostImageProxy::deleteFailedData() +{ + // 1、删除临时镜像文件 + QFile kyimg(m_destPath + "/" + UDISK_MKSQUASHFS_IMG_NAME); + if (kyimg.exists()) + kyimg.remove(); + + // 2、删除目标镜像文件 + if (!m_kyimg.isEmpty()) { + QFile kyimgDest(m_kyimg); + if (kyimgDest.exists()) + kyimgDest.remove(); + } +} + +/** + * @brief ghost镜像 + */ +void CustomizeGhostImageProxy::doGhostImage() +{ + qDebug() << "CustomizeGhostImageProxy::doGhostImage invoke begin"; + + // 同步到U盘 + m_p = new RsyncPathToDirProcess(this); + connect(m_p, &RsyncPathToDirProcess::progress, this, &CustomizeGhostImageProxy::progress); + connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool resultRsync) { + // 如果是取消了操作,则不再发送其它信息 + if (m_bCancel) + return ; + + m_isForce = false; + m_isFinished = true; + if (resultRsync) { + // 文件更名 + QString imgFileName = m_destPath + "/" + UDISK_MKSQUASHFS_IMG_NAME; + if (Utils::filsExists(imgFileName)) { + QStringList args; + args << imgFileName; + args << m_kyimg; + QProcess::execute("mv", args); + + chown(m_kyimg.toLocal8Bit().data(), m_backupWrapper.m_frontUid, m_backupWrapper.m_gid); + m_bSuccess = true; + resultRsync = true; + } else { + resultRsync = false; + } + } + + emit this->workResult(resultRsync); + }); + + QStringList arguments; + arguments << "-av"; + arguments << "--info=progress2"; + arguments << m_imgDst; + arguments << m_destPath + "/"; + + QTimer::singleShot(1*1000, this, &CustomizeGhostImageProxy::checkDestDirExists); + m_p->start(arguments, false); + + qDebug() << "CustomizeGhostImageProxy::doGhostImage invoke end"; +} + +/** + * @brief 校验移动盘是否还在 + * @return: bool,存在返回true;不存在返回false + * @author: zhaominyong + * @since: 2021/05/24 + * @note: + * add by zhaominyong at 2021/05/24 for bug:54377 【备份还原】备份数据到U盘的过程中拔出U盘,备份还原工具仍然一直显示正在备份数据 + */ +bool CustomizeGhostImageProxy::checkDestDirExists() +{ + if (!m_isFinished) + { + // 拔掉U盘后,没有响应的场景(怀疑可能是某应用程序关闭引起,希望不是dbus服务关掉了) + if (m_isForce) { + emit this->workResult(false); + return false; + } + + if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) { + qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); + if (m_p) + m_p->stop(); + // 10s钟后如果还没有退出,则强制退出 + QTimer::singleShot(10*1000, this, &CustomizeGhostImageProxy::checkDestDirExists); + m_isForce = true; + } else { + QTimer::singleShot(1*1000, this, &CustomizeGhostImageProxy::checkDestDirExists); + } + } + + return true; +} + diff --git a/backup-daemon/customizeghostImageproxy.h b/backup-daemon/customizeghostImageproxy.h new file mode 100755 index 0000000..b130051 --- /dev/null +++ b/backup-daemon/customizeghostImageproxy.h @@ -0,0 +1,53 @@ +#ifndef CUSTOMIZEGHOSTIMAGEPROXY_H +#define CUSTOMIZEGHOSTIMAGEPROXY_H + +#include "workerfactory.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class CustomizeGhostImageProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeGhostImageProxy) +public: + explicit CustomizeGhostImageProxy(); + virtual ~CustomizeGhostImageProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + + // 任务取消 + virtual void cancelEx(); + +private: + void doGhostImage(); + bool checkDestDirExists(); + void deleteFailedData(); + + ParseBackupList::BackupPoint m_backupPoint; + + // 存放.kyimg文件的目录 + QString m_destPath; + // .kyimg文件 + QString m_kyimg; + // 源文件dst.img文件所在目录 + QString m_srcPath; + // dst.img文件 + QString m_imgDst; + + // 备份进程 + RsyncPathToDirProcess *m_p; + + // 是否成功 + bool m_bSuccess; + // 是否结束 + bool m_isFinished; + // 强制结束标志(stop后没反应的情况,系统处于睡眠状态) + bool m_isForce; +}; + +#endif // CUSTOMIZEGHOSTIMAGEPROXY_H diff --git a/backup-daemon/customizesystembackupproxy.cpp b/backup-daemon/customizesystembackupproxy.cpp new file mode 100755 index 0000000..6f6ee97 --- /dev/null +++ b/backup-daemon/customizesystembackupproxy.cpp @@ -0,0 +1,400 @@ +#include "customizesystembackupproxy.h" + +#include +#include +#include +#include +#include +#include "../common/utils.h" +#include "../common/mydusizetool.h" +#include "mymountproxy.h" + +IMPLEMENT_DYNCREATE(CustomizeSystemBackupProxy) + +CustomizeSystemBackupProxy::CustomizeSystemBackupProxy() +{ + m_bSuccess = false; + m_isFinished = false; + m_size = 0; + m_calc = new CalcBackupSize(this); + m_isOnlyCheck = true; + m_mksquashfs = nullptr; + + connect(this, &CustomizeSystemBackupProxy::cancel, this, &CustomizeSystemBackupProxy::cancelEx); +} + +CustomizeSystemBackupProxy::~CustomizeSystemBackupProxy() +{ + delete m_calc; + m_calc = nullptr; + + delete m_mksquashfs; + m_mksquashfs = nullptr; + + // 如果备份失败,则删除备份数据 + if (!m_bSuccess) { + deleteFailedData(); + } +} + +/** + * @brief 环境检测 + * @return + */ +bool CustomizeSystemBackupProxy::checkEnvEx() +{ + qDebug() << "CustomizeSystemBackupProxy::checkEnv invoke begin"; + + // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载。 + MyMountProxy mountProxy; + MountResult result = mountProxy.mountBackupPartition(); + // 无备份分区 + if (MountResult::CANNOT_GET_BACKUPUUID == result) { + qInfo() << "There is no backup partition!"; + + QString snapshotsPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH; + snapshotsPath.replace("//", "/"); + Utils::mkpath(snapshotsPath); + Utils::generateExcludePathsFile(); + } else if (MountResult::MOUNTED != result) { + emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL)); + return false; + } + + if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) { + qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); + Utils::mkpath(m_backupWrapper.m_prefixDestPath); + } + + // 因为有可能是选择的移动设备,故进行校验移动设备情况:空间大小、文件格式、挂载模式等 + QString backupPath(m_backupWrapper.m_prefixDestPath); + backupPath.replace("//", "/"); + QStorageInfo udisk(backupPath); + QString udisk_type = udisk.fileSystemType(); + qDebug() << "udisk's filesystemtype is " << udisk_type; + if (udisk_type == "vfat") { + qCritical() << m_backupWrapper.m_prefixDestPath + " udisk's filesystemtype is vfat"; + emit checkResult(int(BackupResult::UDISK_FILESYSTEM_TYPE_IS_VFAT)); + return false; + } + if (udisk.isReadOnly()) { + // 只读挂载的U盘 + qCritical() << QString("udisk(%s) is readonly filesystem").arg(m_backupWrapper.m_prefixDestPath); + emit checkResult(int(BackupResult::UDISK_FILESYSTEM_IS_READONLY)); + return false; + } + + // 2、计算备份大小 + calcSizeForBackup(); + + qDebug() << "CustomizeSystemBackupProxy::checkEnv invoke end"; + return true; +} + +/** + * @brief 执行备份逻辑 + */ +void CustomizeSystemBackupProxy::doWorkEx() +{ + qDebug() << "CustomizeSystemBackupProxy::doWorkEx invoke begin"; + + m_isOnlyCheck = false; + // 环境检测 + checkEnvEx(); + + qDebug() << "CustomizeSystemBackupProxy::doWorkEx invoke end"; +} + +void CustomizeSystemBackupProxy::cancelEx() +{ + m_bCancel = true; + if (!m_isFinished) { + emit this->checkResult(int(BackupResult::START_CANCEL)); + + if (m_calc) + m_calc->stop(); + if (m_mksquashfs) + m_mksquashfs->stop(); + + QProcess::execute("sync"); + Utils::wait(5); + deleteFailedData(); + emit this->checkResult(int(BackupResult::CANCEL_SUCCESS)); + } +} + +/** + * @brief 失败则删除相应数据 + */ +void CustomizeSystemBackupProxy::deleteFailedData() +{ + if (m_curUuid.isEmpty()) + return; + + // 1、删除备份目录 + QString destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid; + destPath.replace("//", "/"); + QStringList args; + args << "-rf"; + args << destPath; + QProcess::execute("rm", args); + + // 2、删除xml文件中的备份项 + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + parse.deleteItem(m_curUuid); +} + +/** + * @brief 校验剩余空间是否满足备份 + */ +void CustomizeSystemBackupProxy::checkFreeCapacity(qint64 itotalSize) +{ + qDebug() << "CustomizeSystemBackupProxy::checkFreeCapacity invoke begin"; + + // 如果是取消了操作,则不再发送其它信息 + if (m_bCancel) + return ; + + // 1、计算待备份数据的大小 + m_size = itotalSize; + // 备份过程中会有一些临时文件产生,会占用一部分空间,故我们预留500M的空间 + itotalSize += 500 * MB; + // 判断是否需要先压缩成img文件,压缩后一般小于原大小的70% + itotalSize = itotalSize * 7 / 10; + + // 2、计算备份分区剩余空间大小 + QString backupPath(m_backupWrapper.m_prefixDestPath); + backupPath.replace("//", "/"); + QStorageInfo backupDisk(backupPath); + qint64 freeSize = backupDisk.bytesAvailable(); + + // 3、校验空间是否足够 + if (itotalSize > freeSize) { + emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH)); + return ; + } else { + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + } + + if (m_isOnlyCheck) + return ; + + // 4、准备 + if (!doPrepare()) + return ; + + // 5、开始制作img + doMksqushfs(); + + qDebug() << "CustomizeSystemBackupProxy::checkFreeCapacity invoke end"; +} + +/** + * @brief 计算备份所需空间大小 + * @return 计算备份所需空间大小,单位字节 + */ +void CustomizeSystemBackupProxy::calcSizeForBackup() +{ + // 拼接备份源路径和目标路径,测试所需备份空间大小;目标路径为一虚拟路径 + QString srcPath = Utils::getSysRootPath(); + srcPath += "/"; + srcPath.replace("//", "/"); + QString destPath = Utils::getSysRootPath(); + destPath += CHECK_PATH; + destPath.replace("//", "/"); + Utils::mkpath(destPath); + + QStringList args = getRsyncArgs(CustomizeSystemBackupScene::TRY_SYSTEM_BACKUP); + args << srcPath; + args << destPath; + + connect(m_calc, &CalcBackupSize::finished, this, &CustomizeSystemBackupProxy::checkFreeCapacity); + m_calc->start(args, false); +} + +/** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return rsync的参数信息 + */ +QStringList CustomizeSystemBackupProxy::getRsyncArgs(CustomizeSystemBackupScene scene) +{ + QStringList args; + QStringList excludes; + + switch (scene) { + case CustomizeSystemBackupScene::TRY_SYSTEM_BACKUP : + args << "-aAHXrn"; + args << "--stats"; + args << "--ignore-missing-args"; + break ; + case CustomizeSystemBackupScene::MKSQUASHFS : + Utils::excludeFstabBindPath(excludes); + // --exclude=排除路径设置 + for (QString item : m_backupWrapper.m_backupExcludePaths) { + if (excludes.contains(item)) + continue; + if (item.endsWith("/*")) { + item.replace("/*", ""); + } + + args << "-e" << item; + } + return args; + default: + return args; + } + + // --exclude=排除路径设置 + for (const QString & item : m_backupWrapper.m_backupExcludePaths) { + args << QString("--exclude=%1").arg(item); + } + + return args; +} + +/** + * @brief mksqushfs + */ +void CustomizeSystemBackupProxy::doMksqushfs() +{ + qDebug() << "CustomizeSystemBackupProxy::doMksqushfs invoke begin"; + + m_mksquashfs = new MkSquashFSProcess(this); + connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &CustomizeSystemBackupProxy::progress); + connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [=](bool result) { + // 如果是取消了操作,则不再发送其它信息 + if (m_bCancel) + return ; + + m_isFinished = true; + if (result) { + m_backupPoint.m_state = BACKUP_PARSE_STATE_SUCCESS_STRTING; + m_backupPoint.m_size = Utils::StringBySize(Utils::getDirOrFileSize(m_destPath)); + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + if (ParseBackupList::ParseResult::SUCCESS != parse.updateItem(m_backupPoint)) { + qCritical() << "update backuplist.xml error in sendBackupResult"; + result = false; + } else { + // Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ","+ m_backupWrapper.m_note + "," + m_backupPoint.m_size+ "," + QString::number(m_backupWrapper.m_frontUid)); + Utils::writeBackupLog(m_backupPoint.m_time + "," + + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + "," + + m_backupWrapper.m_note + "," + m_backupPoint.m_size + + ",," + m_backupWrapper.m_backupName); + + Utils::update_backup_unique_settings(m_curUuid, m_backupPoint.m_backupName); + m_bSuccess = true; + } + } + emit this->workResult(result); + }); + + QString srcPath = Utils::getSysRootPath(); + srcPath += "/"; + srcPath.replace("//", "/"); + QString dstImg = m_destPath + "/" + UDISK_MKSQUASHFS_IMG_NAME; + QStringList args; + args << srcPath << dstImg; + args.append(getRsyncArgs(CustomizeSystemBackupScene::MKSQUASHFS)); + + if (m_mksquashfs->start(args)) { + do_kylin_security(m_destPath); + } else { + emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL)); + } + + qDebug() << "CustomizeSystemBackupProxy::doMksqushfs invoke end"; +} + +/** + * @brief 备份准备 + * @return true,准备成功;false,准备失败 + */ +bool CustomizeSystemBackupProxy::doPrepare() +{ + qDebug() << "CustomizeSystemBackupProxy::doPrepare invoke begin"; + + m_bSuccess = false; + + // 1、设置当前备份的Uuid + m_curUuid += Utils::createUuid(); + + // 2、准备备份目录及文件 + m_destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/data"; + m_destPath.replace("//", "/"); + if (!Utils::mkpath(m_destPath)) { + qCritical() << QString("mkdir %1 failed !").arg(m_destPath) ; + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + QString userFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + PATHS_USER_FILE; + userFile.replace("//", "/"); + if (!Utils::writeFileByLines(userFile, m_backupWrapper.m_backupPaths)) { + qCritical() << QString("create file %1 failed !").arg(userFile); + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + QString excludeUserFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + EXCLUDE_PATHS_USER_FILE; + excludeUserFile.replace("//", "/"); + if (!Utils::writeFileByLines(excludeUserFile, m_backupWrapper.m_backupExcludePaths)) { + qCritical() << QString("create file %1 failed !").arg(excludeUserFile); + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + // 3、记录/backup/snapshots/backuplist.xml文件 + if (!recordBackupPoint()) { + qCritical() << "add or update item to backuplist.xml failed !"; + return false; + } + + qDebug() << "CustomizeSystemBackupProxy::doPrepare invoke end"; + return true; +} + +/** + * @brief 记录/backup/snapshots/backuplist.xml文件 + * @return true-成功;false-失败 + */ +bool CustomizeSystemBackupProxy::recordBackupPoint() +{ + m_backupPoint.m_backupName = m_backupWrapper.m_backupName; + m_backupPoint.m_uuid = m_curUuid; + m_backupPoint.m_iPosition = m_backupWrapper.m_iPosition; + m_backupPoint.m_type = m_backupWrapper.m_type; + m_backupPoint.m_size = Utils::StringBySize(m_size); + m_backupPoint.m_time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); + m_backupPoint.m_state = BACKUP_PARSE_STATE_FAIL_STRTING; + m_backupPoint.m_os = SystemInfo::m_os; + m_backupPoint.m_arch = SystemInfo::m_arch; + m_backupPoint.m_archdetect = SystemInfo::m_archDetect; + m_backupPoint.m_path = m_backupWrapper.m_prefixDestPath; + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) { + emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL)); + return false; + } + + return true; +} + +void CustomizeSystemBackupProxy::do_kylin_security(const QString& dstDir) +{ + int ret = 0; + ret = kysec_getstatus(); + if (ret > 0) { + QString seFilePath(dstDir + "/.exectl"); + QFile file(seFilePath); + file.open(QIODevice::WriteOnly); + file.close(); + } +} + diff --git a/backup-daemon/customizesystembackupproxy.h b/backup-daemon/customizesystembackupproxy.h new file mode 100755 index 0000000..38964d9 --- /dev/null +++ b/backup-daemon/customizesystembackupproxy.h @@ -0,0 +1,91 @@ +#ifndef CUSTOMIZESYSTEMBACKUPPROXY_H +#define CUSTOMIZESYSTEMBACKUPPROXY_H + +#include "workerfactory.h" +#include "myprocess/calcbackupsize.h" +#include "myprocess/mksquashfsprocess.h" +#include "parsebackuplist.h" + +class CustomizeSystemBackupProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeSystemBackupProxy) + +public: + // 系统备份的几种场景 + enum CustomizeSystemBackupScene { + TRY_SYSTEM_BACKUP, // 测试系统备份,可用于计算备份传输数据大小 + MKSQUASHFS, // 生成img文件 + }; + + explicit CustomizeSystemBackupProxy(); + virtual ~CustomizeSystemBackupProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + +signals: + +private slots: + // 校验剩余空间是否满足备份 + void checkFreeCapacity(qint64 itotalSize); + + // mksqushfs + void doMksqushfs(); + + // 任务取消 + virtual void cancelEx(); + +private: + // 计算备份所需空间大小 + void calcSizeForBackup(); + + /** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ + QStringList getRsyncArgs(CustomizeSystemBackupScene scene); + + /** + * @brief 记录/backup/snapshots/backuplist.xml文件 + * @return true-成功;false-失败 + */ + bool recordBackupPoint(); + + // 备份准备 + bool doPrepare(); + + bool backup(const QStringList &args); + + void do_kylin_security(const QString& dstDir); + + // 失败则删除相应数据 + void deleteFailedData(); + + // 计算备份空间大小的进程 + CalcBackupSize *m_calc; + // 压缩进程 + MkSquashFSProcess *m_mksquashfs; + + // 是否完成 + bool m_isFinished; + // 是否备份成功 + bool m_bSuccess; + // 当前备份uuid + QString m_curUuid; + // 当前备份目标目录 + QString m_destPath; + // 当前备份所需空间大小 + qint64 m_size; + // 当前备份节点 + ParseBackupList::BackupPoint m_backupPoint; + // 是否只是检测 + bool m_isOnlyCheck; +}; + +#endif // CUSTOMIZESYSTEMBACKUPPROXY_H diff --git a/backup-daemon/customizesystemrestoreproxy.cpp b/backup-daemon/customizesystemrestoreproxy.cpp new file mode 100755 index 0000000..38c8d1f --- /dev/null +++ b/backup-daemon/customizesystemrestoreproxy.cpp @@ -0,0 +1,343 @@ +#include "customizesystemrestoreproxy.h" +#include +#include +#include +#include +#include +#include "../common/utils.h" +#include "mymountproxy.h" + +IMPLEMENT_DYNCREATE(CustomizeSystemRestoreProxy) + +/** + * @brief 构造函数 + */ +CustomizeSystemRestoreProxy::CustomizeSystemRestoreProxy() +{ + m_bSuccess = false; + m_p = nullptr; +} + +/** + * @brief 析构函数 + */ +CustomizeSystemRestoreProxy::~CustomizeSystemRestoreProxy() +{ + delete m_p; +} + +/** + * @brief 环境检测 + * @return false,检测失败;true,检测成功 + */ +bool CustomizeSystemRestoreProxy::checkEnvEx() +{ + qDebug() << "CustomizeSystemRestoreProxy::checkEnvEx invoke begin"; + + // 1、检测xml中的还原点是否还存在 + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + m_backupPoint = parse.findBackupPointByUuid(m_backupWrapper.m_uuid); + if (m_backupPoint.m_uuid.isEmpty()) { + qCritical("xml中还原点不存在"); + emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR)); + return false; + } + + // 2、检测.user.txt是否存在 + m_userFile = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + PATHS_USER_FILE; + m_userFile.replace("//", "/"); + if (!Utils::filsExists(m_userFile)) { + qCritical(".user.txt文件不存在"); + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + // 3、检测.exclude.user.txt是否存在 + m_excludeUserFile = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + EXCLUDE_PATHS_USER_FILE; + m_excludeUserFile.replace("//", "/"); + if (!Utils::filsExists(m_excludeUserFile)) { + qCritical(".exclude.user.txt文件不存在"); + emit checkResult(int(BackupResult::WRITE_EXCLUDE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + // 4、检测还原点是否存在 + m_backupPath = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + m_backupPath.replace("//", "/"); + if (Utils::isDirEmpty(m_backupPath)) { + qCritical("还原点{uuid}/data目录不存在"); + emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR)); + return false; + } + m_imgFileName = m_backupPath + "/" + UDISK_MKSQUASHFS_IMG_NAME; + if (!Utils::filsExists(m_imgFileName)) { + qCritical("还原点{uuid}/data/dst.img文件不存在"); + emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR)); + return false; + } + + m_curUuid = m_backupWrapper.m_uuid; + + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + + qDebug() << "CustomizeSystemRestoreProxy::checkEnvEx invoke end"; + return true; +} + +/** + * @brief 执行还原逻辑 + */ +void CustomizeSystemRestoreProxy::doWorkEx() +{ + qDebug() << "CustomizeSystemRestoreProxy::doWorkEx invoke begin"; + + // 1、校验 + if (!checkEnvEx()) + return ; + + // 2、还原系统 + restoreSystem(); + + qDebug() << "CustomizeSystemRestoreProxy::doWorkEx invoke end"; +} + +/** + * @brief 备份准备 + * @return true,准备成功;false,准备失败 + */ +bool CustomizeSystemRestoreProxy::doPrepare() +{ + qDebug() << "CustomizeSystemRestoreProxy::doPrepare invoke begin"; + + // 1、dst.img文件需要挂载到目录 + if (!mountImg()) + return false; + + // 2、停止安全防护 + QProcess::execute("systemctl stop kysec-init.service"); + + // 3、以读写方式重新挂载boot分区,因为有的机器默认以只读挂载 + remountBoot(); + + // 4、是否有/boot/efi目录,有则认为有efi分区,需重新rw挂载 + QString efiPath = Utils::getSysRootPath() + "/boot/efi"; + efiPath.replace("//", "/"); + if (!Utils::isDirEmpty(efiPath)) { + // 重新rw读写挂载 + remountEfi(); + } + + qDebug() << "CustomizeSystemRestoreProxy::doPrepare invoke end"; + return true; +} + +/** + * @brief 将dst.img文件挂载到/backup/imgbackup目录 + * @return + */ +bool CustomizeSystemRestoreProxy::mountImg() +{ + // 自定义路径系统备份,需要先将dst.img文件挂载到/backup/imgbackup目录 + // 1、检测目录/backup/imgbackup是否存在,不存在则创建此目录 + QString dstImgMountPath = Utils::getSysRootPath() + BACKUP_IMGBACKUP_PATH; + dstImgMountPath.replace("//", "/"); + Utils::mkpath(dstImgMountPath); + + // 2、先卸载/backup/imgbackup上的mount + MountBackupProcess *processMount = new MountBackupProcess(this); + processMount->umount(dstImgMountPath); + + // 3、将img文件挂载到/backup/imgbackup上 + if (!processMount->mount(m_imgFileName, dstImgMountPath)) { + emit checkResult(int(BackupResult::RESTOREDIR_PREPARE_FAILED)); + return false; + } + + m_srcPath = dstImgMountPath; + + return true; +} + +/** + * @brief 重新rw读写挂载efi分区 + */ +void CustomizeSystemRestoreProxy::remountEfi() +{ + QString mountPath = Utils::getSysRootPath() + "/boot/efi"; + mountPath.replace("//", "/"); + QStringList args; + args << "-o" + << "rw,remount" + << mountPath; + QProcess::execute("mount", args); +} + +/** + * @brief 重新rw读写挂载boot分区 + */ +void CustomizeSystemRestoreProxy::remountBoot() +{ + QString mountPath = Utils::getSysRootPath() + "/boot"; + mountPath.replace("//", "/"); + QStringList args; + args << "-o" + << "rw,remount" + << mountPath; + QProcess::execute("mount", args); +} + +/** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ +QStringList CustomizeSystemRestoreProxy::getRsyncArgs(CustomizeSystemRestoreScene scene) +{ + QStringList args; + args << "-avAHXr"; + args << "--info=progress2"; + args << "--no-inc-recursive"; + args << "--ignore-missing-args"; + args << "--delete"; + + QStringList excludes; + // 自定义备份的路径也需要跳过,不进行还原 + Utils::excludeCustomizePath(excludes); + + switch (scene) { + case CustomizeSystemRestoreScene::RESTORE_SYSTEM_WITH_DATA : + args << "--exclude=/home"; + args << "--exclude=/root"; + if (Utils::isHuawei990()) { + args << "--exclude=/data"; + } else { + args << "--exclude=/data/usershare"; + } + // 保留指纹数据,用户密码、角色、权限、生物识别等信息不需要改变 + args << "--exclude=/var/lib/biometric-auth"; + args << "--exclude=/data/sec_storage_data"; + args << "--exclude=/etc/passwd"; + args << "--exclude=/etc/shadow"; + args << "--exclude=/etc/group"; + args << "--exclude=/etc/gshadow"; + args << "--exclude=/etc/sudoers"; + args << "--exclude=/data/home"; + args << "--exclude=/data/root"; + + // 云桌面背景路径属于用户数据 + args << "--exclude=/var/lib/AccountsService"; + + // 域用户相关信息,还原后保持不退域 + args << "--exclude=/etc/sssd"; + args << "--exclude=/var/lib/sss"; + args << "--exclude=/usr/share/sssd"; + args << "--exclude=/etc/ipa"; + args << "--exclude=/etc/krb5.keytab"; + args << "--exclude=/etc/krb5.conf"; + args << "--exclude=/var/lib/ipa-client"; + args << "--exclude=/etc/nsswitch.conf"; + args << "--exclude=/etc/pam.d"; + args << "--exclude=/etc/hosts"; + args << "--exclude=/etc/hostname"; + args << "--exclude=/etc/hedron"; + args << "--exclude=/etc/kcm"; + args << "--exclude=/usr/hedron/hedronagent"; + args << "--exclude=/etc/.kyinfo"; + args << "--exclude=/etc/LICENSE"; + args << "--exclude=/etc/ssl/certs"; + args << "--exclude=/usr/share/ca-certificates"; + args << "--exclude=/etc/NetworkManager"; + + // 此处不要break,因为还需要排除SYSTEM_RESTORE中的项 + + case CustomizeSystemRestoreScene::SYSTEM_RESTORE : + // 还原工具不还原自身 + args << "--exclude=/usr/bin/backup-daemon"; + args << "--exclude=/usr/bin/kybackup"; + args << "--exclude=/usr/bin/mount_fstab_efi"; + args << "--exclude=/usr/bin/backup-auto-efi"; + args << "--exclude=/usr/bin/backup-auto"; + args << "--exclude=/usr/bin/rsync"; + args << "--exclude=/usr/share/rsync"; + args << "--exclude=/usr/share/initramfs-tools/hooks/kybackup-hooks"; + args << "--exclude=/usr/share/initramfs-tools/scripts/local-bottom/kybackup"; + + for (const QString& item : excludes) { + args << QString("--exclude=") + item; + } + + args << "--exclude-from" << m_excludeUserFile; + args << "--files-from" << m_userFile; + + break ; + default: + return args; + } + + return args; +} + +/** + * @brief 系统还原 + */ +void CustomizeSystemRestoreProxy::restoreSystem() +{ + qDebug() << "CustomizeSystemRestoreProxy::restoreSystem invoke begin"; + + // 还原前准备 + doPrepare(); + + QString destPath = Utils::getSysRootPath(); + + QStringList args; + // 保留用户数据还原 + if ( m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM_WITH_DATA) { + args = getRsyncArgs(CustomizeSystemRestoreScene::RESTORE_SYSTEM_WITH_DATA); + } else { + args = getRsyncArgs(CustomizeSystemRestoreScene::SYSTEM_RESTORE); + } + + args << m_srcPath + "/"; + destPath += "/"; + destPath.replace("//", "/"); + args << destPath; + + m_p = new RsyncPathToDirProcess(this); + connect(m_p, &RsyncPathToDirProcess::progress, this, &CustomizeSystemRestoreProxy::progress); + connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) { + if (result) { + QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); + // Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid)); + Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,,," + m_backupPoint.m_backupName); + + Utils::updateSyncFile(); + Utils::wait(2); + QString fileIfSync = Utils::getSysRootPath() + FILE_IF_SYNC; + fileIfSync.replace("//", "/"); + QFileInfo file(fileIfSync); + QDateTime beginTime = file.fileTime(QFileDevice::FileModificationTime); + QProcess::execute("sync"); + Utils::wait(20); + Utils::updateSyncFile(); + while (1) { + Utils::wait(2); + QFileInfo file1(fileIfSync); + QDateTime UpdateTime = file1.fileTime(QFileDevice::FileModificationTime); + if (UpdateTime > beginTime) + break; + } + + emit this->workResult(result); + Utils::wait(2); + reboot(RB_AUTOBOOT); + } + emit this->workResult(result); + }); + + m_p->start(args, false); + + qDebug() << "CustomizeSystemRestoreProxy::restoreSystem invoke end"; +} + diff --git a/backup-daemon/customizesystemrestoreproxy.h b/backup-daemon/customizesystemrestoreproxy.h new file mode 100755 index 0000000..45e35b2 --- /dev/null +++ b/backup-daemon/customizesystemrestoreproxy.h @@ -0,0 +1,73 @@ +#ifndef CUSTOMIZESYSTEMRESTOREPROXY_H +#define CUSTOMIZESYSTEMRESTOREPROXY_H + + +#include "workerfactory.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class CustomizeSystemRestoreProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeSystemRestoreProxy) +public: + // 系统还原的几种场景 + enum CustomizeSystemRestoreScene { + SYSTEM_RESTORE, // 系统还原 + RESTORE_SYSTEM_WITH_DATA, // 保留用户数据还原 + }; + + explicit CustomizeSystemRestoreProxy(); + virtual ~CustomizeSystemRestoreProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + +private: + // 将img文件挂载到/backup/imgbackup目录 + bool mountImg(); + // 以读写方式重新挂载efi分区 + void remountEfi(); + // 以读写方式重新挂载boot分区 + void remountBoot(); + // 还原前准备 + bool doPrepare(); + // 系统还原 + void restoreSystem(); + + /** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ + QStringList getRsyncArgs(CustomizeSystemRestoreScene scene); + + // .user.txt文件路径 + QString m_userFile; + // .exclude.user.txt文件路径 + QString m_excludeUserFile; + // 备份数据所在的data目录 + QString m_backupPath; + // 压缩的img文件全名 + QString m_imgFileName; + + // 是否还原成功 + bool m_bSuccess; + // 当前备份uuid + QString m_curUuid; + // 当前还原源目录 + QString m_srcPath; + // 备份进程 + RsyncPathToDirProcess *m_p; + // 当前备份节点 + ParseBackupList::BackupPoint m_backupPoint; + +}; + + +#endif // CUSTOMIZESYSTEMRESTOREPROXY_H + diff --git a/backup-daemon/data/README b/backup-daemon/data/README new file mode 100755 index 0000000..3510527 --- /dev/null +++ b/backup-daemon/data/README @@ -0,0 +1 @@ +backup-auto与backup-auto-efi为同一个程序,用来替换backup-auto/backup-auto,后者使用Qt程序会导致/boot/内核超过16M. diff --git a/backup-daemon/data/backup-auto b/backup-daemon/data/backup-auto new file mode 100755 index 0000000..07cde1f --- /dev/null +++ b/backup-daemon/data/backup-auto @@ -0,0 +1,1053 @@ +#!/bin/bash +#Author Buquan Liu, liubuquan@kylinos.cn, walt_lbq@163.com +#本程序本质上是对backup-auto/autobackup.cpp的重写. +#因为采用Qt程序则打包进入内核,相应的库会导致内核超过16M,故改为shell程序. + +#backup-auto --autobackup ${rootpath} /backup +#backup-auto --autorestore ${rootpath} /backup + +#xgs备份还原要保留更多的文件或目录: +#kybackup/maindialog.cpp, backup-daemon/mountpoint.cpp, backup-daemon/data/backup-auto-efi +XGS=false + +INFO="/etc/.bootinfo" +METAINFO=".METAINFO" +KB=1024 +MB=1048576 +GB=1073741824 + +if [ $# -lt 3 ] ; then + exit +fi + +#是否有/data数据分区 +hasDataPartition=0 +backupORrestore=$1 +rootpath=$2 +m_mountPath=$3 + +BACKUP= +if [[ -e "${rootpath}/backup/BACKUP/snapshots" ]]; then + BACKUP="/BACKUP" +fi + +m_backuplistPath=${m_mountPath}${BACKUP}"/snapshots/backuplist.xml" +EXCLUDEFILE=${m_mountPath}${BACKUP}"/snapshots/.exclude" +PLOGFILEDIR="${m_mountPath}${BACKUP}/log" +PLOGFILE="$PLOGFILEDIR/log-$(date +%Y%m%d%H%M)" +LOGFILE="/backup"${BACKUP}"/log.txt" #LOGFILE="/tmp/log.txt" + +#是否是出厂备份 +m_isFactory=false +factory_uuid="00000000-0000-0000-0000-000000000000" +auto_uuid="01234567-0123-0123-0123-0123456789ab" +PERSONAL_EXCLUDEFILE=".exclude.user.txt" +PERSONAL_BACKUPFILE=".user.txt" + +#如果/backup不存在,则创建该目录 +mkdir -p ${m_mountPath}${BACKUP} + +m_restoreUuid="" +m_enabled="" +global_system_usedDisk=0 +m_size=0 +newSize=0 +#----------------------------------------------------------------- +#see backup-auto/autobackup.cpp +getBackupInfo(){ + if [ "$rootpath" = "/" ]; then + bootinfo=$INFO + else + bootinfo=$rootpath$INFO + fi + + if [ ! -e "$bootinfo" ]; then + echo "$bootinfo file not exist!" + exit 200 + fi + + which_line=0 + content=`cat "$bootinfo" | grep -Ev "^#" | grep "=" | awk '{print $1}'` + for line in $content; + do + #parse_device "$device" + #只读第1行:RECOVERY_DEV_UUID=c965e712-9903-4139-b8da-c6e1eef0af6a + if [ $which_line -eq 0 ]; then + m_restoreUuid=`echo $line | sed 's:.*=::' | tr -d "\n"` + which_line=`expr $which_line + 1` + else + m_enabled=`echo $line | sed 's:.*=::' | tr -d "\n"` + which_line=`expr $which_line + 1` + fi + done +} + +#----------------------------------------------------------------- + +#该函数是对backup-daemon/parsebackuplist.cpp中相应函数的替换 +createBackupList(){ + local backuplistDir=${m_mountPath}${BACKUP}"/snapshots/" + + if [ ! -e "$backuplistDir" ]; then + mkdir -p $backuplistDir + fi + + if [ ! -e "$m_backuplistPath" ]; then + #echo "$m_backuplistPath file not exist!" + + #第1行'>'会清空后写文件 + echo "" >$m_backuplistPath + #echo "" >>$m_backuplistPath + #echo "" >>$m_backuplistPath + echo "" >>$m_backuplistPath #QDomDocument在节点为空时如此生成根节点 + fi +} + +#----------------------------------------------------------------- +#see backup-auto/autobackup.cpp +mountBackup() +{ + local myuuid="/dev/disk/by-uuid/"$m_restoreUuid + #echo "myuuid: $myuuid" + + #support lvm by zhangze + tmp_root_dev=$(mount|grep " /root "| cut -d ' ' -f 1) + case "$tmp_root_dev" in + /dev/mapper/*) + eval $(dmsetup splitname --nameprefixes --noheadings --rows "${tmp_root_dev#/dev/mapper/}") + if [ "$DM_VG_NAME" ] && [ "$DM_LV_NAME" ];then + lvm lvchange -aay -y --sysinit --ignoreskippedcluster "$DM_VG_NAME" + fi + ;; + esac + + mount $myuuid $m_mountPath + + mkdir -p $PLOGFILEDIR + if [ $? -ne 0 ]; then + echo "Could not create log directory in /backup" + exit 22 + fi + + touch $PLOGFILE + if [ $? -ne 0 ]; then + echo "Could not create log file" + exit 23 + fi + echo "Log for backuping and restoring...." >$PLOGFILE + + createBackupList #创建备份信息 +} + +#----------------------------------------------------------------- +#see backup-auto/autobackup.cpp +umountBackup() +{ + umount $m_mountPath +} + +#----------------------------------------------------------------- +#see backup-auto/backupcommon.cpp +#在grub时,根分区为/root;在进入系统后,根分区为/ +#parameters: rootDiskName +#返回值 elements=( $totalSize $freeDisk $usedDisk ) +caculateDiskSize(){ + local origalParas + + origalParas=(`echo "$@"`) + num=$[ $# ] + + #if [ $# -ne 2 ]; then + if [ $num -ne 2 ]; then + echo "You shoud input the rootDiskName and disk" + exit + fi + + local fullDiskName + local totalSize + local freeDisk + local usedDisk + + if [ "${origalParas[1]}" = "/" ]; then + fullDiskName=${origalParas[0]} + else + fullDiskName=${origalParas[0]}${origalParas[1]} + fi + + if [ ! -e "$fullDiskName" ]; then + ##因为要返回数组,所以下面的echo没有上面用,不会显示,只会作为返回值 + #echo "$fullDiskName not exist!" + elements=( 0 0 0 ) + echo ${elements[*]} + return + fi + + ##因为要返回数组,这里不能echo "fullDiskName: $fullDiskName" + sss=`df -k $fullDiskName | sed '1d' | tr -d "\n"` + freeDisk=`echo $sss|awk '{print $4}'` + freeDisk=`expr 1024 \* $freeDisk` + usedDisk=`echo $sss|awk '{print $3}'` + usedDisk=`expr 1024 \* $usedDisk` + + #totalSize没有从df命令的第2列来取,该值比下面2个的和还要大 + totalSize=`expr $usedDisk + $freeDisk` + + ##因为要返回数组,这里不能echo "freeDisk=$freeDisk" + ##因为要返回数组,这里不能echo "usedDisk=$usedDisk" + ##因为要返回数组,这里不能echo "totalSize=$totalSize" + + #local elements + elements=( $totalSize $freeDisk $usedDisk ) + echo ${elements[*]} +} + +parse_device(){ + device=$1 + if echo $device | grep -E -q "^UUID="; then + echo $device | sed 's:^UUID=:/dev/disk/by-uuid/:' | tr -d "\n" + elif echo $device | grep -E -q "^LABEL="; then + echo $device | sed 's:^LABEL=:/dev/disk/by-label/:' | tr -d "\n" + else + echo $device | tr -d "\n" + fi +} + +is_remote(){ + fstype=$1 + if [ nfs = "$fstype" ] || [ nfs4 = "$fstype" ] || [ smbfs = "$fstype" ] || [ cifs = "$fstype" ] || [ coda = "$fstype" ] || [ ncp = "$fstype" ]; then + echo yes + elif [ ncpfs = "$fstype" ] || [ ocfs2 = "$fstype" ] || [ gfs = "$fstype" ] || [ gfs2 = "$fstype" ] || [ ceph = "$fstype" ]; then + echo yes + else + echo no + fi +} + +function caculateDirSize() { + mkdir -p /backup/snapshots/check/data + local total_file_size=$(rsync -aAHXrn --stats --ignore-missing-args --exclude=/backup --exclude=/cdrom --exclude=/dev --exclude=/efi --exclude=/etc/uid_list --exclude=/data/ghost --exclude=/ghost --exclude=/lost+found --exclude=/media --exclude=/mnt --exclude=/proc --exclude=/run --exclude=/swap_file --exclude=/sys --exclude=/tmp --exclude=/var/lib/docker/overlay2 --exclude=/var/lib/kmre/data --exclude=/var/lib/kmre/kmre-*-*/data/media/0/0-麒麟* --exclude=/var/lib/udisks2 --exclude=/var/log --exclude=*/backup/snapshots --exclude=/data/home --exclude=/data/root "${rootpath}/" /backup/snapshots/check/data/ | grep "Total file size:" | awk '{print $4}' | sed 's/,//g') + total_file_size=$(expr ${total_file_size} + 200000000) + echo "备份所需空间大小:${total_file_size}" >>$PLOGFILE + echo "${total_file_size}" +} + +#parameters: 无 +#返回值:(system_totalSize system_freeDisk system_usedDisk) +caculateSystemSize(){ + local root_totalSize root_freeDisk root_usedDisk + local system_totalSize system_freeDisk system_usedDisk + local origalParas + local found item result + local device mntdir fstype options dump passno + local -a alreadyDisks #数组,已经分析过的磁盘目录 + local -a elements #存放返回值 + + #alreadyDisks=(${alreadyDisks[*]} "/boot") #插入一个元素"/boot" + + #result=(`caculateDiskSize "$rootpath" "$rootpath"`) + #origalParas=($rootpath "/") + #arg1=`echo ${origalParas[*]}` + + #result=(`caculateDiskSize $arg1`) + #root_totalSize=${result[0]} + #root_freeDisk=${result[1]} + #root_usedDisk=${result[2]} + + #system_totalSize=$root_totalSize + #system_freeDisk=$root_freeDisk + #system_usedDisk=$root_usedDisk + + #echo "system_totalSize="$system_totalSize + #echo "system_freeDisk="$system_freeDisk + #echo "system_usedDisk="$system_usedDisk + + ##############3#caculateDiskSize / /backup + fstab_path=${rootpath}/etc/fstab + + if [ ! -e "$fstab_path" ]; then + echo "$fstab_path file not exist!" + exit 701 + fi + + system_totalSize=0 + system_freeDisk=0 + system_usedDisk=0 + + while read line; + do + #取第1个字符 + if [ "${line:0:1}" = "#" ]; then + continue + fi + + echo $line + echo $line >>$PLOGFILE + + device=$( echo "$line"|awk '{print $1}') + mntdir=$( echo "$line"|awk '{print $2}') + fstype=$( echo "$line"|awk '{print $3}') + options=$( echo "$line"|awk '{print $4}') + dump=$( echo "$line"|awk '{print $5}') + passno=$( echo "$line"|awk '{print $6}') + + device=$(parse_device $device) + + if [[ $options =~ "bind" ]]; then + continue + fi + + # nodev filesystems + ( cat /proc/filesystems | grep "$fstype" | grep -q nodev ) && continue + + # virtual or network filesystems + [ none = "$mntdir" ] || [ yes = $(is_remote $fstype) ] && continue + + # swap or rootfs + [ swap = "$fstype" ] && continue +#[ swap = "$fstype" ] || [ / = "$mntdir" ] || [ /home = "$mntdir" ] || [ /var/log = "$mntdir" ]&& continue + + #取第1个字符 + if [ ${mntdir:0:1} != "/" ]; then + continue + fi + + if [ $mntdir = "/backup" ]; then #没有写成$m_mountPath,看下面if + continue + fi + + if [ $mntdir = $m_mountPath ]; then #没有写成$m_mountPath + continue + fi + + #注意: 没有备份数据分区 + if [ $mntdir = "/data" ]; then + hasDataPartition=1 + fi + + #echo "1: " $device + #echo "2: " $mntdir + #echo "3: " $fstype + #echo "4: " $options + #echo "5: " $dump + #echo "6: " $passno + + # not system partition +# [ no = $(is_system_partition $mntdir) ] && continue + + ################计算df $target_mntdir的各项值 + origalParas=($rootpath $mntdir) + arg1=`echo ${origalParas[*]}` + + result=(`caculateDiskSize $arg1`) + disk_totalSize=${result[0]} + disk_freeDisk=${result[1]} + disk_usedDisk=${result[2]} + + if [[ $disk_totalSize = $root_totalSize && $disk_freeDisk = $root_freeDisk && $disk_usedDisk = $root_usedDisk ]]; then + continue + fi + + #echo "disk_totalSize="$disk_totalSize + #echo "disk_freeDisk="$disk_freeDisk + #echo "disk_usedDisk="$disk_usedDisk + + system_totalSize=$( expr $system_totalSize + $disk_totalSize ) + system_freeDisk=$( expr $system_freeDisk + $disk_freeDisk ) + system_usedDisk=$( expr $system_usedDisk + $disk_usedDisk ) + + #echo "system_totalSize="$system_totalSize + #echo "system_freeDisk="$system_freeDisk + #echo "system_usedDisk="$system_usedDisk + + done < "$fstab_path"; + + #echo "system_totalSize="$system_totalSize + #echo "system_freeDisk="$system_freeDisk + #echo "system_usedDisk="$system_usedDisk + + #local elements + elements=( $system_totalSize $system_freeDisk $system_usedDisk ) + echo ${elements[*]} +} + +#----------------------------------------------------------------- +#//检查备份还原分区/backup剩余空间是否满足备份需求 +#see backup-auto/backupcommon.cpp +#返回值: 0表示/backup剩余容量不能够满足备份需求;1表示满足 +checkBackupCapacity() { #(m_rootPath.toStdString().c_str(), retstatus)) + local origalParas + local root_totalSize root_freeDisk root_usedDisk + local backup_totalSize backup_freeDisk backup_usedDisk + local system_totalSize system_freeDisk system_usedDisk + ###which_line=`expr $which_line + 1` + + #从函数caculateSystemSize取返回值 + result=(`caculateSystemSize`) + system_totalSize=${result[0]} + system_freeDisk=${result[1]} + system_usedDisk=${result[2]} + + #global_system_usedDisk=$system_usedDisk + global_system_usedDisk=$(caculateDirSize) + + #echo "--system_totalSize="$system_totalSize + #echo "--system_freeDisk="$system_freeDisk + #echo "--system_usedDisk="$system_usedDisk + + #-------------------------------------------------------- + #result=(`caculateDiskSize "$rootpath" "$rootpath"`) + origalParas=($rootpath "/") + arg1=`echo ${origalParas[*]}` + + result=(`caculateDiskSize $arg1`) + root_totalSize=${result[0]} + root_freeDisk=${result[1]} + root_usedDisk=${result[2]} + #echo "--root_totalSize="$root_totalSize + #echo "--root_freeDisk="$root_freeDisk + #echo "--root_usedDisk="$root_usedDisk + + #-------------------------------------------------------- + #caculateDiskSize / /backup + #origalParas=($rootpath "/backup") + #origalParas=("/" "/backup") #/backup不会是/root/backup + origalParas=("/" $m_mountPath) #/backup不会是/root/backup + arg1=`echo ${origalParas[*]}` + + result=(`caculateDiskSize $arg1`) + backup_totalSize=${result[0]} + backup_freeDisk=${result[1]} + backup_usedDisk=${result[2]} + + #echo "--backup_totalSize="$backup_totalSize + #echo "--backup_freeDisk="$backup_freeDisk + #echo "--backup_usedDisk="$backup_usedDisk + + if [[ $root_totalSize = $backup_totalSize && $root_freeDisk = $backup_freeDisk && $root_usedDisk = $backup_usedDisk ]]; then #"/backup"是不是一个独立的盘 + echo "Cannot find the /backup disk or not be mounted!" + exit 403 + fi + + #echo "bybobbi: bakup_freeDisk is " $backup_freeDisk + #echo "bybobbi: system_usedDisk is " $system_usedDisk + echo "free size : $backup_freeDisk; sys_size : $global_system_usedDisk" + echo "free size : $backup_freeDisk; sys_size : $global_system_usedDisk" >>$PLOGFILE + if [ ${backup_freeDisk} -gt ${global_system_usedDisk} ]; then + return 1 + else + return 0 + fi +} + +updateStateByComment() { + tmpFile=${m_backuplistPath}".tmp" + #echo "tmpFile: $tmpFile" + is_first_line=1 + + foundComment=0 #是否发现了要修改的comment + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; + do + #去除了前后空格 + xxx=$( echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g" ) + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + if [ $xxx = "${m_comment}" ]; then + foundComment=1 #当前comment是要修改的mycomment + else + foundComment=0 #当前comment不是要修改的mycomment + fi + fi + + if [[ "$xxx" =~ "" ]]; then + if [[ $foundComment -eq 1 ]]; then + line=" ${newSize}" + fi + fi + + if [[ "$xxx" =~ "" ]]; then + if [ $foundComment -eq 1 ]; then + line=" ${m_state}" + fi + fi + + if [ "$is_first_line" -eq 1 ]; then + echo "$line" >$tmpFile + else + echo "$line" >>$tmpFile + fi + + is_first_line=0 + + done < "$m_backuplistPath"; + IFS=$IFS_old + + cp -f $tmpFile ${m_backuplistPath} + rm -f $tmpFile +} + +#参照backup-daemon/backupmanager.cpp, 写日志文件 +writeLogFile() { + echo $1 >>$LOGFILE +} + +#参照backup-daemon/mountpoint.cpp +bFileExists() { + local theFile theOldFile + theOldFile=$1 #必须以"/"开始,但本身是个相对路径,因为没有加rootpath. + + if [ $rootpath = "/" ]; then + theFile=$theOldFile + else + theFile=$theOldFile + theFile="$rootpath""$theFile" + fi + + if [ -e "$theFile" ]; then + echo "$theOldFile" >>$EXCLUDEFILE + fi +} + +#参照backup-daemon/mountpoint.cpp +generateExcludeFile() { + local backupOrRestore + backupOrRestore=$1 #0: backup 1:restore + + #exclude的必须是相对目录,其实在efi启动时为/root/data + #see backup-daemon/mountpoint.cpp, backup-daemon/data/backup-auto-efi + #also kybackup: exclude.ui databackupdirs.ui dataincbackupdirs.ui + #if [ ! -e "$restoreDir" ]; then + #注意下面的>和>>----------------------------------------------- + echo "/efi" >$EXCLUDEFILE + echo "/backup" >>$EXCLUDEFILE + #echo "/boot/efi" >>$EXCLUDEFILE + echo "/dev" >>$EXCLUDEFILE + echo "/ghost" >>$EXCLUDEFILE #ghost镜像文件 + echo "/mnt" >>$EXCLUDEFILE + echo "/proc" >>$EXCLUDEFILE + echo "/run" >>$EXCLUDEFILE + echo "/sys" >>$EXCLUDEFILE + echo "/media" >>$EXCLUDEFILE + echo "/tmp" >>$EXCLUDEFILE + echo "/lost+found" >>$EXCLUDEFILE + echo "/var/lib/udisks2" >>$EXCLUDEFILE + #echo "/data/home/*" >>$EXCLUDEFILE + #echo "/data/root/*" >>$EXCLUDEFILE + #if [ -e "${rootpath}/data/home" ]; then + # echo "/home" >>$EXCLUDEFILE + # echo "/root" >>$EXCLUDEFILE + #fi + echo "/cdrom" >>$EXCLUDEFILE + echo "/swap_file" >>$EXCLUDEFILE + echo "/var/lib/docker/overlay2" >>$EXCLUDEFILE + echo "/var/log" >>$EXCLUDEFILE + + #bind挂载的目录不进行备份或还原 + cat ${rootpath}/etc/fstab | awk '{if($4~/bind/) print $1}' | + while read excludePath + do + echo "$excludePath" >>$EXCLUDEFILE + done + + #bFileExists "/etc/.bootinfo" + #bFileExists "/etc/fpb" #管控,暂时 + #echo "/etc/.bootinfo" >>$EXCLUDEFILE + + #数据分区是否使用由用户输入,最终放到/backup/snapshots/.excludeuser + #因为GRUB没有界面,所以是不是先在其他模式下做一次备份,生成这个文件;然后GRUB就可以了。 + #echo "/data/*" >>$EXCLUDEFILE #用户可以把数据放到该分区或者目录 + + #是否覆盖备份还原工具自身 + #if [ $backupOrRestore -eq 1 ]; then + # bFileExists "/usr/bin/backup-daemon" #备份还原 + # bFileExists "/usr/bin/kybackup" #备份还原 + # bFileExists "/usr/bin/mount_fstab" #备份还原 + # bFileExists "/usr/bin/backup-auto" #备份还原 + # bFileExists "/usr/bin/mount_fstab_efi" #备份还原 + # bFileExists "/usr/bin/backup-auto-efi" #备份还原 + #fi + + #是否使用由用户输入,最终放到/backup/snapshots/.excludeuser + #if [ "$XGS" = true ]; then + # echo "/etc/passwd" >>$EXCLUDEFILE + # echo "/etc/group" >>$EXCLUDEFILE + # echo "/etc/shadow" >>$EXCLUDEFILE + # if [ -e "/etc/uid_list" ]; then + # echo "/etc/uid_list" >>$EXCLUDEFILE + # fi + + # echo "/home/*" >>$EXCLUDEFILE + # echo "/opt/softmanager/log/log_cur.txt" >>$EXCLUDEFILE + # echo "/opt/softmanager/conf/audit/auditLogCur.txt" >>$EXCLUDEFILE + # echo "/opt/xgs/Audit/*" >>$EXCLUDEFILE + # echo "/opt/LinuxKpc/log/*" >>$EXCLUDEFILE + # echo "/var/log/*" >>$EXCLUDEFILE + # echo "/var/run/*" >>$EXCLUDEFILE + # echo "/root/*" >>$EXCLUDEFILE + # echo "/var/mail/*" >>$EXCLUDEFILE + # echo "/boot/efi/*" >>$EXCLUDEFILE #xgs出厂还原时会失败 + #fi + #fi +} + +backuping() { + local src dst + + src=$1 + dst=$2 + + #0:backup 1:restore + generateExcludeFile 0 + echo "/" > ${m_mountPath}${BACKUP}/snapshots/$m_uuid/$PERSONAL_BACKUPFILE + cp $EXCLUDEFILE ${m_mountPath}${BACKUP}/snapshots/$m_uuid/$PERSONAL_EXCLUDEFILE + + # /boot/efi不再单独备份 + #if [ -d ${src}/boot/efi ];then + # mkdir -p ${dst}/boot + # rsync -av --ignore-missing-args ${src}/boot/efi ${dst}/boot + #fi + + echo "Begin to backup..." >>$PLOGFILE + echo "Begin to backup..." + + mkdir -p ${rootpath}/var/log + touch ${rootpath}/var/log/backup.log + #是否有数据分区 + if [ $hasDataPartition -eq 0 ]; then + #exclude的必须是相对目录,其实在efi启动时为/root/data + #echo rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst + #rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst + + #不是目录,也不备份 + echo rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >>${rootpath}/var/log/backup.log 2>&1 + rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >>${rootpath}/var/log/backup.log 2>&1 + else + #exclude的必须是相对目录,其实在efi启动时为/root/data + #echo rsync -avAXH --ignore-missing-args --exclude data --exclude-from=$EXCLUDEFILE $src $dst + #The question is that the 'data' directories can not be copied + echo rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >>${rootpath}/var/log/backup.log 2>&1 + rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >>${rootpath}/var/log/backup.log 2>&1 + fi + + if [ $? -eq 0 -o $? -eq 24 -o $? -eq 23 ]; then + #将状态从"备份未完成"改成"0" + #updateStateByComment $m_comment 0 + m_state="backup finished" + touch $dst/.exectl + updateStateByComment + + else + echo "backup update backuplist.xml failed for updateBackupAutoFinishedState!"; + m_state="backup unfinished" + updateStateByComment + fi + + #写文件 + metainfo_file="${m_mountPath}${BACKUP}/snapshots/$m_uuid/$METAINFO" + #echo "metainfo_file="$metainfo_file + #第1行清空写 + #这里写的不是xml文件,是一个文本文件,这时候的状态是0或者backup failed + echo "COMMENT=$m_comment" >$metainfo_file + echo "TIME=$m_time" >>$metainfo_file + echo "UUID=$m_uuid" >>$metainfo_file + echo "SIZE=$m_size" >>$metainfo_file + echo "STATE=$m_state" >>$metainfo_file + + #写日志文件 + writeLogFile "${m_time},${m_uuid},0,出厂备份,${newSize}" #grub时只有全盘备份,没有增量备份 + echo "sync..." >>$PLOGFILE + sync + echo "Backup end..." >>$PLOGFILE +} + +CreateUuid() { + local uuid=`cat /proc/sys/kernel/random/uuid|tr -d "\n"` + while [ "$uuid" = $factory_uuid -o "$uuid" = $auto_uuid ] + do + uuid=`cat /proc/sys/kernel/random/uuid|tr -d "\n"` + done + echo $uuid +} + +findCommentByUuid() { + + local ret=1 + local local_uuid=$1 + local comment0 + local ret_comment + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; + do + #去除了前后空格 + xxx=$( echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g" ) + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + comment0=`echo $xxx | awk -F "" '{print $2}' |awk -F "" '{print $1}' | tr -d "\n"` + fi + + if [[ "$xxx" =~ "" ]]; then + if [ $xxx = "${local_uuid}" ]; then + ret=0 + ret_comment=$comment0 + break + fi + fi + done < "$m_backuplistPath"; + IFS=$IFS_old + + echo $ret_comment + return $ret +} + +deleteItemByComment() { + local local_comment=$1 +# echo "arg local_comment = $local_comment" + + tmpFile=${m_backuplistPath}".tmp" + cp -f ${m_backuplistPath} $tmpFile +# echo "tmpFile: $tmpFile" + + local foundComment=0 #是否发现了要修改的comment + local i=0 + local ii=0 + local iii=0 + local backupPointTmp=0 + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; + do + let i+=1 + #去除了前后空格 + xxx=$( echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g" ) + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + backupPointTmp=$i + fi + if [[ "$xxx" =~ "" ]]; then + if [ $xxx = "$local_comment" ]; then + foundComment=1 #当前comment是要修改的mycomment + ii=$backupPointTmp +# echo "delete foundComment = 1" + else + foundComment=0 #当前comment不是要修改的mycomment + fi + fi + if [[ "$xxx" =~ "" ]]; then + if [ $foundComment -eq 1 ]; then + iii=$i + break + fi + fi + done < "$tmpFile"; + IFS=$IFS_old + +# echo "ii: $ii, iii: $iii" + if [ $iii -ne 0 ]; then + sed -i "${ii},${iii}d" $tmpFile + cp -f $tmpFile ${m_backuplistPath} + fi + rm -f $tmpFile +} + +DeleteFactoryBackup() { + if [ -e "/backup${BACKUP}/snapshots/{${factory_uuid}}" ]; then + rm /backup${BACKUP}/snapshots/{${factory_uuid}} -rf + fi + + local comment=$(findCommentByUuid "{${factory_uuid}}") + echo "DeleteFactoryBackup comment is $comment" + if [ $? -eq 0 ]; then + deleteItemByComment "$comment" + fi +} + +#see backup-auto/autobackup.cpp +backupAuto() { #备份 + if [ $m_isFactory = true ]; then + DeleteFactoryBackup + fi + local xxx + checkBackupCapacity + ret=$? + if [ "$ret" -eq 0 ]; then + echo "The backup disk space is not enough" + exit 100 + fi + + #global_system_usedDisk + #创建一个uuid + if [ $m_isFactory = false ]; then + m_uuid=$(CreateUuid) + else + m_uuid="$factory_uuid" + fi + m_uuid="{"${m_uuid}"}" + echo "BYbobbi: m_uuid is $m_uuid" + m_time=`date "+%y-%m-%d %H:%M:%S"|tr -d "\n"` + m_comment=$m_time #这个是全局变量 + dst="${m_mountPath}${BACKUP}/snapshots/${m_uuid}/data" + #echo "dst: $dst" + mkdir -p $dst + m_size=$global_system_usedDisk + + #m_size=188248 + newSize1=$( echo "scale=2;$m_size / $GB"|bc) + newSize2=$( echo "scale=2;$m_size / $MB"|bc) + newSize3=$( echo "scale=2;$m_size / $KB"|bc) + + if [ $( echo "$newSize1 > 1.0"|bc ) = 1 ]; then + newSize=$newSize1"GB" + elif [ $( echo "$newSize2 > 1.0"|bc ) = 1 ]; then + newSize=$newSize2"MB" + else + newSize=$newSize3"KB" + fi + + state="backup unfinished" + + #写入文件m_backuplistPath=$m_mountPath"/snapshots/backuplist1.xml" + #bool AutoBackup::backupAuto()251 if (!m_parse->addItem(m_comment, time, m_uuid, newsize, "backup unfinished")) { + # + tmpFile=${m_backuplistPath}".tmp" + #echo "tmpFile: $tmpFile" + new_content="" + is_first_line=1 + + hasHead=false #有头吗"" + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + + while read line; + do + #去除了前后空格 + xxx=$( echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g" ) + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + hasHead=true + fi + + if [[ "$xxx" =~ "" ]]; then + xxx="" #与图形界面一致 + line="" + fi + + #插入新的记录 + if [ $xxx = "" ]; then + if [ "$hasHead" = false ]; then + echo "" >>$tmpFile + hasHead=true + fi + + echo " " >>$tmpFile + echo " $m_comment" >>$tmpFile + echo " " >>$tmpFile + echo " $m_uuid" >>$tmpFile + echo " $newSize" >>$tmpFile + echo " $state" >>$tmpFile + echo " 0" >>$tmpFile + echo " " >>$tmpFile + fi + + if [ "$is_first_line" -eq 1 ]; then + echo "$line" >$tmpFile + else + echo "$line" >>$tmpFile + fi + + is_first_line=0 + + done < "$m_backuplistPath"; + IFS=$IFS_old + + cp -f $tmpFile ${m_backuplistPath} + rm -f $tmpFile + + #写文件 + metainfo_file="${m_mountPath}${BACKUP}/snapshots/$m_uuid/$METAINFO" + #echo "metainfo_file="$metainfo_file + #第1行清空写 + #这里写的不是xml文件,是一个文本文件,这时候的状态是backup unfinished + echo "COMMENT=$m_comment" >$metainfo_file + echo "TIME=$m_time" >>$metainfo_file + echo "UUID=$m_uuid" >>$metainfo_file + echo "SIZE=$m_size" >>$metainfo_file + echo "STATE=$state" >>$metainfo_file + echo "TYPE=0" >>$metainfo_file + + backuping ${rootpath}/ $dst +} + +#返回值: +getLastUsefulBackupPointUuid() { + local xxx currentUuid foundComment + currentUuid="" + currentState=false + currentType=true + lastUsefulBackupPointUuid="" + foundComment=0 #是否发现了要修改的comment + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; + do + #去除了前后空格 + xxx=$( echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g" ) + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + currentUuid=$xxx + currentState=false + currentType=true + fi + + if [[ "$xxx" =~ "backup finished" ]]; then + #lastUsefulBackupPointUuid=$currentUuid + currentState=true + fi + + if [[ "$xxx" =~ "2" ]]; then + currentType=false + fi + + if [[ "$xxx" =~ "3" ]]; then + currentType=false + fi + + + if [[ "$xxx" =~ "" ]]; then + if [ "$currentState" = true -a "$currentType" = true ]; then #"/backup"是不是一个独立的盘 + lastUsefulBackupPointUuid=$currentUuid + fi + fi + done < "$m_backuplistPath"; + IFS=$IFS_old + + if [ "$lastUsefulBackupPointUuid" = "" ]; then + echo "can't find a useful backup for restoring" + exit 300 + fi + + lastUsefulBackupPointUuid=`echo $lastUsefulBackupPointUuid | sed 's:::' | tr -d "\n"` + lastUsefulBackupPointUuid=`echo $lastUsefulBackupPointUuid | sed 's:.*::' | tr -d "\n"` + #echo "lastUsefulBackupPointUuid=$lastUsefulBackupPointUuid" +} + +#see backup-auto/autobackup.cpp +restoreAuto() { #还原 + local xxx + + getLastUsefulBackupPointUuid + #echo "lastUsefulBackupPointUuid=$lastUsefulBackupPointUuid" + + #写日志文件 + local m_time=`date "+%y-%m-%d %H:%M:%S"|tr -d "\n"` + writeLogFile "${m_time},${lastUsefulBackupPointUuid},4,grub系统还原" #grub时只有一键还原,没有增量还原 + + restoreDir="${m_mountPath}${BACKUP}/snapshots/$lastUsefulBackupPointUuid" + + if [ ! -e "$restoreDir" ]; then + echo "full restore directory not exists!" + exit 301 #备份文件不存在,不能还原系统 + fi + + #0:backup 1:restore + generateExcludeFile 1 + + if [ -d "${restoreDir}/data/efi" ];then + mkdir -p ${rootpath}/boot + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete ${restoreDir}/data/efi ${rootpath}/boot + elif [ -d "${restoreDir}/data/boot/efi" ];then + mkdir -p ${rootpath}/boot + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete ${restoreDir}/data/boot/efi ${rootpath}/boot + fi + #yi jian huan yuan + if [ ! -e "$restoreDir/data/data" ]; then + #这两行要一致 + echo "rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete --exclude /data --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath >/dev/null 2>${rootpath}/var/log/backup.log" + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete --exclude /data --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath >/dev/null 2>${rootpath}/var/log/backup.log + else + #这两行要一致 + echo "rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath >/dev/null 2>${rootpath}/var/log/backup.log" + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath >/dev/null 2>${rootpath}/var/log/backup.log + fi + + if [ $? -ne 0 -a $? -ne 24 -a $? -ne 23 ]; then + echo "full restore process finished failed!" + exit 303 + fi + + #还原时清空目录 + #if [ -e "$rootpath/home/data" ]; then + # rm -r "$rootpath/home/data" ] + #fi + #mkdir -p "$rootpath/home/data" ] +} + +#----------------------------------------------------------------- +#see backup-auto/autobackup.cpp +updateBackupAutoFinishedState() { + echo "this is updateBackupAutoFinishedState" + +} + +#----------------------------------------------------------------- +#--------主程序从这里开始----------------------------------------- + +if [ "${rootpath}" = "/" ]; then + echo "This program is used in boot time" +# exit +fi + +getBackupInfo +#echo "m_restoreUuid="$m_restoreUuid +#echo "m_enabled="$m_enabled +#不加引号报错 +if [ "$m_restoreUuid" = "" ] || [ "$m_enabled" = "" ]; then + echo "bootinfo file is not correct!" + exit 202 +fi + +if [ $backupORrestore = "--autobackup" ]; then + mountBackup + backupAuto #备份, grub时只有全盘备份,没有增量备份 + updateBackupAutoFinishedState + #umountBackup + + echo "This is autobackup" + +elif [ $backupORrestore = "--autorestore" ]; then + mountBackup + restoreAuto #还原, grub时只有一键还原,没有增量还原 + #umountBackup + echo "This is autorestore" + +elif [ $backupORrestore = "--factorybackup" ]; then + m_isFactory=true + mountBackup + mount >>$PLOGFILE + backupAuto #备份, grub时只有全盘备份,没有增量备份 + updateBackupAutoFinishedState + #umountBackup + + echo "This is factorybackup" +else + echo "Not correct command" + exit 1 +fi + +exit 0 diff --git a/backup-daemon/data/backup-auto-efi b/backup-daemon/data/backup-auto-efi new file mode 100755 index 0000000..47e3e85 --- /dev/null +++ b/backup-daemon/data/backup-auto-efi @@ -0,0 +1,1231 @@ +#!/bin/bash +#Author Buquan Liu, liubuquan@kylinos.cn, walt_lbq@163.com +#本程序本质上是对backup-auto/autobackup.cpp的重写. +#因为采用Qt程序则打包进入内核,相应的库会导致内核超过16M,故改为shell程序. + +#backup-auto --autobackup ${rootpath} /backup +#backup-auto --autorestore ${rootpath} /backup + +#xgs备份还原要保留更多的文件或目录: +#kybackup/maindialog.cpp, backup-daemon/mountpoint.cpp, backup-daemon/data/backup-auto-efi +XGS=false + +INFO="/etc/.bootinfo" +METAINFO=".METAINFO" +KB=1024 +MB=1048576 +GB=1073741824 + +if [ $# -lt 3 ]; then + exit 18 +fi + +#是否有/data数据分区 +hasDataPartition=0 +backupORrestore=$1 +rootpath=$2 +m_mountPath=$3 +m_default_uuid=$4 + +BACKUP= +if [[ -e "${rootpath}/backup/BACKUP/snapshots" ]]; then + BACKUP="/BACKUP" +fi + +m_backuplistPath=${m_mountPath}${BACKUP}"/snapshots/backuplist.xml" +EXCLUDEFILE=${m_mountPath}${BACKUP}"/snapshots/.exclude" +PLOGFILEDIR="${m_mountPath}${BACKUP}/log" +PLOGFILE="$PLOGFILEDIR/log-$(date +%Y%m%d%H%M)" +LOGFILE="${m_mountPath}${BACKUP}/log.txt" #LOGFILE="/tmp/log.txt" + +#是否是出厂备份 +m_isFactory=false +factory_uuid="00000000-0000-0000-0000-000000000000" +auto_uuid="01234567-0123-0123-0123-0123456789ab" +PERSONAL_EXCLUDEFILE=".exclude.user.txt" +PERSONAL_BACKUPFILE=".user.txt" +m_isRetainUserData=false + +#如果/backup不存在,则创建该目录 +mkdir -p ${m_mountPath}${BACKUP} +if [ $? -ne 0 ]; then + echo "Could not create /backup in initrd!" + exit 20 +fi +m_restoreUuid="" +m_enabled="" +global_system_usedDisk=0 +m_size=0 +newSize=0 + +#----------------------------------------------------------------- +get_is_990_9a0() { + local ret=false + # 匹配 kirin 990 5g, kirin990, kirin 9006c + if egrep -qi 'kirin.?9[09]0' /proc/cpuinfo; then + ret=true + elif egrep -qi 'PANGU' /proc/cpuinfo; then + ret=true + + fi + echo $ret +} +is_990_9a0=$(get_is_990_9a0) +#see backup-auto/autobackup.cpp +getBackupInfo() { + if [ "$rootpath" = "/" ]; then + bootinfo=$INFO + else + bootinfo=${rootpath}${INFO} + fi + + if [ ! -e "$bootinfo" ]; then + # 系统崩坏,如操作失误删除了/etc目录 + if [ -e $INFO ]; then + bootinfo=$INFO + else + echo "$bootinfo file not exist!" + exit 1 + fi + fi + + which_line=0 + content=$(cat "$bootinfo" | grep -Ev "^#" | grep "=" | awk '{print $1}') + for line in $content; + do + #parse_device "$device" + #只读第1行:RECOVERY_DEV_UUID=c965e712-9903-4139-b8da-c6e1eef0af6a + if [ $which_line -eq 0 ]; then + m_restoreUuid=$(echo $line | sed 's:.*=::' | tr -d "\n") + which_line=$(expr $which_line + 1) + else + m_enabled=$(echo $line | sed 's:.*=::' | tr -d "\n") + which_line=$(expr $which_line + 1) + fi + done +} + +#----------------------------------------------------------------- + +#该函数是对backup-daemon/parsebackuplist.cpp中相应函数的替换 +createBackupList() { + local backuplistDir=${m_mountPath}${BACKUP}"/snapshots/" + + if [ ! -e "$backuplistDir" ]; then + mkdir -p $backuplistDir + fi + + if [ ! -e "$m_backuplistPath" ]; then + #echo "$m_backuplistPath file not exist!" + + #第1行'>'会清空后写文件 + echo "" >$m_backuplistPath + #echo "" >>$m_backuplistPath + #echo "" >>$m_backuplistPath + echo "" >>$m_backuplistPath #QDomDocument在节点为空时如此生成根节点 + fi +} + +#----------------------------------------------------------------- +#see backup-auto/autobackup.cpp +mountBackup() { + local myuuid="/dev/disk/by-uuid/"$m_restoreUuid + #echo "myuuid: $myuuid" + + #support lvm by zhangze + tmp_root_dev=$(mount | grep " /root " | cut -d ' ' -f 1) + case "$tmp_root_dev" in + /dev/mapper/*) + eval $(dmsetup splitname --nameprefixes --noheadings --rows "${tmp_root_dev#/dev/mapper/}") + if [ "$DM_VG_NAME" ] && [ "$DM_LV_NAME" ]; then + lvm lvchange -aay -y --sysinit --ignoreskippedcluster "$DM_VG_NAME" + fi + ;; + esac + + #mount $myuuid $m_mountPath + mount -o defaults,rw -U $m_restoreUuid $m_mountPath + if [ $? -ne 0 ]; then + echo "Mount backup failed!" + exit 21 + fi + + mkdir -p $PLOGFILEDIR + if [ $? -ne 0 ]; then + echo "Could not create log directory in /backup" + exit 22 + fi + + touch $PLOGFILE + if [ $? -ne 0 ]; then + echo "Could not create log file" + exit 23 + fi + echo "Log for backuping and restoring...." >$PLOGFILE + createBackupList #创建备份信息 +} + +#----------------------------------------------------------------- +#see backup-auto/autobackup.cpp +umountBackup() { + umount $m_mountPath +} + +#----------------------------------------------------------------- +#see backup-auto/backupcommon.cpp +#在grub时,根分区为/root;在进入系统后,根分区为/ +#parameters: rootDiskName +#返回值 elements=( $totalSize $freeDisk $usedDisk ) +caculateDiskSize() { + local origalParas + + origalParas=($(echo "$@")) + num=$(($#)) + + #if [ $# -ne 2 ]; then + if [ $num -ne 2 ]; then + echo "You shoud input the rootDiskName and disk" + exit 19 + fi + + local fullDiskName + local totalSize + local freeDisk + local usedDisk + + if [ "${origalParas[1]}" = "/" ]; then + fullDiskName=${origalParas[0]} + else + fullDiskName=${origalParas[0]}${origalParas[1]} + fi + + if [ ! -e "$fullDiskName" ]; then + ##因为要返回数组,所以下面的echo没有上面用,不会显示,只会作为返回值 + #echo "$fullDiskName not exist!" + elements=(0 0 0) + echo ${elements[*]} + return + fi + + ##因为要返回数组,这里不能echo "fullDiskName: $fullDiskName" + sss=$(df -k $fullDiskName | sed '1d' | tr -d "\n") + freeDisk=$(echo $sss | awk '{print $4}') + freeDisk=$(expr 1024 \* $freeDisk) + usedDisk=$(echo $sss | awk '{print $3}') + usedDisk=$(expr 1024 \* $usedDisk) + + #totalSize没有从df命令的第2列来取,该值比下面2个的和还要大 + totalSize=$(expr $usedDisk + $freeDisk) + + ##因为要返回数组,这里不能echo "freeDisk=$freeDisk" + ##因为要返回数组,这里不能echo "usedDisk=$usedDisk" + ##因为要返回数组,这里不能echo "totalSize=$totalSize" + + #local elements + elements=($totalSize $freeDisk $usedDisk) + echo ${elements[*]} +} + +parse_device() { + device=$1 + if echo $device | grep -E -q "^UUID="; then + echo $device | sed 's:^UUID=:/dev/disk/by-uuid/:' | tr -d "\n" + elif echo $device | grep -E -q "^LABEL="; then + echo $device | sed 's:^LABEL=:/dev/disk/by-label/:' | tr -d "\n" + else + echo $device | tr -d "\n" + fi +} + +is_remote() { + fstype=$1 + if [ nfs = "$fstype" ] || [ nfs4 = "$fstype" ] || [ smbfs = "$fstype" ] || [ cifs = "$fstype" ] || [ coda = "$fstype" ] || [ ncp = "$fstype" ]; then + echo yes + elif [ ncpfs = "$fstype" ] || [ ocfs2 = "$fstype" ] || [ gfs = "$fstype" ] || [ gfs2 = "$fstype" ] || [ ceph = "$fstype" ]; then + echo yes + else + echo no + fi +} + +function caculateDirSize() { + mkdir -p /backup/snapshots/check/data + local total_file_size=$(rsync -aAHXrn --stats --ignore-missing-args --exclude=/backup --exclude=/cdrom --exclude=/dev --exclude=/efi --exclude=/etc/uid_list --exclude=/data/ghost --exclude=/ghost --exclude=/lost+found --exclude=/media --exclude=/mnt --exclude=/proc --exclude=/run --exclude=/swap_file --exclude=/sys --exclude=/tmp --exclude=/var/lib/docker/overlay2 --exclude=/var/lib/kmre/data --exclude=/var/lib/kmre/kmre-*-*/data/media/0/0-麒麟* --exclude=/var/lib/udisks2 --exclude=/var/log --exclude=*/backup/snapshots --exclude=/data/home --exclude=/data/root "${rootpath}/" /backup/snapshots/check/data/ | grep "Total file size:" | awk '{print $4}' | sed 's/,//g') + total_file_size=$(expr ${total_file_size} + 200000000) + echo "备份所需空间大小:${total_file_size}" >>$PLOGFILE + echo "${total_file_size}" +} + +#parameters: 无 +#返回值:(system_totalSize system_freeDisk system_usedDisk) +caculateSystemSize() { + local root_totalSize root_freeDisk root_usedDisk + local system_totalSize system_freeDisk system_usedDisk + local origalParas + local found item result + local device mntdir fstype options dump passno + #local -a alreadyDisks #数组,已经分析过的磁盘目录 + local -a elements #存放返回值 + + #alreadyDisks=(${alreadyDisks[*]} "/boot") #插入一个元素"/boot" + + #result=(`caculateDiskSize "$rootpath" "$rootpath"`) + #origalParas=($rootpath "/") + #arg1=$(echo ${origalParas[*]}) + + #result=($(caculateDiskSize $arg1)) + #root_totalSize=${result[0]} + #root_freeDisk=${result[1]} + #root_usedDisk=${result[2]} + + #system_totalSize=$root_totalSize + #system_freeDisk=$root_freeDisk + #system_usedDisk=$root_usedDisk + + #echo "system_totalSize="$system_totalSize + #echo "system_freeDisk="$system_freeDisk + #echo "system_usedDisk="$system_usedDisk + + ##############3#caculateDiskSize / /backup + fstab_path=${rootpath}/etc/fstab + + if [ ! -e "$fstab_path" ]; then + fstab_path=/etc/fstab-backup + if [ ! -e "$fstab_path" ]; then + echo "$fstab_path file not exist!" + exit 16 + fi + fi + + system_totalSize=0 + system_freeDisk=0 + system_usedDisk=0 + while read line; do + #取第1个字符 + if [ "${line:0:1}" = "#" ]; then + continue + fi + + echo $line + echo $line >>$PLOGFILE + + device=$(echo "$line" | awk '{print $1}') + mntdir=$(echo "$line" | awk '{print $2}') + fstype=$(echo "$line" | awk '{print $3}') + options=$(echo "$line" | awk '{print $4}') + dump=$(echo "$line" | awk '{print $5}') + passno=$(echo "$line" | awk '{print $6}') + + device=$(parse_device $device) + + if [[ $options =~ "bind" ]]; then + continue + fi + + # nodev filesystems + (cat /proc/filesystems | grep "$fstype" | grep -q nodev) && continue + + # virtual or network filesystems + [ none = "$mntdir" ] || [ yes = $(is_remote $fstype) ] && continue + + # swap or rootfs + [ swap = "$fstype" ] && continue + #[ swap = "$fstype" ] || [ / = "$mntdir" ] || [ /home = "$mntdir" ] || [ /var/log = "$mntdir" ]&& continue + + #取第1个字符 + if [ "${mntdir:0:1}" != "/" ]; then + continue + fi + + if [ $mntdir = "/backup" ]; then #没有写成$m_mountPath,看下面if + continue + fi + + if [ $mntdir = $m_mountPath ]; then #没有写成$m_mountPath + continue + fi + + #注意: 没有备份数据分区 + if [ $mntdir = "/data" ]; then + hasDataPartition=1 + fi + + #echo "1: " $device + #echo "2: " $mntdir + #echo "3: " $fstype + #echo "4: " $options + #echo "5: " $dump + #echo "6: " $passno + + # not system partition + # [ no = $(is_system_partition $mntdir) ] && continue + + ################计算df $target_mntdir的各项值 + origalParas=($rootpath $mntdir) + arg1=$(echo ${origalParas[*]}) + + result=($(caculateDiskSize $arg1)) + disk_totalSize=${result[0]} + disk_freeDisk=${result[1]} + disk_usedDisk=${result[2]} + + #echo "disk_totalSize="$disk_totalSize + #echo "disk_freeDisk="$disk_freeDisk + #echo "disk_usedDisk="$disk_usedDisk + + system_totalSize=$(expr $system_totalSize + $disk_totalSize) + system_freeDisk=$(expr $system_freeDisk + $disk_freeDisk) + system_usedDisk=$(expr $system_usedDisk + $disk_usedDisk) + + #echo "system_totalSize="$system_totalSize + #echo "system_freeDisk="$system_freeDisk + #echo "system_usedDisk="$system_usedDisk + + done <"$fstab_path" + + #echo "system_totalSize="$system_totalSize + #echo "system_freeDisk="$system_freeDisk + #echo "system_usedDisk="$system_usedDisk + + #local elements + elements=($system_totalSize $system_freeDisk $system_usedDisk) + echo ${elements[*]} +} + +#----------------------------------------------------------------- +#//检查备份还原分区/backup剩余空间是否满足备份需求 +#see backup-auto/backupcommon.cpp +#返回值: 0表示/backup剩余容量不能够满足备份需求;1表示满足 +checkBackupCapacity() { #(m_rootPath.toStdString().c_str(), retstatus)) + local origalParas + local root_totalSize root_freeDisk root_usedDisk + local backup_totalSize backup_freeDisk backup_usedDisk + local system_totalSize system_freeDisk system_usedDisk + ###which_line=`expr $which_line + 1` + + #从函数caculateSystemSize取返回值 + result=($(caculateSystemSize)) + system_totalSize=${result[0]} + system_freeDisk=${result[1]} + system_usedDisk=${result[2]} + + #global_system_usedDisk=$system_usedDisk + global_system_usedDisk=$(caculateDirSize) + + #echo "--system_totalSize="$system_totalSize + #echo "--system_freeDisk="$system_freeDisk + #echo "--system_usedDisk="$system_usedDisk + + #-------------------------------------------------------- + #result=(`caculateDiskSize "$rootpath" "$rootpath"`) + origalParas=($rootpath "/") + arg1=$(echo ${origalParas[*]}) + + result=($(caculateDiskSize $arg1)) + root_totalSize=${result[0]} + root_freeDisk=${result[1]} + root_usedDisk=${result[2]} + #echo "--root_totalSize="$root_totalSize + #echo "--root_freeDisk="$root_freeDisk + #echo "--root_usedDisk="$root_usedDisk + + #-------------------------------------------------------- + #caculateDiskSize / /backup + #origalParas=($rootpath "/backup") + #origalParas=("/" "/backup") #/backup不会是/root/backup + origalParas=("/" $m_mountPath) #/backup不会是/root/backup + arg1=$(echo ${origalParas[*]}) + + result=($(caculateDiskSize $arg1)) + backup_totalSize=${result[0]} + backup_freeDisk=${result[1]} + backup_usedDisk=${result[2]} + + #echo "--backup_totalSize="$backup_totalSize + #echo "--backup_freeDisk="$backup_freeDisk + #echo "--backup_usedDisk="$backup_usedDisk + + if [[ $root_totalSize = $backup_totalSize && $root_freeDisk = $backup_freeDisk && $root_usedDisk = $backup_usedDisk ]]; then #"/backup"是不是一个独立的盘 + echo "Cannot find the /backup disk or not be mounted!" + exit 12 + fi + + echo "free size : $backup_freeDisk; sys_size : $global_system_usedDisk" + echo "free size : $backup_freeDisk; sys_size : $global_system_usedDisk" >>$PLOGFILE + if [ ${backup_freeDisk} -gt ${global_system_usedDisk} ]; then + return 1 + else + return 0 + fi +} + +updateStateByComment() { + tmpFile=${m_backuplistPath}".tmp" + #echo "tmpFile: $tmpFile" + is_first_line=1 + + foundComment=0 #是否发现了要修改的comment + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; do + #去除了前后空格 + xxx=$(echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g") + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + if [ $xxx = "${m_comment}" ]; then + foundComment=1 #当前comment是要修改的mycomment + else + foundComment=0 #当前comment不是要修改的mycomment + fi + fi + + if [[ "$xxx" =~ "" ]]; then + if [[ $foundComment -eq 1 ]]; then + line=" ${newSize}" + fi + fi + + + if [[ "$xxx" =~ "" ]]; then + if [ $foundComment -eq 1 ]; then + line=" ${m_state}" + fi + fi + + if [ "$is_first_line" -eq 1 ]; then + echo "$line" >$tmpFile + else + echo "$line" >>$tmpFile + fi + + is_first_line=0 + + done <"$m_backuplistPath" + IFS=$IFS_old + + cp -f $tmpFile ${m_backuplistPath} + rm -f $tmpFile +} + +#参照backup-daemon/backupmanager.cpp, 写日志文件 +writeLogFile() { + echo $1 >>$LOGFILE +} + +#参照backup-daemon/mountpoint.cpp +bFileExists() { + local theFile theOldFile + theOldFile=$1 #必须以"/"开始,但本身是个相对路径,因为没有加rootpath. + + if [ $rootpath = "/" ]; then + theFile=$theOldFile + else + theFile=$theOldFile + theFile="$rootpath""$theFile" + fi + + if [ -e "$theFile" ]; then + echo "$theOldFile" >>$EXCLUDEFILE + fi +} + +#参照backup-daemon/mountpoint.cpp +generateExcludeFile() { + local backupOrRestore + backupOrRestore=$1 #0: backup 1:restore + + #exclude的必须是相对目录,其实在efi启动时为/root/data + #see backup-daemon/mountpoint.cpp, backup-daemon/data/backup-auto-efi + #also kybackup: exclude.ui databackupdirs.ui dataincbackupdirs.ui + #if [ ! -e "$restoreDir" ]; then + #注意下面的>和>>----------------------------------------------- + echo "/efi" >$EXCLUDEFILE + echo "/backup" >>$EXCLUDEFILE + #echo "/boot/efi" >>$EXCLUDEFILE + echo "/dev" >>$EXCLUDEFILE + echo "/ghost" >>$EXCLUDEFILE #ghost镜像文件 + echo "/mnt" >>$EXCLUDEFILE + echo "/proc" >>$EXCLUDEFILE + echo "/run" >>$EXCLUDEFILE + echo "/sys" >>$EXCLUDEFILE + echo "/media" >>$EXCLUDEFILE + echo "/tmp" >>$EXCLUDEFILE + echo "/lost+found" >>$EXCLUDEFILE + echo "/var/lib/udisks2" >>$EXCLUDEFILE + #echo "/data/home/*" >>$EXCLUDEFILE + #echo "/data/root/*" >>$EXCLUDEFILE + #if [ -e "${rootpath}/data/home" ]; then + # echo "/home" >>$EXCLUDEFILE + # echo "/root" >>$EXCLUDEFILE + #fi + echo "/cdrom" >>$EXCLUDEFILE + echo "/swap_file" >>$EXCLUDEFILE + echo "/var/lib/docker/overlay2" >>$EXCLUDEFILE + echo "*/backup/snapshots" >>$EXCLUDEFILE + echo "/var/log" >>$EXCLUDEFILE + + #bind挂载的目录不进行备份或还原 + if [ -z $fstab_path ]; then + fstab_path=${rootpath}/etc/fstab + + if [ ! -e "$fstab_path" ]; then + fstab_path=/etc/fstab-backup + if [ ! -e "$fstab_path" ]; then + echo "$fstab_path file not exist!" + exit 16 + fi + fi + fi + cat $fstab_path | awk '{if($4~/bind/) print $1}' | + while read excludePath + do + echo "$excludePath" >>$EXCLUDEFILE + done + + #bFileExists "/etc/.bootinfo" + #bFileExists "/etc/fpb" #管控,暂时 + #echo "/etc/.bootinfo" >>$EXCLUDEFILE + + #数据分区是否使用由用户输入,最终放到/backup/snapshots/.excludeuser + #因为GRUB没有界面,所以是不是先在其他模式下做一次备份,生成这个文件;然后GRUB就可以了。 + #echo "/data/*" >>$EXCLUDEFILE #用户可以把数据放到该分区或者目录 + + #是否覆盖备份还原工具自身,因为grub备份还原使用的工具是initrd.img里面的东西,故不存在时可以还原备份还原工具本身(例如工具被删除的场景) + #if [ $backupOrRestore -eq 1 ]; then + # bFileExists "/usr/bin/backup-daemon" + # bFileExists "/usr/bin/kybackup" + # bFileExists "/usr/bin/backup-auto" + # bFileExists "/usr/bin/mount_fstab_efi" + # bFileExists "/usr/bin/backup-auto-efi" + # bFileExists "/usr/bin/rsync" + # bFileExists "/usr/share/rsync" + # bFileExists "/usr/share/initramfs-tools/hooks/kybackup-hooks" + # bFileExists "/usr/share/initramfs-tools/scripts/local-bottom/kybackup" + #fi + + # 安全模块会将文件/usr/share/kysec-utils/data/readonly_list中的文件列表限制只读,无法修改、备份(包含扩展属性时)、删除等 + # 现在里面仅有/etc/uid_list,先暂时排除掉;等后续安全模块有其它保护方案后再进一步修改 + # 新:用安全保护程序/usr/bin/setstatus可以关闭保护,故不再排除此文件 + # echo "/etc/uid_list" >>$EXCLUDEFILE + #if [ $backupOrRestore -eq 1 ]; then + # bFileExists "/usr/bin/backup-daemon" #备份还原 + # bFileExists "/usr/bin/kybackup" #备份还原 + # bFileExists "/usr/bin/mount_fstab" #备份还原 + # bFileExists "/usr/bin/backup-auto" #备份还原 + # bFileExists "/usr/bin/mount_fstab_efi" #备份还原 + # bFileExists "/usr/bin/backup-auto-efi" #备份还原 + #fi + + #是否使用由用户输入,最终放到/backup/snapshots/.excludeuser + #if [ "$XGS" = true ]; then + # echo "/etc/passwd" >>$EXCLUDEFILE + # echo "/etc/group" >>$EXCLUDEFILE + # echo "/etc/shadow" >>$EXCLUDEFILE + # if [ -e "/etc/uid_list" ]; then + # echo "/etc/uid_list" >>$EXCLUDEFILE + # fi + + # echo "/home/*" >>$EXCLUDEFILE + # echo "/opt/softmanager/log/log_cur.txt" >>$EXCLUDEFILE + # echo "/opt/softmanager/conf/audit/auditLogCur.txt" >>$EXCLUDEFILE + # echo "/opt/xgs/Audit/*" >>$EXCLUDEFILE + # echo "/opt/LinuxKpc/log/*" >>$EXCLUDEFILE + # echo "/var/log/*" >>$EXCLUDEFILE + # echo "/var/run/*" >>$EXCLUDEFILE + # echo "/root/*" >>$EXCLUDEFILE + # echo "/var/mail/*" >>$EXCLUDEFILE + # echo "/boot/efi/*" >>$EXCLUDEFILE #xgs出厂还原时会失败 + #fi + #fi +} + +backuping() { + local src dst + + src=$1 + dst=$2 + + #0:backup 1:restore + generateExcludeFile 0 + echo "/" > ${m_mountPath}${BACKUP}/snapshots/$m_uuid/$PERSONAL_BACKUPFILE + cp $EXCLUDEFILE ${m_mountPath}${BACKUP}/snapshots/$m_uuid/$PERSONAL_EXCLUDEFILE + + #echo "Begin to backup efi directory..." >>$PLOGFILE + #echo "Begin to backup efi directory..." + # /boot/efi不再单独备份 + #if [ -d "${src}/boot/efi" ]; then + # mkdir -p ${dst}/boot + # rsync -av --ignore-missing-args ${src}/boot/efi ${dst}/boot >/dev/null 2>>$PLOGFILE + # if [ $? -ne 0 -a $? -ne 24 -a $? -ne 23 ]; then + # echo "backup update backuplist.xml failed for updateBackupAutoFinishedState!" + # echo "backup update backuplist.xml failed for updateBackupAutoFinishedState!" >>$PLOGFILE + # m_state="backup unfinished" + # updateStateByComment + # echo "System backuping failed, please reboot your system!" + # echo "System backuping failed, please reboot your system!" >>$PLOGFILE + # exit 14 + # fi + #fi + + echo "Begin to backup other directories..." >>$PLOGFILE + echo "Begin to backup other directories..." + #是否有数据分区 + if [ $hasDataPartition -eq 0 ]; then + #exclude的必须是相对目录,其实在efi启动时为/root/data + #echo rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst + #rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst + + #不是目录,也不备份 + echo rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >/dev/null 2>>$PLOGFILE + rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >/dev/null 2>>$PLOGFILE + else + #exclude的必须是相对目录,其实在efi启动时为/root/data + #echo rsync -avAXH --ignore-missing-args --exclude data --exclude-from=$EXCLUDEFILE $src $dst + #The question is that the 'data' directories can not be copied + echo rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >/dev/null 2>>$PLOGFILE + rsync -avAXH --ignore-missing-args --exclude-from $EXCLUDEFILE $src $dst >/dev/null 2>>$PLOGFILE + fi + + if [ $? -eq 0 -o $? -eq 24 -o $? -eq 23 ]; then + #updateStateByComment $m_comment 0 + sync + m_state="backup finished" + touch $dst/.exectl + #caculateDirSize $dst + + updateStateByComment + + else + #将状态从"backup unfinished"改成"backup failed" + echo "backup update backuplist.xml failed for updateBackupAutoFinishedState!" + echo "backup update backuplist.xml failed for updateBackupAutoFinishedState!" >>$PLOGFILE + m_state="backup unfinished" + updateStateByComment + echo "System backuping failed, please reboot your system!" + echo "System backuping failed, please reboot your system!" >>$PLOGFILE + exit 14 + fi + + #写文件 + metainfo_file="${m_mountPath}/snapshots/$m_uuid/$METAINFO" + #echo "metainfo_file="$metainfo_file + #第1行清空写 + #这里写的不是xml文件,是一个文本文件,这时候的状态是0或者backup failed + echo "COMMENT=$m_comment" >$metainfo_file + echo "TIME=$m_time" >>$metainfo_file + echo "UUID=$m_uuid" >>$metainfo_file + echo "SIZE=$m_size" >>$metainfo_file + echo "STATE=$m_state" >>$metainfo_file + + #写日志文件 + writeLogFile "${m_time},${m_uuid},0,grub备份,${newSize}" #grub时只有全盘备份,没有增量备份 + sync +} + +CreateUuid() { + local uuid=`cat /proc/sys/kernel/random/uuid|tr -d "\n"` + while [ "$uuid" = $factory_uuid -o "$uuid" = $auto_uuid ] + do + uuid=`cat /proc/sys/kernel/random/uuid|tr -d "\n"` + done + echo $uuid +} + +findCommentByUuid() { + + local ret=1 + local local_uuid=$1 + local comment0 + local ret_comment + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; + do + #去除了前后空格 + xxx=$( echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g" ) + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + comment0=`echo $xxx | awk -F "" '{print $2}' |awk -F "" '{print $1}' | tr -d "\n"` + fi + + if [[ "$xxx" =~ "" ]]; then + if [ $xxx = "${local_uuid}" ]; then + ret=0 + ret_comment=$comment0 + break + fi + fi + done < "$m_backuplistPath"; + IFS=$IFS_old + + echo $ret_comment + return $ret +} + +deleteItemByComment() { + local local_comment=$1 + echo "arg local_comment = $local_comment" + + tmpFile=${m_backuplistPath}".tmp" + cp -f ${m_backuplistPath} $tmpFile +# echo "tmpFile: $tmpFile" + + local foundComment=0 #是否发现了要修改的comment + local i=0 + local ii=0 + local iii=0 + local backupPointTmp=0 + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; + do + let i+=1 + #去除了前后空格 + xxx=$( echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g" ) + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + backupPointTmp=$i + fi + if [[ "$xxx" =~ "" ]]; then + if [ $xxx = "$local_comment" ]; then + foundComment=1 #当前comment是要修改的mycomment + ii=$backupPointTmp + #echo "delete foundComment = 1" + else + foundComment=0 #当前comment不是要修改的mycomment + fi + fi + if [[ "$xxx" =~ "" ]]; then + if [ $foundComment -eq 1 ]; then + iii=$i + break + fi + fi + done < "$tmpFile"; + IFS=$IFS_old + +# echo "ii: $ii, iii: $iii" + + if [ $iii -ne 0 ]; then + sed -i "${ii},${iii}d" $tmpFile + cp -f $tmpFile ${m_backuplistPath} + fi + rm -f $tmpFile +} + +DeleteFactoryBackup() { + if [ -e "/backup${BACKUP}/snapshots/{${factory_uuid}}" ]; then + rm /backup${BACKUP}/snapshots/{${factory_uuid}} -rf + fi + + local comment=$(findCommentByUuid "{${factory_uuid}}") +# echo "DeleteFactoryBackup comment is $comment" + if [ $? -eq 0 ]; then + deleteItemByComment "$comment" + fi +} + +#see backup-auto/autobackup.cpp +backupAuto() { #备份 + if [ $m_isFactory = true ]; then + DeleteFactoryBackup + fi + + local xxx + checkBackupCapacity + ret=$? + if [ "$ret" -eq 0 ]; then + echo "The backup disk space is not enough" + exit 4 + fi + + #global_system_usedDisk + #创建一个uuid + if [ $m_isFactory = false ]; then + m_uuid=$(CreateUuid) + else + m_uuid="$factory_uuid" + fi + m_uuid="{"${m_uuid}"}" +# echo "BYbobbi: m_uuid is $m_uuid" + m_time=$(date "+%y-%m-%d %H:%M:%S" | tr -d "\n") + m_comment=$m_time #这个是全局变量 + dst="${m_mountPath}${BACKUP}/snapshots/$m_uuid/data" + #echo "dst: $dst" + mkdir -p $dst + m_size=$global_system_usedDisk + + #m_size=188248 + newSize1=$(echo "scale=2;$m_size / $GB" | bc) + newSize2=$(echo "scale=2;$m_size / $MB" | bc) + newSize3=$(echo "scale=2;$m_size / $KB" | bc) + + if [ $(echo "$newSize1 > 1.0" | bc) = 1 ]; then + newSize=$newSize1"GB" + elif [ $(echo "$newSize2 > 1.0" | bc) = 1 ]; then + newSize=$newSize2"MB" + else + newSize=$newSize3"KB" + fi + + state="backup unfinished" + + #写入文件m_backuplistPath=$m_mountPath"/snapshots/backuplist1.xml" + #bool AutoBackup::backupAuto()251 if (!m_parse->addItem(m_comment, time, m_uuid, newsize, "backup unfinished")) { + # + tmpFile=${m_backuplistPath}".tmp" + #echo "tmpFile: $tmpFile" + new_content="" + is_first_line=1 + + hasHead=false #有头吗"" + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + + while read line; do + #去除了前后空格 + xxx=$(echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g") + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + hasHead=true + fi + + if [[ "$xxx" =~ "" ]]; then + xxx="" #与图形界面一致 + line="" + fi + + #插入新的记录 + if [ $xxx = "" ]; then + if [ "$hasHead" = false ]; then + echo "" >>$tmpFile + hasHead=true + fi + + echo " " >>$tmpFile + echo " $m_comment" >>$tmpFile + echo " " >>$tmpFile + echo " $m_uuid" >>$tmpFile + echo " $newSize" >>$tmpFile + echo " $state" >>$tmpFile + echo " 0" >>$tmpFile + echo " " >>$tmpFile + fi + + if [ "$is_first_line" -eq 1 ]; then + echo "$line" >$tmpFile + else + echo "$line" >>$tmpFile + fi + + is_first_line=0 + + done <"$m_backuplistPath" + IFS=$IFS_old$ + + cp -f $tmpFile ${m_backuplistPath} + rm -f $tmpFile + + #写文件 + metainfo_file="${m_mountPath}${BACKUP}/snapshots/$m_uuid/$METAINFO" + #echo "metainfo_file="$metainfo_file + #第1行清空写 + #这里写的不是xml文件,是一个文本文件,这时候的状态是backup unfinished + echo "COMMENT=$m_comment" >$metainfo_file + echo "TIME=$m_time" >>$metainfo_file + echo "UUID=$m_uuid" >>$metainfo_file + echo "SIZE=$m_size" >>$metainfo_file + echo "STATE=$state" >>$metainfo_file + echo "TYPE=0" >>$metainfo_file + + backuping ${rootpath}/ $dst +} + +#返回值: +getLastUsefulBackupPointUuid() { + local xxx currentUuid foundComment + currentUuid="" + currentState=false + currentType=true + lastUsefulBackupPointUuid="" + foundComment=0 #是否发现了要修改的comment + lastbackupname="" + + #如果不定义IFS,则echo $line会去掉前后空格,导致写到文件中去时没有格式 + IFS_old=$IFS + IFS=$'\n' + while read line; do + #去除了前后空格 + xxx=$(echo "$line" | sed "s/^[ \t]*//g" | sed "s/[ \t]*$//g") + #echo "xxx: $xxx" + + if [[ "$xxx" =~ "" ]]; then + lastbackupname=$xxx + fi + + if [[ "$xxx" =~ "" ]]; then + currentUuid=$xxx + currentState=false + currentType=true + fi + + if [[ "$xxx" =~ "backup finished" ]]; then + #lastUsefulBackupPointUuid=$currentUuid + currentState=true + fi + + if [[ "$xxx" =~ "2" ]]; then + currentType=false + fi + + if [[ "$xxx" =~ "3" ]]; then + currentType=false + fi + + if [[ "$xxx" =~ "3" ]]; then + currentType=false + fi + + if [[ "$xxx" =~ "" ]]; then + if [ "$currentState" = true -a "$currentType" = true ]; then #"/backup"是不是一个独立的盘 + lastUsefulBackupPointUuid=$currentUuid + fi + fi + done <"$m_backuplistPath" + IFS=$IFS_old + + if [ "$lastUsefulBackupPointUuid" = "" ]; then + echo "can't find a useful backup for restoring" + exit 6 + fi + + lastUsefulBackupPointUuid=$(echo $lastUsefulBackupPointUuid | sed 's:::' | tr -d "\n") + lastUsefulBackupPointUuid=$(echo $lastUsefulBackupPointUuid | sed 's:.*::' | tr -d "\n") + #echo "lastUsefulBackupPointUuid=$lastUsefulBackupPointUuid" + lastbackupname=$(echo $lastbackupname | sed 's:::' | tr -d "\n") + lastbackupname=$(echo $lastbackupname | sed 's:.*::' | tr -d "\n") +} + +#see backup-auto/autobackup.cpp +restoreAuto() { #还原 + local xxx + local uuid + if [ $m_isFactory = false ]; then + #echo "lastUsefulBackupPointUuid=$lastUsefulBackupPointUuid" + if [ x"$m_default_uuid" != x"" ]; then + #m_default_uuid=$lastUsefulBackupPointUuid + uuid=$m_default_uuid + else + getLastUsefulBackupPointUuid + uuid=$lastUsefulBackupPointUuid + fi + else + uuid="{${factory_uuid}}" + fi + #写日志文件 + local m_time=$(date "+%y-%m-%d %H:%M:%S" | tr -d "\n") + writeLogFile "${m_time},${uuid},4,grub系统还原,,,${lastbackupname}" #grub时只有一键还原,没有增量还原 + + restoreDir="${m_mountPath}${BACKUP}/snapshots/$uuid" + + if [ ! -e "$restoreDir" ]; then + echo "full restore directory not exists!" + exit 7 #备份文件不存在,不能还原系统 + fi + + if [ "$uuid" = "{$auto_uuid}" ]; then + m_isRetainUserData=true + fi + + #0:backup 1:restore + #generateExcludeFile 1 + echo "Begin to restore efi directory..." >>$PLOGFILE + echo "Begin to restore efi directory..." + #额外排除目录或文件 + local excludes= + if [ -d ${restoreDir}/data/efi ]; then + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete ${restoreDir}/data/efi ${rootpath}/boot >/dev/null 2>>$PLOGFILE + if [ $? -ne 0 -a $? -ne 24 -a $? -ne 23 ]; then + echo "System restoring failed, please reboot your system!" + echo "System restoring failed, please reboot your system!" >>$PLOGFILE + exit 9 + fi + elif [ -d ${restoreDir}/data/boot/efi ]; then + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete ${restoreDir}/data/boot/efi ${rootpath}/boot >/dev/null 2>>$PLOGFILE + if [ $? -ne 0 -a $? -ne 24 -a $? -ne 23 ]; then + echo "System restoring failed, please reboot your system!" + echo "System restoring failed, please reboot your system!" >>$PLOGFILE + exit 9 + fi + fi + + echo "Begin to restore other directories..." + echo "Begin to restore other directories..." >>$PLOGFILE + #保留用户数据还原 + if [[ x${m_isRetainUserData} = x"true" ]]; then + # 用户数据目录或文件 + if [ -e "${rootpath}/var/lib/biometric-auth" ]; then + excludes="${excludes} --exclude=/var/lib/biometric-auth" + fi + if [ -e "${rootpath}/data/sec_storage_data" ]; then + excludes="${excludes} --exclude=/data/sec_storage_data" + fi + if [ -e "${rootpath}/etc/passwd" ]; then + excludes="${excludes} --exclude=/etc/passwd" + fi + if [ -e "${rootpath}/etc/shadow" ]; then + excludes="${excludes} --exclude=/etc/shadow" + fi + if [ -e "${rootpath}/etc/group" ]; then + excludes="${excludes} --exclude=/etc/group" + fi + if [ -e "${rootpath}/etc/gshadow" ]; then + excludes="${excludes} --exclude=/etc/gshadow" + fi + if [ -e "${rootpath}/etc/sudoers" ]; then + excludes="${excludes} --exclude=/etc/sudoers" + fi + excludes="${excludes} --exclude=/home --exclude=/root --exclude=/data/home --exclude=/data/root --exclude=/var/lib/AccountsService" + #下面是域用户相关信息,保留用户数据还原后不退域 + excludes="${excludes} --exclude=/etc/sssd --exclude=/var/lib/sss --exclude=/usr/share/sssd --exclude=/etc/ipa --exclude=/etc/krb5.keytab" + excludes="${excludes} --exclude=/etc/krb5.conf --exclude=/var/lib/ipa-client --exclude=/etc/nsswitch.conf --exclude=/etc/pam.d --exclude=/etc/hosts" + excludes="${excludes} --exclude=/etc/hostname --exclude=/etc/hedron --exclude=/etc/kcm --exclude=/usr/hedron/hedronagent --exclude=/etc/.kyinfo --exclude=/etc/LICENSE" + excludes="${excludes} --exclude=/etc/ssl/certs --exclude=/usr/share/ca-certificates --exclude=/etc/NetworkManager" + + #如果是990,排除/data;否则,排除/data/usershare + if [ x${is_990_9a0} == x"true" ]; then + excludes="${excludes} --exclude=/data" + else + excludes="${excludes} --exclude=/data/usershare" + fi + #如果是出厂备份的还原,还需要保留语言和时区配置 + if [[ ${uuid} = "{${factory_uuid}}" && x${is_990_9a0} != x"true" ]]; then + if [ -e "${rootpath}/etc/localtime" ]; then + excludes="${excludes} --exclude=/etc/localtime" + fi + if [ -e "${rootpath}/usr/share/zoneinfo" ]; then + excludes="${excludes} --exclude=/usr/share/zoneinfo" + fi + if [ -e "${rootpath}/etc/default/locale" ]; then + excludes="${excludes} --exclude=/etc/default/locale" + fi + if [ -e "${rootpath}/usr/share/i18n" ]; then + excludes="${excludes} --exclude=/usr/share/i18n" + fi + fi + fi + # 兼容以前的老备份数据,后面可以尝试去掉此条件的逻辑 + if [ ! -e "${restoreDir}/data/etc/uid_list" ]; then + excludes="${excludes} --exclude=/etc/uid_list" + fi + if [ ! -e "${restoreDir}/data/boot/efi" ]; then + excludes="${excludes} --exclude=/boot/efi" + fi + excludes="${excludes} --exclude=/var/log" + excludes="${excludes} --exclude=*/backup/snapshots" + #yi jian huan yuan + if [ ! -e "${restoreDir}/data/data" ]; then + #这两行要一致 + echo "rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete --exclude=/data ${excludes} --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath" >>$PLOGFILE + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete --exclude=/data ${excludes} --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath >/dev/null 2>>$PLOGFILE + else + #这两行要一致 + echo "rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete ${excludes} --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath" >>$PLOGFILE + rsync -avAXHr --no-inc-recursive --ignore-missing-args --delete ${excludes} --exclude-from $restoreDir/$PERSONAL_EXCLUDEFILE $restoreDir/data/ $rootpath >/dev/null 2>>$PLOGFILE + fi + + if [ $? -ne 0 -a $? -ne 24 -a $? -ne 23 ]; then + echo "System restoring failed, please reboot your system!" + echo "System restoring failed, please reboot your system!" >>$PLOGFILE + exit 9 + fi + + echo "restore other directories end" + echo "restore other directories end" >>$PLOGFILE + sync + #还原时清空目录 + #if [ -e "$rootpath/home/data" ]; then + # rm -r "$rootpath/home/data" ] + #fi + #mkdir -p "$rootpath/home/data" ] +} + +#----------------------------------------------------------------- +#see backup-auto/autobackup.cpp +updateBackupAutoFinishedState() { + echo "this is updateBackupAutoFinishedState" + +} + +#----------------------------------------------------------------- +#--------主程序从这里开始----------------------------------------- + +if [ "${rootpath}" = "/" ]; then + echo "This program is used in boot time" +# exit +fi + +getBackupInfo +#echo "m_restoreUuid="$m_restoreUuid +#echo "m_enabled="$m_enabled +#不加引号报错 +if [ "$m_restoreUuid" = "" ] || [ "$m_enabled" = "" ]; then + echo "bootinfo file is not correct!" + exit 3 +fi + +if [ $backupORrestore = "--autobackup" ]; then + mountBackup + mount >>$PLOGFILE + backupAuto #备份, grub时只有全盘备份,没有增量备份 + updateBackupAutoFinishedState + #umountBackup + + echo "This is autobackup" + +elif [ $backupORrestore = "--autorestore" ]; then + mountBackup + mount >>$PLOGFILE + restoreAuto #还原, grub时只有一键还原,没有增量还原 + #umountBackup + echo "This is autorestore" + +elif [ $backupORrestore = "--factorybackup" ]; then + m_isFactory=true + mountBackup + backupAuto #备份, grub时只有全盘备份,没有增量备份 + updateBackupAutoFinishedState + #umountBackup +elif [ $backupORrestore = "--restoreretainuserdata" ]; then + m_isRetainUserData=true + mountBackup + mount >>$PLOGFILE + restoreAuto +elif [ $backupORrestore = "--factoryrestore" ]; then + m_isFactory=true + mountBackup + mount >>$PLOGFILE + restoreAuto #还原, grub时只有一键还原,没有增量还原 + #umountBackup + + echo "This is factorybackup" +else + echo "Not correct command" + exit 18 +fi + +sync + +exit 0 diff --git a/backup-daemon/data/com.kylin.backup.conf b/backup-daemon/data/com.kylin.backup.conf new file mode 100755 index 0000000..1f83044 --- /dev/null +++ b/backup-daemon/data/com.kylin.backup.conf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/backup-daemon/data/com.kylin.backup.policy b/backup-daemon/data/com.kylin.backup.policy new file mode 100755 index 0000000..41cfdca --- /dev/null +++ b/backup-daemon/data/com.kylin.backup.policy @@ -0,0 +1,19 @@ + + + + + Backup Tools From KYLIN + www.kylinos.cn + + + Determine Recovery Access + 更改启动模式操作需要管理权限,请输入管理员密码进行身份认证 + + auth_admin + auth_admin + auth_admin_keep + + + diff --git a/backup-daemon/data/com.kylin.backup.service b/backup-daemon/data/com.kylin.backup.service new file mode 100755 index 0000000..46ec2c6 --- /dev/null +++ b/backup-daemon/data/com.kylin.backup.service @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=com.kylin.backup +Exec=/usr/bin/backup-daemon +User=root + diff --git a/backup-daemon/data/initramfs-tools/conf-hooks.d/kybackup b/backup-daemon/data/initramfs-tools/conf-hooks.d/kybackup new file mode 100755 index 0000000..e71684e --- /dev/null +++ b/backup-daemon/data/initramfs-tools/conf-hooks.d/kybackup @@ -0,0 +1 @@ +FRAMEBUFFER=y diff --git a/backup-daemon/data/initramfs-tools/hooks/kybackup-hooks b/backup-daemon/data/initramfs-tools/hooks/kybackup-hooks new file mode 100755 index 0000000..b81458a --- /dev/null +++ b/backup-daemon/data/initramfs-tools/hooks/kybackup-hooks @@ -0,0 +1,91 @@ +#!/bin/sh -e + +PREREQS="" + +prereqs() { echo "$PREREQS"; } + +case "$1" in + prereqs) + prereqs + exit 0 + ;; +esac + +# 上面的内容和initramfs脚本很像,可以不管。 +# hook脚本的功能是将一些文件放到initramfs文件里去。 +# 把该脚本放到 /usr/share/initramfs-tools/hooks/ 目录下,权限为 0755 +# 在更新initramfs时会自动执行该脚本,安装文件。 + +# 下面这一行是加载hook-functions里的一些功能,用到这个脚本来。 +# 这一行必须有 +# /usr/share/initramfs-tools/hook-functions定义了copy_exec +. /usr/share/initramfs-tools/hook-functions + +ExistCopyExec() +{ + if [ -f $1 ]; then + copy_exec $1 $2 + fi +} + +ExistCp() +{ + if [ -f $1 ]; then + cp $1 $2 + fi +} + +# 以添加rsync程序为例 +# 在系统中rsync在/usr/bin/ 目录下 +# 首先创建目录 /usr/bin/ ,一定要加上 $DESTDIR +# 然后用copy_exec 将系统中的 rsync 拷贝到 initramfs 的预期位置中。 +# 对于动态可执行文件, copy_exec 会将其依赖库也拷贝到 initramfs 中。 +# 使用 copy_exec 时,不要用 $DESTDIR +# 但是如果是拷贝普通文件,则用 cp ,并且还要使用 $DESTDIR ,例如 +# mkdir -p $DESTDIR/common/file +# cp /common/file/path $DESTDIR/common/file/ + +mkdir -p $DESTDIR/usr/bin +mkdir -p $DESTDIR/bin +mkdir -p $DESTDIR/etc +ExistCopyExec /etc/.bootinfo /etc +ExistCopyExec /etc/fstab /etc/fstab-backup +ExistCopyExec /usr/bin/rsync /usr/bin +###backup-auto-efi需要调用bc来计算磁盘用量 +ExistCopyExec /usr/bin/bc /usr/bin +###Qt program: ExistCopyExec /usr/bin/backup-auto /usr/bin +ExistCopyExec /usr/bin/backup-auto-efi /usr/bin +###Now backup-auto is identical to backup-auto-efi +ExistCopyExec /usr/bin/backup-auto /usr/bin +ExistCopyExec /usr/bin/mount_fstab_efi /usr/bin + +ExistCopyExec /bin/bash /bin +ExistCopyExec /bin/mkdir /bin +ExistCopyExec /bin/ls /bin +ExistCopyExec /bin/mount /bin +ExistCopyExec /bin/sync /bin +ExistCopyExec /sbin/reboot /bin +#fix wrong timezone in initrd(#) +#ExistCopyExec /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + +ExistCopyExec /usr/bin/kylin_activation_check /usr/bin +ExistCopyExec /usr/bin/gpg /usr/bin + +mkdir -p $DESTDIR//var/lib/dpkg/info +ExistCp /var/lib/dpkg/info/libkylin-activation.md5sums $DESTDIR/var/lib/dpkg/info + +mkdir -p $DESTDIR/etc/xdg/autostart +ExistCp /etc/xdg/autostart/kylin-notification.desktop $DESTDIR/etc/xdg/autostart + +mkdir -p $DESTDIR/etc/update-motd.d +ExistCp /etc/update-motd.d/99-kylin-verify-info $DESTDIR/etc/update-motd.d/ + +ExistCopyExec /usr/bin/kylin-verify /usr/bin +###打包很多库,包括gdk,内核会超过16M +###ExistCopyExec /usr/bin/kylin-verify-gui /usr/bin +ExistCopyExec /bin/bash /bin +ExistCp /var/lib/dpkg/info/kylin-verify.md5sums $DESTDIR/var/lib/dpkg/info/ +ExistCp /var/lib/dpkg/info/bash.md5sums $DESTDIR/var/lib/dpkg/info/ + +#fix wrong timezone in initrd(#316) +#ExistCp /usr/share/zoneinfo/Asia/Shanghai $DESTDIR/etc/localtime diff --git a/backup-daemon/data/initramfs-tools/scripts/local-bottom/kybackup b/backup-daemon/data/initramfs-tools/scripts/local-bottom/kybackup new file mode 100755 index 0000000..9c5b067 --- /dev/null +++ b/backup-daemon/data/initramfs-tools/scripts/local-bottom/kybackup @@ -0,0 +1,528 @@ +#!/bin/bash + +BACKUP_FLAG=backup +RESTORE_FLAG=restore +ROLLBACK_FLAG=rollback-backup +# 把这个文件放在 /usr/share/initramfs-tools/scripts/local-bottom/ 目录下。 +# 权限为 0755,在自动更新initrd时,这个脚本就被放到initrd中了,例如, +# update-initramfs -u +# 更新后,这个脚本将在initrd 运行到 local-bottom 阶段时自动执行。 +# 此时根分区已经挂载到了${rootmnt}下,可以直接访问该分区的文件。 + +# PREREQ指定需要在什么脚本之后执行(同目录下的),也就是指定该脚本的依赖,以确定执行顺序。 +# 如果没有依赖,就可以是空字符串。 +PREREQ="" + +prereqs() +{ + echo "$PREREQ" +} + +case $1 in + prereqs) + prereqs + exit 0 + ;; +esac + +# 上述部分是initramfs脚本的固定格式,不用动它。 +# 除非有依赖,才需要改 PREREQ 的值。 + +#. /scripts/security-functions +show_text_mesg() +{ + echo "$1" +} + +plymouth_is_running() +{ + if [ -e "/bin/plymouth" ] && /bin/plymouth --ping; then + return 0 + else + return 1 + fi +} + +show_message() +{ + if plymouth_is_running; then + /bin/plymouth message --text="$1" + show_text_mesg "$1" + else + show_text_mesg "$1" + fi +} + +clear_message() +{ + if plymouth_is_running; then + plymouth message --text="" + fi + clear +} + +isEnglish() +{ + LOCALFILE="${rootmnt}/etc/default/locale" + if [ ! -e "$LOCALFILE" ]; then + return 0 + fi + + localcontent=`grep -E "^LANG" $LOCALFILE | grep -i "zh_CN.UTF"` + + #echo "localcontent: $localcontent" + + if [ -z $localcontent ]; then + #echo "This is English" + return 1 + else + #echo "系统支持中文" + return 0 + fi +} + +isEnglish +v_isEnglish=$? + +myoutput() +{ + #echo "v_isEnglish is "$v_isEnglish + + #echo "first para: $1" + #echo "second para: $2" + + if [ $v_isEnglish -eq 1 ]; then + echo $1 + #plymouth message --text=$1 + #echo "English" + show_message "$1" + else + echo $2 + #plymouth message --text=$2 + #echo "中文" + show_message "$2" + fi +} + +loop_wait() +{ + #去掉原来的死循环,退出 + sleep 5 + reboot -f + #while(true) + #do + # sleep 1 + #done +} + +get_is_990_9a0() { + local ret=false + # 匹配 kirin 990 5g, kirin990, kirin 9006c + if egrep -qi 'kirin.?9[09]0' /proc/cpuinfo; then + ret=true + elif egrep -qi 'PANGU' /proc/cpuinfo; then + ret=true + + fi + echo $ret +} +is_990_9a0=$(get_is_990_9a0) + +factory_restore_warnning() +{ + if [ x${is_990_9a0} == x"true" ]; then + local is_sure= + while : + do + if plymouth_is_running; then + myoutput "You are doing factory restoration. Are you sure to continue? [y/n]" "正在准备进行出厂还原操作,是否继续?[y/n]" + is_sure=$(/bin/plymouth watch-keystroke) + echo is_sure=$is_sure + case $is_sure in + y|Y) + break;; + n|N) + # 注意,reboot重启不了,可用reboot -f + reboot -f;; + *) ;; + esac + fi + done + fi +} + +factory_restore_warnning_again() +{ + if [ x${is_990_9a0} == x"true" ]; then + local is_sure= + while : + do + if plymouth_is_running; then + myoutput "Please confirm again whether to restore the system to factory defaults. Are you sure to continue? [y/n]" "请再一次确认是否进行系统出厂还原,是否继续?[y/n]" + is_sure=$(/bin/plymouth watch-keystroke) + echo is_sure=$is_sure + case $is_sure in + y|Y) + break;; + n|N) + # 注意,reboot重启不了,可用reboot -f + reboot -f;; + *) ;; + esac + fi + done + fi +} + +restore_warnning() +{ + local is_sure= + while : + do + if plymouth_is_running; then + if [ x"$FACTORY_RESTORE" = "xy" ]; then + myoutput "This operation will delete all data and restore system to factory default state, data may be lost. Are you sure to continue? [y/n]" "本操作将会删除所有用户数据并恢复系统到出厂状态,有数据丢失风险,是否继续?[y/n]" + else + myoutput "This operation may delete some data, which may cause data loss. Are you sure to continue? [y/n]" "此操作可能会删除一些数据,造成丢失数据风险,是否继续?[y/n]" + fi + is_sure=$(/bin/plymouth watch-keystroke) + echo is_sure=$is_sure + case $is_sure in + y|Y) + break;; + n|N) + # 注意,reboot重启不了,可用reboot -f + reboot -f;; + *) ;; + esac + fi + done +} + +display_result() +{ + #myoutput "返回值: $RET" + #sleep 3 + RET=$1 + + if [ $RET -eq 1 ]; then + myoutput "Can't find /etc/.bootinfo." "没有找到/etc/.bootinfo" + loop_wait + elif [ $RET -eq 2 ]; then + myoutput "Can't open /etc/.bootinfo." "无法打开/etc/.bootinfo" + loop_wait + elif [ $RET -eq 3 ]; then + myoutput "/etc/.bootinfo is not correct." "/etc/.bootinfo不正确" + loop_wait + elif [ $RET -eq 4 ]; then + myoutput "The backup disk space is not enough, please delete unnecessary backups." "备份分区空间不足,请删除过期或者不需要的备份" + loop_wait + elif [ $RET -eq 5 ]; then + myoutput "The backup disk must be /backup" "备份还原分区路径不是/backup" + loop_wait + elif [ $RET -eq 6 ]; then + myoutput "There are not any good backups,and you can't restore the system." "没有有效的备份,不能还原系统" + loop_wait + elif [ $RET -eq 7 ]; then + myoutput "The backup does not exist, and you can't restore it." "备份文件不存在,不能还原系统" + loop_wait + elif [ $RET -eq 8 ]; then + myoutput "Can't start the restore process, please redo it." "不能启动还原进程,请重新还原系统" + loop_wait + elif [ $RET -eq 9 ]; then + myoutput "Failed to restore the system, please redo it." "还原失败,请重新还原系统" + loop_wait + elif [ $RET -eq 10 ]; then + myoutput "Can't start the mount process for backup." "不能启动备份还原分区安装进程,请重新启动系统" + loop_wait + elif [ $RET -eq 11 ]; then + myoutput "Failed to wait for the process for backup." "备份还原分区安装进程等待结束时出错,请重新启动系统" + loop_wait + elif [ $RET -eq 12 ]; then + myoutput "The backup disk does not exist or it is not mounted." "备份还原分区不存在或没有mount" + loop_wait + elif [ $RET -eq 13 ]; then + myoutput "Can't start the backup process." "不能启动备份进程,请重新备份系统" + loop_wait + elif [ $RET -eq 14 ]; then + myoutput "Failed to backup the system." "备份失败,请重新备份系统" + loop_wait + elif [ $RET -eq 15 ]; then + myoutput "The usage of mount_fstab is not correct." "mount_fstab用法不正确,无法安装系统" + loop_wait + elif [ $RET -eq 16 ]; then + myoutput "Failed to backup the system because /etc/fstab does not exist." "/etc/fstab不存在,无法备份系统" + loop_wait + elif [ $RET -eq 17 ]; then + myoutput "Can't open /etc/fstab." "/etc/fstab打开失败,无法备份系统" + loop_wait + elif [ $RET -eq 18 ]; then + myoutput "Wrong arguments." "调用backup-auto-efi 参数错误" + loop_wait + elif [ $RET -eq 19 ]; then + myoutput "Wrong with caculate disk size." "计算磁盘大小出错" + loop_wait + elif [ $RET -eq 20 ]; then + myoutput "Could not create /backup in initrd!" "无法在initrd中创建backup目录" + loop_wait + elif [ $RET -eq 21 ]; then + myoutput "Could not mount backup partition in initrd!" "无法挂载备份还原分区" + loop_wait + elif [ $RET -eq 22 ]; then + myoutput "Could not create log directory in /backup!" "无法创建日志目录" + loop_wait + elif [ $RET -eq 23 ]; then + myoutput "Could not create log file!" "无法创建日志文件" + loop_wait + fi +} + +# 需要先指定BACKUP_FLAG,例如设置为 BACKUP_FLAG=backup,那么内核参数就可以选择下述两种方式的一种了 +# 1. backup 只要有backup,就表示需要备份 +# 2. backup=0 或者 backup=1 0表示不用备份,1表示需要备份。 +# 当然,还可以选择别的字符串做关键字,但一定要指定BACKUP_FLAG内容。 +#BACKUP_FLAG= +NEED_BACKUP= +NEED_RESTORE= +NEED_ROLLBACK= +NEED_RETAIN_USERDATA= +# 针对 BACKUP_FLAG 为简单字符串(非键值对)的情况,可以这样获取内核参数。 +for x in $(cat /proc/cmdline); do + case $x in + backup) + NEED_BACKUP=y + ;; + restore) + NEED_RESTORE=y + ;; + restore-retain-userdata) + NEED_RESTORE=y + NEED_RETAIN_USERDATA=y + ;; + rollback-backup) + NEED_ROLLBACK=y + ;; + factory-restore) + NEED_RESTORE=y + FACTORY_RESTORE=y + ;; + *) + ;; + esac +done + +#for x in $(cat /proc/cmdline); do +# if [ "$x" = "$BACKUP_FLAG" ]; then +# NEED_BACKUP=y +# fi +#done +# +#for x in $(cat /proc/cmdline); do +# if [ "$x" = "$RESTORE_FLAG" ]; then +# NEED_RESTORE=y +# fi +#done +#for x in $(cat /proc/cmdline); do +# if [ "$x" = "$ROLLBACK_FLAG" ]; then +# NEED_ROLLBACK=y +# fi +#done + +#for x in $(cat /proc/cmdline); do +# if [ "$x" = "$BACKUP_FLAG" ]; then +# NEED_BACKUP=y +# fi +#done +# +#for x in $(cat /proc/cmdline); do +# if [ "$x" = "$RESTORE_FLAG" ]; then +# NEED_RESTORE=y +# fi +#done +#for x in $(cat /proc/cmdline); do +# if [ "$x" = "$ROLLBACK_FLAG" ]; then +# NEED_ROLLBACK=y +# fi +#done + +# 然后就可以根据 $NEED_BACKUP 来进行操作了。 +# 如果没有发现需要备份的标记,直接退出脚本,继续启动系统。 + +if [ "$NEED_BACKUP" = "y" ]; then + mkdir /backup + #useless: . /scripts/backup #就是本目录下的backup + #useless: mount_fstab #定义在backup脚本中 + + #如果没有${rootmnt}/etc/fstab,则系统坏了,只能还原而不能备份. + if [ ! -e ${rootmnt}/etc/fstab ]; then + if [ ! -e /etc/fstab-backup ]; then + #myoutput "The system has been destroyed and can not be backuped." + myoutput "/etc/fstab does not exist." "/etc/fstab不存在,无法备份." + loop_wait + fi + fi + if [ ! -e ${rootmnt}/etc/.bootinfo ]; then + if [ ! -e /etc/.bootinfo ]; then + #myoutput "The system has been destroyed and you can restore it in the next version." + myoutput "/etc/.bootinfo does not exist." "/etc/.bootinfo不存在,无法备份." + loop_wait + fi + fi + + mount_fstab_efi ${rootmnt} mount + myoutput "Backuping the system, please wait for a moment." "正在备份系统" + #myoutput "Backuping the system, please wait for a moment." + backup-auto-efi --autobackup ${rootmnt} /backup + + RET=$? + display_result $RET + + myoutput "The system has been backuped." "系统备份完成" + #myoutput "The system has been backuped." + #mount_fstab ${rootmnt} umount #because root + sleep 3 + reboot -f +fi + +if [ "$NEED_RESTORE" = "y" ]; then + factory_restore_warnning + restore_warnning + factory_restore_warnning_again + #如果没有${rootmnt}/etc/fstab,则系统坏了,应该能还原,但需要在下一个版本中支持。 + if [ ! -e ${rootmnt}/etc/fstab ]; then + if [ ! -e /etc/fstab-backup ]; then + #myoutput "The system has been destroyed and you can restore it in the next version." + myoutput "/etc/fstab does not exist." "/etc/fstab不存在,无法还原." + loop_wait + fi + fi + if [ ! -e ${rootmnt}/etc/.bootinfo ]; then + if [ ! -e /etc/.bootinfo ]; then + #myoutput "The system has been destroyed and you can restore it in the next version." + myoutput "/etc/.bootinfo does not exist." "/etc/.bootinfo不存在,无法还原." + loop_wait + fi + fi + + mount_fstab_efi ${rootmnt} mount + mkdir /backup + myoutput "Restoring the system, please wait for a moment." "正在还原系统" + #myoutput "Restoring the system, please wait for a moment." + mount -o remount,rw ${rootmnt} + if [ $? -ne 0 ];then + myoutput "Could not mount filesystem with rw arguments." "错误:无法以读写方式挂载根文件系统." + loop_wait + + fi + #. /scripts/backup + if [ x"$NEED_RETAIN_USERDATA" = "xy" ]; then + backup-auto-efi --restoreretainuserdata ${rootmnt} /backup + elif [ x"$FACTORY_RESTORE" = "xy" ];then + backup-auto-efi --factoryrestore ${rootmnt} /backup + else + backup-auto-efi --autorestore ${rootmnt} /backup + fi + + RET=$? + display_result $RET + + if [ ! -e ${rootmnt}/proc ]; then + mkdir ${rootmnt}/proc + fi + + if [ ! -e ${rootmnt}/sys ]; then + mkdir ${rootmnt}/sys + fi + + if [ ! -e ${rootmnt}/dev ]; then + mkdir ${rootmnt}/dev + fi + + if [ ! -e ${rootmnt}/run ]; then + mkdir ${rootmnt}/run + fi + + if [ ! -e ${rootmnt}/backup ]; then + mkdir ${rootmnt}/backup + fi + + myoutput "The system has been restored." "系统还原完成" + #myoutput "The system has been restored." + #/scripts/mount_fstab ${rootmnt} umount #because root + sleep 3 + reboot -f +fi + + +if [ "$NEED_ROLLBACK" = "y" ]; then + factory_restore_warnning + restore_warnning + factory_restore_warnning_again + #如果没有${rootmnt}/etc/fstab,则系统坏了,应该能还原,但需要在下一个版本中支持。 + if [ ! -e ${rootmnt}/etc/fstab ]; then + if [ ! -e /etc/fstab-backup ]; then + #myoutput "The system has been destroyed and you can restore it in the next version." + myoutput "/etc/fstab does not exist." "/etc/fstab不存在,无法还原." + loop_wait + fi + fi + if [ ! -e ${rootmnt}/etc/.bootinfo ]; then + if [ ! -e /etc/.bootinfo ]; then + #myoutput "The system has been destroyed and you can restore it in the next version." + myoutput "/etc/.bootinfo does not exist." "/etc/.bootinfo不存在,无法还原." + loop_wait + fi + fi + + mount_fstab_efi ${rootmnt} mount + mkdir /backup + myoutput "Restoring the system, please wait for a moment." "正在还原系统" + #myoutput "Restoring the system, please wait for a moment." + mount -o remount,rw ${rootmnt} + if [ $? -ne 0 ];then + myoutput "Could not mount filesystem with rw arguments." "错误:无法以读写方式挂载根文件系统." + loop_wait + + fi + #. /scripts/backup + backup-auto-efi --autorestore ${rootmnt} /backup {01234567-0123-0123-0123-0123456789ab} + + RET=$? + display_result $RET + + if [ ! -e ${rootmnt}/proc ]; then + mkdir ${rootmnt}/proc + fi + + if [ ! -e ${rootmnt}/sys ]; then + mkdir ${rootmnt}/sys + fi + + if [ ! -e ${rootmnt}/dev ]; then + mkdir ${rootmnt}/dev + fi + + if [ ! -e ${rootmnt}/run ]; then + mkdir ${rootmnt}/run + fi + + if [ ! -e ${rootmnt}/backup ]; then + mkdir ${rootmnt}/backup + fi + + myoutput "The system has been rollbacked." "系统回滚完成" + reboot -f +fi + +# 否则 +# TODO 执行mount_fstab_efi ${rootmnt} mount备份操作 + +# 此时根分区已经挂载在 ${rootmnt} 处了,直接使用 ${rootmnt} 就可以了。 +# 例如查看lsb-release文件 +# cat ${rootmnt}/etc/lsb-release +# 如果想要根据 UUID 或 LABEL 来确定分区,还可以执行blkid命令。例如, +# /sbin/blkid -U 该命令返回UUID对应的分区设备 +# /sbin/blkid -L