Import Upstream version 4.1.0.1

This commit is contained in:
KevinDuan 2022-11-01 10:40:05 +08:00
parent 3a6a2d24c3
commit 40baa08d7a
261 changed files with 35149 additions and 0 deletions

7
README.md Executable file
View File

@ -0,0 +1,7 @@
# Kylin Backup Tools
1、对备份还原工具4.0.13版本进行了重构
2、界面和流程对比4.0.13版本全都发送了变化
3、适配UKui3.1界面风格
4、新增了一些功能或对原功能进行了优化

97
backup-daemon/backup-daemon.pro Executable file
View File

@ -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

View File

@ -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 <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* 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<YourObjectType *>(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<YourObjectType *>(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;
}

View File

@ -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 <QtCore/QObject>
#include <QtDBus/QtDBus>
#include "mybackupmanager.h"
QT_BEGIN_NAMESPACE
class QByteArray;
template<class T> class QList;
template<class Key, class Value> 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", ""
" <interface name=\"com.kylin.backup.manager\">\n"
" <signal name=\"sendEnvCheckResult\">\n"
" <arg direction=\"out\" type=\"i\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"sendStartBackupResult\">\n"
" <arg direction=\"out\" type=\"i\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"sendBackupResult\">\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"sendRate\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </signal>\n"
" <signal name=\"sendRestoreResult\">\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"sendDeleteResult\">\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"sendGhostBackupResult\">\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"backupFinished\">\n"
" <arg direction=\"out\" type=\"b\" name=\"result\"/>\n"
" </signal>\n"
" <signal name=\"progress\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </signal>\n"
" <method name=\"Mount_backup_partition\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </method>\n"
" <method name=\"umountBackupPartition\">\n"
" <arg direction=\"out\" type=\"b\"/>\n"
" </method>\n"
" <method name=\"autoBackUpForSystemUpdate_noreturn\">\n"
" <arg direction=\"in\" type=\"s\" name=\"autobackup_name\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"create_note\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"inc_note\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"frontUserName\"/>\n"
" <arg direction=\"in\" type=\"i\" name=\"frontUid\"/>\n"
" </method>\n"
" <method name=\"getBackupCommentForSystemUpdate\">\n"
" <arg direction=\"out\" type=\"s\"/>\n"
" <arg direction=\"out\" type=\"s\" name=\"state\"/>\n"
" </method>\n"
" <method name=\"getBackupState\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <arg direction=\"out\" type=\"b\" name=\"isActive\"/>\n"
" </method>\n"
" <method name=\"checkEnv\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <annotation value=\"BackupWrapper\" name=\"org.qtproject.QtDBus.QtTypeName.In0\"/>\n"
" <arg direction=\"in\" type=\"a(i)\" name=\"backupWrapper\"/>\n"
" </method>\n"
" <method name=\"goBackup\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <annotation value=\"BackupWrapper\" name=\"org.qtproject.QtDBus.QtTypeName.In0\"/>\n"
" <arg direction=\"in\" type=\"a(i)\" name=\"backupWrapper\"/>\n"
" </method>\n"
" <method name=\"goRestore\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <annotation value=\"BackupWrapper\" name=\"org.qtproject.QtDBus.QtTypeName.In0\"/>\n"
" <arg direction=\"in\" type=\"a(i)\" name=\"backupWrapper\"/>\n"
" </method>\n"
" <method name=\"deleteBackupPoint\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <annotation value=\"BackupWrapper\" name=\"org.qtproject.QtDBus.QtTypeName.In0\"/>\n"
" <arg direction=\"in\" type=\"a(i)\" name=\"backupWrapper\"/>\n"
" </method>\n"
" <method name=\"ghostBackup\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" <annotation value=\"BackupWrapper\" name=\"org.qtproject.QtDBus.QtTypeName.In0\"/>\n"
" <arg direction=\"in\" type=\"a(i)\" name=\"backupWrapper\"/>\n"
" </method>\n"
" <method name=\"cancel\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
" </method>\n"
" </interface>\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

View File

@ -0,0 +1,70 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="com.kylin.backup.manager">
<signal name="sendEnvCheckResult">
<arg name="result" type="i" direction="out"/>
</signal>
<signal name="sendStartBackupResult">
<arg name="result" type="i" direction="out"/>
</signal>
<signal name="sendBackupResult">
<arg name="result" type="b" direction="out"/>
</signal>
<signal name="sendRate">
<arg type="i" direction="out"/>
<arg type="i" direction="out"/>
</signal>
<signal name="sendRestoreResult">
<arg name="result" type="b" direction="out"/>
</signal>
<signal name="sendDeleteResult">
<arg name="result" type="b" direction="out"/>
</signal>
<signal name="sendGhostBackupResult">
<arg name="result" type="b" direction="out"/>
</signal>
<method name="Mount_backup_partition">
<arg type="i" direction="out"/>
</method>
<method name="autoBackUpForSystemUpdate_noreturn">
<arg name="autobackup_name" type="s" direction="in"/>
<arg name="create_note" type="s" direction="in"/>
<arg name="inc_note" type="s" direction="in"/>
<arg name="frontUserName" type="s" direction="in"/>
<arg name="frontUid" type="i" direction="in"/>
</method>
<method name="getBackupCommentForSystemUpdate">
<arg type="s" direction="out"/>
<arg name="state" type="s" direction="out"/>
</method>
<method name="getBackupState">
<arg type="i" direction="out"/>
<arg name="isActive" type="b" direction="out"/>
</method>
<method name="checkEnv">
<arg type="i" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="BackupWrapper"/>
<arg name="backupWrapper" type="a(i)" direction="in"/>
</method>
<method name="goBackup">
<arg type="i" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="BackupWrapper"/>
<arg name="backupWrapper" type="a(i)" direction="in"/>
</method>
<method name="goRestore">
<arg type="i" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="BackupWrapper"/>
<arg name="backupWrapper" type="a(i)" direction="in"/>
</method>
<method name="deleteBackupPoint">
<arg type="i" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="BackupWrapper"/>
<arg name="backupWrapper" type="a(i)" direction="in"/>
</method>
<method name="ghostBackup">
<arg type="i" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="BackupWrapper"/>
<arg name="backupWrapper" type="a(i)" direction="in"/>
</method>
</interface>
</node>

View File

@ -0,0 +1,484 @@
#include "customizedatabackupproxy.h"
#include <QStorageInfo>
#include <QDateTime>
#include <QDebug>
#include <kysec/status.h>
#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 truefalse
*/
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();
}
}

View File

@ -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

View File

@ -0,0 +1,240 @@
#include "customizeghostImageproxy.h"
#include <QStorageInfo>
#include <QFileInfo>
#include <QDateTime>
#include <QTimer>
#include <QDebug>
#include <kysec/status.h>
#include <unistd.h>
#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,truefalse
* @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;
}

View File

@ -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

View File

@ -0,0 +1,400 @@
#include "customizesystembackupproxy.h"
#include <QStorageInfo>
#include <QDateTime>
#include <QDebug>
#include <QTimer>
#include <kysec/status.h>
#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();
}
}

View File

@ -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

View File

@ -0,0 +1,343 @@
#include "customizesystemrestoreproxy.h"
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include <unistd.h>
#include <sys/reboot.h>
#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";
}

View File

@ -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

1
backup-daemon/data/README Executable file
View File

@ -0,0 +1 @@
backup-auto与backup-auto-efi为同一个程序用来替换backup-auto/backup-auto后者使用Qt程序会导致/boot/内核超过16M.

1053
backup-daemon/data/backup-auto Executable file

File diff suppressed because it is too large Load Diff

1231
backup-daemon/data/backup-auto-efi Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the service -->
<policy user="root">
<allow own="com.kylin.backup"/>
<allow send_interface="com.kylin.backup.manager" />
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow send_interface="com.kylin.backup.manager"/>
<allow send_destination="com.kylin.backup"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="com.kylin.backup"
send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<vendor>Backup Tools From KYLIN</vendor>
<vendor_url>www.kylinos.cn</vendor_url>
<action id="com.kylin.backup.manager">
<description>Determine Recovery Access</description>
<message>更改启动模式操作需要管理权限,请输入管理员密码进行身份认证</message>
<defaults>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@ -0,0 +1,5 @@
[D-BUS Service]
Name=com.kylin.backup
Exec=/usr/bin/backup-daemon
User=root

View File

@ -0,0 +1 @@
FRAMEBUFFER=y

View File

@ -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

View File

@ -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 backupsand 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> 该命令返回UUID对应的分区设备
# /sbin/blkid -L <LABEL> 该命令返回LABEL对应的分区设备
# 如果需要安装一些命令需要写一个initramfs的hook脚本。
exit 0

View File

@ -0,0 +1,237 @@
#!/bin/bash
BACKUP_FLAG=backup
RESTORE_FLAG=restore
# 把这个文件放在 /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 的值。
display_result()
{
#plymouth message --text="返回值: $RET"
#sleep 3
if [ $RET -eq 200 ]; then
plymouth message --text="没有找到备份还原配置文件/etc/.bootinfo"
sleep 3
reboot
elif [ $RET -eq 201 ]; then
plymouth message --text="无法打开备份还原配置文件/etc/.bootinfo"
sleep 3
reboot
elif [ $RET -eq 202 ]; then
plymouth message --text="备份还原配置文件内容不对/etc/.bootinfo"
sleep 3
reboot
elif [ $RET -eq 100 ]; then
plymouth message --text="备份分区空间不足,请删除过期或者不需要的备份"
sleep 3
reboot
elif [ $RET -eq 101 ]; then
plymouth message --text="备份还原分区路径不是/backup"
sleep 3
reboot
elif [ $RET -eq 300 ]; then
plymouth message --text="没有有效的备份,不能还原系统"
sleep 3
reboot
elif [ $RET -eq 301 ]; then
plymouth message --text="备份文件不存在,不能还原系统"
sleep 3
reboot
elif [ $RET -eq 302 ]; then
plymouth message --text="不能启动还原进程,请重新还原系统"
sleep 3
reboot
elif [ $RET -eq 303 ]; then
plymouth message --text="还原失败,请重新还原系统"
sleep 3
reboot
elif [ $RET -eq 401 ]; then
plymouth message --text="不能启动备份还原分区安装进程,请重新启动系统"
sleep 3
reboot
elif [ $RET -eq 402 ]; then
plymouth message --text="备份还原分区安装进程等待结束时出错,请重新启动系统"
sleep 3
reboot
elif [ $RET -eq 403 ]; then
plymouth message --text="备份还原分区不存在或没有mount"
sleep 3
reboot
elif [ $RET -eq 501 ]; then
plymouth message --text="不能启动备份进程,请重新备份系统"
sleep 3
reboot
elif [ $RET -eq 502 ]; then
plymouth message --text="备份失败,请重新备份系统"
sleep 3
reboot
elif [ $RET -eq 700 ]; then
plymouth message --text="mount_fstab用法不正确无法安装系统"
sleep 3
reboot
elif [ $RET -eq 701 ]; then
plymouth message --text="/etc/fstab不存在无法备份系统"
sleep 3
reboot
elif [ $RET -eq 702 ]; then
plymouth message --text="/etc/fstab打开失败无法备份系统"
sleep 3
reboot
fi
}
# 需要先指定BACKUP_FLAG例如设置为 BACKUP_FLAG=backup那么内核参数就可以选择下述两种方式的一种了
# 1. backup 只要有backup就表示需要备份
# 2. backup=0 或者 backup=1 0表示不用备份1表示需要备份。
# 当然还可以选择别的字符串做关键字但一定要指定BACKUP_FLAG内容。
#BACKUP_FLAG=
NEED_BACKUP=
NEED_RESTORE=
# 针对 BACKUP_FLAG 为简单字符串(非键值对)的情况,可以这样获取内核参数。
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
# 然后就可以根据 $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
#plymouth message --text="The system has been destroyed and can not be backuped."
plymouth message --text="/etc/fstab不存在无法备份."
sleep 3
reboot
exit 0
fi
if [ ! -e ${rootmnt}/etc/.bootinfo ]; then
#plymouth message --text="The system has been destroyed and you can restore it in the next version."
plymouth message --text="/etc/.bootinfo不存在无法备份."
sleep 3
reboot
exit 0
fi
mount_fstab_efi ${rootmnt} mount
plymouth message --text="正在备份系统"
#plymouth message --text="Backuping the system, please wait for a moment."
backup-auto-efi --autobackup ${rootmnt} /backup
RET=$?
display_result $RET
plymouth message --text="系统备份完成"
#plymouth message --text="The system has been backuped."
#mount_fstab ${rootmnt} umount #because root
sleep 3
reboot
exit 0
fi
if [ "$NEED_RESTORE" = "y" ]; then
#如果没有${rootmnt}/etc/fstab则系统坏了应该能还原但需要在下一个版本中支持。
if [ ! -e ${rootmnt}/etc/fstab ]; then
#plymouth message --text="The system has been destroyed and you can restore it in the next version."
plymouth message --text="/etc/fstab不存在无法还原."
sleep 3
reboot
exit 0
fi
if [ ! -e ${rootmnt}/etc/.bootinfo ]; then
#plymouth message --text="The system has been destroyed and you can restore it in the next version."
plymouth message --text="/etc/.bootinfo不存在无法还原."
sleep 3
reboot
exit 0
fi
mount_fstab_efi ${rootmnt} mount
mkdir /backup
plymouth message --text="正在还原系统"
#plymouth message --text="Restoring the system, please wait for a moment."
mount -o remount,rw ${rootmnt}
#. /scripts/backup
backup-auto-efi --autorestore ${rootmnt} /backup
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
plymouth message --text="系统还原完成"
#plymouth message --text="The system has been restored."
#/scripts/mount_fstab ${rootmnt} umount #because root
sleep 3
reboot
exit 0
fi
# 否则
# TODO 执行备份操作
# 此时根分区已经挂载在 ${rootmnt} 处了,直接使用 ${rootmnt} 就可以了。
# 例如查看lsb-release文件
# cat ${rootmnt}/etc/lsb-release
# 如果想要根据 UUID 或 LABEL 来确定分区还可以执行blkid命令。例如
# /sbin/blkid -U <UUID> 该命令返回UUID对应的分区设备
# /sbin/blkid -L <LABEL> 该命令返回LABEL对应的分区设备
# 如果需要安装一些命令需要写一个initramfs的hook脚本。
exit 0

View File

@ -0,0 +1,230 @@
#!/bin/bash
BACKUP_FLAG=backup
RESTORE_FLAG=restore
# 把这个文件放在 /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 的值。
display_result()
{
if [ $RET -eq 200 ]; then
plymouth message --text="Can't find /etc/.bootinfo."
sleep 3
reboot
elif [ $RET -eq 201 ]; then
plymouth message --text="Can't open /etc/.bootinfo."
sleep 3
reboot
elif [ $RET -eq 202 ]; then
plymouth message --text="/etc/.bootinfo is not correct."
sleep 3
reboot
elif [ $RET -eq 100 ]; then
plymouth message --text="The backup disk space is not enough, please delete unnecessary backups."
sleep 3
reboot
elif [ $RET -eq 101 ]; then
plymouth message --text="The backup disk should be /backup"
sleep 3
reboot
elif [ $RET -eq 300 ]; then
plymouth message --text="There are not any good backupsand you can't restore the system."
sleep 3
reboot
elif [ $RET -eq 301 ]; then
plymouth message --text="There is not the backup directory of your selection, and you can't restore it."
sleep 3
reboot
elif [ $RET -eq 302 ]; then
plymouth message --text="The restore process can't be started."
sleep 3
reboot
elif [ $RET -eq 303 ]; then
plymouth message --text="Failed to restore the system, please redo it."
sleep 3
reboot
elif [ $RET -eq 401 ]; then
plymouth message --text="The mount process for backup disk can't be started."
sleep 3
reboot
elif [ $RET -eq 402 ]; then
plymouth message --text="Failed to wait for the process for backup disk."
sleep 3
reboot
elif [ $RET -eq 403 ]; then
plymouth message --text="The backup disk does not exist or it is not mounted."
sleep 3
reboot
elif [ $RET -eq 501 ]; then
plymouth message --text="Can't start the backup process."
sleep 3
reboot
elif [ $RET -eq 502 ]; then
plymouth message --text="Failed to backup the system."
sleep 3
reboot
elif [ $RET -eq 700 ]; then
plymouth message --text="The usage of mount_fstab is not correct."
sleep 3
reboot
elif [ $RET -eq 701 ]; then
plymouth message --text="Failed to backup the system because there is not /etc/fstab."
sleep 3
reboot
elif [ $RET -eq 702 ]; then
plymouth message --text="Can't to open /etc/fstab."
sleep 3
reboot
fi
}
# 需要先指定BACKUP_FLAG例如设置为 BACKUP_FLAG=backup那么内核参数就可以选择下述两种方式的一种了
# 1. backup 只要有backup就表示需要备份
# 2. backup=0 或者 backup=1 0表示不用备份1表示需要备份。
# 当然还可以选择别的字符串做关键字但一定要指定BACKUP_FLAG内容。
#BACKUP_FLAG=
NEED_BACKUP=
NEED_RESTORE=
# 针对 BACKUP_FLAG 为简单字符串(非键值对)的情况,可以这样获取内核参数。
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
# 然后就可以根据 $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
plymouth message --text="The system has been destroyed and can not be backuped."
sleep 3
reboot
exit 0
fi
if [ ! -e ${rootmnt}/etc/.bootinfo ]; then
plymouth message --text="The system has been destroyed and you can restore it in the next version."
sleep 3
reboot
exit 0
fi
mount_fstab_efi ${rootmnt} mount
#plymouth message --text="正在备份系统"
plymouth message --text="Backuping the system, please wait for a moment."
backup-auto-efi --autobackup ${rootmnt} /backup
RET=$?
display_result $RET
#plymouth message --text="系统备份完成"
plymouth message --text="The system has been backuped."
#mount_fstab ${rootmnt} umount #because root
sleep 3
reboot
exit 0
fi
if [ "$NEED_RESTORE" = "y" ]; then
#如果没有${rootmnt}/etc/fstab则系统坏了应该能还原但需要在下一个版本中支持。
if [ ! -e ${rootmnt}/etc/fstab ]; then
plymouth message --text="The system has been destroyed and you can restore it in the next version."
sleep 3
reboot
exit 0
fi
if [ ! -e ${rootmnt}/etc/.bootinfo ]; then
plymouth message --text="The system has been destroyed and you can restore it in the next version."
sleep 3
reboot
exit 0
fi
mount_fstab_efi ${rootmnt} mount
mkdir /backup
#plymouth message --text="正在还原系统"
plymouth message --text="Restoring the system, please wait for a moment."
mount -o remount,rw ${rootmnt}
#. /scripts/backup
backup-auto-efi --autorestore ${rootmnt} /backup
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
#plymouth message --text="系统还原完成"
plymouth message --text="The system has been restored."
#/scripts/mount_fstab ${rootmnt} umount #because root
sleep 3
reboot
exit 0
fi
# 否则
# TODO 执行备份操作
# 此时根分区已经挂载在 ${rootmnt} 处了,直接使用 ${rootmnt} 就可以了。
# 例如查看lsb-release文件
# cat ${rootmnt}/etc/lsb-release
# 如果想要根据 UUID 或 LABEL 来确定分区还可以执行blkid命令。例如
# /sbin/blkid -U <UUID> 该命令返回UUID对应的分区设备
# /sbin/blkid -L <LABEL> 该命令返回LABEL对应的分区设备
# 如果需要安装一些命令需要写一个initramfs的hook脚本。
exit 0

View File

@ -0,0 +1,124 @@
#!/bin/bash
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
}
findmnt_dev()
{
fstab_path=$1
target_dir=$2
cat "$fstab_path" | grep -Ev "^#" | awk '{print $1 " " $2 " " $3 " " $4 " " $5 " " $6}' |
while read device mntdir fstype options dump passno ;
do
if [ "$mntdir" = "$target_dir" ] ; then
parse_device "$device"
break
fi
done
}
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
}
parse_options(){
options=$1
optstr="showthrough bootwait nobootwait timeout ro"
echo "$options" | awk -v optstr="$optstr" 'BEGIN{FS=","; split(optstr, opts, " ");} {
result="";
for( i=1; i<=NF; i++ ){
flag=0;
for( opt in opts ){
if( $i == opts[opt] ){
flag=1;
break;
}
}
if( flag == 1 )
continue;
if( i==1 )
result=$i;
else
result=result","$i;
}
print result
}'
}
is_system_partition(){
mntdir=$1
sys_parts="/boot /home /var /usr /srv /opt /usr/local" # files under /tmp is temporary, need to remove
echo $sys_parts | grep -Eq "(^| )"$mntdir"( |$)" && echo "yes" || echo "no"
}
mount_fstab(){
fstab_path=${rootmnt}/etc/fstab
if [ ! -e "$fstab_path" ]; then
fstab_path=/etc/fstab-backup
fi
echo "mount $fstab_path begin"
cat "$fstab_path" | grep -Ev "^#" | awk '{print $1 " " $2 " " $3 " " $4 " " $5 " " $6}' |
while read device mntdir fstype options dump passno ;
do
device=$(parse_device $device)
#mntopts=$(parse_options $options)
mntopts=defaults,rw
#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" ] || [ / = "$mntdir" ]) && continue
#[ swap = "$fstype" ] || [ / = "$mntdir" ] || [ /home = "$mntdir" ] || [ /var/log = "$mntdir" ]&& continue
#not system partition
#[ no = $(is_system_partition $mntdir) ] && continue
#去掉首字符'/'
#mntdir_name=$(echo $mntdir | sed 's:^/::g')
#target_mntdir="/$(echo $mntdir_name | sed 's:/:-:g').orig"
target_mntdir=${rootmnt}${mntdir}
mkdir -p "$target_mntdir"
if [[ $options =~ bind ]] ; then
# bind挂载的就不再挂了
echo "mount -o $options ${rootmnt}${device} $target_mntdir"
mount -o $options "${rootmnt}${device}" "$target_mntdir"
else
echo "mount -n -t $fstype -o $mntopts $device $target_mntdir"
mount -n -t $fstype -o $mntopts $device "$target_mntdir"
fi
#如果一个分区已经安装了,则下列会报错后不再继续
#if [ $? -ne 0 ]; then
# panic "Failed to mount %s on %s" "$device" "$target_mntdir"
# break
#fi
done || exit 1
echo "mount $fstab_path end"
}
mount_fstab

445
backup-daemon/databackupproxy.cpp Executable file
View File

@ -0,0 +1,445 @@
#include "databackupproxy.h"
#include <QStorageInfo>
#include <QDateTime>
#include <QDebug>
#include <kysec/status.h>
#include "../common/utils.h"
#include "../common/mydusizetool.h"
#include "mymountproxy.h"
#include "myprocess/calcbackupsize.h"
IMPLEMENT_DYNCREATE(DataBackupProxy)
DataBackupProxy::DataBackupProxy()
{
m_isOnlyCheck = true;
m_bSuccess = false;
m_isFinished = false;
m_p = nullptr;
m_size = 0;
m_calc = new CalcBackupSize(this);
connect(this, &DataBackupProxy::cancel, this, &DataBackupProxy::cancelEx);
}
DataBackupProxy::~DataBackupProxy()
{
delete m_p;
m_p = nullptr;
delete m_calc;
m_calc = nullptr;
}
/**
* @brief
* @return false,;true,
*/
bool DataBackupProxy::checkEnvEx()
{
qDebug() << "DataBackupProxy::checkEnv invoke begin";
// 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备都得保证/backup挂载上); 若没挂载,挂载
MyMountProxy mountProxy;
if ( MountResult::MOUNTED != mountProxy.mountBackupPartition() ) {
emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL));
return false;
}
// 2、判断备份是否增量备份
isIncBackup();
// 3、检测空间是否满足备份
calcSizeForBackup();
qDebug() << "DataBackupProxy::checkEnv invoke end";
return true;
}
/**
* @brief
*/
void DataBackupProxy::doWorkEx()
{
qDebug() << "DataBackupProxy::doWorkEx invoke begin";
m_isOnlyCheck = false;
// 环境检测
checkEnvEx();
qDebug() << "DataBackupProxy::doWorkEx invoke end";
}
/**
* @brief
*/
void DataBackupProxy::cancelEx()
{
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));
}
}
/**
* @brief
*/
void DataBackupProxy::deleteFailedData()
{
if (m_curUuid.isEmpty())
return;
// 1、删除备份目录
QString destPath = Utils::getSysRootPath() + 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 DataBackupProxy::isIncBackup()
{
QString backupPath;
ParseBackupList::BackupPoint point;
if (m_backupWrapper.m_uuid.isEmpty()) {
return false;
} else {
backupPath = Utils::getSysRootPath() + 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 DataBackupProxy::checkFreeCapacity(qint64 itotalSize)
{
qDebug() << "DataBackupProxy::checkFreeCapacity invoke begin";
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return false;
// 1、计算待备份数据的大小
m_size = itotalSize;
// 备份过程中会有一些临时文件产生会占用一部分空间故我们预留5M的空间
itotalSize += 5 * MB;
// 2、计算备份分区剩余空间大小
QString backupPath(Utils::getSysRootPath() + BACKUP_PATH);
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() << "DataBackupProxy::checkFreeCapacity invoke end";
return result;
}
/**
* @brief
* @return ,
*/
void DataBackupProxy::calcSizeForBackup()
{
QString destPath = Utils::getSysRootPath();
QStringList args;
if (m_backupWrapper.m_bIncrement) {
// 在原来的备份点上增量备份场景
args = getRsyncArgs(DataBackupScene::TRY_INC_DATA_BACKUP);
destPath += BACKUP_SNAPSHOTS_PATH;
destPath += "/";
destPath += m_backupWrapper.m_uuid;
destPath += "/data/";
} else {
// 全量备份场景
args = getRsyncArgs(DataBackupScene::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, &DataBackupProxy::checkFreeCapacity);
m_calc->start(args, false);
}
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList DataBackupProxy::getRsyncArgs(DataBackupScene scene)
{
QStringList args;
QString backupFile = "/tmp/.backup.user";
Utils::writeFileByLines(backupFile, m_backupWrapper.m_backupPaths);
switch (scene) {
case DataBackupScene::DATA_BACKUP :
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
args << "--files-from" << backupFile;
break ;
case DataBackupScene::INC_DATA_BACKUP :
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
args << "--files-from" << backupFile;
break ;
case DataBackupScene::TRY_DATA_BACKUP :
args << "-aAHXrn";
args << "--stats";
args << "--ignore-missing-args";
args << "--files-from" << backupFile;
break ;
case DataBackupScene::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 DataBackupProxy::doBackup()
{
qDebug() << "DataBackupProxy::doBackup invoke begin";
// 准备
if (!doPrepare())
return ;
// 启动数据备份
backupData();
qDebug() << "DataBackupProxy::doBackup invoke end";
}
/**
* @brief
* @return true,false
*/
bool DataBackupProxy::doPrepare()
{
qDebug() << "DataBackupProxy::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 = Utils::getSysRootPath() + 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 = Utils::getSysRootPath() + 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 = Utils::getSysRootPath() + 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() << "DataBackupProxy::doPrepare invoke end";
return true;
}
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool DataBackupProxy::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;
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 truefalse
*/
bool DataBackupProxy::backupData()
{
qDebug() << "DataBackupProxy::backupData invoke begin";
QStringList args;
if (m_backupWrapper.m_bIncrement) {
// 在原来的备份点上增量备份场景
args = getRsyncArgs(DataBackupScene::INC_DATA_BACKUP);
} else {
// 全量备份场景
args = getRsyncArgs(DataBackupScene::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, &DataBackupProxy::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() << "DataBackupProxy::backupData invoke end";
return true;
}
void DataBackupProxy::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();
}
}

99
backup-daemon/databackupproxy.h Executable file
View File

@ -0,0 +1,99 @@
#ifndef DATABACKUPPROXY_H
#define DATABACKUPPROXY_H
#include "workerfactory.h"
#include "myprocess/calcbackupsize.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class DataBackupProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(DataBackupProxy)
public:
// 系统备份的几种场景
enum DataBackupScene {
DATA_BACKUP, // 系统备份
INC_DATA_BACKUP, // 增量系统备份
TRY_DATA_BACKUP, // 测试系统备份,可用于计算备份传输数据大小
TRY_INC_DATA_BACKUP, // 测试增量系统备份,可用于计算备份传输数据大小
};
explicit DataBackupProxy();
virtual ~DataBackupProxy();
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:
// 判断是否增量备份
virtual bool isIncBackup();
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList getRsyncArgs(DataBackupScene 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 // DATABACKUPPROXY_H

View File

@ -0,0 +1,156 @@
#include "datarestoreproxy.h"
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include <unistd.h>
#include <sys/reboot.h>
#include "../common/utils.h"
#include "mymountproxy.h"
#include "parsebackuplist.h"
IMPLEMENT_DYNCREATE(DataRestoreProxy)
/**
* @brief
*/
DataRestoreProxy::DataRestoreProxy()
{
m_bSuccess = false;
m_p = nullptr;
}
/**
* @brief
*/
DataRestoreProxy::~DataRestoreProxy()
{
delete m_p;
}
/**
* @brief
* @return false,;true,
*/
bool DataRestoreProxy::checkEnvEx()
{
qDebug() << "DataRestoreProxy::checkEnvEx invoke begin";
if (m_backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE)
m_prePath = m_backupWrapper.m_prefixDestPath;
else
m_prePath = Utils::getSysRootPath();
// 1、检测.user.txt是否存在
m_userFile = m_prePath + 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;
}
// 2、检测.exclude.user.txt是否存在
m_excludeUserFile = m_prePath + 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;
}
// 3、检测还原点是否存在
m_backupPath = m_prePath + 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;
}
// 4、检测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;
}
emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS));
qDebug() << "DataRestoreProxy::checkEnvEx invoke end";
return true;
}
/**
* @brief
*/
void DataRestoreProxy::doWorkEx()
{
qDebug() << "DataRestoreProxy::doWorkEx invoke begin";
// 1、校验
if (!checkEnvEx())
return ;
// 2、还原数据
restoreData();
qDebug() << "DataRestoreProxy::doWorkEx invoke end";
}
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList DataRestoreProxy::getRsyncArgs(DataRestoreScene scene)
{
QStringList args;
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
switch (scene) {
case DataRestoreScene::DATA_RESTORE :
args << "--files-from" << m_userFile;
break ;
default:
return args;
}
return args;
}
/**
* @brief
*/
void DataRestoreProxy::restoreData()
{
QStringList args = getRsyncArgs(DataRestoreScene::DATA_RESTORE);
m_srcPath = m_backupPath;
QString destPath = Utils::getSysRootPath();
destPath.replace("//", "/");
args << m_srcPath + "/";
args << destPath + "/";
m_p = new RsyncPathToDirProcess(this);
connect(m_p, &RsyncPathToDirProcess::progress, this, &DataRestoreProxy::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_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid)+ "," + m_backupPoint.m_backupName);
}
emit this->workResult(result);
});
m_p->start(args, false);
}

View File

@ -0,0 +1,59 @@
#ifndef DATARESTOREPROXY_H
#define DATARESTOREPROXY_H
#include "workerfactory.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class DataRestoreProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(DataRestoreProxy)
public:
// 数据还原的几种场景
enum DataRestoreScene {
DATA_RESTORE, // 数据还原
};
explicit DataRestoreProxy();
virtual ~DataRestoreProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
private:
// 数据还原
void restoreData();
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList getRsyncArgs(DataRestoreScene scene);
// .user.txt文件路径
QString m_userFile;
// .exclude.user.txt文件路径
QString m_excludeUserFile;
// 备份数据所在的data目录
QString m_backupPath;
// 备份点前缀路径
QString m_prePath;
// 是否还原成功
bool m_bSuccess;
// 当前还原源目录
QString m_srcPath;
// 还原进程
RsyncPathToDirProcess *m_p;
// 当前备份节点
ParseBackupList::BackupPoint m_backupPoint;
};
#endif // DATARESTOREPROXY_H

View File

@ -0,0 +1,97 @@
#include "deletebackupproxy.h"
#include <QProcess>
#include <QDateTime>
#include <QRegularExpression>
#include <QDebug>
#include "../common/utils.h"
IMPLEMENT_DYNCREATE(DeleteBackupProxy)
DeleteBackupProxy::DeleteBackupProxy()
{}
DeleteBackupProxy::~DeleteBackupProxy()
{}
void DeleteBackupProxy::doWorkEx()
{
qDebug() << "DeleteBackupProxy::doWorkEx invoke begin";
// 1、删除/backup/snapshots/backuplist.xml对应项
if (!deleteXmlBackupPoint())
return;
// 2、删除/backup/snapshots/{uuid}对应目录
deleteBackupPointDir();
// 3、同时删除唯一识别文件中对应的条目
Utils::deleteBackupUniqueRecord(m_backupWrapper.m_backupName);
// 删除成功信号
emit workResult(true);
qDebug() << "DeleteBackupProxy::doWorkEx invoke end";
}
/**
* @brief /backup/snapshots/backuplist.xml对应项
*/
bool DeleteBackupProxy::deleteXmlBackupPoint()
{
qDebug() << "DeleteBackupProxy::deleteXmlBackupPoint invoke begin";
QString xmlPath;
if (m_backupWrapper.m_iPosition == BackupPosition::LOCAL || m_backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE)
xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
else
xmlPath = m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
// 查询节点
m_backupPoint = parse.findBackupPointByUuid(m_backupWrapper.m_uuid);
// 删除节点
if (!m_backupWrapper.m_uuid.contains(QRegularExpression("^\\{.*-.*-.*-.*-.*\\}$")) || parse.deleteItem(m_backupWrapper.m_uuid) != ParseBackupList::SUCCESS) {
qCritical() << QString("failed to delete %1 from backuplist.xml!").arg(m_backupWrapper.m_uuid);
emit workResult(false);
return false;
}
qDebug() << "DeleteBackupProxy::deleteXmlBackupPoint invoke end";
return true;
}
/**
* @brief /backup/snapshots/uuid对应目录
*/
void DeleteBackupProxy::deleteBackupPointDir()
{
qDebug() << "DeleteBackupProxy::deleteBackupPointDir invoke begin";
QString uuidPath;
if (m_backupPoint.m_iPosition == BackupPosition::LOCAL)
uuidPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid;
else if (m_backupPoint.m_iPosition == BackupPosition::CUSTOMIZE)
uuidPath = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid;
else
uuidPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid;
uuidPath.replace("//", "/");
QStringList args;
args << "-rf";
args << uuidPath;
QProcess::execute("rm", args);
QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
if ( int(BackupType::BACKUP_DATA) == m_backupPoint.m_type
|| int(BackupType::INC_BACKUP_DATA) == m_backupPoint.m_type) {
Utils::writeBackupLog(time + "," + m_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid)+ "," + m_backupPoint.m_backupName);
} else {
Utils::writeBackupLog(time + "," + m_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + ",,,," + m_backupPoint.m_backupName);
}
qDebug() << "DeleteBackupProxy::deleteBackupPointDir invoke end";
}

View File

@ -0,0 +1,27 @@
#ifndef DELETEBACKUPPROXY_H
#define DELETEBACKUPPROXY_H
#include "workerfactory.h"
#include "parsebackuplist.h"
class DeleteBackupProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(DeleteBackupProxy)
public:
explicit DeleteBackupProxy();
virtual ~DeleteBackupProxy();
public:
// 任务处理
virtual void doWorkEx();
private:
bool deleteXmlBackupPoint();
void deleteBackupPointDir();
ParseBackupList::BackupPoint m_backupPoint;
};
#endif // DELETEBACKUPPROXY_H

180
backup-daemon/ghostimageproxy.cpp Executable file
View File

@ -0,0 +1,180 @@
#include "ghostimageproxy.h"
#include <QStorageInfo>
#include <QFileInfo>
#include <QDateTime>
#include <QDebug>
#include <kysec/status.h>
#include <unistd.h>
#include "../common/utils.h"
#include "../common/mydusizetool.h"
#include "mymountproxy.h"
#include "myprocess/calcbackupsize.h"
IMPLEMENT_DYNCREATE(GhostImageProxy)
GhostImageProxy::GhostImageProxy()
{
m_mksquashfs = nullptr;
m_bSuccess = false;
m_isFinished = false;
connect(this, &GhostImageProxy::cancel, this, &GhostImageProxy::cancelEx);
}
GhostImageProxy::~GhostImageProxy()
{
delete m_mksquashfs;
m_mksquashfs = nullptr;
}
/**
* @brief
*/
bool GhostImageProxy::checkEnvEx()
{
qDebug() << "GhostImageProxy::checkEnvEx invoke begin";
// 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备都得保证/backup挂载上); 若没挂载,挂载
MyMountProxy mountProxy;
if ( MountResult::MOUNTED != mountProxy.mountBackupPartition() ) {
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);
ParseBackupList::BackupPoint backupPoint = xmlParse.findBackupPointByUuid(m_backupWrapper.m_uuid);
if (backupPoint.m_backupName.isEmpty()) {
emit checkResult(int(BackupResult::GHOST_CANNOT_FIND_BACKUPPOINT));
return false;
}
// 3、校验备份数据是否存在
QString dataPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
dataPath.replace("//", "/");
if (Utils::isDirEmpty(dataPath)) {
emit checkResult(int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST));
return false;
}
qint64 itotalSize = Utils::getDirOrFileSize(dataPath);
// 4、校验空间大小是否充足
m_destPath = Utils::getSysRootPath() + GHOST_PATH;
m_destPath.replace("//", "/");
Utils::mkpath(m_destPath);
m_kyimg = Utils::getSysRootPath() + GHOST_PATH + "/" + 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 / 2) {
emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH));
return false;
}
emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS));
qDebug() << "GhostImageProxy::checkEnvEx invoke end";
return true;
}
/**
* @brief
*/
void GhostImageProxy::doWorkEx()
{
qDebug() << "GhostImageProxy::doWorkEx invoke begin";
if (!checkEnvEx())
return ;
doGhostImage();
qDebug() << "GhostImageProxy::doWorkEx invoke end";
}
/**
* @brief
*/
void GhostImageProxy::cancelEx()
{
qDebug() << "GhostImageProxy::cancelEx invoke begin";
m_bCancel = true;
if (!m_isFinished) {
emit this->checkResult(int(BackupResult::START_CANCEL));
if (m_mksquashfs)
m_mksquashfs->stop();
QProcess::execute("sync");
Utils::wait(5);
deleteFailedData();
emit this->checkResult(int(BackupResult::CANCEL_SUCCESS));
}
qDebug() << "GhostImageProxy::cancelEx invoke end";
}
/**
* @brief
*/
void GhostImageProxy::deleteFailedData()
{
// 1、删除镜像文件
if (!m_kyimg.isEmpty()) {
QFile kyimg(m_kyimg);
if (kyimg.exists())
kyimg.remove();
}
}
/**
* @brief ghost镜像
*/
void GhostImageProxy::doGhostImage()
{
qDebug() << "GhostImageProxy::doGhostImage invoke begin";
QStringList args;
// 拼接备份源路径和目标路径
QString srcPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
srcPath.replace("//", "/");
args << srcPath;
args << m_kyimg;
m_mksquashfs = new MkSquashFSProcess(this);
connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &GhostImageProxy::progress);
connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [&](bool result) {
qDebug() << "GhostImageProxy::finished invoke begin";
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
m_isFinished = true;
if (result) {
chown(m_kyimg.toLocal8Bit().data(), m_backupWrapper.m_frontUid, m_backupWrapper.m_gid);
// QFileInfo fileInfo(m_kyimg);
// QString imgSize = Utils::StringBySize(fileInfo.size());
// QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
// Utils::writeBackupLog(time + ","
// + m_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + ","
// + m_backupWrapper.m_note + "," + imgSize
// + ",," + m_backupWrapper.m_backupName);
m_bSuccess = true;
}
emit this->workResult(result);
qDebug() << "GhostImageProxy::finished invoke end";
});
m_bSuccess = false;
m_mksquashfs->start(args);
qDebug() << "GhostImageProxy::doGhostImage invoke end";
}

46
backup-daemon/ghostimageproxy.h Executable file
View File

@ -0,0 +1,46 @@
#ifndef GHOSTIMAGEPROXY_H
#define GHOSTIMAGEPROXY_H
#include "workerfactory.h"
#include "myprocess/mksquashfsprocess.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class GhostImageProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(GhostImageProxy)
public:
explicit GhostImageProxy();
virtual ~GhostImageProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
// 任务取消
virtual void cancelEx();
private:
void doGhostImage();
void deleteFailedData();
// 存放.kyimg文件的目录
QString m_destPath;
// .kyimg文件
QString m_kyimg;
// 压缩进程
MkSquashFSProcess *m_mksquashfs;
// 是否成功
bool m_bSuccess;
// 是否完成
bool m_isFinished;
};
#endif // GHOSTIMAGEPROXY_H

33
backup-daemon/main.cpp Executable file
View File

@ -0,0 +1,33 @@
#include <QCoreApplication>
#include <QTextCodec>
#include "../common/utils.h"
#include "../common/mydefine.h"
#include "backupmanager_adaptor.h"
int main(int argc, char *argv[])
{
// 以防客户端传入的参数中的中文信息乱码
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QCoreApplication a(argc, argv);
a.setApplicationName("backup-daemon");
a.setApplicationVersion("4.0.14");
// 初始化日志
QString qsAppPath = QCoreApplication::applicationDirPath();
Utils::initSysRootPath(qsAppPath);
qInstallMessageHandler(Utils::customMessageHandler);
Utils::initSystemInfo();
MyBackupManager* backup_deamon = new MyBackupManager;
new ManagerAdaptor(backup_deamon);
QDBusConnection conn = QDBusConnection::systemBus();
if (!conn.registerService("com.kylin.backup")) {
qDebug() << conn.lastError().message();
}
conn.registerObject("/", backup_deamon);
return a.exec();
}

590
backup-daemon/mybackupmanager.cpp Executable file
View File

@ -0,0 +1,590 @@
/**
* brief moveToThread中去执行
*/
#include <QTimer>
#include <QDateTime>
#include <QDBusArgument>
#include <QDBusMessage>
#include <QDBusPendingReply>
#include <QDBusMessage>
#include <QDBusUnixFileDescriptor>
#include <QDBusConnection>
#include <mutex>
#include <sys/reboot.h>
#include <kysec/status.h>
#include "mybackupmanager.h"
#include "../common/utils.h"
#include "mymountproxy.h"
#include "workerfactory.h"
#include "parsebackuplist.h"
/**
* @brief
*/
MyBackupManager::MyBackupManager()
{
// 注册BackupWrapper类型之后qdbus接口才能使用
BackupWrapper::registerMetaType();
}
/**
* @brief
*/
MyBackupManager::~MyBackupManager()
{
if (workerThread.isRunning()) {
workerThread.quit();
workerThread.wait();
}
}
/**
* @brief backup分区
* @return
*/
int MyBackupManager::Mount_backup_partition()
{
m_needUmount = false;
MyMountProxy mymount;
return int(mymount.mountBackupPartition());
}
/**
* @brief backup分区
* @return
*/
bool MyBackupManager::umountBackupPartition()
{
m_needUmount = true;
return umountBackup();
}
/**
* @brief backup分区
* @return
*/
bool MyBackupManager::umountBackup()
{
// /backup分区卸载
if (m_needUmount && m_isActive) {
QTimer::singleShot(5*1000, this, &MyBackupManager::umountBackup);
return true;
} else if (m_needUmount) {
MyMountProxy mymount;
return mymount.umountBackupPartition();
}
return true;
}
/**
* @brief
* @param backupWrapper
* @return 00
*/
int MyBackupManager::checkEnv(const BackupWrapper& backupWrapper)
{
qDebug("MyBackupManager::checkEnv invoke begin");
if (m_isActive || !lock(backupWrapper.m_frontUid)) {
emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
return int(BackupResult::LOCK_PROGRAM_FAIL);
}
Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
if (nullptr == worker) {
emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
return int(BackupResult::NO_FOUND_DEALCLASS);
}
worker->setParam(backupWrapper);
connect(worker, &Worker::checkResult, this, [&] (int result) {
emit this->sendEnvCheckResult(result);
this->finished();
});
worker->moveToThread(&workerThread);
connect(&workerThread, &MyThread::started, worker, &Worker::checkEnv);
connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
workerThread.start();
qDebug("MyBackupManager::checkEnv invoke end");
return int(BackupResult::BACKUP_RESULT_INIT);
}
/**
* @brief
* @param backupWrapper
* @return 00
*/
int MyBackupManager::goBackup(const BackupWrapper& backupWrapper)
{
qDebug("MyBackupManager::goBackup invoke begin");
if (m_isActive || !lock(backupWrapper.m_frontUid)) {
emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
return int(BackupResult::LOCK_PROGRAM_FAIL);
}
Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
if (nullptr == worker) {
emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
return int(BackupResult::NO_FOUND_DEALCLASS);
}
worker->setParam(backupWrapper);
connect(worker, &Worker::checkResult, this, [&](int result) {
emit this->sendEnvCheckResult(result);
switch (result) {
case int(BackupResult::CHECK_ENV_SUCCESS) :
case int(BackupResult::MKSQUASHFS_START_SUCCESS) :
case int(BackupResult::BACKUP_START_SUCCESS) :
case int(BackupResult::START_CANCEL) :
break;
default:
this->finished();
break;
}
});
connect(worker, &Worker::progress, this, [&](int rate) {
emit this->progress(int(BackupState::WORKING), rate);
});
connect(worker, &Worker::workResult, this, [&] (bool result) {
emit this->backupFinished(result);
this->finished();
});
worker->moveToThread(&workerThread);
connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
connect(&workerThread, &MyThread::cancelWork, worker, &Worker::cancel);
workerThread.start();
qDebug("MyBackupManager::goBackup invoke end");
return int(BackupResult::BACKUP_RESULT_INIT);
}
/**
* @brief
* @param backupWrapper
* @return
*/
int MyBackupManager::goRestore(const BackupWrapper& backupWrapper)
{
qDebug("MyBackupManager::goRestore invoke begin");
if (m_isActive || !lock(backupWrapper.m_frontUid)) {
emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
return int(BackupResult::LOCK_PROGRAM_FAIL);
}
Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
if (nullptr == worker) {
emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
return int(BackupResult::NO_FOUND_DEALCLASS);
}
worker->setParam(backupWrapper);
connect(worker, &Worker::checkResult, this, [&](int result) {
emit this->sendEnvCheckResult(result);
switch (result) {
case int(BackupResult::CHECK_ENV_SUCCESS) :
case int(BackupResult::RESTORE_START_SUCCESS) :
break;
default:
this->finished();
break;
}
});
connect(worker, &Worker::progress, this, [&](int rate) {
emit this->progress(int(BackupState::WORKING), rate);
});
connect(worker, &Worker::workResult, this, [&] (bool result) {
emit this->sendRestoreResult(result);
this->finished();
});
worker->moveToThread(&workerThread);
connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
workerThread.start();
qDebug("MyBackupManager::goRestore invoke end");
return int(BackupResult::BACKUP_RESULT_INIT);
}
/**
* @brief
* @param backupWrapper
* @return
*/
int MyBackupManager::deleteBackupPoint(const BackupWrapper& backupWrapper)
{
qDebug("MyBackupManager::deleteBackupPoint invoke begin");
if (m_isActive || !lock(backupWrapper.m_frontUid)) {
emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
return int(BackupResult::LOCK_PROGRAM_FAIL);
}
Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
if (nullptr == worker) {
emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
return int(BackupResult::NO_FOUND_DEALCLASS);
}
worker->setParam(backupWrapper);
connect(worker, &Worker::workResult, this, [&] (bool result) {
emit this->sendDeleteResult(result);
this->finished();
});
worker->moveToThread(&workerThread);
connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
workerThread.start();
qDebug("MyBackupManager::deleteBackupPoint invoke end");
return int(BackupResult::BACKUP_RESULT_INIT);
}
/**
* @brief ghost镜像
* @param backupWrapper
* @return
*/
int MyBackupManager::ghostBackup(const BackupWrapper& backupWrapper)
{
qDebug("MyBackupManager::ghostBackup invoke begin");
if (m_isActive || !lock(backupWrapper.m_frontUid)) {
emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL));
return int(BackupResult::LOCK_PROGRAM_FAIL);
}
Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
if (nullptr == worker) {
emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS));
return int(BackupResult::NO_FOUND_DEALCLASS);
}
worker->setParam(backupWrapper);
connect(worker, &Worker::checkResult, this, [&](int result) {
emit this->sendEnvCheckResult(result);
switch (result) {
case int(BackupResult::CHECK_ENV_SUCCESS) :
case int(BackupResult::GHOST_START_SUCCESS) :
case int(BackupResult::MKSQUASHFS_DO_SUCCESS) :
case int(BackupResult::START_CANCEL) :
break;
default:
this->finished();
break;
}
});
connect(worker, &Worker::progress, this, [&](int rate) {
emit this->progress(int(BackupState::WORKING), rate);
});
connect(worker, &Worker::workResult, this, [&] (bool result) {
emit this->sendGhostBackupResult(result);
this->finished();
});
worker->moveToThread(&workerThread);
connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
connect(&workerThread, &MyThread::cancelWork, worker, &Worker::cancel);
workerThread.start();
qDebug("MyBackupManager::ghostBackup invoke end");
return int(BackupResult::BACKUP_RESULT_INIT);
}
/**
* @brief
* @param autobackup_name
* @param create_note
* @param inc_note
* @param frontUserName
* @param frontUid id
*/
void MyBackupManager::autoBackUpForSystemUpdate_noreturn(const QString& autobackup_name, const QString& create_note, const QString& inc_note, const QString& frontUserName, int frontUid)
{
qDebug("MyBackupManager::autoBackUpForSystemUpdate_noreturn invoke begin");
if (m_isActive || !lock(frontUid)) {
emit sendStartBackupResult(int(BackupResult::LOCK_PROGRAM_FAIL));
return ;
}
BackupWrapper backupWrapper;
backupWrapper.m_uuid = AUTO_BACKUP_UUID;
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parseXml(xmlPath);
ParseBackupList::BackupPoint backupPoint = parseXml.findBackupPointByUuid(backupWrapper.m_uuid);
if (autobackup_name.isEmpty()) {
if (backupPoint.m_backupName.isEmpty())
backupWrapper.m_backupName = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
else
backupWrapper.m_backupName = backupPoint.m_backupName;
} else {
backupWrapper.m_backupName = autobackup_name;
}
backupWrapper.m_backupPaths << "/";
backupWrapper.m_backupExcludePaths = Utils::getFromExcludePathsFile();
backupWrapper.m_backupExcludePaths << "/home";
backupWrapper.m_backupExcludePaths << "/root";
backupWrapper.m_backupExcludePaths << "/data/home";
backupWrapper.m_backupExcludePaths << "/data/root";
backupWrapper.m_type = BackupType::BACKUP_SYSTEM;
backupWrapper.m_iPosition = BackupPosition::LOCAL;
backupWrapper.m_frontUserName = frontUserName;
backupWrapper.m_frontUid = frontUid;
backupWrapper.m_note = create_note.isEmpty() ? inc_note : create_note;
Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition);
if (nullptr == worker) {
emit sendStartBackupResult(int(BackupResult::NO_FOUND_DEALCLASS));
return ;
}
worker->setParam(backupWrapper);
connect(worker, &Worker::checkResult, this, [&](int result) {
switch (result) {
case int(BackupResult::CHECK_ENV_SUCCESS) :
case int(BackupResult::MKSQUASHFS_START_SUCCESS) :
case int(BackupResult::BACKUP_START_SUCCESS) :
break;
default:
emit this->sendStartBackupResult(result);
this->finished();
if (!Utils::isRunning("kybackup")) {
this->umountBackupPartition();
}
break;
}
});
connect(worker, &Worker::progress, this, [&](int rate) {
emit this->sendRate(int(BackupState::WORKING), rate);
});
connect(worker, &Worker::workResult, this, [&] (bool result) {
emit this->sendBackupResult(result);
this->finished();
if (!Utils::isRunning("kybackup")) {
this->umountBackupPartition();
}
});
worker->moveToThread(&workerThread);
connect(&workerThread, &MyThread::started, worker, &Worker::doWork);
connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater);
workerThread.start();
qDebug("MyBackupManager::autoBackUpForSystemUpdate_noreturn invoke end");
}
/**
* @brief
* @param state
* @return
*/
QString MyBackupManager::getBackupCommentForSystemUpdate(QString& state)
{
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parseXml(xmlPath);
ParseBackupList::BackupPoint backupPoint = parseXml.findBackupPointByUuid(AUTO_BACKUP_UUID);
state = backupPoint.m_state;
return backupPoint.m_backupName;
}
/**
* @brief
* @param isActive
* @return
*/
int MyBackupManager::getBackupState(bool& isActive)
{
isActive = m_isActive;
return int(m_backupState);
}
/**
* @brief
*/
int MyBackupManager::cancel()
{
if (workerThread.isRunning())
emit workerThread.cancelWork();
else
return 1;
return 0;
}
/**
* @brief
* @return
*/
int MyBackupManager::reboot()
{
return ::reboot(RB_AUTOBOOT);;
}
/**
* @brief
*/
void MyBackupManager::finished()
{
if (workerThread.isRunning()) {
workerThread.quit();
workerThread.wait();
}
unlock();
}
/**
* @brief
* @param frontUidid
* @return bool
*/
bool MyBackupManager::lock(int frontUid)
{
std::lock_guard<spinlock_mutex> lock(m_mutex);
int lock_file_fd = Utils::lockProgram(frontUid);
if (lock_file_fd < 0) {
Utils::rmLockFile();
return false;
}
// 用于替换上面的文件锁的对应需求8688
// if (!inhibit())
// return false;
inhibit();
m_fdLockFile = lock_file_fd;
m_backupState = BackupState::PREPARING;
m_isActive = true;
setKysecStatus(false);
return true;
}
/**
* @brief session关机用dbus新接口Inhibit来替换锁文件的方式
* Inhibti函数有四个参数
what:shutdown()sleep shutdown:sleep
who:ukui-sessions
whypackage installing
mode:block()delay
使block
使ListInhibitors方法获取当前的inhibit锁
* @return
*/
bool MyBackupManager::inhibit()
{
QDBusMessage message;
message = QDBusMessage::createMethodCall("org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
QStringLiteral("Inhibit"));
QString why("The backup and restore tool is busy, please wait.");
// 区分中英文
QString locale = QLocale::system().name();
if (locale == "zh_CN") {
why = QString("备份还原工具正在忙碌,请稍等");
} else if (locale == "bo_CN") {
why = QString("གྲབས་ཉར་སོར་ཆུད་ལག་ཆ་བྲེལ་བཞིན་ཡོད་།ཏོག་ཙམ་སྒུག་རོགས་།");
}
// QVariantList({what, who, why, mode})
QVariantList args;
args << QString("shutdown:sleep");
args << QString("backup-daemon");
args << why;
args << QString("block");
message.setArguments(args);
QDBusPendingReply<QDBusUnixFileDescriptor> reply = QDBusConnection::systemBus().call(message);
if (!reply.isValid()) {
qDebug() << "inhibit faile!";
return false;
}
QDBusUnixFileDescriptor inhibitFileDescriptor = reply.value();
inhibitFileDescriptor.swap(m_inhibitFileDescriptor);
qDebug() << "inhibit success!";
return true;
}
void MyBackupManager::uninhibit()
{
if (!m_inhibitFileDescriptor.isValid()) {
return;
}
qDebug() << "uninhibit success!";
m_inhibitFileDescriptor = QDBusUnixFileDescriptor();
}
/**
* @brief
* @return bool
*/
bool MyBackupManager::unlock()
{
std::lock_guard<spinlock_mutex> lock(m_mutex);
setKysecStatus(true);
Utils::unLockProgram(m_fdLockFile);
uninhibit();
m_fdLockFile = -1;
m_backupState = BackupState::BACKUP_STATE_INIT;
m_isActive = false;
return true;
}
/**
* @brief
* @param status,true-false-
* @note status=true时不一定会开启;
* 使setKysecStatus(false);setKysecStatus(true);
* 使/
*/
void MyBackupManager::setKysecStatus(bool status)
{
if (status) {
if (m_bOpenKysec)
Utils::setKysecStatus(true);
m_bOpenKysec = false;
if (m_bStartKysecDeamon)
Utils::setKysecDaemon(true);
m_bStartKysecDeamon = false;
} else {
if (KYSEC_STATUS_ENABLED == kysec_getstatus()) {
// 安全保护已开启
m_bOpenKysec = true;
Utils::setKysecStatus(false);
// 开启安全防护
// QProcess::execute("systemctl start kysec-init.service");
} else {
m_bOpenKysec = false;
}
if(Utils::isRunning("kysec-sync-daemon")){
m_bStartKysecDeamon = true;
Utils::setKysecDaemon(false);
// 停止安全防护
// QProcess::execute("systemctl stop kysec-init.service");
} else {
m_bStartKysecDeamon = false;
}
}
}

103
backup-daemon/mybackupmanager.h Executable file
View File

@ -0,0 +1,103 @@
#ifndef MYBACKUPMANAGER_H
#define MYBACKUPMANAGER_H
#include <QObject>
#include <QtDBus/QDBusVariant>
#include <QHash>
#include <QDBusUnixFileDescriptor>
#include "mythread.h"
#include "../common/mydefine.h"
#include "../common/spinlock_mutex.h"
class MyBackupManager : public QObject {
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.kylin.backup.manager")
public:
explicit MyBackupManager();
virtual ~MyBackupManager();
signals:
// 这3个信号供控制面板/自动更新等外围模块使用
// 开始备份结果信号
void sendStartBackupResult(int result);
// 备份结果信号
void sendBackupResult(bool result);
// 进度信号
void sendRate(int, int);
// 模块内使用的信号
// 进度信号
void progress(int, int);
// 备份结果信号
void backupFinished(bool result);
// 环境检查结果信号
void sendEnvCheckResult(int result);
// 还原结果信号
void sendRestoreResult(bool result);
// 删除备份结果信号
void sendDeleteResult(bool result);
// ghost备份结果信号
void sendGhostBackupResult(bool result);
public slots:
// 备份分区挂载
int Mount_backup_partition();
// 卸载backup分区
bool umountBackupPartition();
// 环境检测
int checkEnv(const BackupWrapper& backupWrapper);
// 备份
int goBackup(const BackupWrapper& backupWrapper);
// 还原
int goRestore(const BackupWrapper& backupWrapper);
// 删除备份
int deleteBackupPoint(const BackupWrapper& backupWrapper);
// ghost镜像
int ghostBackup(const BackupWrapper& backupWrapper);
// 控制面板调用的备份接口,重构暂时先兼容以前的老接口
void autoBackUpForSystemUpdate_noreturn(const QString& autobackup_name, const QString& create_note, const QString& inc_note, const QString& frontUserName, int frontUid);
// 控制面板调用的获取系统备份接口
QString getBackupCommentForSystemUpdate(QString& state);
// 获取备份状态
int getBackupState(bool& isActive);
// 取消操作
int cancel();
// 重启操作
int reboot();
// 任务结束
void finished();
private slots:
// 卸载backup分区
bool umountBackup();
private:
// 锁定
bool lock(int frontUid);
bool inhibit();
// 解锁
bool unlock();
void uninhibit();
// 自旋锁
spinlock_mutex m_mutex;
// 关闭、开启安全保护
void setKysecStatus(bool status);
private:
// 锁文件描述符
int m_fdLockFile = -1;
QDBusUnixFileDescriptor m_inhibitFileDescriptor;
// 备份还原状态
BackupState m_backupState = BackupState::BACKUP_STATE_INIT;
// 是否活动的,兼容以前用的,和m_backupState的实际使用功能有重复
bool m_isActive = false;
bool m_needUmount = false;
bool m_bOpenKysec = false;
bool m_bStartKysecDeamon = false;
// 工作者线程
MyThread workerThread;
};
#endif // MYBACKUPMANAGER_H

45
backup-daemon/mymountproxy.cpp Executable file
View File

@ -0,0 +1,45 @@
#include <QtDebug>
#include <QDir>
#include "mymountproxy.h"
#include "../common/utils.h"
MyMountProxy::MyMountProxy(QObject* parent) : QObject(parent)
{
m_processMount = new MountBackupProcess(this);
}
MountResult MyMountProxy::mountBackupPartition()
{
QString backupPartitionUuid = Utils::getBackupPartitionUuid();
if (backupPartitionUuid.isEmpty()) {
qCritical("can't open /etc/.bootinfo or can't get RECOVERY_DEV_UUID");
return MountResult::CANNOT_GET_BACKUPUUID;
}
QString rootPath = Utils::getSysRootPath();
QString backPath = rootPath + BACKUP_PATH;
backPath.replace("//", "/");
Utils::mkpath(backPath);
bool res = m_processMount->Do(backupPartitionUuid);
if (res) {
QString snapshotsPath = rootPath + BACKUP_SNAPSHOTS_PATH;
snapshotsPath.replace("//", "/");
Utils::mkpath(snapshotsPath);
if (!Utils::generateExcludePathsFile()) {
qCritical("mount success, but generate some important file failed!");
return MountResult::GENERATE_IMPORT_FILE_FAIL;
}
return MountResult::MOUNTED;
}
return MountResult::NO_MOUNTED;
}
bool MyMountProxy::umountBackupPartition()
{
return m_processMount->umountBackupPartition();
}

22
backup-daemon/mymountproxy.h Executable file
View File

@ -0,0 +1,22 @@
#ifndef MYMOUNTPROXY_H
#define MYMOUNTPROXY_H
#include <QObject>
#include "myprocess/mountbackupprocess.h"
#include "../common/mydefine.h"
class MyMountProxy : public QObject {
Q_OBJECT
public:
MyMountProxy(QObject* parent = nullptr);
MountResult mountBackupPartition();
bool umountBackupPartition();
private:
MountBackupProcess* m_processMount;
};
#endif // MYMOUNTPROXY_H

View File

@ -0,0 +1,127 @@
#include "calcbackupsize.h"
#include <QDebug>
#include <QRegularExpression>
CalcBackupSize::CalcBackupSize(QObject* parent) :
QObject(parent),
m_process(new QProcess(this))
{
connect(m_process, &QProcess::readyRead, this, [&]() {
parseResult();
});
connect(m_process, &QProcess::readyReadStandardError, this, [&]() {
QByteArray err = m_process->readAllStandardError();
qCritical("backup process error: %s", err.data());
});
connect(m_process, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
Q_UNUSED(error)
m_process->kill();
});
connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinish(int, QProcess::ExitStatus)));
}
CalcBackupSize::~CalcBackupSize()
{
if (!m_process && m_process->state() != QProcess::NotRunning) {
m_process->kill();
}
}
/**
* @brief
* @param args rsync参数列表
* @param block true
* @return block为true时返回值为待备份数据大小block为false时0;
*/
qint64 CalcBackupSize::start(QStringList args, bool block)
{
qDebug() << "CalcBackupSize::calcBackupSize invoke begin";
QString cmd("rsync ");
for (const QString& item : args) {
cmd += " ";
cmd += item;
}
qDebug() << cmd;
m_size = 0;
m_process->start("rsync", args);
if (!m_process->waitForStarted()) {
qCritical("rsync start failed");
return m_size;
}
if (block) {
m_process->waitForFinished();
}
qDebug() << "CalcBackupSize::calcBackupSize invoke end";
return m_size;
}
/**
* @brief process的输出结果
*/
void CalcBackupSize::parseResult()
{
// 解析结果,结果内容样例如下:
/*
Number of files: 256,274 (reg: 207,101, dir: 23,764, link: 25,407, special: 2)
Number of created files: 256,274 (reg: 207,101, dir: 23,764, link: 25,407, special: 2)
Number of deleted files: 0
Number of regular files transferred: 207,101
Total file size: 12,160,363,663 bytes
Total transferred file size: 12,159,780,794 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 786,254
File list generation time: 0.001 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 8,273,515
Total bytes received: 794,093
sent 8,273,515 bytes received 794,093 bytes 1,209,014.40 bytes/sec
total size is 12,160,363,663 speedup is 1,341.08 (DRY RUN)
*/
QString out(m_process->readAll());
QStringList lines = out.split("\n");
qDebug() << out;
for (QString& line : lines) {
// 获取文件夹数目
if (line.startsWith("Number of files:")) {
int indexOfDir = line.indexOf("dir: ");
indexOfDir += 5;
int indexOfDirEnd = line.indexOf(QRegularExpression("[ )]"), indexOfDir);
int numOfDirs = line.mid(indexOfDir, indexOfDirEnd-indexOfDir).replace(",","").trimmed().toInt();
// 每个目录下还都有.和..,故总数还要*3
numOfDirs *= 3;
// ext4格式的目录本身要占用4K空间4096
m_size += numOfDirs * 4096;
}
if (line.startsWith("Total transferred file size: ")) {
m_size += line.replace("Total transferred file size: ", "").replace("bytes", "").replace(",","").trimmed().toLongLong();
}
}
}
/**
* @brief process结束
* @param exitCode
*/
void CalcBackupSize::processFinish(int exitCode, QProcess::ExitStatus)
{
qDebug() << "CalcBackupSize::getCalcResult invoke begin";
Q_UNUSED(exitCode)
if (exitCode == QProcess::NormalExit)
parseResult();
emit finished(m_size);
qDebug() << "CalcBackupSize::getCalcResult invoke end";
}

View File

@ -0,0 +1,47 @@
#ifndef CALCBACKUPSIZE_H
#define CALCBACKUPSIZE_H
#include <QStringList>
#include <QProcess>
#define GB (1024 * 1024 * 1024)
#define MB (1024 * 1024)
#define KB (1024)
class CalcBackupSize : public QObject
{
Q_OBJECT
public:
CalcBackupSize(QObject* parent = nullptr);
~CalcBackupSize();
/**
* @brief
* @param args rsync参数列表
* @param block true
* @return block为true时返回值为待备份数据大小block为false时0
*/
qint64 start(QStringList args, bool block = true);
void stop() { m_process->kill(); }
signals:
// 计算结束信号
void finished(qint64 size);
private slots:
// process结束槽
void processFinish(int exitCode, QProcess::ExitStatus);
private:
/**
* @brief process的输出结果
*/
void parseResult();
QProcess * m_process;
qint64 m_size = 0; // 备份大小,单位字节
};
#endif // CALCBACKUPSIZE_H

View File

@ -0,0 +1,92 @@
#include "mksquashfsprocess.h"
#include <QDebug>
MkSquashFSProcess::MkSquashFSProcess(QObject* parent)
: QObject(parent)
, m_p(new QProcess(this))
, m_syncProcess(new QProcess(this))
{
connect(m_p, &QProcess::readyRead, this, [&]() {
QString str = QString(m_p->readAll());
qDebug() << str;
if (str.contains("]") && str.contains("%")) {
if (str.split("%").at(0).length() < 3)
return;
int tmpRate = str.split("%").at(0).right(3).toInt();
if (m_currentRate == tmpRate)
return;
m_currentRate = tmpRate;
emit progress(m_currentRate);
}
});
connect(m_p, &QProcess::readyReadStandardError, this, [&]() {
QByteArray err = m_p->readAllStandardError();
qCritical() << err;
});
connect(m_p, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
Q_UNUSED(error)
m_p->kill();
});
connect(m_p, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(mksquashfs_finished(int, QProcess::ExitStatus)));
connect(m_syncProcess, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
Q_UNUSED(error)
m_syncProcess->kill();
});
connect(m_syncProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(sync_finished(int, QProcess::ExitStatus)));
}
MkSquashFSProcess::~MkSquashFSProcess()
{
if (!m_p && m_p->state() != QProcess::NotRunning) {
m_p->kill();
}
if (!m_syncProcess && m_syncProcess->state() != QProcess::NotRunning) {
m_syncProcess->kill();
}
}
bool MkSquashFSProcess::start(const QStringList &args)
{
QString cmd("mksquashfs ");
for (const QString& item : args) {
cmd += " ";
cmd += item;
}
qDebug() << cmd;
m_p->start("mksquashfs", args);
if (!m_p->waitForStarted()) {
qCritical("mksquashfs start failed!");
return false;
}
return true;
}
void MkSquashFSProcess::mksquashfs_finished(int exitCode, QProcess::ExitStatus)
{
if (exitCode == QProcess::NormalExit) {
m_syncProcess->start("sync");
if (!m_syncProcess->waitForStarted()) {
emit finished(false);
}
} else {
emit finished(false);
}
}
void MkSquashFSProcess::sync_finished(int exitCode, QProcess::ExitStatus)
{
if (exitCode == QProcess::NormalExit) {
emit progress(100);
emit finished(true);
} else {
emit finished(false);
}
}

View File

@ -0,0 +1,40 @@
#ifndef MKSQUASHFSPROCESS_H
#define MKSQUASHFSPROCESS_H
#include <QProcess>
#include <QObject>
class MkSquashFSProcess : public QObject
{
Q_OBJECT
public:
explicit MkSquashFSProcess(QObject *parent = nullptr);
virtual ~MkSquashFSProcess();
bool start(const QStringList &args);
void stop() {
m_p->kill();
m_syncProcess->kill();
}
signals:
// 结果信号
void finished(bool result);
// 进度百分比
void progress(int currentRate);
private slots:
// m_p执行结束
void mksquashfs_finished(int exitCode, QProcess::ExitStatus);
// m_syncProcess执行结束
void sync_finished(int exitCode, QProcess::ExitStatus);
private:
QProcess * m_p;
QProcess* m_syncProcess;
int m_currentRate;
bool m_bSuccess;
};
#endif // MKSQUASHFSPROCESS_H

View File

@ -0,0 +1,119 @@
#ifndef MOUNTBACKUPPROCESS_CPP
#define MOUNTBACKUPPROCESS_CPP
#include <QFile>
#include <QtDebug>
#include "mountbackupprocess.h"
#include "../../common/mydefine.h"
#include "../../common/utils.h"
MountBackupProcess::MountBackupProcess(QObject* parent)
: QObject(parent)
, m_p(new QProcess(this))
{
connect(m_p, &QProcess::readyReadStandardError, this, [&]() {
QByteArray err = m_p->readAllStandardError();
qCritical("%s", err.data());
});
}
bool MountBackupProcess::Do(const QString& diskUuid)
{
QString rootPath(Utils::getSysRootPath());
// 若备份路径下的xml已经存在则说明已经挂载过了
QString backupXmlPath = rootPath + BACKUP_XML_PATH;
backupXmlPath.replace("//", "/");
QFile file(backupXmlPath);
if (file.exists())
return true;
QStringList arguments;
QString backupPartion = rootPath + BACKUP_PATH;
backupPartion.replace("//","/");
arguments << QString("-o") << QString("rw") << QString("/dev/disk/by-uuid/%1").arg(diskUuid) << backupPartion;
m_p->start("mount", arguments);
if (!m_p->waitForStarted()) {
qCritical("mount rw /backup process start failed!");
return false;
}
if (!m_p->waitForFinished()) {
qCritical("mount rw backup process end failed!");
return false;
}
return true;
}
bool MountBackupProcess::umountBackupPartition()
{
QStringList arguments;
arguments << QString("/backup");
m_p->start("umount", arguments);
if (!m_p->waitForStarted()) {
qCritical("umount /backup process start failed!");
return false;
}
if (!m_p->waitForFinished()) {
qCritical("umount /backup process end failed!");
return false;
}
return true;
}
/**
* @brief
* @param mountPath
* @return
*/
bool MountBackupProcess::umount(const QString& mountPath)
{
QStringList arguments;
arguments << mountPath;
m_p->start("umount", arguments);
if (!m_p->waitForStarted()) {
qCritical() << tr("umount %1 process start failed!").arg(mountPath);
return false;
}
if (!m_p->waitForFinished()) {
qCritical() << tr("umount %1 process end failed!").arg(mountPath);
return false;
}
return true;
}
/**
* @brief
* @param source
* @param target
* @return
*/
bool MountBackupProcess::mount(const QString& source, const QString& target, const QString& options)
{
QStringList arguments;
if (!options.isEmpty())
arguments << options;
arguments << source;
arguments << target;
m_p->start("mount", arguments);
if (!m_p->waitForStarted()) {
qCritical("mount process start failed!");
return false;
}
if (!m_p->waitForFinished()) {
qCritical("mount process end failed!");
return false;
}
return true;
}
#endif // MOUNTBACKUPPROCESS_CPP

View File

@ -0,0 +1,25 @@
#ifndef MOUNTBACKUPPROCESS_H
#define MOUNTBACKUPPROCESS_H
#include <QProcess>
#include <sys/mount.h>
class MountBackupProcess : public QObject {
Q_OBJECT
public:
MountBackupProcess(QObject* parent = nullptr);
bool Do(const QString& diskUuid);
bool umountBackupPartition();
bool umount(const QString& mountPath);
bool mount(const QString& source, const QString& target, const QString& options = "");
private:
QProcess* m_p;
};
#endif // MOUNTBACKUPPROCESS_H

View File

@ -0,0 +1,130 @@
#include "rsyncpathtodirprocess.h"
#include <QDebug>
RsyncPathToDirProcess::RsyncPathToDirProcess(QObject *parent) :
QObject(parent),
m_p(new QProcess(this)),
m_syncProcess(new QProcess(this))
{
m_currentRate = 0;
m_bSuccess = false;
connect(m_p, &QProcess::readyRead, this, [&]() {
QString str = QString(m_p->readAll());
qDebug() << str;
// if (str.contains("B\/s") && str.contains("%")) {
if (str.contains("B/s") && str.contains("%")) {
if (str.split("%").at(0).length() < 3)
return;
int tmpRate = str.split("%").at(0).right(3).toInt();
if (m_currentRate == tmpRate)
return;
m_currentRate = tmpRate;
emit progress(m_currentRate);
}
});
connect(m_p, &QProcess::readyReadStandardError, this, [&]() {
QByteArray err = m_p->readAllStandardError();
qCritical("backup process error: %s", err.data());
});
connect(m_p, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
Q_UNUSED(error)
m_p->kill();
});
connect(m_p, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(rsync_finished(int, QProcess::ExitStatus)));
connect(m_syncProcess, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
Q_UNUSED(error)
m_syncProcess->kill();
});
connect(m_syncProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(sync_finished(int, QProcess::ExitStatus)));
}
RsyncPathToDirProcess::~RsyncPathToDirProcess()
{
if (!m_p && m_p->state() != QProcess::NotRunning) {
m_p->kill();
}
if (!m_syncProcess && m_syncProcess->state() != QProcess::NotRunning) {
m_syncProcess->kill();
}
}
bool RsyncPathToDirProcess::start(QStringList args, bool block)
{
qDebug() << "RsyncPathToDirProcess::start invoke begin";
m_currentRate = 0;
m_bSuccess = false;
m_block = block;
QString cmd("rsync");
for (const QString& item : args) {
cmd += " ";
cmd += item;
}
qDebug() << cmd;
m_p->start("rsync", args);
if (!m_p->waitForStarted()) {
qCritical("rsync started failed");
return false;
}
if (m_block) {
if (!m_p->waitForFinished()) {
qCritical("rsync finished failed");
return false;
}
}
qDebug() << "RsyncPathToDirProcess::start invoke end";
return m_bSuccess;
}
void RsyncPathToDirProcess::rsync_finished(int exitCode, QProcess::ExitStatus)
{
qDebug() << "RsyncPathToDirProcess::rsync_finished invoke begin";
if (exitCode == QProcess::NormalExit || exitCode == 24 || exitCode == 23) {
if (m_block) {
qDebug() << "RsyncPathToDirProcess::rsync_finished block mode";
m_bSuccess = true;
emit progress(100);
emit finished(true);
} else {
qDebug() << "RsyncPathToDirProcess::rsync_finished unblock mode";
emit progress(100);
m_syncProcess->start("sync");
if (!m_syncProcess->waitForStarted()) {
qDebug() << "sync start failed";
m_bSuccess = false;
emit finished(false);
}
}
} else {
qDebug() << "RsyncPathToDirProcess::rsync_finished failed";
m_bSuccess = false;
emit finished(false);
}
qDebug() << "RsyncPathToDirProcess::rsync_finished invoke end";
}
void RsyncPathToDirProcess::sync_finished(int exitCode, QProcess::ExitStatus)
{
qDebug() << "RsyncPathToDirProcess::sync_finished invoke begin";
if (exitCode == QProcess::NormalExit || exitCode == 24 || exitCode == 23) {
qDebug() << "RsyncPathToDirProcess::sync_finished success";
m_bSuccess = true;
emit finished(true);
} else {
qDebug() << "RsyncPathToDirProcess::sync_finished failed";
m_bSuccess = false;
emit finished(false);
}
qDebug() << "RsyncPathToDirProcess::sync_finished invoke end";
}

View File

@ -0,0 +1,40 @@
#ifndef RSYNCPATHTODIRPROCESS_H
#define RSYNCPATHTODIRPROCESS_H
#include <QProcess>
class RsyncPathToDirProcess : public QObject
{
Q_OBJECT
public:
explicit RsyncPathToDirProcess(QObject *parent = nullptr);
virtual ~RsyncPathToDirProcess();
bool start(QStringList args, bool block = true);
inline void stop() {
m_p->kill();
m_syncProcess->kill();
}
signals:
// 结果信号
void finished(bool result);
// 进度百分比
void progress(int currentRate);
private slots:
// m_p执行结束
void rsync_finished(int exitCode, QProcess::ExitStatus);
// m_syncProcess执行结束
void sync_finished(int exitCode, QProcess::ExitStatus);
private:
QProcess * m_p;
QProcess* m_syncProcess;
int m_currentRate;
bool m_bSuccess;
bool m_block;
};
#endif // RSYNCPATHTODIRPROCESS_H

8
backup-daemon/mythread.cpp Executable file
View File

@ -0,0 +1,8 @@
#include "mythread.h"
MyThread::MyThread(QObject *parent) :
QThread(parent)
{}
MyThread::~MyThread()
{}

17
backup-daemon/mythread.h Executable file
View File

@ -0,0 +1,17 @@
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(QObject *parent = nullptr);
virtual ~MyThread();
signals:
void cancelWork();
};
#endif // MYTHREAD_H

600
backup-daemon/parsebackuplist.cpp Executable file
View File

@ -0,0 +1,600 @@
#include "parsebackuplist.h"
#include <QFile>
#include <QTextStream>
#include <QXmlStreamReader>
#include <QDebug>
#include <algorithm>
/*
<?xml version='1.0'?>
<backupList>
<BackupPoint>
<Comment>21-07-21 14:14:01</Comment>
<Time>21-07-21 14:14:03</Time>
<Uuid>{beecb746-561f-4fa1-99ba-19fb849a1ba7}</Uuid>
<Size>24.26KB</Size>
<State>backup finished</State>
<Type>2</Type>
<Position>0</Position>
<UserId>1000</UserId>
</BackupPoint>
</backupList>
*/
#define BACKUPLIST "backupList"
#define BACKUPPOINT "BackupPoint"
#define COMMENT "Comment"
#define TIME "Time"
#define UUID "Uuid"
#define SIZE "Size"
#define STATE "State"
#define TYPE "Type"
#define POSITION "Position"
#define USERID "UserId"
#define OS "OS"
#define ARCH "Arch"
#define ARCHDETECT "ArchDetect"
#define PREFIXDESTPATH "PrefixDestPath"
#define STATUE_BACKUP_FINESHED "backup finished"
ParseBackupList::ParseBackupList(const QString& xmlPath)
: m_xmlPath(xmlPath)
{
QFile xmlFile(m_xmlPath);
if (!xmlFile.exists() || 0 == xmlFile.size()) {
InitXml();
}
}
/**
* @brief xml文件
* @return
*/
bool ParseBackupList::InitXml()
{
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
return false;
QDomDocument doc;
QDomProcessingInstruction ins = doc.createProcessingInstruction("xml", "version=\'1.0\'");
doc.appendChild(ins);
QDomElement root = doc.createElement(BACKUPLIST);
doc.appendChild(root);
QTextStream out(&xmlFile);
doc.save(out, QDomNode::NodeType::CDATASectionNode);
xmlFile.close();
return true;
}
/**
* @brief xml格式是否正确
* @return
*/
bool ParseBackupList::isXmlCorrect()
{
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Open file " << m_xmlPath << " failure";
return false;
}
QXmlStreamReader reader(&xmlFile);
while (!reader.atEnd()) {
if (reader.isStartElement()) {
}
reader.readNext();
}
if (reader.hasError()) {
qDebug() << "xml parse error!";
xmlFile.close();
return false;
}
xmlFile.close();
return true;
}
/**
* @brief xml文件
* @param doc
* @return
*/
bool ParseBackupList::Doc_setContent(QDomDocument& doc)
{
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::ReadOnly)) {
qDebug() << "open backuplist.xml failed!";
return false;
}
QString errStr;
int errLine;
int errCol;
if (!doc.setContent(&xmlFile, false, &errStr, &errLine, &errCol)) {
qDebug() << QString("parse backuplist.xml error at line %1, column %2:%3").arg(errLine).arg(errCol).arg(errStr);
xmlFile.close();
return false;
}
xmlFile.close();
return true;
}
/**
* @brief ParseBackupList::BackupPoint::operator <
* @param other
* @return
*/
bool ParseBackupList::BackupPoint::operator < (const BackupPoint &other)
{
return other.m_time > this->m_time;
}
/**
* @brief backuplist.xml
* @param factoryBackupUuiduuid
* @return ParseResult枚举类型
* @author zhaominyong
* @since 2021/06/27
*/
ParseBackupList::ParseResult ParseBackupList::updateForFactoryRestore(const QString& factoryBackupUuid)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return XML_PARSE_ERR;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue ;
QDomElement element = node.toElement();
QDomNodeList nodes = element.elementsByTagName(UUID);
if (0 < nodes.count()) {
QDomElement uuidElement = nodes.at(0).toElement();
QString tag = uuidElement.tagName();
QString text = uuidElement.text();
if (uuidElement.text() != factoryBackupUuid) {
root.removeChild(node);
--i;
}
}
}
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::WriteOnly)) {
qDebug() << "update state failed";
return FAIL;
}
QTextStream out(&xmlFile);
doc.save(out, QDomNode::NodeType::EntityReferenceNode);
out.flush();
xmlFile.close();
return SUCCESS;
}
/**
* @brief comment查找uuid
* @param comment
* @return uuid
*/
QString ParseBackupList::findUuidByComment(const QString& comment)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return "";
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleComment = node.firstChildElement(COMMENT);
if (eleComment.isNull())
continue;
if (comment != eleComment.text())
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull())
return "";
return eleUuid.text();
}
return "";
}
/**
* @brief Uuid查找备份点信息
* @param Uuid
* @return
*/
ParseBackupList::BackupPoint ParseBackupList::findBackupPointByUuid(const QString& Uuid)
{
BackupPoint backupPoint;
QDomDocument doc;
if (!Doc_setContent(doc))
return backupPoint;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull() || Uuid != eleUuid.text())
continue;
elementNodeToBackupPoint(node.toElement(), backupPoint);
return backupPoint;
}
return backupPoint;
}
/**
* @brief xml文件中备份点uuid和backupname的映射
* @param uuid_namexml文件中备份点uuid和backupname的映射
* @return
*/
void ParseBackupList::getXmlUuidNameMap(QMap<QString, QString> &uuid_name)
{
BackupPoint backupPoint;
QDomDocument doc;
if (!Doc_setContent(doc))
return ;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull() || eleUuid.text().isEmpty())
continue;
QDomElement eleBackupName = node.firstChildElement(COMMENT);
if (eleBackupName.isNull() || eleBackupName.text().isEmpty())
continue;
uuid_name.insert(eleUuid.text(), eleBackupName.text());
}
return ;
}
/**
* @brief
* @param customizePaths
*/
void ParseBackupList::getCustomizePaths(QStringList &customizePaths)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return ;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement elePrefixPath = node.firstChildElement(PREFIXDESTPATH);
if (!elePrefixPath.isNull()) {
customizePaths << elePrefixPath.text() + BACKUP_SNAPSHOTS_PATH;
}
}
}
/**
* @brief
* @return
*/
ParseBackupList::BackupPoint ParseBackupList::getLastSysBackupPoint()
{
BackupPoint backupPoint;
QDomDocument doc;
if (!Doc_setContent(doc))
return backupPoint;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleType = node.firstChildElement(TYPE);
if (eleType.isNull() || (BackupType::BACKUP_SYSTEM != eleType.text().toInt() && BackupType::INC_BACKUP_SYSTEM != eleType.text().toInt()))
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull() || eleUuid.text() == AUTO_BACKUP_UUID)
continue;
QDomElement eleState = node.firstChildElement(STATE);
if (eleState.isNull() || eleState.text() != QString(STATUE_BACKUP_FINESHED))
continue;
QDomElement elePosition = node.firstChildElement(POSITION);
if (!elePosition.isNull() && elePosition.text().toInt() == BackupPosition::CUSTOMIZE)
continue;
elementNodeToBackupPoint(node.toElement(), backupPoint);
}
return backupPoint;
}
/**
* @brief elementNode --> BackupPoint
* @param node, QDomElement
* @param backupPoint, BackupPoint
*/
void ParseBackupList::elementNodeToBackupPoint(const QDomElement& node, BackupPoint& backupPoint)
{
QDomElement eleUuid = node.firstChildElement(UUID);
if (!eleUuid.isNull())
backupPoint.m_uuid = eleUuid.text();
QDomElement eleComment = node.firstChildElement(COMMENT);
if (!eleComment.isNull())
backupPoint.m_backupName = eleComment.text();
if (backupPoint.m_uuid == FACTORY_BACKUP_UUID)
backupPoint.m_backupName = QObject::tr("factory backup");
QDomElement eleTime = node.firstChildElement(TIME);
if (!eleTime.isNull())
backupPoint.m_time = eleTime.text();
QDomElement eleSize = node.firstChildElement(SIZE);
if (!eleSize.isNull())
backupPoint.m_size = eleSize.text();
QDomElement eleState = node.firstChildElement(STATE);
if (!eleState.isNull())
backupPoint.m_state = eleState.text();
QDomElement eleType = node.firstChildElement(TYPE);
if (!eleType.isNull())
backupPoint.m_type = eleType.text().toInt();
QDomElement elePosition = node.firstChildElement(POSITION);
if (!elePosition.isNull() && !elePosition.text().isEmpty())
backupPoint.m_iPosition = elePosition.text().toInt();
QDomElement eleUserId = node.firstChildElement(USERID);
if (!eleUserId.isNull())
backupPoint.m_userId = eleUserId.text();
QDomElement eleOS = node.firstChildElement(OS);
if (!eleOS.isNull())
backupPoint.m_os = eleOS.text();
QDomElement eleArch = node.firstChildElement(ARCH);
if (!eleArch.isNull())
backupPoint.m_arch = eleArch.text();
QDomElement eleArchDetect = node.firstChildElement(ARCHDETECT);
if (!eleArchDetect.isNull())
backupPoint.m_archdetect = eleArchDetect.text();
QDomElement elePrefixPath = node.firstChildElement(PREFIXDESTPATH);
if (!elePrefixPath.isNull()) {
backupPoint.m_path = elePrefixPath.text();
} else {
backupPoint.m_path = m_xmlPath;
backupPoint.m_path.replace(BACKUP_XML_PATH, "");
}
}
/**
* @brief backupPoint --> ElementNode
* @param backupPoint, BackupPoint
* @param node, QDomElement
*/
void ParseBackupList::backupPointToElementNode(const BackupPoint& backupPoint, QDomDocument& doc, QDomNode& node)
{
node.appendChild(createTextElement(doc, COMMENT, backupPoint.m_backupName));
node.appendChild(createTextElement(doc, TIME, backupPoint.m_time));
node.appendChild(createTextElement(doc, UUID, backupPoint.m_uuid));
node.appendChild(createTextElement(doc, SIZE, backupPoint.m_size));
node.appendChild(createTextElement(doc, STATE, backupPoint.m_state));
node.appendChild(createTextElement(doc, TYPE, QString::number(backupPoint.m_type)));
node.appendChild(createTextElement(doc, POSITION, QString::number(backupPoint.m_iPosition)));
if (!backupPoint.m_userId.isEmpty()) {
node.appendChild(createTextElement(doc, USERID, backupPoint.m_userId));
}
if (!backupPoint.m_os.isEmpty()) {
node.appendChild(createTextElement(doc, OS, backupPoint.m_os));
}
if (!backupPoint.m_arch.isEmpty()) {
node.appendChild(createTextElement(doc, ARCH, backupPoint.m_arch));
}
if (!backupPoint.m_archdetect.isEmpty()) {
node.appendChild(createTextElement(doc, ARCHDETECT, backupPoint.m_archdetect));
}
if (!backupPoint.m_path.isEmpty()) {
node.appendChild(createTextElement(doc, PREFIXDESTPATH, backupPoint.m_path));
}
}
/**
* @brief createTextElement
* @param tagName
* @param text
* @return <tagName>text</tagName>
*/
QDomElement ParseBackupList::createTextElement(QDomDocument& doc, const QString& tagName, const QString& text)
{
QDomElement node = doc.createElement(tagName);
QDomText textNode = doc.createTextNode(text);
node.appendChild(textNode);
return node;
}
/**
* @brief
* @param backupPoint,
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseBackupList::ParseResult ParseBackupList::addItem(const BackupPoint& backupPoint)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return XML_PARSE_ERR;
QDomElement backupPointNode = doc.createElement(BACKUPPOINT);
backupPointToElementNode(backupPoint, doc, backupPointNode);
QDomElement root = doc.documentElement();
root.appendChild(backupPointNode);
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
return FAIL;
QTextStream out(&xmlFile);
doc.save(out, QDomNode::NodeType::CDATASectionNode);
xmlFile.close();
return SUCCESS;
}
/**
* @brief
* @param backupPoint
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseBackupList::ParseResult ParseBackupList::updateItem(const BackupPoint & backupPoint)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return XML_PARSE_ERR;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
int i = 0;
for (; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull() || backupPoint.m_uuid != eleUuid.text())
continue;
break ;
}
// 找到了旧节点
if (i < list.count()) {
// 移除旧节点,更新后的节点放到最后
root.removeChild(list.at(i));
QDomElement newNode = doc.createElement(BACKUPPOINT);
backupPointToElementNode(backupPoint, doc, newNode);
root.appendChild(newNode);
}
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
return FAIL;
}
QTextStream out(&xmlFile);
doc.save(out, QDomNode::NodeType::EntityReferenceNode);
xmlFile.close();
return SUCCESS;
}
/**
* @brief
* @param uuid
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseBackupList::ParseResult ParseBackupList::deleteItem(const QString& uuid)
{
QDomDocument doc;
if (!Doc_setContent(doc))
return XML_PARSE_ERR;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull() || uuid != eleUuid.text())
continue;
root.removeChild(node);
break ;
}
QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
return FAIL;
}
QTextStream out(&xmlFile);
doc.save(out, QDomNode::NodeType::EntityReferenceNode);
xmlFile.close();
return SUCCESS;
}
/**
* @brief
* @return
*/
QList<ParseBackupList::BackupPoint> ParseBackupList::getBackupPointList()
{
QList<ParseBackupList::BackupPoint> backupPointList;
QDomDocument doc;
if (!Doc_setContent(doc))
return backupPointList;
QDomElement root = doc.documentElement();
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.count(); i++) {
QDomNode node = list.at(i);
if (!node.isElement())
continue;
QDomElement eleUuid = node.firstChildElement(UUID);
if (eleUuid.isNull())
continue;
BackupPoint backupPoint;
elementNodeToBackupPoint(node.toElement(), backupPoint);
backupPointList << backupPoint;
}
// std::sort(backupPointList.begin(), backupPointList.end());
return backupPointList;
}

138
backup-daemon/parsebackuplist.h Executable file
View File

@ -0,0 +1,138 @@
#ifndef PARSEBACKUPLIST_H
#define PARSEBACKUPLIST_H
#include <QString>
#include <QDomDocument>
#include <QDomElement>
#include <QList>
#include "../common/mydefine.h"
class ParseBackupList {
public:
enum ParseResult {
// xml解析错误
XML_PARSE_ERR,
// 失败
FAIL,
// 成功
SUCCESS,
};
struct BackupPoint {
// 备份或还原指定的UUID
QString m_uuid;
// 备份名称用来替换m_comment
QString m_backupName;
// 备份时间
QString m_time;
// 本地备份还是U盘备份: 0-本地备份1-移动设备备份2-异机备份仅用于业务场景标记不用于持久化记录3-自定义路径备份
int m_iPosition = -1;
// 操作类型,如:系统备份, 系统还原
int m_type = -1;
// 备份大小
QString m_size;
// 备份状态,如是否完成
QString m_state;
// 备份用户id
QString m_userId;
// 备份机器操作系统
QString m_os;
// 备份机器架构
QString m_arch;
// 备份机器引导方式
QString m_archdetect;
// 备份点所在设备挂载路径(这个暂只在查询中界面展示选择中使用)或自定义的备份路径
QString m_path;
bool isNull() { return m_uuid.isEmpty(); }
bool operator< (const BackupPoint &other);
};
ParseBackupList(const QString& xmlPath);
/**
* @brief backuplist.xml
* @param factoryBackupUuiduuid
* @return ParseResult枚举类型
* @author zhaominyong
* @since 2021/06/27
*/
ParseResult updateForFactoryRestore(const QString &factoryBackupUuid);
/**
* @brief comment查找uuid
* @param comment
* @return uuid
*/
QString findUuidByComment(const QString& comment);
/**
* @brief Uuid查找备份点信息
* @param Uuid
* @return
*/
BackupPoint findBackupPointByUuid(const QString& Uuid);
/**
* @brief xml文件中备份点uuid和backupname的映射
* @param uuid_namexml文件中备份点uuid和backupname的映射
* @return
*/
void getXmlUuidNameMap(QMap<QString, QString> &uuid_name);
/**
* @brief
* @return
*/
BackupPoint getLastSysBackupPoint();
/**
* @brief
* @param customizePaths
*/
void getCustomizePaths(QStringList &customizePaths);
/**
* @brief
* @param backupPoint,
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseResult addItem(const BackupPoint& backupPoint);
/**
* @brief
* @param backupPoint
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseResult updateItem(const BackupPoint & backupPoint);
/**
* @brief
* @param uuid
* @return SUCCESS成功 XML_PARSE_ERR失败
*/
ParseResult deleteItem(const QString& uuid);
/**
* @brief
* @return
*/
QList<BackupPoint> getBackupPointList();
inline QString getXmlPath() { return m_xmlPath; }
private:
bool InitXml();
bool isXmlCorrect();
bool Doc_setContent(QDomDocument& doc);
void elementNodeToBackupPoint(const QDomElement& eleNode, BackupPoint& backupPoint);
void backupPointToElementNode(const BackupPoint& backupPoint, QDomDocument& doc, QDomNode& eleNode);
QDomElement createTextElement(QDomDocument& doc, const QString& tagName, const QString& text);
private:
QString m_xmlPath;
};
#endif // PARSEBACKUPLIST_H

View File

@ -0,0 +1,529 @@
#include "systembackupproxy.h"
#include <QStorageInfo>
#include <QDateTime>
#include <QDebug>
#include <kysec/status.h>
#include "../common/utils.h"
#include "../common/mydusizetool.h"
#include "mymountproxy.h"
#include "myprocess/calcbackupsize.h"
IMPLEMENT_DYNCREATE(SystemBackupProxy)
SystemBackupProxy::SystemBackupProxy()
{
m_bSuccess = false;
m_isFinished = false;
m_p = nullptr;
m_size = 0;
m_isOnlyCheck = true;
m_calc = new CalcBackupSize(this);
connect(this, &SystemBackupProxy::cancel, this, &SystemBackupProxy::cancelEx);
}
SystemBackupProxy::~SystemBackupProxy()
{
delete m_p;
m_p = nullptr;
delete m_calc;
m_calc = nullptr;
}
/**
* @brief
* @return
*/
bool SystemBackupProxy::checkEnvEx()
{
qDebug() << "SystemBackupProxy::checkEnv invoke begin";
// 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备都得保证/backup挂载上); 若没挂载,挂载
MyMountProxy mountProxy;
if ( MountResult::MOUNTED != mountProxy.mountBackupPartition() ) {
emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL));
return false;
}
// 2、判断备份是否增量备份
isIncBackup();
// 3、检测空间是否满足备份
calcSizeForBackup();
qDebug() << "SystemBackupProxy::checkEnv invoke end";
return true;
}
/**
* @brief
*/
void SystemBackupProxy::doWorkEx()
{
qDebug() << "SystemBackupProxy::doWorkEx invoke begin";
m_isOnlyCheck = false;
// 环境检测
checkEnvEx();
qDebug() << "SystemBackupProxy::doWorkEx invoke end";
}
/**
* @brief
*/
void SystemBackupProxy::cancelEx()
{
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));
}
}
/**
* @brief
*/
void SystemBackupProxy::deleteFailedData()
{
if (m_curUuid.isEmpty())
return;
// 1、删除备份目录
QString destPath = Utils::getSysRootPath() + 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 SystemBackupProxy::isIncBackup()
{
QString backupPath;
ParseBackupList::BackupPoint point;
if (m_backupWrapper.m_uuid.isEmpty()) {
QString xmlPath(Utils::getSysRootPath() + BACKUP_XML_PATH);
xmlPath.replace("//", "/");
ParseBackupList parser(xmlPath);
point = parser.getLastSysBackupPoint();
if (point.m_uuid.isEmpty())
return false;
backupPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + point.m_uuid + "/data";
} else {
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
m_backupPoint = parse.findBackupPointByUuid(m_backupWrapper.m_uuid);
backupPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
}
backupPath.replace("//", "/");
if (Utils::isDirExist(backupPath) && !m_backupPoint.m_backupName.isEmpty()) {
m_backupWrapper.m_baseUuid = point.m_uuid;
m_backupWrapper.m_bIncrement = true;
m_backupWrapper.m_type = BackupType::INC_BACKUP_SYSTEM;
return true;
}
return false;
}
/**
* @brief
*/
void SystemBackupProxy::checkFreeCapacity(qint64 itotalSize)
{
qDebug() << "SystemBackupProxy::checkFreeCapacity invoke begin";
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
// 1、计算待备份数据的大小
m_size = itotalSize;
// 备份过程中会有一些临时文件产生会占用一部分空间故我们预留500M的空间
itotalSize += 500 * MB;
// 2、计算备份分区剩余空间大小
QString backupPath(Utils::getSysRootPath() + BACKUP_PATH);
backupPath.replace("//", "/");
QStorageInfo backupDisk(backupPath);
qint64 freeSize = backupDisk.bytesAvailable();
qDebug() << "需要空间" << itotalSize << ", 剩余空间" << freeSize;
// 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、开始备份
doBackup();
qDebug() << "SystemBackupProxy::checkFreeCapacity invoke end";
}
/**
* @brief
* @return
*/
void SystemBackupProxy::calcSizeForBackup()
{
QString destPath = Utils::getSysRootPath();
QStringList args;
if (m_backupWrapper.m_bIncrement) {
if (m_backupWrapper.m_uuid.isEmpty()) {
// 新增增量备份点场景
args = getRsyncArgs(SystemBackupScene::TRY_INC_SYSTEM_BACKUP);
destPath += CHECK_PATH;
} else {
// 在原来的备份点上增量备份场景
args = getRsyncArgs(SystemBackupScene::TRY_INC_SYSTEM_BACKUP_AT_BASE);
destPath += BACKUP_SNAPSHOTS_PATH;
destPath += "/";
destPath += m_backupWrapper.m_uuid;
destPath += "/data/";
}
} else {
// 全量备份场景
args = getRsyncArgs(SystemBackupScene::TRY_SYSTEM_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, &SystemBackupProxy::checkFreeCapacity);
m_calc->start(args, false);
}
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList SystemBackupProxy::getRsyncArgs(SystemBackupScene scene)
{
QStringList args;
switch (scene) {
case SystemBackupScene::SYSTEM_BACKUP :
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
break ;
case SystemBackupScene::INC_SYSTEM_BACKUP :
args << "-avAXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << QString("--link-dest=../../%1/data").arg(m_backupWrapper.m_baseUuid);
break ;
case SystemBackupScene::INC_SYSTEM_BACKUP_AT_BASE :
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
break ;
case SystemBackupScene::TRY_SYSTEM_BACKUP :
args << "-aAHXrn";
args << "--stats";
args << "--ignore-missing-args";
break ;
case SystemBackupScene::TRY_INC_SYSTEM_BACKUP :
args << "-aAXrn";
args << "--stats";
args << "--ignore-missing-args";
args << QString("--link-dest=../../%1/data").arg(m_backupWrapper.m_baseUuid);
break ;
case SystemBackupScene::TRY_INC_SYSTEM_BACKUP_AT_BASE :
args << "-aAHXrn";
args << "--stats";
args << "--ignore-missing-args";
args << "--delete";
break ;
case SystemBackupScene::EFI_BACKUP :
args << "-avAHXr";
args << "--info=progress2";
args << "--ignore-missing-args";
break ;
default:
return args;
}
if (SystemBackupScene::EFI_BACKUP != scene) {
// --exclude=排除路径设置
for (const QString & item : m_backupWrapper.m_backupExcludePaths) {
args << QString("--exclude=%1").arg(item);
}
}
// 系统更新备份不再备份用户数据目录
if (AUTO_BACKUP_UUID == m_curUuid) {
if (Utils::isHuawei990()) {
args << "--exclude=/data";
} else {
args << "--exclude=/data/usershare";
}
args << "--exclude=/home";
args << "--exclude=/data/home";
args << "--exclude=/root";
args << "--exclude=/data/root";
}
return args;
}
/**
* @brief
*/
void SystemBackupProxy::doBackup()
{
qDebug() << "SystemBackupProxy::doBackup invoke begin";
// 准备
if (!doPrepare())
return ;
// 启动备份efi, 修改为和其它目录统一备份,不再单独进行备份
// if (!backupEfi()) {
// emit checkResult(int(BackupResult::EFI_RSYNC_FAIL));
// return ;
// }
// 启动系统备份
backupSystem();
qDebug() << "SystemBackupProxy::doBackup invoke end";
}
/**
* @brief
* @return true,false
*/
bool SystemBackupProxy::doPrepare()
{
qDebug() << "SystemBackupProxy::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 = Utils::getSysRootPath() + 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;
}
QString userFile = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + PATHS_USER_FILE;
userFile.replace("//", "/");
if (!Utils::writeFileByLines(userFile, m_backupWrapper.m_backupPaths)) {
emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
qCritical() << QString("create file %1 failed !").arg(userFile);
return false;
}
QString excludeUserFile = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + EXCLUDE_PATHS_USER_FILE;
excludeUserFile.replace("//", "/");
if (!Utils::writeFileByLines(excludeUserFile, m_backupWrapper.m_backupExcludePaths)) {
emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
qCritical() << QString("create file %1 failed !").arg(excludeUserFile);
return false;
}
// 3、记录/backup/snapshots/backuplist.xml文件
if (!recordBackupPoint()) {
qCritical() << "add or update item to backuplist.xml failed !";
return false;
}
qDebug() << "SystemBackupProxy::doPrepare invoke end";
return true;
}
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool SystemBackupProxy::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;
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
if (m_backupWrapper.m_uuid.isEmpty() || !m_backupWrapper.m_bIncrement) {
if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL));
return false;
}
} else {
if (parse.updateItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_UPDATE_ITEM_FAIL));
return false;
}
}
return true;
}
/**
* @brief /boot/efi
* @return truefalse
*/
bool SystemBackupProxy::backupEfi()
{
qDebug() << "SystemBackupProxy::backupEfi invoke begin";
// /boot/efi分区是自动挂载的这里不用去挂载
QString efiPath = Utils::getSysRootPath() + "/boot/efi";
efiPath.replace("//", "/");
QStringList args;
if (Utils::isDirExist(efiPath)) {
args = getRsyncArgs(SystemBackupScene::EFI_BACKUP);
}
args << efiPath;
QString destPath = m_destPath + "/boot/efi";
destPath.replace("//", "/");
Utils::mkpath(destPath);
args << destPath;
m_p = new RsyncPathToDirProcess(this);
bool result = m_p->start(args);
delete m_p;
m_p = nullptr;
qDebug() << "SystemBackupProxy::backupEfi invoke end";
return result;
}
/**
* @brief
* @return truefalse
*/
bool SystemBackupProxy::backupSystem()
{
qDebug() << "SystemBackupProxy::backupSystem invoke begin";
QStringList args;
if (m_backupWrapper.m_bIncrement) {
if (m_backupWrapper.m_uuid.isEmpty()) {
// 新增增量备份点场景
args = getRsyncArgs(SystemBackupScene::INC_SYSTEM_BACKUP);
} else {
// 在原来的备份点上增量备份场景
args = getRsyncArgs(SystemBackupScene::INC_SYSTEM_BACKUP_AT_BASE);
}
} else {
// 全量备份场景
args = getRsyncArgs(SystemBackupScene::SYSTEM_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, &SystemBackupProxy::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
+ ",," + 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() << "SystemBackupProxy::backupSystem invoke end";
return true;
}
void SystemBackupProxy::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();
}
}

View File

@ -0,0 +1,99 @@
#ifndef SYSTEMBACKUPPROXY_H
#define SYSTEMBACKUPPROXY_H
#include "workerfactory.h"
#include "myprocess/calcbackupsize.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class SystemBackupProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(SystemBackupProxy)
public:
// 系统备份的几种场景
enum SystemBackupScene {
SYSTEM_BACKUP, // 系统备份
INC_SYSTEM_BACKUP, // 增量系统备份
INC_SYSTEM_BACKUP_AT_BASE, // 在原备份点里增量备份
TRY_SYSTEM_BACKUP, // 测试系统备份,可用于计算备份传输数据大小
TRY_INC_SYSTEM_BACKUP, // 测试增量系统备份,可用于计算备份传输数据大小
TRY_INC_SYSTEM_BACKUP_AT_BASE, // 测试增量系统备份,在原备份点里增量备份,可用于计算备份传输数据大小
EFI_BACKUP, // 备份/boot/efi
};
explicit SystemBackupProxy();
virtual ~SystemBackupProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
// 任务取消
virtual void cancelEx();
private:
// 判断是否增量备份
bool isIncBackup();
// 校验剩余空间是否满足备份
void checkFreeCapacity(qint64 itotalSize);
// 计算备份所需空间大小
void calcSizeForBackup();
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList getRsyncArgs(SystemBackupScene scene);
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool recordBackupPoint();
// 备份准备
bool doPrepare();
// 备份
void doBackup();
// 备份/boot/efi
bool backupEfi();
// 备份系统
bool backupSystem();
void do_kylin_security(const QString& dstDir);
// 失败则删除相应数据
void deleteFailedData();
// 是否只是检测
bool m_isOnlyCheck;
// 是否备份成功
bool m_bSuccess;
// 是否完成
bool m_isFinished;
// 当前备份uuid
QString m_curUuid;
// 当前备份目标目录
QString m_destPath;
// 当前备份所需空间大小
qint64 m_size;
// 计算备份空间大小的进程
CalcBackupSize *m_calc;
// 备份进程
RsyncPathToDirProcess *m_p;
// 当前备份节点
ParseBackupList::BackupPoint m_backupPoint;
};
#endif // SYSTEMBACKUPPROXY_H

View File

@ -0,0 +1,510 @@
#include "systemrestoreproxy.h"
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include <unistd.h>
#include <sys/reboot.h>
#include "../common/utils.h"
#include "mymountproxy.h"
IMPLEMENT_DYNCREATE(SystemRestoreProxy)
/**
* @brief
*/
SystemRestoreProxy::SystemRestoreProxy()
{
m_bSuccess = false;
m_p = nullptr;
m_bRetainUserData = false;
}
/**
* @brief
*/
SystemRestoreProxy::~SystemRestoreProxy()
{
delete m_p;
}
/**
* @brief
* @return false,;true,
*/
bool SystemRestoreProxy::checkEnvEx()
{
qDebug() << "SystemRestoreProxy::checkEnvEx invoke begin";
// 1、检测.user.txt是否存在
m_userFile = Utils::getSysRootPath() + 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;
}
// 2、检测.exclude.user.txt是否存在
m_excludeUserFile = Utils::getSysRootPath() + 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;
}
// 3、检测还原点是否存在
m_backupPath = Utils::getSysRootPath() + 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;
}
// 4、检测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;
}
m_curUuid = m_backupWrapper.m_uuid;
emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS));
qDebug() << "SystemRestoreProxy::checkEnvEx invoke end";
return true;
}
/**
* @brief
*/
void SystemRestoreProxy::doWorkEx()
{
qDebug() << "SystemRestoreProxy::doWorkEx invoke begin";
// 1、校验
if (!checkEnvEx())
return ;
// 2、还原efi(兼容旧版本的备份)
if (!restoreEfi()) {
qCritical("/boot/efi目录同步失败");
emit checkResult(int(BackupResult::EFI_RSYNC_FAIL));
return ;
}
// 3、还原系统
restoreSystem();
qDebug() << "SystemRestoreProxy::doWorkEx invoke end";
}
/**
* @brief efi()
* @return
*/
bool SystemRestoreProxy::restoreEfi()
{
qDebug() << "SystemRestoreProxy::restoreEfi invoke begin";
// 是否有/boot/efi目录
QString efiPath = Utils::getSysRootPath() + "/boot/efi";
efiPath.replace("//", "/");
if (!Utils::isDirEmpty(efiPath)) {
// 1、修复源数据
repairEfi();
sync();
// 2、重新rw读写挂载
remountEfi();
// 3、同步efi
return rsyncEfi();
}
qDebug() << "SystemRestoreProxy::restoreEfi invoke end";
return true;
}
/**
* @brief efi目录
*/
void SystemRestoreProxy::repairEfi()
{
QString qsEfiPath = m_backupPath + "/efi";
if (!Utils::isDirEmpty(qsEfiPath)) {
// 存在/efi说明是老备份数据尽量修正老数据
QStringList args;
args << "-f";
args << qsEfiPath;
QString newPath = m_backupPath + "/boot";
args << newPath;
QProcess::execute("mv", args);
}
}
/**
* @brief rw读写挂载efi分区
*/
void SystemRestoreProxy::remountEfi()
{
QString mountPath = Utils::getSysRootPath() + "/boot/efi";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/**
* @brief rw读写挂载boot分区
*/
void SystemRestoreProxy::remountBoot()
{
QString mountPath = Utils::getSysRootPath() + "/boot";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/**
* @brief efi
*/
bool SystemRestoreProxy::rsyncEfi()
{
QString efiPath = m_backupPath + "/boot/efi/";
if (Utils::isDirEmpty(efiPath))
efiPath = efiPath = m_backupPath + "/efi/";
if (Utils::isDirEmpty(efiPath))
return true;
QStringList args = getRsyncArgs(SystemRestoreScene::EFI_RESTORE);
QString mountPath = Utils::getSysRootPath() + "/boot/efi/";
mountPath.replace("//", "/");
args << efiPath << mountPath;
m_p = new RsyncPathToDirProcess(this);
bool result = m_p->start(args);
delete m_p;
m_p = nullptr;
return result;
}
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList SystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
{
QStringList args;
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
QDir dataDir(m_srcPath + "/data");
QFile file(m_srcPath + "/etc/uid_list");
QDir efiDir(m_srcPath + "/boot/efi");
QStringList excludes;
switch (scene) {
case SystemRestoreScene::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 SystemRestoreScene::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";
// 以前的出厂备份和grub备份没有备份/data还原时需要判断/data是否存在如不存在需要屏蔽掉不然会将主机上的/data删除造成问题
// 此为兼容以前备份的老数据而改,等以后老的备份估计不存在了可已去掉
if (!dataDir.exists()) {
args << "--exclude=/data";
}
if (!file.exists()) {
args << "--exclude=/etc/uid_list";
}
// 为兼容以前的老备份数据,增加下面几行
if (efiDir.isEmpty()) {
args << QString("--exclude=/boot/efi");
}
// 系统安装后有的会将/data/home /data/root挂载到的/home /root上实际文件是存放在/data/home /data/root下面
Utils::excludeFstabBindPath(excludes);
// 自定义备份的路径也需要跳过,不进行还原
Utils::excludeCustomizePath(excludes);
for (const QString& item : excludes) {
QDir itemDir(m_srcPath + item);
// 以后统一用/home /root这种路径 兼容老备份数据原来的U盘备份在mksquashfs时排除bind挂载的任意一方时都备份不上
if (item == "/data/home") {
QDir homeDir(m_srcPath + "/home");
if (!homeDir.isEmpty()) {
args << QString("--exclude=/data/home");
} else if (!itemDir.isEmpty()) {
args << QString("--exclude=/home");
} else {
args << QString("--exclude=/data/home");
args << QString("--exclude=/home");
}
continue;
} else if (item == "/data/root") {
QDir homeDir(m_srcPath + "/root");
if (!homeDir.isEmpty()) {
args << QString("--exclude=/data/root");
} else if (!itemDir.isEmpty()) {
args << QString("--exclude=/root");
} else {
args << QString("--exclude=/data/root");
args << QString("--exclude=/root");
}
continue;
}
args << QString("--exclude=") + item;
}
args << "--exclude-from" << m_excludeUserFile;
args << "--files-from" << m_userFile;
break ;
case SystemRestoreScene::EFI_RESTORE :
break ;
default:
return args;
}
return args;
}
/**
* @brief
*/
void SystemRestoreProxy::restoreSystem()
{
// 停止安全防护
QProcess::execute("systemctl stop kysec-init.service");
// 本地系统备份没有img挂载故下面两个路径相等
m_srcPath = m_backupPath;
QString destPath = Utils::getSysRootPath();
// 以读写方式重新挂载boot分区因为有的机器默认以只读挂载
remountBoot();
QStringList args;
// 自动更新的备份还原时保留用户数据
if (m_curUuid == AUTO_BACKUP_UUID || m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM_WITH_DATA) {
args = getRsyncArgs(SystemRestoreScene::RESTORE_SYSTEM_WITH_DATA);
m_bRetainUserData = true;
} else {
args = getRsyncArgs(SystemRestoreScene::SYSTEM_RESTORE);
}
args << m_srcPath + "/";
destPath += "/";
destPath.replace("//", "/");
args << destPath;
m_p = new RsyncPathToDirProcess(this);
connect(m_p, &RsyncPathToDirProcess::progress, this, &SystemRestoreProxy::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;
}
if (FACTORY_BACKUP_UUID == m_curUuid && m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM) {
// 出厂还原有的机器上删除/home/xxx有残留故在此再强制删除一下sudo rm -rf命令一遍还删除不了报错无法删除/home/xx/.config:目录非空,应该是删除后又自动生成了),多删除几次还不是非常干净,^_^
removeHome(QString(Utils::getSysRootPath() + "/home").replace("//", "/"));
removeHome(QString(Utils::getSysRootPath() + "/data/home").replace("//", "/"));
}
QProcess::execute("sync");
Utils::wait(5);
emit this->workResult(result);
Utils::wait(2);
reboot(RB_AUTOBOOT);
} else {
emit this->workResult(result);
}
});
m_p->start(args, false);
}
/**
* @brief
* @param void
* @return
*/
QStringList SystemRestoreProxy::getBackupPointUsers()
{
// 根据备份点里的/home或/data/home的子目录进行统计
QString homePath = m_backupPath + "/home";
homePath.replace("//","/");
QStringList users = getBackupPointUsers(homePath);
QString dataHomePath = m_backupPath + "/data/home";
dataHomePath.replace("//","/");
users.append(getBackupPointUsers(dataHomePath));
return users;
}
QStringList SystemRestoreProxy::getBackupPointUsers(const QString& path)
{
QDir dir(path);
if (dir.exists()) {
return dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs);
} else {
return QStringList();
}
}
/**
* @brief home子目录
* @param path
* @return
*/
void SystemRestoreProxy::removeHome(const QString& path)
{
qDebug() << "SystemRestoreProxy::removeHome invoke begin";
QDir dir(path);
if (dir.exists()) {
QStringList retainUsers = getBackupPointUsers();
qDebug() << retainUsers;
QString subcmd("rm -rf ");
QStringList list = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs);
for (const QString& item : list) {
// 出厂备份里面的/home/oem等保留
if (item == "oem")
continue ;
if (retainUsers.contains(item))
continue ;
QString subPath = path;
subPath += "/";
subPath += item;
QDir subDir(subPath);
subDir.removeRecursively();
qDebug() << (subcmd + subPath);
QProcess::execute(subcmd + subPath);
}
// bool needRm = false;
// QString subcmd("rm -rf ");
// QString delDirs;
// QStringList list = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs);
// for (const QString& item : list) {
// // 出厂备份里面的/home/oem等保留
// if (item == "oem")
// continue ;
// if (retainUsers.contains(item))
// continue ;
// QString subPath = path;
// subPath += "/";
// subPath += item;
// QDir subDir(subPath);
// subDir.removeRecursively();
// delDirs += "${rootmnt}";
// delDirs += path;
// delDirs += "/";
// delDirs += item;
// delDirs += " ";
// needRm = true;
// }
// if (needRm) {
// QString cmd = QString("bash -c \"%1\" ").arg(subcmd + delDirs);
// cmd.replace("${rootmnt}", "");
// QProcess::execute(cmd);
// }
}
qDebug() << "SystemRestoreProxy::removeHome invoke end";
}

View File

@ -0,0 +1,77 @@
#ifndef SYSTEMRESTOREPROXY_H
#define SYSTEMRESTOREPROXY_H
#include "workerfactory.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class SystemRestoreProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(SystemRestoreProxy)
public:
// 系统还原的几种场景
enum SystemRestoreScene {
SYSTEM_RESTORE, // 系统还原
RESTORE_SYSTEM_WITH_DATA, // 保留用户数据还原
EFI_RESTORE, // efi还原
};
explicit SystemRestoreProxy();
virtual ~SystemRestoreProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
private:
// 还原efi
bool restoreEfi();
// 修复efi目录
void repairEfi();
// 以读写方式重新挂载efi分区
void remountEfi();
// 以读写方式重新挂载boot分区
void remountBoot();
// 同步efi
bool rsyncEfi();
// 系统还原
void restoreSystem();
// 删除home下的用户家目录
void removeHome(const QString& path);
// 获取备份点里存在家目录的用户列表
QStringList getBackupPointUsers();
QStringList getBackupPointUsers(const QString& path);
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList getRsyncArgs(SystemRestoreScene scene);
// .user.txt文件路径
QString m_userFile;
// .exclude.user.txt文件路径
QString m_excludeUserFile;
// 备份数据所在的data目录
QString m_backupPath;
// 是否还原成功
bool m_bSuccess;
// 当前备份uuid
QString m_curUuid;
// 当前还原源目录
QString m_srcPath;
// 备份进程
RsyncPathToDirProcess *m_p;
// 当前备份节点
ParseBackupList::BackupPoint m_backupPoint;
// 是否保留用户数据
bool m_bRetainUserData;
};
#endif // SYSTEMRESTOREPROXY_H

View File

@ -0,0 +1,455 @@
#include "udiskdatabackupproxy.h"
#include <QStorageInfo>
#include <QDateTime>
#include <QDebug>
#include <QTimer>
#include <kysec/status.h>
#include "../common/utils.h"
#include "../common/mydusizetool.h"
#include "mymountproxy.h"
#include "myprocess/calcbackupsize.h"
IMPLEMENT_DYNCREATE(UDiskDataBackupProxy)
UDiskDataBackupProxy::UDiskDataBackupProxy()
{
m_isForce = false;
}
UDiskDataBackupProxy::~UDiskDataBackupProxy()
{
m_isForce = false;
}
/**
* @brief
* @return false,;true,
*/
bool UDiskDataBackupProxy::checkEnvEx()
{
qDebug() << "UDiskDataBackupProxy::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;
}
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;
}
QTimer::singleShot(1*1000, this, &UDiskDataBackupProxy::checkDestDirExists);
// 2、判断备份是否增量备份
isIncBackup();
// 3、检测空间是否满足备份
calcSizeForBackupToUdisk();
qDebug() << "UDiskDataBackupProxy::checkEnv invoke end";
return true;
}
/**
* @brief
*/
void UDiskDataBackupProxy::doWorkEx()
{
qDebug() << "UDiskDataBackupProxy::doWorkEx invoke begin";
m_isOnlyCheck = false;
// 环境检测
checkEnvEx();
qDebug() << "UDiskDataBackupProxy::doWorkEx invoke end";
}
/**
* @brief
*/
void UDiskDataBackupProxy::cancelEx()
{
qDebug() << "UDiskDataBackupProxy::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() << "UDiskDataBackupProxy::cancelEx invoke end";
}
/**
* @brief
*/
void UDiskDataBackupProxy::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 = m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
parse.deleteItem(m_curUuid);
}
/**
* @brief
* @return true, false
*/
bool UDiskDataBackupProxy::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 UDiskDataBackupProxy::checkFreeCapacityToUdisk(qint64 itotalSize)
{
qDebug() << "UDiskDataBackupProxy::checkFreeCapacityToUdisk invoke begin";
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return false;
// 拔掉U盘的场景
if (m_isForce) {
emit this->checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
return false;
}
// 1、计算待备份数据的大小
m_size = itotalSize;
// 备份过程中会有一些临时文件产生会占用一部分空间故我们预留500M的空间
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;
// 开始备份
doBackupToUdisk();
qDebug() << "UDiskDataBackupProxy::checkFreeCapacityToUdisk invoke end";
return result;
}
/**
* @brief
* @return ,
*/
void UDiskDataBackupProxy::calcSizeForBackupToUdisk()
{
QString destPath;
QStringList args;
if (m_backupWrapper.m_bIncrement) {
// 在原来的备份点上增量备份场景
args = getRsyncArgs(DataBackupScene::TRY_INC_DATA_BACKUP);
destPath = m_backupWrapper.m_prefixDestPath;
destPath += BACKUP_SNAPSHOTS_PATH;
destPath += "/";
destPath += m_backupWrapper.m_uuid;
destPath += "/data/";
} else {
// 全量备份场景
args = getRsyncArgs(DataBackupScene::TRY_DATA_BACKUP);
destPath = Utils::getSysRootPath();
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, &UDiskDataBackupProxy::checkFreeCapacityToUdisk);
m_calc->start(args, false);
}
/**
* @brief
*/
void UDiskDataBackupProxy::doBackupToUdisk()
{
qDebug() << "UDiskDataBackupProxy::doBackupToUdisk invoke begin";
// 准备
if (!doPrepareToUdisk())
return ;
// 启动数据备份
backupDataToUdisk();
qDebug() << "UDiskDataBackupProxy::doBackupToUdisk invoke end";
}
/**
* @brief
* @return true,false
*/
bool UDiskDataBackupProxy::doPrepareToUdisk()
{
qDebug() << "UDiskDataBackupProxy::doPrepareToUdisk 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 (!recordBackupPointToUdisk()) {
qCritical() << "add or update item to backuplist.xml failed !";
return false;
}
qDebug() << "UDiskDataBackupProxy::doPrepareToUdisk invoke end";
return true;
}
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool UDiskDataBackupProxy::recordBackupPointToUdisk()
{
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;
QString xmlPath = m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
if (!m_backupWrapper.m_bIncrement) {
if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL));
return false;
}
} else {
if (parse.updateItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_UPDATE_ITEM_FAIL));
return false;
}
}
return true;
}
/**
* @brief
* @return truefalse
*/
bool UDiskDataBackupProxy::backupDataToUdisk()
{
qDebug() << "UDiskDataBackupProxy::backupDataToUdisk invoke begin";
QStringList args;
if (m_backupWrapper.m_bIncrement) {
// 在原来的备份点上增量备份场景
args = getRsyncArgs(DataBackupScene::INC_DATA_BACKUP);
} else {
// 全量备份场景
args = getRsyncArgs(DataBackupScene::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, &UDiskDataBackupProxy::progress);
connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) {
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
m_isForce = false;
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 = m_backupWrapper.m_prefixDestPath + 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
+ "," + 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() << "UDiskDataBackupProxy::backupDataToUdisk invoke end";
return true;
}
/**
* @brief
* @return: bool,truefalse
* @author: zhaominyong
* @since: 2021/05/24
* @note:
* add by zhaominyong at 2021/05/24 for bug:54377 U盘的过程中拔出U盘
*/
bool UDiskDataBackupProxy::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);
m_isForce = true;
if (m_calc != nullptr)
m_calc->stop();
if (m_p != nullptr)
m_p->stop();
// 10s钟后如果还没有退出则强制退出
QTimer::singleShot(10*1000, this, &UDiskDataBackupProxy::checkDestDirExists);
} else {
QTimer::singleShot(1*1000, this, &UDiskDataBackupProxy::checkDestDirExists);
}
}
return true;
}

View File

@ -0,0 +1,68 @@
#ifndef UDISKDATABACKUPPROXY_H
#define UDISKDATABACKUPPROXY_H
#include "databackupproxy.h"
class UDiskDataBackupProxy : public DataBackupProxy
{
Q_OBJECT
DECLARE_DYNCREATE(UDiskDataBackupProxy)
public:
explicit UDiskDataBackupProxy();
virtual ~UDiskDataBackupProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
public slots:
// 任务取消
virtual void cancelEx();
private slots:
// 校验剩余空间是否满足备份
bool checkFreeCapacityToUdisk(qint64 itotalSize);
// 备份
void doBackupToUdisk();
/**
* @brief
* @return: bool,truefalse
* @author: zhaominyong
* @since: 2021/05/24
* @note:
* add by zhaominyong at 2021/05/24 for bug:54377 U盘的过程中拔出U盘
*/
bool checkDestDirExists();
protected:
// 失败则删除相应数据
virtual void deleteFailedData();
// 判断是否增量备份
virtual bool isIncBackup();
private:
// 计算备份所需空间大小
void calcSizeForBackupToUdisk();
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool recordBackupPointToUdisk();
// 备份准备
bool doPrepareToUdisk();
// 备份系统
bool backupDataToUdisk();
// 强制结束标志(stop后没反应的情况系统处于睡眠状态)
bool m_isForce;
};
#endif // UDISKDATABACKUPPROXY_H

View File

@ -0,0 +1,186 @@
#include "udiskdatarestoreproxy.h"
#include <QDateTime>
#include <QDir>
#include <QTimer>
#include <QDebug>
#include <unistd.h>
#include <sys/reboot.h>
#include "../common/utils.h"
#include "mymountproxy.h"
IMPLEMENT_DYNCREATE(UDiskDataRestoreProxy)
/**
* @brief
*/
UDiskDataRestoreProxy::UDiskDataRestoreProxy()
{
m_bSuccess = false;
m_isFinished = false;
m_p = nullptr;
m_isForce = false;
}
/**
* @brief
*/
UDiskDataRestoreProxy::~UDiskDataRestoreProxy()
{
delete m_p;
}
/**
* @brief
* @return false,;true,
*/
bool UDiskDataRestoreProxy::checkEnvEx()
{
qDebug() << "UDiskDataRestoreProxy::checkEnvEx invoke begin";
// 1、检测.user.txt是否存在
m_userFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + PATHS_USER_FILE;
if (!Utils::filsExists(m_userFile)) {
qCritical(".user.txt文件不存在");
emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
return false;
}
// 2、检测.exclude.user.txt是否存在
m_excludeUserFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + EXCLUDE_PATHS_USER_FILE;
if (!Utils::filsExists(m_excludeUserFile)) {
qCritical(".exclude.user.txt文件不存在");
emit checkResult(int(BackupResult::WRITE_EXCLUDE_BACKUP_PATHS_TO_USER_FAILED));
return false;
}
// 3、检测还原点是否存在
m_backupPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
if (Utils::isDirEmpty(m_backupPath)) {
qCritical("还原点{uuid}/data目录不存在");
emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR));
return false;
}
// 4、检测xml中的还原点是否还存在
QString xmlPath = m_backupWrapper.m_prefixDestPath + 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;
}
emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS));
qDebug() << "UDiskDataRestoreProxy::checkEnvEx invoke end";
return true;
}
/**
* @brief
*/
void UDiskDataRestoreProxy::doWorkEx()
{
qDebug() << "UDiskDataRestoreProxy::doWorkEx invoke begin";
// 1、校验
if (!checkEnvEx())
return ;
// 2、还原数据
restoreData();
qDebug() << "UDiskDataRestoreProxy::doWorkEx invoke end";
}
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList UDiskDataRestoreProxy::getRsyncArgs(DataRestoreScene scene)
{
QStringList args;
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
switch (scene) {
case DataRestoreScene::DATA_RESTORE :
args << "--files-from" << m_userFile;
break ;
default:
return args;
}
return args;
}
/**
* @brief
*/
void UDiskDataRestoreProxy::restoreData()
{
qDebug() << "UDiskDataRestoreProxy::restoreData invoke begin";
QStringList args = getRsyncArgs(DataRestoreScene::DATA_RESTORE);
m_srcPath = m_backupPath;
QString destPath = Utils::getSysRootPath();
destPath.replace("//", "/");
args << m_srcPath + "/";
args << destPath + "/";
m_p = new RsyncPathToDirProcess(this);
connect(m_p, &RsyncPathToDirProcess::progress, this, &UDiskDataRestoreProxy::progress);
connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) {
m_isForce = false;
m_bSuccess = result;
m_isFinished = true;
if (result) {
QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
Utils::writeBackupLog(time + "," + m_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid)+ "," + m_backupPoint.m_backupName);
}
emit this->workResult(result);
});
QTimer::singleShot(1*1000, this, &UDiskDataRestoreProxy::checkUdiskExists);
m_isFinished = false;
m_bSuccess = false;
m_p->start(args, false);
qDebug() << "UDiskDataRestoreProxy::restoreData invoke end";
}
/**
* @brief
* @return true-false-
*/
bool UDiskDataRestoreProxy::checkUdiskExists()
{
if (!m_isFinished) {
// 拔掉U盘后没有响应的场景怀疑可能是某应用程序关闭引起希望不是dbus服务关掉了
if (m_isForce) {
emit this->workResult(false);
return false;
}
if (Utils::isDirEmpty(m_backupPath)) {
if (m_p != nullptr)
m_p->stop();
// 10s钟后如果还没有退出则强制退出
QTimer::singleShot(10*1000, this, &UDiskDataRestoreProxy::checkUdiskExists);
m_isForce = true;
} else {
QTimer::singleShot(1*1000, this, &UDiskDataRestoreProxy::checkUdiskExists);
}
}
return true;
}

View File

@ -0,0 +1,63 @@
#ifndef UDISKDATARESTOREPROXY_H
#define UDISKDATARESTOREPROXY_H
#include "workerfactory.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class UDiskDataRestoreProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(UDiskDataRestoreProxy)
public:
// 数据还原的几种场景
enum DataRestoreScene {
DATA_RESTORE, // 数据还原
};
explicit UDiskDataRestoreProxy();
virtual ~UDiskDataRestoreProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
private slots:
bool checkUdiskExists();
private:
// 数据还原
void restoreData();
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList getRsyncArgs(DataRestoreScene scene);
// .user.txt文件路径
QString m_userFile;
// .exclude.user.txt文件路径
QString m_excludeUserFile;
// 备份数据所在的data目录
QString m_backupPath;
// 是否还原成功
bool m_bSuccess;
// 是否完成
bool m_isFinished;
// 当前还原源目录
QString m_srcPath;
// 还原进程
RsyncPathToDirProcess *m_p;
// 当前备份节点
ParseBackupList::BackupPoint m_backupPoint;
// 强制结束标志(stop后没反应的情况系统处于睡眠状态)
bool m_isForce;
};
#endif // UDISKDATARESTOREPROXY_H

View File

@ -0,0 +1,306 @@
#include "udiskghostImageproxy.h"
#include <QStorageInfo>
#include <QFileInfo>
#include <QDateTime>
#include <QTimer>
#include <QDebug>
#include <kysec/status.h>
#include <unistd.h>
#include "../common/utils.h"
#include "../common/mydusizetool.h"
#include "mymountproxy.h"
#include "myprocess/calcbackupsize.h"
IMPLEMENT_DYNCREATE(UDiskGhostImageProxy)
UDiskGhostImageProxy::UDiskGhostImageProxy()
{
m_mksquashfs = nullptr;
m_p = nullptr;
m_bSuccess = false;
m_isFinished = false;
m_isForce = false;
connect(this, &UDiskGhostImageProxy::cancel, this, &UDiskGhostImageProxy::cancelEx);
}
UDiskGhostImageProxy::~UDiskGhostImageProxy()
{
if (!m_kyimg.isEmpty()) {
QFile kyimg(m_kyimg);
if (kyimg.exists())
kyimg.remove();
}
}
/**
* @brief
*/
bool UDiskGhostImageProxy::checkEnvEx()
{
qDebug() << "UDiskGhostImageProxy::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);
ParseBackupList::BackupPoint backupPoint = xmlParse.findBackupPointByUuid(m_backupWrapper.m_uuid);
if (backupPoint.m_backupName.isEmpty()) {
emit checkResult(int(BackupResult::GHOST_CANNOT_FIND_BACKUPPOINT));
return false;
}
// 3、校验备份数据是否存在
QString dataPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
dataPath.replace("//", "/");
if (Utils::isDirEmpty(dataPath)) {
emit checkResult(int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST));
return false;
}
qint64 itotalSize = Utils::getDirOrFileSize(dataPath);
// 4、校验移动设备情况空间大小、文件格式、挂载模式等
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;
}
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 / 2) {
emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH));
return false;
}
// 5、依托本地存储先行压缩再拷贝到移动设备在此查找空闲分区临时借用
bool found = false;
QHash<QString, qint64> hash = Utils::getAvailableSizeOfPartitions();
for (QHash<QString, qint64>::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) {
QString path = it.key();
qint64 leftSize = it.value();
if (leftSize > itotalSize / 2) {
Utils::mkpath(path + GHOST_PATH);
m_kyimg = path + GHOST_PATH + "/" + m_backupWrapper.m_backupName;
m_kyimg.replace("//", "/");
QFile kyimg(m_kyimg);
if (kyimg.exists())
kyimg.remove();
found = true;
break ;
}
}
if (!found) {
emit checkResult(int(BackupResult::BACKUP_CAPACITY_FOR_UDISKIMG_IS_NOT_ENOUGH));
return false;
}
emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS));
qDebug() << "UDiskGhostImageProxy::checkEnvEx invoke end";
return true;
}
/**
* @brief
*/
void UDiskGhostImageProxy::doWorkEx()
{
qDebug() << "UDiskGhostImageProxy::doWorkEx invoke begin";
if (!checkEnvEx())
return ;
doGhostImage();
qDebug() << "UDiskGhostImageProxy::doWorkEx invoke end";
}
/**
* @brief
*/
void UDiskGhostImageProxy::cancelEx()
{
m_bCancel = true;
if (!m_isFinished) {
emit this->checkResult(int(BackupResult::START_CANCEL));
if (m_mksquashfs)
m_mksquashfs->stop();
if (m_p)
m_p->stop();
QProcess::execute("sync");
Utils::wait(5);
deleteFailedData();
emit this->checkResult(int(BackupResult::CANCEL_SUCCESS));
}
}
/**
* @brief
*/
void UDiskGhostImageProxy::deleteFailedData()
{
// 1、删除临时镜像文件
if (!m_kyimg.isEmpty()) {
QFile kyimg(m_kyimg);
if (kyimg.exists())
kyimg.remove();
}
// 2、删除目标镜像文件
QString kyimgFile = m_destPath + "/" + m_backupWrapper.m_backupName;
kyimgFile.replace("//", "/");
QFile kyimgDest(kyimgFile);
if (kyimgDest.exists())
kyimgDest.remove();
}
/**
* @brief ghost镜像
*/
void UDiskGhostImageProxy::doGhostImage()
{
qDebug() << "UDiskGhostImageProxy::doGhostImage invoke begin";
QStringList args;
// 拼接备份源路径和目标路径
QString srcPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
srcPath.replace("//", "/");
args << srcPath;
args << m_kyimg;
m_mksquashfs = new MkSquashFSProcess(this);
connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &UDiskGhostImageProxy::progress);
connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [&](bool result) {
qDebug() << "UDiskGhostImageProxy::finished invoke begin";
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
if (result && !m_isForce) {
chown(m_kyimg.toLocal8Bit().data(), m_backupWrapper.m_frontUid, m_backupWrapper.m_gid);
// 同步到U盘
m_p = new RsyncPathToDirProcess(this);
connect(m_p, &RsyncPathToDirProcess::progress, this, &UDiskGhostImageProxy::progress);
connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool resultRsync) {
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
m_isForce = false;
m_isFinished = true;
if (resultRsync) {
// QFileInfo fileInfo(m_kyimg);
// QString imgSize = Utils::StringBySize(fileInfo.size());
// QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
// Utils::writeBackupLog(time + ","
// + m_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + ","
// + m_backupWrapper.m_note + "," + imgSize
// + ",," + m_backupWrapper.m_backupName);
m_bSuccess = true;
}
QFile kyimg(m_kyimg);
if (kyimg.exists())
kyimg.remove();
emit this->workResult(resultRsync);
});
QStringList arguments;
arguments << "-av";
arguments << "--info=progress2";
arguments << m_kyimg;
arguments << m_destPath + "/";
m_p->start(arguments, false);
emit this->checkResult(int(BackupResult::MKSQUASHFS_DO_SUCCESS));
emit this->progress(0);
} else {
m_isFinished = true;
emit this->workResult(false);
}
qDebug() << "UDiskGhostImageProxy::finished invoke end";
});
m_bSuccess = false;
m_isFinished = false;
m_mksquashfs->start(args);
emit checkResult(int(BackupResult::GHOST_START_SUCCESS));
QTimer::singleShot(1*1000, this, &UDiskGhostImageProxy::checkDestDirExists);
qDebug() << "UDiskGhostImageProxy::doGhostImage invoke end";
}
/**
* @brief
* @return: bool,truefalse
* @author: zhaominyong
* @since: 2021/05/24
* @note:
* add by zhaominyong at 2021/05/24 for bug:54377 U盘的过程中拔出U盘
*/
bool UDiskGhostImageProxy::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_mksquashfs)
m_mksquashfs->stop();
if (m_p)
m_p->stop();
// 10s钟后如果还没有退出则强制退出
QTimer::singleShot(10*1000, this, &UDiskGhostImageProxy::checkDestDirExists);
m_isForce = true;
} else {
QTimer::singleShot(1*1000, this, &UDiskGhostImageProxy::checkDestDirExists);
}
}
return true;
}

View File

@ -0,0 +1,51 @@
#ifndef UDISKGHOSTIMAGEPROXY_H
#define UDISKGHOSTIMAGEPROXY_H
#include "workerfactory.h"
#include "myprocess/mksquashfsprocess.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class UDiskGhostImageProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(UDiskGhostImageProxy)
public:
explicit UDiskGhostImageProxy();
virtual ~UDiskGhostImageProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
// 任务取消
virtual void cancelEx();
private:
void doGhostImage();
bool checkDestDirExists();
void deleteFailedData();
// 存放.kyimg文件的目录
QString m_destPath;
// .kyimg文件
QString m_kyimg;
// 压缩进程
MkSquashFSProcess *m_mksquashfs;
// 备份进程
RsyncPathToDirProcess *m_p;
// 是否成功
bool m_bSuccess;
// 是否结束
bool m_isFinished;
// 强制结束标志(stop后没反应的情况系统处于睡眠状态)
bool m_isForce;
};
#endif // UDISKGHOSTIMAGEPROXY_H

View File

@ -0,0 +1,619 @@
#include "udisksystembackupproxy.h"
#include <QStorageInfo>
#include <QDateTime>
#include <QDebug>
#include <QTimer>
#include <kysec/status.h>
#include "../common/utils.h"
#include "../common/mydusizetool.h"
#include "mymountproxy.h"
IMPLEMENT_DYNCREATE(UDiskSystemBackupProxy)
UDiskSystemBackupProxy::UDiskSystemBackupProxy()
{
m_bSuccess = false;
m_isFinished = false;
m_p = nullptr;
m_size = 0;
m_calc = new CalcBackupSize(this);
m_isOnlyCheck = true;
m_mksquashfs = nullptr;
m_isForce = false;
connect(this, &UDiskSystemBackupProxy::cancel, this, &UDiskSystemBackupProxy::cancelEx);
}
UDiskSystemBackupProxy::~UDiskSystemBackupProxy()
{
delete m_p;
m_p = nullptr;
delete m_calc;
m_calc = nullptr;
delete m_mksquashfs;
m_mksquashfs = nullptr;
QString rm("rm -rf ");
rm += m_imgPath;
QProcess::execute(rm);
}
/**
* @brief
* @return
*/
bool UDiskSystemBackupProxy::checkEnvEx()
{
qDebug() << "UDiskSystemBackupProxy::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;
}
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;
}
QTimer::singleShot(1*1000, this, &UDiskSystemBackupProxy::checkDestDirExists);
// 2、计算备份大小
calcSizeForBackup();
qDebug() << "UDiskSystemBackupProxy::checkEnv invoke end";
return true;
}
/**
* @brief
*/
void UDiskSystemBackupProxy::doWorkEx()
{
qDebug() << "UDiskSystemBackupProxy::doWorkEx invoke begin";
m_isOnlyCheck = false;
// 环境检测
checkEnvEx();
qDebug() << "UDiskSystemBackupProxy::doWorkEx invoke end";
}
void UDiskSystemBackupProxy::cancelEx()
{
qDebug() << "UDiskSystemBackupProxy::cancelEx invoke begin";
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();
if (m_p)
m_p->stop();
QProcess::execute("sync");
Utils::wait(5);
deleteFailedData();
emit this->checkResult(int(BackupResult::CANCEL_SUCCESS));
}
qDebug() << "UDiskSystemBackupProxy::cancelEx invoke end";
}
/**
* @brief
*/
void UDiskSystemBackupProxy::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 = m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
parse.deleteItem(m_curUuid);
}
/**
* @brief
* @return true, false
*/
bool UDiskSystemBackupProxy::isIncBackup()
{
QString backupPath;
ParseBackupList::BackupPoint point;
if (m_backupWrapper.m_uuid.isEmpty()) {
QString xmlPath(m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH);
xmlPath.replace("//", "/");
ParseBackupList parser(xmlPath);
point = parser.getLastSysBackupPoint();
if (point.m_uuid.isEmpty())
return false;
backupPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + point.m_uuid + "/data";
} else {
backupPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
}
backupPath.replace("//", "/");
if (Utils::isDirExist(backupPath)) {
m_backupWrapper.m_baseUuid = point.m_uuid;
m_backupWrapper.m_bIncrement = true;
m_backupWrapper.m_type = BackupType::INC_BACKUP_SYSTEM;
return true;
}
return false;
}
/**
* @brief
*/
void UDiskSystemBackupProxy::checkFreeCapacity(qint64 itotalSize)
{
qDebug() << "UDiskSystemBackupProxy::checkFreeCapacity invoke begin";
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
// 拔掉U盘的场景
if (m_isForce) {
emit this->checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
return ;
}
// 1、计算待备份数据的大小
m_size = itotalSize;
// 备份过程中会有一些临时文件产生会占用一部分空间故我们预留500M的空间
itotalSize += 500 * MB;
// 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、判断是否需要先压缩成img文件压缩后一般小于原大小的70%
itotalSize = itotalSize * 7 / 10;
QHash<QString, QString> hash = Utils::getLeftSizeOfPartitions();
for (QHash<QString, QString>::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) {
QString path = it.key();
QString size = it.value();
if (size.endsWith("G")) {
size.replace("G", "");
qint64 leftSize = size.toLongLong() * 1000 * 1000 * 1000;
if (itotalSize < leftSize) {
m_imgPath = path + IMGBACKUP_PATH;
m_imgPath.replace("//", "/");
break ;
}
}
}
// 5、开始制作img或开始备份
if (m_imgPath.isEmpty()) {
doBackup();
} else {
doMksqushfs();
}
qDebug() << "UDiskSystemBackupProxy::checkFreeCapacity invoke end";
}
/**
* @brief
* @return ,
*/
void UDiskSystemBackupProxy::calcSizeForBackup()
{
// 拼接备份源路径和目标路径,测试所需备份空间大小;目标路径为一虚拟路径
QString srcPath = Utils::getSysRootPath();
srcPath += "/";
srcPath.replace("//", "/");
QString destPath = Utils::getSysRootPath();
destPath += CHECK_PATH;
destPath.replace("//", "/");
Utils::mkpath(destPath);
QStringList args = getRsyncArgs(UDiskSystemBackupScene::TRY_SYSTEM_BACKUP);
args << srcPath;
args << destPath;
connect(m_calc, &CalcBackupSize::finished, this, &UDiskSystemBackupProxy::checkFreeCapacity);
m_calc->start(args, false);
}
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList UDiskSystemBackupProxy::getRsyncArgs(UDiskSystemBackupScene scene)
{
QStringList args;
QStringList excludes;
switch (scene) {
case UDiskSystemBackupScene::SYSTEM_BACKUP :
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
break ;
case UDiskSystemBackupScene::TRY_SYSTEM_BACKUP :
args << "-aAHXrn";
args << "--stats";
args << "--ignore-missing-args";
break ;
case UDiskSystemBackupScene::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;
}
args << "-e" << m_imgPath;
return args;
case UDiskSystemBackupScene::IMG_BACKUP :
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
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 UDiskSystemBackupProxy::doMksqushfs()
{
qDebug() << "UDiskSystemBackupProxy::doMksqushfs invoke begin";
m_mksquashfs = new MkSquashFSProcess(this);
connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &UDiskSystemBackupProxy::progress);
connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [=](bool result) {
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
if (result && !m_isForce) {
// 开始备份
doBackup();
} else {
m_isFinished = true;
emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL));
}
});
Utils::mkpath(m_imgPath);
QString srcPath = Utils::getSysRootPath();
srcPath += "/";
srcPath.replace("//", "/");
QString dstImg = m_imgPath + "/" + UDISK_MKSQUASHFS_IMG_NAME;
QStringList args;
args << srcPath << dstImg;
args.append(getRsyncArgs(UDiskSystemBackupScene::MKSQUASHFS));
if (m_mksquashfs->start(args)) {
emit checkResult(int(BackupResult::MKSQUASHFS_START_SUCCESS));
} else {
emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL));
}
qDebug() << "UDiskSystemBackupProxy::doMksqushfs invoke end";
}
/**
* @brief
*/
void UDiskSystemBackupProxy::doBackup()
{
qDebug() << "UDiskSystemBackupProxy::doBackup invoke begin";
// 准备
if (!doPrepare())
return ;
// 启动备份efi, 修改为和其它目录统一备份,不再单独进行备份
// if (!backupEfi()) {
// emit checkResult(int(BackupResult::EFI_RSYNC_FAIL));
// return ;
// }
if (m_imgPath.isEmpty()) {
// 启动系统备份
backupSystem();
} else {
// 备份img文件
backupImg();
}
qDebug() << "UDiskSystemBackupProxy::doBackup invoke end";
}
/**
* @brief
* @return true,false
*/
bool UDiskSystemBackupProxy::doPrepare()
{
qDebug() << "UDiskSystemBackupProxy::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() << "UDiskSystemBackupProxy::doPrepare invoke end";
return true;
}
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool UDiskSystemBackupProxy::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;
QString xmlPath = m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH;
xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath);
if (m_backupWrapper.m_uuid.isEmpty() || !m_backupWrapper.m_bIncrement) {
if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL));
return false;
}
} else {
if (parse.updateItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_UPDATE_ITEM_FAIL));
return false;
}
}
return true;
}
/**
* @brief
* @return truefalse
*/
bool UDiskSystemBackupProxy::backupSystem()
{
qDebug() << "UDiskSystemBackupProxy::backupSystem invoke begin";
// 全量备份场景
QStringList args = getRsyncArgs(UDiskSystemBackupScene::SYSTEM_BACKUP);
// 拼接备份源路径和目标路径
QString srcPath = Utils::getSysRootPath();
srcPath += "/";
srcPath.replace("//", "/");
args << srcPath;
QString destPath = m_destPath + "/";
destPath.replace("//", "/");
args << destPath;
return backup(args);
}
/**
* @brief img文件
* @return truefalse
*/
bool UDiskSystemBackupProxy::backupImg()
{
qDebug() << "UDiskSystemBackupProxy::backupImg invoke";
QStringList args;
QString srcPath = m_imgPath + "/" + UDISK_MKSQUASHFS_IMG_NAME;
QString destPath = m_destPath + "/";
destPath.replace("//", "/");
args << srcPath << destPath;
return backup(args);
}
/**
* @brief
* @param args
* @return truefalse
*/
bool UDiskSystemBackupProxy::backup(const QStringList &args)
{
qDebug() << "UDiskSystemBackupProxy::backup invoke begin";
m_p = new RsyncPathToDirProcess(this);
connect(m_p, &RsyncPathToDirProcess::progress, this, &UDiskSystemBackupProxy::progress);
connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) {
// 如果是取消了操作,则不再发送其它信息
if (m_bCancel)
return ;
m_isForce = false;
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 = m_backupWrapper.m_prefixDestPath + 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);
});
m_p->start(args, false);
emit checkResult(int(BackupResult::BACKUP_START_SUCCESS));
do_kylin_security(m_destPath);
qDebug() << "UDiskSystemBackupProxy::backup invoke end";
return true;
}
void UDiskSystemBackupProxy::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();
}
}
/**
* @brief
* @return: bool,truefalse
* @author: zhaominyong
* @since: 2021/05/24
* @note:
* add by zhaominyong at 2021/05/24 for bug:54377 U盘的过程中拔出U盘
*/
bool UDiskSystemBackupProxy::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);
m_isForce = true;
if (m_calc != nullptr)
m_calc->stop();
if (m_mksquashfs != nullptr)
m_mksquashfs->stop();
if (m_p != nullptr)
m_p->stop();
// 10s钟后如果还没有退出则强制退出
QTimer::singleShot(10*1000, this, &UDiskSystemBackupProxy::checkDestDirExists);
} else {
QTimer::singleShot(1*1000, this, &UDiskSystemBackupProxy::checkDestDirExists);
}
}
return true;
}

View File

@ -0,0 +1,122 @@
#ifndef UDISKSYSTEMBACKUPPROXY_H
#define UDISKSYSTEMBACKUPPROXY_H
#include "workerfactory.h"
#include "myprocess/calcbackupsize.h"
#include "myprocess/mksquashfsprocess.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class UDiskSystemBackupProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(UDiskSystemBackupProxy)
public:
// 系统备份的几种场景
enum UDiskSystemBackupScene {
SYSTEM_BACKUP, // 系统备份
TRY_SYSTEM_BACKUP, // 测试系统备份,可用于计算备份传输数据大小
MKSQUASHFS, // 生成img文件
IMG_BACKUP, // 备份img文件
};
explicit UDiskSystemBackupProxy();
virtual ~UDiskSystemBackupProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
signals:
private slots:
// 校验剩余空间是否满足备份
void checkFreeCapacity(qint64 itotalSize);
// mksqushfs
void doMksqushfs();
// 备份
void doBackup();
// 任务取消
virtual void cancelEx();
/**
* @brief
* @return: bool,truefalse
* @author: zhaominyong
* @since: 2021/05/24
* @note:
* add by zhaominyong at 2021/05/24 for bug:54377 U盘的过程中拔出U盘
*/
bool checkDestDirExists();
private:
// 判断是否增量备份
bool isIncBackup();
// 计算备份所需空间大小
void calcSizeForBackup();
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList getRsyncArgs(UDiskSystemBackupScene scene);
/**
* @brief /backup/snapshots/backuplist.xml文件
* @return true-false-
*/
bool recordBackupPoint();
// 备份准备
bool doPrepare();
// 备份系统
bool backupSystem();
// 备份img文件
bool backupImg();
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;
// 备份进程
RsyncPathToDirProcess *m_p;
// 当前备份节点
ParseBackupList::BackupPoint m_backupPoint;
// 是否只是检测
bool m_isOnlyCheck;
// img文件存放路径
QString m_imgPath;
// 强制结束标志(stop后没反应的情况系统处于睡眠状态)
bool m_isForce;
};
#endif // UDISKSYSTEMBACKUPPROXY_H

View File

@ -0,0 +1,533 @@
#include "udisksystemrestoreproxy.h"
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include <QTimer>
#include <unistd.h>
#include <sys/reboot.h>
#include "../common/utils.h"
#include "mymountproxy.h"
#include "myprocess/mksquashfsprocess.h"
IMPLEMENT_DYNCREATE(UDiskSystemRestoreProxy)
/**
* @brief
*/
UDiskSystemRestoreProxy::UDiskSystemRestoreProxy()
{
m_isFinished = false;
m_p = nullptr;
m_isForce = false;
}
/**
* @brief
*/
UDiskSystemRestoreProxy::~UDiskSystemRestoreProxy()
{
delete m_p;
}
/**
* @brief
* @return false,;true,
*/
bool UDiskSystemRestoreProxy::checkEnvEx()
{
qDebug() << "UDiskSystemRestoreProxy::checkEnvEx invoke begin";
// 1、检测.user.txt是否存在
m_userFile = m_backupWrapper.m_prefixDestPath + 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;
}
// 2、检测.exclude.user.txt是否存在
m_excludeUserFile = m_backupWrapper.m_prefixDestPath + 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;
}
// 3、检测还原点是否存在
m_backupPath = m_backupWrapper.m_prefixDestPath + 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;
}
// 4、检测xml中的还原点是否还存在
QString xmlPath = m_backupWrapper.m_prefixDestPath + 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;
}
m_curUuid = m_backupWrapper.m_uuid;
emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS));
qDebug() << "UDiskSystemRestoreProxy::checkEnvEx invoke end";
return true;
}
/**
* @brief
*/
void UDiskSystemRestoreProxy::doWorkEx()
{
qDebug() << "UDiskSystemRestoreProxy::doWorkEx invoke begin";
// 1、校验
if (!checkEnvEx())
return ;
// 2、还原efi(兼容旧版本的备份)
if (!restoreEfi()) {
qCritical("/boot/efi目录同步失败");
emit checkResult(int(BackupResult::EFI_RSYNC_FAIL));
return ;
}
// 3、还原系统
if (doPrepare())
restoreSystem();
qDebug() << "UDiskSystemRestoreProxy::doWorkEx invoke end";
}
/**
* @brief efi()
* @return
*/
bool UDiskSystemRestoreProxy::restoreEfi()
{
qDebug() << "UDiskSystemRestoreProxy::restoreEfi invoke begin";
// 是否有/boot/efi目录
QString efiPath = Utils::getSysRootPath() + "/boot/efi";
efiPath.replace("//", "/");
if (!Utils::isDirEmpty(efiPath)) {
// 1、修复源数据
repairEfi();
// 2、重新rw读写挂载
remountEfi();
// 3、同步efi
return rsyncEfi();
}
qDebug() << "UDiskSystemRestoreProxy::restoreEfi invoke end";
return true;
}
/**
* @brief efi目录
*/
void UDiskSystemRestoreProxy::repairEfi()
{
QString qsEfiPath = m_backupPath + "/efi";
if (!Utils::isDirEmpty(qsEfiPath)) {
// 存在/efi说明是老备份数据尽量修正老数据
QStringList args;
args << "-f";
args << qsEfiPath;
QString newPath = m_backupPath + "/boot";
args << newPath;
QProcess::execute("mv", args);
QProcess::execute("sync");
}
}
/**
* @brief rw读写挂载efi分区
*/
void UDiskSystemRestoreProxy::remountEfi()
{
QString mountPath = Utils::getSysRootPath() + "/boot/efi";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/**
* @brief rw读写挂载boot分区
*/
void UDiskSystemRestoreProxy::remountBoot()
{
QString mountPath = Utils::getSysRootPath() + "/boot";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/**
* @brief efi
*/
bool UDiskSystemRestoreProxy::rsyncEfi()
{
QString efiPath = m_backupPath + "/boot/efi/";
if (Utils::isDirEmpty(efiPath))
efiPath = efiPath = m_backupPath + "/efi/";
if (Utils::isDirEmpty(efiPath))
return true;
QStringList args = getRsyncArgs(SystemRestoreScene::EFI_RESTORE);
QString mountPath = Utils::getSysRootPath() + "/boot/efi/";
mountPath.replace("//", "/");
args << efiPath << mountPath;
m_p = new RsyncPathToDirProcess(this);
bool result = m_p->start(args);
delete m_p;
m_p = nullptr;
return result;
}
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList UDiskSystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
{
QStringList args;
args << "-avAHXr";
args << "--info=progress2";
args << "--no-inc-recursive";
args << "--ignore-missing-args";
args << "--delete";
QDir dataDir(m_srcPath + "/data");
QFile file(m_srcPath + "/etc/uid_list");
QDir efiDir(m_srcPath + "/boot/efi");
QStringList excludes;
switch (scene) {
case SystemRestoreScene::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 SystemRestoreScene::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";
// 以前的出厂备份和grub备份没有备份/data还原时需要判断/data是否存在如不存在需要屏蔽掉不然会将主机上的/data删除造成问题
// 此为兼容以前备份的老数据而改,等以后老的备份估计不存在了可已去掉
if (!dataDir.exists()) {
args << "--exclude=/data";
}
if (!file.exists()) {
args << "--exclude=/etc/uid_list";
}
// 为兼容以前的老备份数据,增加下面几行
if (efiDir.isEmpty()) {
args << QString("--exclude=/boot/efi");
}
// 系统安装后有的会将/data/home /data/root挂载到的/home /root上实际文件是存放在/data/home /data/root下面
Utils::excludeFstabBindPath(excludes);
// 自定义备份的路径也需要跳过,不进行还原
Utils::excludeCustomizePath(excludes);
for (const QString& item : excludes) {
QDir itemDir(m_srcPath + item);
// 以后统一用/home /root这种路径 兼容老备份数据原来的U盘备份在mksquashfs时排除bind挂载的任意一方时都备份不上
if (item == "/data/home") {
QDir homeDir(m_srcPath + "/home");
if (!homeDir.isEmpty()) {
args << QString("--exclude=/data/home");
} else if (!itemDir.isEmpty()) {
args << QString("--exclude=/home");
} else {
args << QString("--exclude=/data/home");
args << QString("--exclude=/home");
}
continue;
} else if (item == "/data/root") {
QDir homeDir(m_srcPath + "/root");
if (!homeDir.isEmpty()) {
args << QString("--exclude=/data/root");
} else if (!itemDir.isEmpty()) {
args << QString("--exclude=/root");
} else {
args << QString("--exclude=/data/root");
args << QString("--exclude=/root");
}
continue;
}
args << QString("--exclude=") + item;
}
// 异机还原
if (m_backupWrapper.m_isOtherMachine) {
args << "--exclude=/etc/.bootinfo";
args << "--exclude=/etc/fstab";
}
args << "--exclude-from" << m_excludeUserFile;
args << "--files-from" << m_userFile;
break ;
case SystemRestoreScene::EFI_RESTORE :
break ;
default:
return args;
}
return args;
}
/**
* @brief
* @return
*/
bool UDiskSystemRestoreProxy::doPrepare()
{
qDebug() << "UDiskSystemRestoreProxy::doPrepare invoke begin";
// 移动设备系统备份如果有img则需要先将img挂载到/backup/imgbackup目录
QString imgPath = m_backupPath + "/" + UDISK_MKSQUASHFS_IMG_NAME;
if (Utils::filsExists(imgPath)) {
// 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(imgPath, dstImgMountPath)) {
emit checkResult(int(BackupResult::RESTOREDIR_PREPARE_FAILED));
return false;
}
m_srcPath = dstImgMountPath;
} else
m_srcPath = m_backupPath;
// 以读写方式重新挂载boot分区因为有的机器默认以只读挂载
remountBoot();
qDebug() << "UDiskSystemRestoreProxy::doPrepare invoke end";
return true;
}
/**
* @brief
*/
void UDiskSystemRestoreProxy::restoreSystem()
{
qDebug() << "UDiskSystemRestoreProxy::restoreSystem invoke begin";
// 理论上开始不会走下面这个U盘拔出的校验
if (m_isForce) {
qCritical("U盘已拔出");
emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR));
return ;
}
// 停止安全防护
QProcess::execute("systemctl stop kysec-init.service");
QString destPath = Utils::getSysRootPath();
QStringList args;
// 自动更新的备份还原时保留用户数据
if (m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM_WITH_DATA) {
args = getRsyncArgs(SystemRestoreScene::RESTORE_SYSTEM_WITH_DATA);
} else {
args = getRsyncArgs(SystemRestoreScene::SYSTEM_RESTORE);
}
args << m_srcPath + "/";
destPath += "/";
destPath.replace("//", "/");
args << destPath;
m_p = new RsyncPathToDirProcess(this);
connect(m_p, &RsyncPathToDirProcess::progress, this, &UDiskSystemRestoreProxy::progress);
connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) {
m_isForce = false;
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);
// sync();
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;
}
if (m_backupWrapper.m_isOtherMachine) {
Utils::wait(10);
updateGrubUUid();
sync();
QProcess::execute("sync");
Utils::wait(5);
}
}
if (Utils::isDirEmpty(m_backupPath))
result = false;
emit this->workResult(result);
m_isFinished = true;
if (result) {
Utils::wait(2);
reboot(RB_AUTOBOOT);
}
});
QTimer::singleShot(1*1000, this, &UDiskSystemRestoreProxy::checkUdiskExists);
m_isFinished = false;
m_p->start(args, false);
qDebug() << "UDiskSystemRestoreProxy::restoreSystem invoke end";
}
/**
* @brief grub.cfg中的分区UUID
*/
void UDiskSystemRestoreProxy::updateGrubUUid()
{
QString srcFstab = Utils::getSysRootPath() + BACKUP_IMGBACKUP_PATH;
srcFstab += FSTAB_PATH;
srcFstab.replace("//", "/");
QHash<QString, QString> srcPartToUuid = Utils::getPartUuidMap(srcFstab);
QString destFstab = Utils::getSysRootPath() + FSTAB_PATH;
destFstab.replace("//", "/");
QHash<QString, QString> destPartToUuid = Utils::getPartUuidMap(destFstab);
QString findGrub = Utils::executeCmd("find /boot -name grub.cfg");
QStringList grubs = findGrub.split("\n");
for (const QString &grub : grubs) {
if (grub.isEmpty())
continue;
QString root = QString("sed -i 's/%1/%2/g' %3").arg(srcPartToUuid.value("/")).arg(destPartToUuid.value("/")).arg(grub);
qDebug() << Utils::executeCmd(root);
QString boot = QString("sed -i 's/%1/%2/g' %3").arg(srcPartToUuid.value("/boot")).arg(destPartToUuid.value("/boot")).arg(grub);
qDebug() << Utils::executeCmd(boot);
QString swap = QString("sed -i 's/%1/%2/g' %3").arg(srcPartToUuid.value("swap")).arg(destPartToUuid.value("swap")).arg(grub);
qDebug() << Utils::executeCmd(swap);
}
}
/**
* @brief
* @return true-false-
*/
bool UDiskSystemRestoreProxy::checkUdiskExists()
{
if (!m_isFinished) {
// 拔掉U盘后没有响应的场景怀疑可能是某应用程序关闭引起希望不是dbus服务关掉了
if (m_isForce) {
qCritical() << QString("强制退出");
emit this->workResult(false);
return false;
}
if (Utils::isDirEmpty(m_backupPath)) {
qCritical() << QString("srcDir %s is not exist!").arg(m_backupPath);
m_isForce = true;
if (m_p != nullptr)
m_p->stop();
// 10s钟后如果还没有退出则强制退出
QTimer::singleShot(10*1000, this, &UDiskSystemRestoreProxy::checkUdiskExists);
} else {
QTimer::singleShot(1*1000, this, &UDiskSystemRestoreProxy::checkUdiskExists);
}
}
return true;
}

View File

@ -0,0 +1,79 @@
#ifndef UDISKSYSTEMRESTOREPROXY_H
#define UDISKSYSTEMRESTOREPROXY_H
#include "workerfactory.h"
#include "myprocess/rsyncpathtodirprocess.h"
#include "parsebackuplist.h"
class UDiskSystemRestoreProxy : public Worker
{
Q_OBJECT
DECLARE_DYNCREATE(UDiskSystemRestoreProxy)
public:
// 系统还原的几种场景
enum SystemRestoreScene {
RESTORE_SYSTEM_WITH_DATA, // 保留用户数据还原
SYSTEM_RESTORE, // 系统还原
EFI_RESTORE, // efi还原
};
explicit UDiskSystemRestoreProxy();
virtual ~UDiskSystemRestoreProxy();
public:
// 环境检测
virtual bool checkEnvEx();
// 任务处理
virtual void doWorkEx();
private slots:
bool checkUdiskExists();
private:
// 还原efi
bool restoreEfi();
// 修复efi目录
void repairEfi();
// 以读写方式重新挂载efi分区
void remountEfi();
// 以读写方式重新挂载boot分区
void remountBoot();
// 同步efi
bool rsyncEfi();
// 系统还原
void restoreSystem();
// 还原前准备
bool doPrepare();
// 异机还原时更新grub.cfg中的分区UUID
void updateGrubUUid();
/**
* @brief rsync命令参数
* @param scene
* @return rsync的参数信息
*/
QStringList getRsyncArgs(SystemRestoreScene scene);
// .user.txt文件路径
QString m_userFile;
// .exclude.user.txt文件路径
QString m_excludeUserFile;
// 备份数据所在的data目录
QString m_backupPath;
// 是否还原结束
bool m_isFinished;
// 当前备份uuid
QString m_curUuid;
// 当前还原源目录
QString m_srcPath;
// 备份进程
RsyncPathToDirProcess *m_p;
// 当前备份节点
ParseBackupList::BackupPoint m_backupPoint;
// 强制结束标志(stop后没反应的情况系统处于睡眠状态)
bool m_isForce;
};
#endif // UDISKSYSTEMRESTOREPROXY_H

113
backup-daemon/workerfactory.cpp Executable file
View File

@ -0,0 +1,113 @@
#include "workerfactory.h"
#include <QString>
Worker::Worker() :
QObject(nullptr),
m_bCancel(false)
{}
Worker::~Worker()
{}
//
/**
* @brief
*/
void Worker::checkEnv()
{
checkEnvEx();
}
/**
* @brief
*/
void Worker::doWork()
{
doWorkEx();
}
// 环境检测,个性化部分派生类去实现
bool Worker::checkEnvEx()
{
return true;
}
// 任务处理,个性化部分派生类去实现
void Worker::doWorkEx()
{}
// 任务取消,个性化部分派生类去实现
void Worker::cancelEx()
{}
/**
* @brief
* @param type ,
* @param position U盘备份: 0-1-
* @return Worker派生类对象指针
*/
Worker * WorkerFactory::createWorker(int type, int position)
{
QString className;
switch (type) {
case BackupType::BACKUP_SYSTEM:
case BackupType::INC_BACKUP_SYSTEM:
if (BackupPosition::UDISK == position) {
className = "UDiskSystemBackupProxy";
} else if (BackupPosition::CUSTOMIZE == position) {
className = "CustomizeSystemBackupProxy";
} else {
className = "SystemBackupProxy";
}
break;
case BackupType::RESTORE_SYSTEM:
case BackupType::RESTORE_SYSTEM_WITH_DATA:
if (BackupPosition::UDISK == position || BackupPosition::OTHER == position) {
className = "UDiskSystemRestoreProxy";
} else if (BackupPosition::CUSTOMIZE == position) {
className = "CustomizeSystemRestoreProxy";
} else {
className = "SystemRestoreProxy";
}
break;
case BackupType::BACKUP_DATA:
case BackupType::INC_BACKUP_DATA:
if (BackupPosition::UDISK == position) {
className = "UDiskDataBackupProxy";
} else if (BackupPosition::CUSTOMIZE == position) {
className = "CustomizeDataBackupProxy";
} else {
className = "DataBackupProxy";
}
break;
case BackupType::RESTORE_DATA:
if (BackupPosition::UDISK == position) {
className = "UDiskDataRestoreProxy";
} else {
className = "DataRestoreProxy";
}
break;
case BackupType::DELETE_BACKUP:
className = "DeleteBackupProxy";
break;
case BackupType::GHOST_IMAGE:
if (BackupPosition::UDISK == position) {
className = "UDiskGhostImageProxy";
} else if (BackupPosition::CUSTOMIZE == position) {
className = "CustomizeGhostImageProxy";
} else {
className = "GhostImageProxy";
}
break;
default:
break;
}
if (className.isEmpty()) {
return nullptr;
}
Worker * p = qobject_cast<Worker *>(Reflect::createObject(className));
return p;
}

70
backup-daemon/workerfactory.h Executable file
View File

@ -0,0 +1,70 @@
#ifndef WORKERFACTORY_H
#define WORKERFACTORY_H
#include <QObject>
#include "../common/mydefine.h"
#include "../common/reflect.h"
/**
* @brief
*/
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker();
virtual ~Worker();
signals:
// 检测结果信号
void checkResult(int result);
// 进度百分比
void progress(int currentRate);
// 工作结果信号
void workResult(bool result);
// 任务取消
void cancel();
public slots:
// 环境检测
void checkEnv();
// 任务处理
void doWork();
protected:
// 环境检测,个性化部分派生类去实现
virtual bool checkEnvEx();
// 任务处理,个性化部分派生类去实现
virtual void doWorkEx();
// 任务取消,个性化部分派生类去实现
virtual void cancelEx();
public:
void setParam(const BackupWrapper& backupWrapper) { m_backupWrapper = backupWrapper; }
public:
// 同一时间只能运行一个备份/还原等操作
BackupWrapper m_backupWrapper;
// 是否取消操作
bool m_bCancel;
};
/**
* @brief
*/
class WorkerFactory
{
public:
/**
* @brief
* @param type ,
* @param position U盘备份: 0-1-
* @return Worker派生类对象指针
*/
static Worker * createWorker(int type, int position);
};
#endif // WORKERFACTORY_H

32
common/mydefine.cpp Executable file
View File

@ -0,0 +1,32 @@
#include "mydefine.h"
#include <QDBusMetaType>
// Marshall the MyStructure data into a D-Bus argument
QDBusArgument &operator<<(QDBusArgument &argument, const BackupWrapper &backupWrapper)
{
argument.beginStructure();
argument << backupWrapper.m_type << backupWrapper.m_iPosition << backupWrapper.m_comment << backupWrapper.m_backupName
<< backupWrapper.m_uuid << backupWrapper.m_backupPaths << backupWrapper.m_backupExcludePaths << backupWrapper.m_prefixDestPath
<< backupWrapper.m_note << backupWrapper.m_frontUserName << backupWrapper.m_frontUid
<< backupWrapper.m_gid << backupWrapper.m_isOtherMachine;
argument.endStructure();
return argument;
}
// Retrieve the MyStructure data from the D-Bus argument
const QDBusArgument &operator>>(const QDBusArgument &argument, BackupWrapper &backupWrapper)
{
argument.beginStructure();
argument >> backupWrapper.m_type >> backupWrapper.m_iPosition >> backupWrapper.m_comment >> backupWrapper.m_backupName
>> backupWrapper.m_uuid >> backupWrapper.m_backupPaths >> backupWrapper.m_backupExcludePaths >> backupWrapper.m_prefixDestPath
>> backupWrapper.m_note >> backupWrapper.m_frontUserName >> backupWrapper.m_frontUid
>> backupWrapper.m_gid >> backupWrapper.m_isOtherMachine;
argument.endStructure();
return argument;
}
void BackupWrapper::registerMetaType()
{
qRegisterMetaType<BackupWrapper>("BackupWrapper");
qDBusRegisterMetaType<BackupWrapper>();
}

294
common/mydefine.h Executable file
View File

@ -0,0 +1,294 @@
#ifndef MYDEFINE_H
#define MYDEFINE_H
#include <QString>
#include <QStringList>
#include <QMetaType>
#include <QDBusArgument>
#define DEFAULT_APP_PATH "/usr/bin"
#define BACKUP_PATH "/backup"
#define BACKUP_SNAPSHOTS_PATH "/backup/snapshots"
#define BACKUP_XML_PATH "/backup/snapshots/backuplist.xml"
#define EXCLUDE_FILE_PATH "/backup/snapshots/.exclude"
#define CHECK_PATH "/backup/snapshots/check/data/"
#define BACKUP_LOG_TEXT_PATH "/backup/log.txt"
#define BACKUP_IMGBACKUP_PATH "/backup/imgbackup"
#define IMGBACKUP_PATH "/imgbackup"
#define UDISK_MKSQUASHFS_IMG_NAME "dst.img"
#define UDISK_UNIQUE_SETTINGS "/backup/udisk_unique_file"
#define GHOST_PATH "/ghost"
#define DATA_PATH "/data"
#define BOOTINFO_PATH "/etc/.bootinfo"
#define FSTAB_PATH "/etc/fstab"
#define LOCK_FILE "/tmp/lock/kylin-backup.lock"
#define LOCK_FILE_PATH "/tmp/lock"
#define PROC_LOG "/var/log/backup.log"
#define FILE_IF_SYNC "/etc/file_if_sync"
#define BACKUP_CLI_NAME "kybackup"
#define AUTO_BACKUP_UUID "{01234567-0123-0123-0123-0123456789ab}"
#define FACTORY_BACKUP_UUID "{00000000-0000-0000-0000-000000000000}"
#define PATHS_USER_FILE ".user.txt"
#define EXCLUDE_PATHS_USER_FILE ".exclude.user.txt"
#define BACKUP_PARSE_STATE_SUCCESS_STRTING "backup finished"
#define BACKUP_PARSE_STATE_FAIL_STRTING "backup unfinished"
#define BACKUP_PARSE_STATE_INC_SUCCESS_STRTING "inc backup finished"
#define BACKUP_PARSE_STATE_INC_FAIL_STRTING "inc backup unfinished"
#define THEME_YHKYLIN_BACKUP_TOOLS "yhkylin-backup-tools"
#define PID_STRING_LEN 1024
// 蓝色
#define COLOR_BLUE "#3790FA"
// 蓝黑
#define COLOR_BLUE_DARK "#3B4251"
// 浅灰
#define COLOR_LIGHT_DARK "#F4F4F4"
// 灰色
#define COLOR_GRAY "#CCCCCC"
// 深灰
#define COLOR_DARK_GRAY "#393A3E"
// 黑灰
#define COLOR_BLACK_GRAY "#2D2E32"
// 字体灰色
#define COLOR_DISABLE_GRAY "#737373"
// 黄色
#define COLOR_YELLOW "#F8A34C"
// 浅蓝
#define COLOR_LIGHT_BLUE "#DDEBFF"
#define END_LINE "\n"
/**
* @brief
*/
enum BackupType {
// 系统备份
BACKUP_SYSTEM = 0,
// 增量系统备份
INC_BACKUP_SYSTEM = 1,
// 数据备份
BACKUP_DATA = 2,
// 增量数据备份
INC_BACKUP_DATA = 3,
// 系统还原
RESTORE_SYSTEM = 4,
// 保留用户数据还原
RESTORE_SYSTEM_WITH_DATA = 5,
// 数据还原
RESTORE_DATA = 6,
// 增量数据还原,实际没有这个类型,为兼容以前的数据仍然保留
INC_RESTORE_DATA = 7,
// 删除备份点
DELETE_BACKUP = 8,
// ghost镜像
GHOST_IMAGE = 9,
// 仅仅初始化BackupType枚举类变量用没有用于持久化不用固定其值
BACKUP_TYPE_INIT,
};
/**
* @brief
*/
struct BackupWrapper {
// 操作类型,如:系统备份, 系统还原
int m_type = -1;
// 本地备份还是U盘备份: 0-本地备份1-移动设备备份;2-异机备份3-自定义路径
int m_iPosition = -1;
// 备份名称,用于识别备份的,默认是时间
QString m_comment;
// 备份名称用来替换m_comment
QString m_backupName;
// 备份或还原指定的UUID
QString m_uuid;
// 待备份目录
QStringList m_backupPaths;
// 备份需要排除的路径
QStringList m_backupExcludePaths;
// 移动设备挂载路径:备份目标路径/还原点路径(前缀),在向移动设备备份(或从移动设备还原)时使用它来指定对应的移动设备路径
QString m_prefixDestPath;
// 备注信息
QString m_note;
// 备份用户名
QString m_frontUserName;
// 备份用户id
int m_frontUid = -1;
// 备份用户所属组id
int m_gid = -1;
// 是否异机备份点: 0-本机备份1-异机备份
int m_isOtherMachine = 0;
// 下面参数不是入参,是服务端自行跟随业务场景设置
// 是否增量备份
bool m_bIncrement = false;
// 新增备份点时增量备份的基准uuid
QString m_baseUuid;
// 备份时间(暂用于日志查询)
QString m_time;
// 备份大小(暂用于日志查询)
QString m_size;
static void registerMetaType();
};
Q_DECLARE_METATYPE(BackupWrapper)
extern QDBusArgument &operator<<(QDBusArgument &argument, const BackupWrapper &backupWrapper);
extern const QDBusArgument &operator>>(const QDBusArgument &argument, BackupWrapper &backupWrapper);
// 为兼容旧版本对外提供的接口,此处结构前面暂时不变,只在后面增加
enum class MountResult {
MOUNT_RESULT_INIT,
CANNOT_GET_BACKUPUUID,
NO_BLKID_EXIST,
NO_MOUNTED,
GENERATE_IMPORT_FILE_FAIL,
MOUNTED
};
/**
* @brief
*/
enum class BackupState {
// 准备中
PREPARING = 1,
// 工作中
WORKING,
// 空闲状态
BACKUP_STATE_INIT = 99
};
/**
* @brief
*/
enum BackupPosition
{
LOCAL, // 本地磁盘
UDISK, // 移动设备
OTHER, // 移动设备中的其它机器的备份点
CUSTOMIZE, // 自定义路径
};
/**
* @brief
* @note
*/
enum class BackupResult {
// 备份初始
BACKUP_RESULT_INIT,
// /etc/.bootinfo读取失败
ETC_BOOTINFO_READ_FAIL,
// /etc/fstab不存在
FSTAB_IS_NOT_EXIST,
// 文件锁定应用失败
LOCK_PROGRAM_FAIL,
//有其他正在进行的备份或者还原
OTHER_BACKUP_OR_RESTORE_RUNNING,
// 有删除任务正在进行
RM_RUNNING,
// 正在制作镜像
MKSQUASHFS_RUNNING,
// 设备uuid不存在
NO_BLKID_EXIST,
// 挂载/backup分区失败
BACKUP_PARTITION_MOUNT_FAIL,
// 生成备份导入文件失败
GENERATE_BACKUP_IMPORT_FILE_FAIL,
// /backup/current目录不存在
BACKUP_CURRENT_DIR_IS_NOT_EXIST,
// /backup/snapshots目录不存在
BACKUP_SNAPSHOTS_DIR_IS_NOT_EXIST,
// /backup/snapshots/backuplist.xml文件不存在或打开失败
BACKUP_STORAGEINFO_FILE_FAIL_TO_OPEN,
// /backup/snapshots/backuplist.xml格式不正确
BACKUP_STORAGEINFO_IS_NOT_CORRECT,
// 写入 /backup/snapshots/backuplist.xml 失败
WRITE_STORAGEINFO_ADD_ITEM_FAIL,
// 修改 /backup/snapshots/backuplist.xml 失败
WRITE_STORAGEINFO_UPDATE_ITEM_FAIL,
// 增量备份未找到对应的uuid
INC_NOT_FOUND_UUID,
// 增量备份(或还原)未找到对应的目录
INC_NOT_FOUND_DIR,
// 将备份路径写入(或读出)/backup/snapshots/{uuid}/.user.txt失败
WRITE_BACKUP_PATHS_TO_USER_FAILED,
// 将备份路径写入(或读出)/backup/snapshots/{uuid}/.exclude.user.txt失败
WRITE_EXCLUDE_BACKUP_PATHS_TO_USER_FAILED,
// /backup备份空间不足
BACKUP_CAPACITY_IS_NOT_ENOUGH,
// U盘文件系统是只读的
UDISK_FILESYSTEM_IS_READONLY,
// u盘中创建目录/backup/snapshots失败
UDISK_STH_ERROR,
// u盘文件系统是vfat格式容量有限或单个文件大小有限
UDISK_FILESYSTEM_TYPE_IS_VFAT,
// DU计算大小失败
DU_ERR,
// 向U盘备份时先压缩到本地成img文件本地备份分区空间不足
BACKUP_CAPACITY_FOR_UDISKIMG_IS_NOT_ENOUGH,
// U盘容量不足
UDISK_CAPACITY_IS_NOT_ENOUGH,
// /boot/efi同步失败
EFI_RSYNC_FAIL,
// 备份启动失败
BACKUP_PROCESS_START_FAIL,
// 备份失败
BACKUP_PROCESS_DO_FAIL,
// 备份进程成功启动
BACKUP_START_SUCCESS,
// 备份成功,信号未使用此结果
BACKUP_DO_SUCCESS,
// 还原初始
RESTORE_RESULT_INIT,
// 还原目录准备失败
RESTOREDIR_PREPARE_FAILED,
// 还原进程开启失败
RESTORE_PROCESS_START_FAIL,
// 还原失败
RESTORE_PROCESS_DO_FAIL,
// 还原成功开始
RESTORE_START_SUCCESS,
// 还原成功,信号未使用此结果
RESTORE_DO_SUCCESS,
// ghost镜像未找到备份点
GHOST_CANNOT_FIND_BACKUPPOINT,
// ghost源目录不存在
GHOST_SRC_DIRECTORY_IS_NOT_EXIST,
// ghost进程启动失败
GHOST_PROCESS_START_FAIL,
// ghost进程启动成功
GHOST_START_SUCCESS,
// 后面的为新版本新增部分
// 根据操作类型动态创建处理类失败
NO_FOUND_DEALCLASS,
// 环境检测成功
CHECK_ENV_SUCCESS,
// mksquashfs启动成功
MKSQUASHFS_START_SUCCESS,
// mksquashfs压缩img文件失败
MKSQUASHFS_DO_FAIL,
// mksquashfs压缩img文件成功开始转移img文件到u盘
MKSQUASHFS_DO_SUCCESS,
// 开始取消操作
START_CANCEL,
// 取消操作成功
CANCEL_SUCCESS,
};
#endif // MYDEFINE_H

114
common/mydusizetool.cpp Executable file
View File

@ -0,0 +1,114 @@
#include "mydusizetool.h"
#include <QDebug>
#include "utils.h"
MyDuSizeTool::MyDuSizeTool(QObject* parent) :
QObject(parent),
m_p(new QProcess(this))
{
connect(m_p, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
connect(m_p, &QProcess::readyReadStandardError, this, [&]() {
QByteArray err = m_p->readAllStandardError();
qCritical("du process error: %s", err.data());
});
}
MyDuSizeTool::~MyDuSizeTool()
{
delete m_p;
}
/**
* @brief MyDuSizeTool::Do
* @param paths, du计算路径列表
* @param excludePathsdu排查路径列表
* @param blockbool,
* @return
*/
qint64 MyDuSizeTool::Do(QStringList paths, QStringList excludePaths, bool block)
{
return _Do(paths, excludePaths, true, block);
}
/**
* @brief MyDuSizeTool::_Do
* @param paths, du计算路径列表
* @param excludePathsdu排查路径列表
* @param needExcludebool
* @param blockbool,
* @return
*/
qint64 MyDuSizeTool::_Do(QStringList paths, QStringList excludePaths, bool needExclude, bool block)
{
Init(paths, excludePaths);
if (paths.isEmpty()) {
qCritical("du paths is empty!");
return -1;
}
if (needExclude && excludePaths.isEmpty()) {
qCritical("du excludepaths is empty!");
return -1;
}
QString cmd = "du";
for (const QString& x : paths) {
cmd.append(QString(" \"%1\"").arg(x));
}
if (needExclude) {
for (const QString& item : excludePaths) {
QString arg = QString(" --exclude=\"%1\"").arg(item);
cmd.append(arg);
}
}
cmd.append(" -sb | tail -1 | awk -F \" \" '{print $1}'");
qDebug("cmd is %s", cmd.toStdString().c_str());
QStringList Args;
Args << "-c" << cmd;
m_p->start("bash", Args);
if (!m_p->waitForStarted())
return -1;
if (block) {
if (!m_p->waitForFinished(-1)) {
return -1;
}
return m_size;
}
return 0;
}
/**
* @brief MyDuSizeTool::Do
* @param paths, du计算路径列表
* @param excludePathsdu排查路径列表
* @param blockbool,
* @return
*/
qint64 MyDuSizeTool::Do(QStringList paths, bool block)
{
return _Do(paths, {}, false, block);
}
void MyDuSizeTool::Init(QStringList paths, QStringList excludePaths)
{
m_paths = paths;
m_excludePaths = excludePaths;
}
void MyDuSizeTool::finished(int, QProcess::ExitStatus)
{
QString result = m_p->readAll();
if (result.isEmpty())
m_size = 0;
else
m_size = result.toLongLong();
emit duFinished(m_size);
}

34
common/mydusizetool.h Executable file
View File

@ -0,0 +1,34 @@
#ifndef MYDUSIZETOOL_H
#define MYDUSIZETOOL_H
#include <QObject>
#include <QProcess>
class MyDuSizeTool : public QObject {
Q_OBJECT
public:
MyDuSizeTool(QObject* parent = nullptr);
~MyDuSizeTool();
qint64 Do(QStringList paths, QStringList excludePaths, bool block = true);
qint64 Do(QStringList paths, bool block = true);
qint64 size() { return m_size; }
private:
void Init(QStringList paths, QStringList excludePaths);
qint64 _Do(QStringList paths, QStringList excludePaths, bool needExclude, bool block = true);
signals:
void duFinished(qint64 size);
private slots:
void finished(int, QProcess::ExitStatus);
private:
QStringList m_paths;
QStringList m_excludePaths;
qint64 m_size = 0;
QProcess* m_p;
};
#endif // !MYDUSIZETOOL_H

265
common/mylittleparse.cpp Executable file
View File

@ -0,0 +1,265 @@
#include "mylittleparse.h"
#include <QString>
#include <QTextStream>
MyLittleParse::MyLittleParse(const QString& path)
: file(new QFile(path))
, m_e(MyLittleParse::DEFAULT)
{
}
MyLittleParse::~MyLittleParse()
{
file->close();
delete file;
}
bool MyLittleParse::add(const QString& key, const QString& value, MyLittleParse::error_enum& e)
{
QString find_value;
if (find(key, find_value, e)) {
//key=<不空值>
if (!find_value.isEmpty()) {
e = MyLittleParse::KEY_EXITS;
return false;
}
//key=<空值>
return mod(key, value, e);
}
if (!file->open(QIODevice::ReadWrite)) {
e = MyLittleParse::OPEN_FILE_FAIL;
return false;
}
QTextStream out(file);
file->readAll();
out << key << "=" << value << "\n";
file->close();
e = MyLittleParse::SUCCESS;
return true;
}
bool MyLittleParse::add(const QString&& key, const QString&& value)
{
QString _key = key;
QString _value = value;
return add(_key, _value);
}
bool MyLittleParse::Add(const QString &key, const QString &value)
{
MyLittleParse::error_enum e;
bool t = add(key, value, e);
if (!t && e == MyLittleParse::KEY_EXITS) {
t = mod(key, value);
}
return t;
}
bool MyLittleParse::add(const QString& key, const QString& value)
{
return add(key, value, m_e);
}
bool MyLittleParse::del(const QString& key, MyLittleParse::error_enum& e)
{
if (!find_only(key, e)) {
return false;
}
if (!file->open(QIODevice::ReadOnly)) {
e = MyLittleParse::OPEN_FILE_FAIL;
return false;
}
QTextStream out(file);
QString strAll = out.readAll();
file->close();
QStringList strList = strAll.split("\n");
QStringList strDstList;
QString tmp_key;
for (QString& line : strList) {
if (line.trimmed().isEmpty())
continue;
tmp_key = line.section('=', 0, 0).trimmed();
if (tmp_key == key)
continue;
strDstList << line << "\n";
}
file->open(QIODevice::Truncate | QIODevice::WriteOnly);
for (auto s : strDstList)
out << s;
out.flush();
file->close();
e = MyLittleParse::SUCCESS;
return true;
}
bool MyLittleParse::del(const QString&& key)
{
QString _key = key;
return del(_key);
}
bool MyLittleParse::del(const QString& key)
{
return del(key, m_e);
}
bool MyLittleParse::mod(const QString& key, const QString& value, MyLittleParse::error_enum& e)
{
if (!find_only(key, e)) {
return false;
}
if (!file->open(QIODevice::ReadOnly)) {
e = MyLittleParse::OPEN_FILE_FAIL;
return false;
}
QTextStream out(file);
QString strAll = out.readAll();
file->close();
QStringList strList = strAll.split("\n");
QStringList strDstList;
QString tmp_key;
QString tmp_value;
for (QString& line : strList) {
tmp_key = line.section('=', 0, 0).trimmed();
if (tmp_key == key) {
tmp_value = line.section('=', 1, 1).trimmed();
if (tmp_value == value) {
e = MyLittleParse::REPEAT;
file->close();
return false;
}
line.clear();
line = key + "=" + value;
}
strDstList << line << "\n";
}
file->open(QIODevice::Truncate | QIODevice::WriteOnly);
for (auto& s : strDstList)
out << s;
out.flush();
file->close();
e = MyLittleParse::SUCCESS;
return true;
}
bool MyLittleParse::mod(const QString& key, const QString& value)
{
return mod(key, value, m_e);
}
bool MyLittleParse::mod(const QString&& key, const QString&& value)
{
QString _key = key;
QString _value = value;
return mod(_key, _value);
}
bool MyLittleParse::Mod(const QString &key, const QString &value)
{
MyLittleParse::error_enum e;
bool t = mod(key, value, e);
if (!t && e == MyLittleParse::REPEAT) {
return true;
}
return t;
}
bool MyLittleParse::find(const QString& key, QString& value, MyLittleParse::error_enum& e)
{
if (!file->open(QIODevice::ReadOnly)) {
e = MyLittleParse::OPEN_FILE_FAIL;
return false;
}
QTextStream out(file);
QString line;
QString tmp_key;
while (1) {
if (out.atEnd())
break;
line = out.readLine();
if (line.trimmed().isEmpty())
continue;
tmp_key = line.section('=', 0, 0).trimmed();
if (key == tmp_key) {
value = line.section('=', 1, 1).trimmed();
file->close();
e = MyLittleParse::SUCCESS;
return true;
}
}
file->close();
e = MyLittleParse::KEY_NOEXITS;
return false;
}
bool MyLittleParse::find(const QString& key, QString& value)
{
return find(key, value, m_e);
}
bool MyLittleParse::find(const QString&& key, QString&& value)
{
QString _key = key;
QString _value = value;
return find(_key, _value);
}
bool MyLittleParse::find_only(const QString& key, MyLittleParse::error_enum& e)
{
QString value;
if (find(key, value, e))
return true;
return false;
}
bool MyLittleParse::find_only(const QString& key)
{
return find_only(key, m_e);
}
bool MyLittleParse::find_only(const QString&& key)
{
QString _key = key;
return find_only(_key);
}
MyLittleParse::error_enum MyLittleParse::getErrEnum() const
{
return m_e;
}
QString MyLittleParse::getErrMsg()
{
switch (m_e) {
case MyLittleParse::DEFAULT:
return "default";
case MyLittleParse::KEY_EXITS:
return "KEY_EXITS";
case MyLittleParse::OPEN_FILE_FAIL:
return "OPEN_FILE_FAIL";
case MyLittleParse::KEY_NOEXITS:
return "KEY_NOEXITS";
case MyLittleParse::REPEAT:
return "REPEAT";
case MyLittleParse::SUCCESS:
return "SUCCESS";
default:
break;
}
return "";
}

121
common/mylittleparse.h Executable file
View File

@ -0,0 +1,121 @@
#pragma once
#include <QFile>
class QString;
class MyLittleParse {
public:
enum error_enum {
DEFAULT,
OPEN_FILE_FAIL,
KEY_EXITS, //key存在value为空, 适用于add
REPEAT, //key和value都存在且重复适用于mod
KEY_NOEXITS, //不能找到key, 适用于find
SUCCESS
};
MyLittleParse(const QString& path);
~MyLittleParse();
/**
* @brief add
* @param key
* @param value
* @param e
* @return
*
* false e = OPEN_FILE_FAIL
* key不存在, key=value, e = SUCCESS
* key存在= mod
* key存在=false, e = KEY_EXISTS
*
*/
bool add(const QString& key, const QString& value, error_enum& e);
bool add(const QString& key, const QString& value);
bool add(const QString&& key, const QString&& value);
/**
* @brief Add
* @param key
* @param value
* @return
*
* key不存在, key=value
* key存在 mod
*
*/
bool Add(const QString& key, const QString& value);
/**
* @brief del
* @param key
* @param e
* @return
*
* find_onlykey = falsefalse
* false e = OPEN_FILE_FAIL
*
*/
bool del(const QString& key, error_enum& e);
bool del(const QString& key);
bool del(const QString&& key);
/**
* @brief mod
* @param key
* @param value
* @param e
* @return
*
* find_onlykey = falsefalse
* false e = OPEN_FILE_FAIL
* key对应的值和value相等false e = REPEAT
*/
bool mod(const QString& key, const QString& value, error_enum& e);
bool mod(const QString& key, const QString& value);
bool mod(const QString&& key, const QString&& value);
/**
* @brief Mod
* @param key
* @param value
* @return
*
*
* key对应的值和value相等true
*/
bool Mod(const QString& key, const QString& value);
/**
* @brief find
* @param key
* @param value
* @param e
* @return
*
* false e = OPEN_FILE_FAIL
* keyvalue
*/
bool find(const QString& key, QString& value, error_enum& e);
bool find(const QString& key, QString& value);
bool find(const QString&& key, QString&& value);
/**
* @brief find_only
* @param key
* @param e
* @return
* false e = OPEN_FILE_FAIL
*/
bool find_only(const QString& key, error_enum& e);
bool find_only(const QString& key);
bool find_only(const QString&& key);
error_enum getErrEnum() const;
QString getErrMsg();
private:
QFile* file;
error_enum m_e;
};

49
common/reflect.cpp Executable file
View File

@ -0,0 +1,49 @@
/**
* brief
* author
* note :
* 1QObject或其派生数的上层节点包含QObject
* 2
* 3使DECLARE_DYNAMIC_CLASS(yourClassName); .cpp使IMPL_DYNAMIC_CLASS(yourClassName)
* 4
* .h中
* #include <QObject>
* #include "../common/reflect.h"
* class MyClass : public QObject
* {
* Q_OBJECT
* DECLARE_DYNCREATE(MyClass)
* }
*
* .cpp文件中
* IMPLEMENT_DYNCREATE(MyClass)
*/
#include "reflect.h"
int Reflect::registerClass(const QString& className, Constructor constructor)
{
QHash<QString, Constructor>& instances = constructors();
instances.insert(className, constructor);
return instances.size();
}
QObject * Reflect::createObject(const QString& className)
{
QHash<QString, Constructor>& instances = constructors();
if (instances.contains(className)) {
Constructor constructor = instances[className];
if (constructor != nullptr) {
return (*constructor)();
}
}
return nullptr;
}
QHash<QString, Constructor>& Reflect::constructors()
{
static QHash<QString, Constructor> _instances;
return _instances;
}

55
common/reflect.h Executable file
View File

@ -0,0 +1,55 @@
/**
* brief
* author
* note :
* 1QObject或其派生数的上层节点包含QObject
* 2
* 3使DECLARE_DYNAMIC_CLASS(yourClassName); .cpp使IMPL_DYNAMIC_CLASS(yourClassName)
* 4
* .h中
* #include <QObject>
* #include "../common/reflect.h"
* class MyClass : public QObject
* {
* Q_OBJECT
* DECLARE_DYNCREATE(MyClass)
* }
*
* .cpp文件中
* IMPLEMENT_DYNCREATE(MyClass)
*/
#ifndef REFLECT_H
#define REFLECT_H
#include <QObject>
#include <QHash>
#include <QString>
typedef QObject * (*Constructor)();
class Reflect
{
public:
static int registerClass(const QString& className, Constructor constructor);
static QObject * createObject(const QString& className);
private:
static QHash<QString, Constructor>& constructors();
};
#define DECLARE_DYNCREATE(class_name) \
public: \
static QObject * create##class_name##Object();
#define IMPLEMENT_DYNCREATE(class_name) \
QObject * class_name::create##class_name##Object() \
{ \
return (QObject *)(new class_name()); \
} \
static int g_icreate##class_name##Object = Reflect::registerClass(#class_name, class_name::create##class_name##Object);
#endif //!REFLECT_H

35
common/singleton.h Executable file
View File

@ -0,0 +1,35 @@
/**
* brief 1.使
* 2.Singleton模板的默认构造函数是受保护的protected
* 3.token类型的参数
* author
* note 使
class MysqlPool :public Singleton<MysqlPool> {
public:
MysqlPool(token) {};
~MysqlPool();
...
};
*/
#ifndef SINGLETON_H_
#define SINGLETON_H_
template <class T>
class Singleton
{
public:
Singleton(const Singleton&) = delete;
Singleton& operator= (const Singleton) = delete;
public:
static T& inst() {
static T _{ token{} };
return _;
}
protected:
struct token {};
Singleton() = default;
};
#endif

37
common/spinlock_mutex.h Executable file
View File

@ -0,0 +1,37 @@
/**
* @author
* @note c++11, std::shared_lock<spinlock_mutex>std::lock_guard<spinlock_mutex>
*/
#pragma once
#ifndef __SPINLOCK_MUTEX_H__
#define __SPINLOCK_MUTEX_H__
#include <atomic>
#include <mutex>
class spinlock_mutex
{
std::atomic_flag flag = ATOMIC_FLAG_INIT; //注意:不能用初始化列表初始化,拷贝构造和复制构造函数已经删除
public:
spinlock_mutex() {}
void lock()
{
while (flag.test_and_set(std::memory_order_acquire));
}
void unlock()
{
flag.clear(std::memory_order_release);
}
bool try_lock()
{
return !flag.test_and_set(std::memory_order_acquire);
}
};
#endif //!__SPINLOCK_MUTEX_H__

1316
common/utils.cpp Executable file

File diff suppressed because it is too large Load Diff

339
common/utils.h Executable file
View File

@ -0,0 +1,339 @@
#ifndef UTILS_H
#define UTILS_H
#include <QString>
#include <QtDebug>
#include "mydefine.h"
class SystemInfo
{
public:
// 操作系统
static QString m_os;
// 架构
static QString m_arch;
// 引导方式
static QString m_archDetect;
};
/**
* @brief
* @author zhaominyong
* @since 2021/07/22
*/
class Utils
{
public:
/**
* @brief initSysRootPath,
* @param qsAppPath
*/
static void initSysRootPath(const QString& qsAppPath);
/**
* @brief getSysRootPath
* @return const QString&
* @note
* initSysRootPath/
*/
static const QString& getSysRootPath() { return m_sysRootPath; }
/**
* @brief customMessageHandler
* @param type debug等
* @param context
* @param msg
*/
static void customMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
/**
* @brief
* @param frontUidid
* @return
*/
static int lockProgram(int frontUid);
/**
* @brief
* @param lock_file_fd
* @return 01
*/
static int unLockProgram(int lock_file_fd);
/**
* @brief
* @return bool
*/
static bool rmLockFile();
/**
* @brief /etc/.bootinfo是否存在并可读UUID等信息
* @return bool
*/
static bool checkBootInfoExists();
/**
* @brief
* @return bool
*/
static bool isTablet();
/**
* @brief UUID
* @return UUID
*/
static QString getBackupPartitionUuid();
/**
* @brief UUID映射关系
* @param fstab文件路径
* @return -UUID
*/
static QHash<QString, QString> getPartUuidMap(const QString &fstab);
/**
* @brief
* @param path
*/
static void mkdir(const QString& path);
/**
* @brief
* @param path
* @return bool
*/
static bool mkpath(const QString& path);
/**
* @brief
* @param excludes
*/
static void excludeFstabBindPath(QStringList &excludes);
/**
* @brief
* @param excludes
*/
static void excludeSomeHomePath(QStringList &excludes);
/**
* @brief
* @param excludes
*/
static void excludeCustomizePath(QStringList &excludes);
/**
* @brief rsync --exclude-from排除路径规则文件
* @return
*/
static bool generateExcludePathsFile();
/**
* @brief
* @return
*/
static QStringList getFromExcludePathsFile();
/**
* @brief
* @param
* @return true,false,
*/
static bool isDirExist(const QString& fullDirName);
/**
* @brief Uuid
* @return UUID
*/
static QString createUuid();
/**
* @brief
* @param fileName
* @param lines
* @return truefalse
*/
static bool writeFileByLines(const QString& fileName, const QStringList& lines);
/**
* @brief
* @param fileName
* @return booltrue-false-
* @author zhaominyong
* @since 2021/05/29
*/
static bool filsExists(const QString &fileName);
/**
* @brief
* @param fullDirName
* @return true-; false-
*/
static bool isDirEmpty(const QString& fullDirName);
/**
* @brief
* @param line
* @return bool, true-false-
* @author zhaominyong
* @since 2021/05/29
* @note
* rebootfdatasync确保缓存落盘
*/
static bool writeBackupLog(QString line);
/**
* @brief
* @param fileName
* @param content
* @return bool
*/
static bool syncWriteFile(const QString &fileName, const QString& content);
/**
* @brief GB等表示的字符串
* @param sizeqint64
* @return GB/MB/KB等表示的字符串型大小
*/
static QString StringBySize(qint64 size);
/**
* @brief
* @return MOUNTPOINT,PATH键值对列表(path中如果含有空格时显示不对)
* @author zhaominyong
* @since 2021/06/07
* @note
* for bug 59636 cpmHUAWEIKOSL0V3试制dev/sdc的路径(++)
* QStorageInfo::mountedVolumes获取的磁盘列表区分不出来是否移动设备
*/
static QHash<QString, QString> getRemovableStorages();
/**
* @brief
* @return
*/
static QList<QString> getLocalDisks();
/**
* @brief
* @param enabletruefalse
*/
static void setKysecStatus(bool enable);
/**
* @brief kysec-sync-daemon服务
* @param enabletruefalse
*/
static void setKysecDaemon(bool enable);
/**
* @brief
* @param processName
* @return true-false-
*/
static bool isRunning(const QString &processName);
/**
* @brief 9909006C处理器
* @return bool
*/
static bool isHuawei990();
/**
* @brief popen()fork()shell以运行命令来开启一个进程
* @param cmd NULL shell bin/sh 使 -c shell
* @param result
* @note 使
*/
static void executeCMD(const char* cmd, QString &result);
static QString executeCmd(const QString &cmd, const QStringList &args = QStringList());
/**
* @brief
* @return
*/
static QList<BackupWrapper> getBackupLogList();
/**
* @brief
*/
static void initSystemInfo();
/**
* @brief getOs
* @return ,
* Kylin-Desktop V10-SP1
* Build 20210407
*/
static QString getOs();
/**
* @brief getArch
* @return arch命令的结果x86_64
*/
static QString getArch();
/**
* @brief getArchDetect
* @return archdetect命令的结果amd64/efi
*/
static QString getArchDetect();
/**
* @brief
* @return
*/
static QHash<QString, QString> getLeftSizeOfPartitions();
/**
* @brief //data分区
* @return
*/
static QHash<QString, qint64> getAvailableSizeOfPartitions();
/**
* @brief
* @param path
* @return
*/
static qint64 getDirOrFileSize(const QString &path);
/**
* @brief uuid及其名称
* @param uuid
* @param backupName
*/
static void update_backup_unique_settings(const QString &uuid, const QString &backupName);
/**
* @brief uuid记录
* @param backupName
*/
static void deleteBackupUniqueRecord(const QString& backupName);
/**
* @brief Uuid-BackupName键值对
* @return Uuid-BackupName键值对
*/
static QMap<QString, QString> getBackupUuidNameMap();
/**
* @brief
* @return
*/
static bool updateSyncFile();
/**
* @brief sleep便
* @param sec
* @author zhaominyong
* @since 2021/05/29
*/
static void wait(uint sec);
private:
// 系统根目录,默认"/"
static QString m_sysRootPath;
};
#endif // UTILS_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Some files were not shown because too many files have changed in this diff Show More