10237 【备份还原】增加可自定义路径
This commit is contained in:
parent
d1e9b967df
commit
b0952490c2
|
@ -31,7 +31,10 @@ HEADERS += \
|
|||
../common/spinlock_mutex.h \
|
||||
../common/utils.h \
|
||||
backupmanager_adaptor.h \
|
||||
customizedatabackupproxy.h \
|
||||
customizeghostImageproxy.h \
|
||||
customizesystembackupproxy.h \
|
||||
customizesystemrestoreproxy.h \
|
||||
databackupproxy.h \
|
||||
datarestoreproxy.h \
|
||||
deletebackupproxy.h \
|
||||
|
@ -60,7 +63,10 @@ SOURCES += \
|
|||
../common/reflect.cpp \
|
||||
../common/utils.cpp \
|
||||
backupmanager_adaptor.cpp \
|
||||
customizedatabackupproxy.cpp \
|
||||
customizeghostImageproxy.cpp \
|
||||
customizesystembackupproxy.cpp \
|
||||
customizesystemrestoreproxy.cpp \
|
||||
databackupproxy.cpp \
|
||||
datarestoreproxy.cpp \
|
||||
deletebackupproxy.cpp \
|
||||
|
|
|
@ -0,0 +1,461 @@
|
|||
#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);
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
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 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;
|
||||
} else {
|
||||
emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS));
|
||||
}
|
||||
|
||||
if (m_isOnlyCheck)
|
||||
return result;
|
||||
|
||||
// 开始备份
|
||||
doBackup();
|
||||
|
||||
qDebug() << "CustomizeDataBackupProxy::checkFreeCapacity invoke end";
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算备份所需空间大小
|
||||
* @return 计算备份所需空间大小,单位字节
|
||||
*/
|
||||
void CustomizeDataBackupProxy::calcSizeForBackup()
|
||||
{
|
||||
QString destPath = Utils::getSysRootPath();
|
||||
|
||||
QStringList args;
|
||||
if (m_backupWrapper.m_bIncrement) {
|
||||
// 在原来的备份点上增量备份场景
|
||||
args = getRsyncArgs(CustomizeDataBackupScene::TRY_INC_DATA_BACKUP);
|
||||
destPath = m_backupWrapper.m_prefixDestPath;
|
||||
destPath += BACKUP_SNAPSHOTS_PATH;
|
||||
destPath += "/";
|
||||
destPath += m_backupWrapper.m_uuid;
|
||||
destPath += "/data/";
|
||||
} else {
|
||||
// 全量备份场景
|
||||
args = getRsyncArgs(CustomizeDataBackupScene::TRY_DATA_BACKUP);
|
||||
destPath += CHECK_PATH;
|
||||
}
|
||||
|
||||
// 拼接备份源路径和目标路径
|
||||
QString srcPath = Utils::getSysRootPath();
|
||||
srcPath += "/";
|
||||
srcPath.replace("//", "/");
|
||||
args << srcPath;
|
||||
destPath.replace("//", "/");
|
||||
args << destPath;
|
||||
Utils::mkpath(destPath);
|
||||
|
||||
connect(m_calc, &CalcBackupSize::finished, this, &CustomizeDataBackupProxy::checkFreeCapacity);
|
||||
m_calc->start(args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据场景获取rsync命令参数
|
||||
* @param scene,场景
|
||||
* @return rsync的参数信息
|
||||
*/
|
||||
QStringList CustomizeDataBackupProxy::getRsyncArgs(CustomizeDataBackupScene scene)
|
||||
{
|
||||
QStringList args;
|
||||
QString backupFile = "/tmp/.backup.user";
|
||||
Utils::writeFileByLines(backupFile, m_backupWrapper.m_backupPaths);
|
||||
|
||||
switch (scene) {
|
||||
case CustomizeDataBackupScene::DATA_BACKUP :
|
||||
args << "-avAHXr";
|
||||
args << "--info=progress2";
|
||||
args << "--no-inc-recursive";
|
||||
args << "--ignore-missing-args";
|
||||
args << "--delete";
|
||||
args << "--files-from" << backupFile;
|
||||
break ;
|
||||
case CustomizeDataBackupScene::INC_DATA_BACKUP :
|
||||
args << "-avAHXr";
|
||||
args << "--info=progress2";
|
||||
args << "--no-inc-recursive";
|
||||
args << "--ignore-missing-args";
|
||||
args << "--delete";
|
||||
args << "--files-from" << backupFile;
|
||||
break ;
|
||||
case CustomizeDataBackupScene::TRY_DATA_BACKUP :
|
||||
args << "-aAHXrn";
|
||||
args << "--stats";
|
||||
args << "--ignore-missing-args";
|
||||
args << "--files-from" << backupFile;
|
||||
break ;
|
||||
case CustomizeDataBackupScene::TRY_INC_DATA_BACKUP :
|
||||
args << "-aAHXrn";
|
||||
args << "--stats";
|
||||
args << "--ignore-missing-args";
|
||||
args << "--delete";
|
||||
args << "--files-from" << backupFile;
|
||||
break ;
|
||||
default:
|
||||
return args;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 备份
|
||||
*/
|
||||
void CustomizeDataBackupProxy::doBackup()
|
||||
{
|
||||
qDebug() << "CustomizeDataBackupProxy::doBackup invoke begin";
|
||||
|
||||
// 准备
|
||||
if (!doPrepare())
|
||||
return ;
|
||||
|
||||
// 启动数据备份
|
||||
backupData();
|
||||
|
||||
qDebug() << "CustomizeDataBackupProxy::doBackup invoke end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 备份准备
|
||||
* @return true,准备成功;false,准备失败
|
||||
*/
|
||||
bool CustomizeDataBackupProxy::doPrepare()
|
||||
{
|
||||
qDebug() << "CustomizeDataBackupProxy::doPrepare invoke begin";
|
||||
|
||||
m_bSuccess = false;
|
||||
|
||||
// 1、设置当前备份的Uuid
|
||||
if (m_backupWrapper.m_uuid.isEmpty()) {
|
||||
// 新增备份点,不指定uuid的场景
|
||||
m_curUuid += Utils::createUuid();
|
||||
} else {
|
||||
// 指定uuid备份的场景
|
||||
m_curUuid = m_backupWrapper.m_uuid;
|
||||
}
|
||||
|
||||
// 2、准备备份目录及文件
|
||||
m_destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/data";
|
||||
m_destPath.replace("//", "/");
|
||||
if (!Utils::mkpath(m_destPath)) {
|
||||
emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
|
||||
qCritical() << QString("mkdir %1 failed !").arg(m_destPath) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_userFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + PATHS_USER_FILE;
|
||||
m_userFile.replace("//", "/");
|
||||
if (!Utils::writeFileByLines(m_userFile, m_backupWrapper.m_backupPaths)) {
|
||||
emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
|
||||
qCritical() << QString("create file %1 failed !").arg(m_userFile) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_excludeUserFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + EXCLUDE_PATHS_USER_FILE;
|
||||
m_excludeUserFile.replace("//", "/");
|
||||
if (!Utils::writeFileByLines(m_excludeUserFile, m_backupWrapper.m_backupExcludePaths)) {
|
||||
emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED));
|
||||
qCritical() << QString("create file %1 failed !").arg(m_excludeUserFile) ;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3、记录/backup/snapshots/backuplist.xml文件
|
||||
if (!recordBackupPoint()) {
|
||||
qCritical() << "add or update item to backuplist.xml failed !";
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "CustomizeDataBackupProxy::doPrepare invoke end";
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 记录/backup/snapshots/backuplist.xml文件
|
||||
* @return true-成功;false-失败
|
||||
*/
|
||||
bool CustomizeDataBackupProxy::recordBackupPoint()
|
||||
{
|
||||
m_backupPoint.m_backupName = m_backupWrapper.m_backupName;
|
||||
m_backupPoint.m_uuid = m_curUuid;
|
||||
m_backupPoint.m_iPosition = m_backupWrapper.m_iPosition;
|
||||
m_backupPoint.m_type = m_backupWrapper.m_type;
|
||||
m_backupPoint.m_size = Utils::StringBySize(m_size);
|
||||
m_backupPoint.m_time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss");
|
||||
m_backupPoint.m_state = BACKUP_PARSE_STATE_FAIL_STRTING;
|
||||
if (0 < m_backupWrapper.m_frontUid)
|
||||
m_backupPoint.m_userId = QString::number(m_backupWrapper.m_frontUid);
|
||||
m_backupPoint.m_os = SystemInfo::m_os;
|
||||
m_backupPoint.m_arch = SystemInfo::m_arch;
|
||||
m_backupPoint.m_archdetect = SystemInfo::m_archDetect;
|
||||
m_backupPoint.m_path = m_backupWrapper.m_prefixDestPath;
|
||||
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
|
||||
xmlPath.replace("//", "/");
|
||||
ParseBackupList parse(xmlPath);
|
||||
if (m_backupWrapper.m_bIncrement) {
|
||||
if (parse.updateItem(m_backupPoint) != ParseBackupList::SUCCESS) {
|
||||
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_UPDATE_ITEM_FAIL));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) {
|
||||
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 备份系统
|
||||
* @return true,备份成功;false,备份失败
|
||||
*/
|
||||
bool CustomizeDataBackupProxy::backupData()
|
||||
{
|
||||
qDebug() << "CustomizeDataBackupProxy::backupData invoke begin";
|
||||
|
||||
QStringList args;
|
||||
if (m_backupWrapper.m_bIncrement) {
|
||||
// 在原来的备份点上增量备份场景
|
||||
args = getRsyncArgs(CustomizeDataBackupScene::INC_DATA_BACKUP);
|
||||
} else {
|
||||
// 全量备份场景
|
||||
args = getRsyncArgs(CustomizeDataBackupScene::DATA_BACKUP);
|
||||
}
|
||||
|
||||
// 拼接备份源路径和目标路径
|
||||
QString srcPath = Utils::getSysRootPath();
|
||||
srcPath += "/";
|
||||
srcPath.replace("//", "/");
|
||||
args << srcPath;
|
||||
QString destPath = m_destPath + "/";
|
||||
destPath.replace("//", "/");
|
||||
args << destPath;
|
||||
|
||||
m_p = new RsyncPathToDirProcess(this);
|
||||
connect(m_p, &RsyncPathToDirProcess::progress, this, &CustomizeDataBackupProxy::progress);
|
||||
connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool result) {
|
||||
// 如果是取消了操作,则不再发送其它信息
|
||||
if (m_bCancel)
|
||||
return ;
|
||||
|
||||
m_isFinished = true;
|
||||
if (result) {
|
||||
m_backupPoint.m_state = BACKUP_PARSE_STATE_SUCCESS_STRTING;
|
||||
m_backupPoint.m_size = Utils::StringBySize(Utils::getDirOrFileSize(m_destPath));
|
||||
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
|
||||
xmlPath.replace("//", "/");
|
||||
ParseBackupList parse(xmlPath);
|
||||
parse.updateItem(m_backupPoint);
|
||||
|
||||
// Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ","+ m_backupWrapper.m_note + "," + m_backupPoint.m_size+ "," + QString::number(m_backupWrapper.m_frontUid));
|
||||
Utils::writeBackupLog(m_backupPoint.m_time + ","
|
||||
+ m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ","
|
||||
+ m_backupWrapper.m_note + "," + m_backupPoint.m_size
|
||||
+ "," + QString::number(m_backupWrapper.m_frontUid)
|
||||
+ "," + m_backupWrapper.m_backupName);
|
||||
Utils::update_backup_unique_settings(m_curUuid, m_backupPoint.m_backupName);
|
||||
m_bSuccess = true;
|
||||
}
|
||||
|
||||
emit this->workResult(result);
|
||||
});
|
||||
m_p->start(args, false);
|
||||
|
||||
do_kylin_security(m_destPath);
|
||||
|
||||
qDebug() << "CustomizeDataBackupProxy::backupData invoke end";
|
||||
return true;
|
||||
}
|
||||
|
||||
void CustomizeDataBackupProxy::do_kylin_security(const QString& dstDir)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = kysec_getstatus();
|
||||
if (ret > 0) {
|
||||
QString seFilePath(dstDir + "/.exectl");
|
||||
QFile file(seFilePath);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -0,0 +1,225 @@
|
|||
#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()
|
||||
{
|
||||
// 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、校验空间是否充足
|
||||
QFileInfo backup(m_srcPath);
|
||||
qint64 itotalSize = backup.size();
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 任务处理
|
||||
*/
|
||||
void CustomizeGhostImageProxy::doWorkEx()
|
||||
{
|
||||
if (!checkEnvEx())
|
||||
return ;
|
||||
|
||||
doGhostImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 任务取消
|
||||
*/
|
||||
void CustomizeGhostImageProxy::cancelEx()
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()
|
||||
{
|
||||
// 同步到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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 校验移动盘是否还在
|
||||
* @return: bool,存在返回true;不存在返回false
|
||||
* @author: zhaominyong
|
||||
* @since: 2021/05/24
|
||||
* @note:
|
||||
* add by zhaominyong at 2021/05/24 for bug:54377 【备份还原】备份数据到U盘的过程中拔出U盘,备份还原工具仍然一直显示正在备份数据
|
||||
*/
|
||||
bool CustomizeGhostImageProxy::checkDestDirExists()
|
||||
{
|
||||
if (!m_isFinished)
|
||||
{
|
||||
// 拔掉U盘后,没有响应的场景(怀疑可能是某应用程序关闭引起,希望不是dbus服务关掉了)
|
||||
if (m_isForce) {
|
||||
emit this->workResult(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) {
|
||||
qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath);
|
||||
if (m_p)
|
||||
m_p->stop();
|
||||
// 10s钟后如果还没有退出,则强制退出
|
||||
QTimer::singleShot(10*1000, this, &CustomizeGhostImageProxy::checkDestDirExists);
|
||||
m_isForce = true;
|
||||
} else {
|
||||
QTimer::singleShot(1*1000, this, &CustomizeGhostImageProxy::checkDestDirExists);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -19,7 +19,6 @@ CustomizeSystemBackupProxy::CustomizeSystemBackupProxy()
|
|||
m_calc = new CalcBackupSize(this);
|
||||
m_isOnlyCheck = true;
|
||||
m_mksquashfs = nullptr;
|
||||
m_isForce = false;
|
||||
|
||||
connect(this, &CustomizeSystemBackupProxy::cancel, this, &CustomizeSystemBackupProxy::cancelEx);
|
||||
}
|
||||
|
@ -32,9 +31,10 @@ CustomizeSystemBackupProxy::~CustomizeSystemBackupProxy()
|
|||
delete m_mksquashfs;
|
||||
m_mksquashfs = nullptr;
|
||||
|
||||
QString rm("rm -rf ");
|
||||
rm += m_imgPath;
|
||||
QProcess::execute(rm);
|
||||
// 如果备份失败,则删除备份数据
|
||||
if (!m_bSuccess) {
|
||||
deleteFailedData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,30 +45,26 @@ bool CustomizeSystemBackupProxy::checkEnvEx()
|
|||
{
|
||||
qDebug() << "CustomizeSystemBackupProxy::checkEnv invoke begin";
|
||||
|
||||
// 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载
|
||||
// 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载。
|
||||
MyMountProxy mountProxy;
|
||||
if ( MountResult::MOUNTED != mountProxy.mountBackupPartition() ) {
|
||||
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();
|
||||
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 (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) {
|
||||
qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath);
|
||||
Utils::mkpath(m_backupWrapper.m_prefixDestPath);
|
||||
}
|
||||
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, &CustomizeSystemBackupProxy::checkDestDirExists);
|
||||
// 2、计算备份大小
|
||||
calcSizeForBackup();
|
||||
|
||||
|
@ -125,42 +121,12 @@ void CustomizeSystemBackupProxy::deleteFailedData()
|
|||
QProcess::execute("rm", args);
|
||||
|
||||
// 2、删除xml文件中的备份项
|
||||
QString xmlPath = m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH;
|
||||
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
|
||||
xmlPath.replace("//", "/");
|
||||
ParseBackupList parse(xmlPath);
|
||||
parse.deleteItem(m_curUuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断是否增量备份
|
||||
* @return true,增量备份; false,全量备份
|
||||
*/
|
||||
bool CustomizeSystemBackupProxy::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 校验剩余空间是否满足备份
|
||||
*/
|
||||
|
@ -172,16 +138,12 @@ void CustomizeSystemBackupProxy::checkFreeCapacity(qint64 itotalSize)
|
|||
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;
|
||||
// 判断是否需要先压缩成img文件,压缩后一般小于原大小的70%
|
||||
itotalSize = itotalSize * 7 / 10;
|
||||
|
||||
// 2、计算备份分区剩余空间大小
|
||||
QString backupPath(m_backupWrapper.m_prefixDestPath);
|
||||
|
@ -199,29 +161,12 @@ void CustomizeSystemBackupProxy::checkFreeCapacity(qint64 itotalSize)
|
|||
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 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 4、准备
|
||||
if (!doPrepare())
|
||||
return ;
|
||||
|
||||
// 5、开始制作img或开始备份
|
||||
if (m_imgPath.isEmpty()) {
|
||||
doBackup();
|
||||
} else {
|
||||
doMksqushfs();
|
||||
}
|
||||
// 5、开始制作img
|
||||
doMksqushfs();
|
||||
|
||||
qDebug() << "CustomizeSystemBackupProxy::checkFreeCapacity invoke end";
|
||||
}
|
||||
|
@ -260,12 +205,6 @@ QStringList CustomizeSystemBackupProxy::getRsyncArgs(CustomizeSystemBackupScene
|
|||
QStringList excludes;
|
||||
|
||||
switch (scene) {
|
||||
case CustomizeSystemBackupScene::SYSTEM_BACKUP :
|
||||
args << "-avAHXr";
|
||||
args << "--info=progress2";
|
||||
args << "--no-inc-recursive";
|
||||
args << "--ignore-missing-args";
|
||||
break ;
|
||||
case CustomizeSystemBackupScene::TRY_SYSTEM_BACKUP :
|
||||
args << "-aAHXrn";
|
||||
args << "--stats";
|
||||
|
@ -283,7 +222,6 @@ QStringList CustomizeSystemBackupProxy::getRsyncArgs(CustomizeSystemBackupScene
|
|||
|
||||
args << "-e" << item;
|
||||
}
|
||||
args << "-e" << m_imgPath;
|
||||
return args;
|
||||
default:
|
||||
return args;
|
||||
|
@ -311,26 +249,40 @@ void CustomizeSystemBackupProxy::doMksqushfs()
|
|||
if (m_bCancel)
|
||||
return ;
|
||||
|
||||
if (result && !m_isForce) {
|
||||
// 开始备份
|
||||
doBackup();
|
||||
} else {
|
||||
m_isFinished = true;
|
||||
emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL));
|
||||
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);
|
||||
});
|
||||
Utils::mkpath(m_imgPath);
|
||||
|
||||
QString srcPath = Utils::getSysRootPath();
|
||||
srcPath += "/";
|
||||
srcPath.replace("//", "/");
|
||||
QString dstImg = m_imgPath + "/" + UDISK_MKSQUASHFS_IMG_NAME;
|
||||
QString dstImg = m_destPath + "/" + UDISK_MKSQUASHFS_IMG_NAME;
|
||||
QStringList args;
|
||||
args << srcPath << dstImg;
|
||||
args.append(getRsyncArgs(CustomizeSystemBackupScene::MKSQUASHFS));
|
||||
|
||||
if (m_mksquashfs->start(args)) {
|
||||
emit checkResult(int(BackupResult::MKSQUASHFS_START_SUCCESS));
|
||||
do_kylin_security(m_destPath);
|
||||
} else {
|
||||
emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL));
|
||||
}
|
||||
|
@ -338,35 +290,6 @@ void CustomizeSystemBackupProxy::doMksqushfs()
|
|||
qDebug() << "CustomizeSystemBackupProxy::doMksqushfs invoke end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 备份
|
||||
*/
|
||||
void CustomizeSystemBackupProxy::doBackup()
|
||||
{
|
||||
qDebug() << "CustomizeSystemBackupProxy::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() << "CustomizeSystemBackupProxy::doBackup invoke end";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 备份准备
|
||||
* @return true,准备成功;false,准备失败
|
||||
|
@ -431,82 +354,18 @@ bool CustomizeSystemBackupProxy::recordBackupPoint()
|
|||
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;
|
||||
m_backupPoint.m_path = m_backupWrapper.m_prefixDestPath;
|
||||
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;
|
||||
}
|
||||
if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) {
|
||||
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 备份系统
|
||||
* @return true,启动备份成功;false,启动备份失败
|
||||
*/
|
||||
bool CustomizeSystemBackupProxy::backupSystem()
|
||||
{
|
||||
qDebug() << "CustomizeSystemBackupProxy::backupSystem invoke begin";
|
||||
|
||||
// 全量备份场景
|
||||
QStringList args = getRsyncArgs(CustomizeSystemBackupScene::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 true,启动备份成功;false,启动备份失败
|
||||
*/
|
||||
bool CustomizeSystemBackupProxy::backupImg()
|
||||
{
|
||||
qDebug() << "CustomizeSystemBackupProxy::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 true,启动备份成功;false,启动备份失败
|
||||
*/
|
||||
bool CustomizeSystemBackupProxy::backup(const QStringList &args)
|
||||
{
|
||||
qDebug() << "CustomizeSystemBackupProxy::backup invoke begin";
|
||||
|
||||
|
||||
emit checkResult(int(BackupResult::BACKUP_START_SUCCESS));
|
||||
|
||||
do_kylin_security(m_destPath);
|
||||
|
||||
qDebug() << "CustomizeSystemBackupProxy::backup invoke end";
|
||||
return true;
|
||||
}
|
||||
|
||||
void CustomizeSystemBackupProxy::do_kylin_security(const QString& dstDir)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -519,38 +378,3 @@ void CustomizeSystemBackupProxy::do_kylin_security(const QString& dstDir)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 校验移动盘是否还在
|
||||
* @return: bool,存在返回true;不存在返回false
|
||||
* @author: zhaominyong
|
||||
* @since: 2021/05/24
|
||||
* @note:
|
||||
* add by zhaominyong at 2021/05/24 for bug:54377 【备份还原】备份数据到U盘的过程中拔出U盘,备份还原工具仍然一直显示正在备份数据
|
||||
*/
|
||||
bool CustomizeSystemBackupProxy::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();
|
||||
// 10s钟后如果还没有退出,则强制退出
|
||||
QTimer::singleShot(10*1000, this, &CustomizeSystemBackupProxy::checkDestDirExists);
|
||||
} else {
|
||||
QTimer::singleShot(1*1000, this, &CustomizeSystemBackupProxy::checkDestDirExists);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ class CustomizeSystemBackupProxy : public Worker
|
|||
public:
|
||||
// 系统备份的几种场景
|
||||
enum CustomizeSystemBackupScene {
|
||||
SYSTEM_BACKUP, // 系统备份
|
||||
TRY_SYSTEM_BACKUP, // 测试系统备份,可用于计算备份传输数据大小
|
||||
MKSQUASHFS, // 生成img文件
|
||||
};
|
||||
|
@ -38,26 +37,10 @@ private slots:
|
|||
// mksqushfs
|
||||
void doMksqushfs();
|
||||
|
||||
// 备份
|
||||
void doBackup();
|
||||
|
||||
// 任务取消
|
||||
virtual void cancelEx();
|
||||
|
||||
/**
|
||||
* @brief 校验移动盘是否还在
|
||||
* @return: bool,存在返回true;不存在返回false
|
||||
* @author: zhaominyong
|
||||
* @since: 2021/05/24
|
||||
* @note:
|
||||
* add by zhaominyong at 2021/05/24 for bug:54377 【备份还原】备份数据到U盘的过程中拔出U盘,备份还原工具仍然一直显示正在备份数据
|
||||
*/
|
||||
bool checkDestDirExists();
|
||||
|
||||
private:
|
||||
// 判断是否增量备份
|
||||
bool isIncBackup();
|
||||
|
||||
// 计算备份所需空间大小
|
||||
void calcSizeForBackup();
|
||||
|
||||
|
@ -77,12 +60,6 @@ private:
|
|||
// 备份准备
|
||||
bool doPrepare();
|
||||
|
||||
// 备份系统
|
||||
bool backupSystem();
|
||||
|
||||
// 备份img文件
|
||||
bool backupImg();
|
||||
|
||||
bool backup(const QStringList &args);
|
||||
|
||||
void do_kylin_security(const QString& dstDir);
|
||||
|
@ -109,10 +86,6 @@ private:
|
|||
ParseBackupList::BackupPoint m_backupPoint;
|
||||
// 是否只是检测
|
||||
bool m_isOnlyCheck;
|
||||
// img文件存放路径
|
||||
QString m_imgPath;
|
||||
// 强制结束标志(stop后没反应的情况,系统处于睡眠状态)
|
||||
bool m_isForce;
|
||||
};
|
||||
|
||||
#endif // CUSTOMIZESYSTEMBACKUPPROXY_H
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
#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";
|
||||
// 此处不要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()
|
||||
{
|
||||
// 还原前准备
|
||||
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();
|
||||
QString fileIfSync = Utils::getSysRootPath() + FILE_IF_SYNC;
|
||||
fileIfSync.replace("//", "/");
|
||||
QFileInfo file(fileIfSync);
|
||||
QDateTime beginTime = file.fileTime(QFileDevice::FileModificationTime);
|
||||
Utils::wait(10);
|
||||
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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -35,8 +35,13 @@ 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 = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + PATHS_USER_FILE;
|
||||
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文件不存在");
|
||||
|
@ -45,7 +50,7 @@ bool DataRestoreProxy::checkEnvEx()
|
|||
}
|
||||
|
||||
// 2、检测.exclude.user.txt是否存在
|
||||
m_excludeUserFile = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + EXCLUDE_PATHS_USER_FILE;
|
||||
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文件不存在");
|
||||
|
@ -54,7 +59,7 @@ bool DataRestoreProxy::checkEnvEx()
|
|||
}
|
||||
|
||||
// 3、检测还原点是否存在
|
||||
m_backupPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
|
||||
m_backupPath = m_prePath + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
|
||||
m_backupPath.replace("//", "/");
|
||||
if (Utils::isDirEmpty(m_backupPath)) {
|
||||
qCritical("还原点{uuid}/data目录不存在");
|
||||
|
|
|
@ -42,6 +42,8 @@ private:
|
|||
QString m_excludeUserFile;
|
||||
// 备份数据所在的data目录
|
||||
QString m_backupPath;
|
||||
// 备份点前缀路径
|
||||
QString m_prePath;
|
||||
|
||||
// 是否还原成功
|
||||
bool m_bSuccess;
|
||||
|
|
|
@ -34,7 +34,7 @@ void DeleteBackupProxy::doWorkEx()
|
|||
bool DeleteBackupProxy::deleteXmlBackupPoint()
|
||||
{
|
||||
QString xmlPath;
|
||||
if (m_backupWrapper.m_iPosition == BackupPosition::LOCAL)
|
||||
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;
|
||||
|
@ -60,8 +60,10 @@ bool DeleteBackupProxy::deleteXmlBackupPoint()
|
|||
void DeleteBackupProxy::deleteBackupPointDir()
|
||||
{
|
||||
QString uuidPath;
|
||||
if (m_backupWrapper.m_iPosition == BackupPosition::LOCAL)
|
||||
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;
|
||||
|
||||
|
|
|
@ -115,9 +115,11 @@ void GhostImageProxy::cancelEx()
|
|||
void GhostImageProxy::deleteFailedData()
|
||||
{
|
||||
// 1、删除镜像文件
|
||||
QFile kyimg(m_kyimg);
|
||||
if (kyimg.exists())
|
||||
kyimg.remove();
|
||||
if (!m_kyimg.isEmpty()) {
|
||||
QFile kyimg(m_kyimg);
|
||||
if (kyimg.exists())
|
||||
kyimg.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#define OS "OS"
|
||||
#define ARCH "Arch"
|
||||
#define ARCHDETECT "ArchDetect"
|
||||
#define PREFIXDESTPATH "PrefixDestPath"
|
||||
|
||||
#define STATUE_BACKUP_FINESHED "backup finished"
|
||||
|
||||
|
@ -279,7 +280,32 @@ void ParseBackupList::getXmlUuidNameMap(QMap<QString, QString> &uuid_name)
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief 获取最后一次系统备份,排除自动备份点
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取最后一次系统备份,排除自动备份点、自定义备份点
|
||||
* @return
|
||||
*/
|
||||
ParseBackupList::BackupPoint ParseBackupList::getLastSysBackupPoint()
|
||||
|
@ -306,10 +332,13 @@ ParseBackupList::BackupPoint ParseBackupList::getLastSysBackupPoint()
|
|||
continue;
|
||||
|
||||
QDomElement eleState = node.firstChildElement(STATE);
|
||||
QString type = eleState.text();
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -369,8 +398,13 @@ void ParseBackupList::elementNodeToBackupPoint(const QDomElement& node, BackupPo
|
|||
if (!eleArchDetect.isNull())
|
||||
backupPoint.m_archdetect = eleArchDetect.text();
|
||||
|
||||
backupPoint.m_path = m_xmlPath;
|
||||
backupPoint.m_path.replace(BACKUP_XML_PATH, "");
|
||||
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, "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -399,6 +433,9 @@ void ParseBackupList::backupPointToElementNode(const BackupPoint& backupPoint, Q
|
|||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
QString m_arch;
|
||||
// 备份机器引导方式
|
||||
QString m_archdetect;
|
||||
// 备份点所在设备挂载路径(这个暂只在查询中界面展示选择中使用)
|
||||
// 备份点所在设备挂载路径(这个暂只在查询中界面展示选择中使用)或自定义的备份路径
|
||||
QString m_path;
|
||||
|
||||
bool isNull() { return m_uuid.isEmpty(); }
|
||||
|
@ -87,6 +87,12 @@ public:
|
|||
*/
|
||||
BackupPoint getLastSysBackupPoint();
|
||||
|
||||
/**
|
||||
* @brief 获取自定义备份路径列表
|
||||
* @param customizePaths
|
||||
*/
|
||||
void getCustomizePaths(QStringList &customizePaths);
|
||||
|
||||
/**
|
||||
* @brief 新增备份节点
|
||||
* @param backupPoint, 备份点信息
|
||||
|
|
|
@ -271,6 +271,8 @@ QStringList SystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
|
|||
}
|
||||
// 系统安装后有的会将/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挂载的任意一方时,都备份不上)
|
||||
|
|
|
@ -162,9 +162,11 @@ void UDiskGhostImageProxy::cancelEx()
|
|||
void UDiskGhostImageProxy::deleteFailedData()
|
||||
{
|
||||
// 1、删除临时镜像文件
|
||||
QFile kyimg(m_kyimg);
|
||||
if (kyimg.exists())
|
||||
kyimg.remove();
|
||||
if (!m_kyimg.isEmpty()) {
|
||||
QFile kyimg(m_kyimg);
|
||||
if (kyimg.exists())
|
||||
kyimg.remove();
|
||||
}
|
||||
|
||||
// 2、删除目标镜像文件
|
||||
QString kyimgFile = m_destPath + "/" + m_backupWrapper.m_backupName;
|
||||
|
|
|
@ -276,6 +276,8 @@ QStringList UDiskSystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
|
|||
}
|
||||
// 系统安装后有的会将/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挂载的任意一方时,都备份不上)
|
||||
|
@ -334,7 +336,7 @@ bool UDiskSystemRestoreProxy::doPrepare()
|
|||
Utils::mkpath(dstImgMountPath);
|
||||
|
||||
// 2、先卸载/backup/imgbackup上的mount
|
||||
MountBackupProcess *processMount = new MountBackupProcess;
|
||||
MountBackupProcess *processMount = new MountBackupProcess(this);
|
||||
processMount->umount(dstImgMountPath);
|
||||
|
||||
// 3、将img文件挂载到/backup/imgbackup上
|
||||
|
|
|
@ -62,7 +62,7 @@ Worker * WorkerFactory::createWorker(int type, int position)
|
|||
break;
|
||||
case BackupType::RESTORE_SYSTEM:
|
||||
case BackupType::RESTORE_SYSTEM_WITH_DATA:
|
||||
if (BackupPosition::UDISK == position) {
|
||||
if (BackupPosition::UDISK == position || BackupPosition::OTHER == position) {
|
||||
className = "UDiskSystemRestoreProxy";
|
||||
} else if (BackupPosition::CUSTOMIZE == position) {
|
||||
className = "CustomizeSystemRestoreProxy";
|
||||
|
@ -83,8 +83,6 @@ Worker * WorkerFactory::createWorker(int type, int position)
|
|||
case BackupType::RESTORE_DATA:
|
||||
if (BackupPosition::UDISK == position) {
|
||||
className = "UDiskDataRestoreProxy";
|
||||
} else if (BackupPosition::CUSTOMIZE == position) {
|
||||
className = "CustomizeDataRestoreProxy";
|
||||
} else {
|
||||
className = "DataRestoreProxy";
|
||||
}
|
||||
|
|
|
@ -416,6 +416,19 @@ void Utils::excludeSomeHomePath(QStringList &excludes)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 排除自定义备份路径
|
||||
* @param excludes,存放需要排除的路径
|
||||
*/
|
||||
void Utils::excludeCustomizePath(QStringList &excludes)
|
||||
{
|
||||
// 本地xml文件中的信息
|
||||
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
|
||||
xmlPath.replace("//", "/");
|
||||
ParseBackupList parse(xmlPath);
|
||||
parse.getCustomizePaths(excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 生成rsync --exclude-from排除路径规则文件
|
||||
* @return
|
||||
|
@ -463,6 +476,7 @@ bool Utils::generateExcludePathsFile()
|
|||
QStringList excludes;
|
||||
Utils::excludeFstabBindPath(excludes);
|
||||
Utils::excludeSomeHomePath(excludes);
|
||||
Utils::excludeCustomizePath(excludes);
|
||||
for (const QString& item : excludes) {
|
||||
in << item << END_LINE;
|
||||
}
|
||||
|
@ -518,6 +532,7 @@ QStringList Utils::getFromExcludePathsFile()
|
|||
QStringList excludes;
|
||||
Utils::excludeFstabBindPath(excludes);
|
||||
Utils::excludeSomeHomePath(excludes);
|
||||
Utils::excludeCustomizePath(excludes);
|
||||
for (const QString& item : excludes) {
|
||||
list << item;
|
||||
}
|
||||
|
@ -704,6 +719,8 @@ QHash<QString, QString> Utils::getRemovableStorages()
|
|||
QString result = process_lsblk.readAllStandardOutput();
|
||||
/*
|
||||
result like bellow :
|
||||
PATH="/dev/sda" RM="0" TYPE="disk" MOUNTPOINT="" TRAN="sata"
|
||||
PATH="/dev/sda1" RM="0" TYPE="part" MOUNTPOINT="/media/zhaominyong/DATA1" TRAN=""
|
||||
PATH="/dev/sdb" RM="1" TYPE="disk" MOUNTPOINT="" TRAN="usb"
|
||||
PATH="/dev/sdb4" RM="1" TYPE="part" MOUNTPOINT="/media/zhaominyong/31 GB" TRAN=""
|
||||
PATH="/dev/sr0" RM="1" TYPE="rom" MOUNTPOINT="/media/zhaominyong/Kylin-Desktop-V10-SP1" TRAN=""
|
||||
|
@ -743,7 +760,6 @@ QHash<QString, QString> Utils::getRemovableStorages()
|
|||
continue;
|
||||
QString mount_point = storageAttrs.at(3);
|
||||
mount_point = mount_point.replace("MOUNTPOINT=\"", "");
|
||||
mount_point = mount_point.left(mount_point.length() - 1);
|
||||
removalbeStorages.insert(path, mount_point);
|
||||
}
|
||||
}
|
||||
|
@ -751,6 +767,78 @@ QHash<QString, QString> Utils::getRemovableStorages()
|
|||
return removalbeStorages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取挂接的计算机内部磁盘
|
||||
* @return 内部磁盘挂接路径列表
|
||||
*/
|
||||
QList<QString> Utils::getLocalDisks()
|
||||
{
|
||||
QList<QString> localDisks;
|
||||
|
||||
QProcess process_lsblk;
|
||||
process_lsblk.start("lsblk -P -o PATH,RM,TYPE,MOUNTPOINT,TRAN");
|
||||
if (!process_lsblk.waitForStarted(-1)) {
|
||||
return localDisks;
|
||||
}
|
||||
if (!process_lsblk.waitForFinished(-1)) {
|
||||
return localDisks;
|
||||
}
|
||||
QString result = process_lsblk.readAllStandardOutput();
|
||||
|
||||
QString userName = qgetenv("USER");
|
||||
QString mountPointPre("/media/");
|
||||
mountPointPre += userName;
|
||||
/*
|
||||
result like bellow :
|
||||
PATH="/dev/sda" RM="0" TYPE="disk" MOUNTPOINT="" TRAN="sata"
|
||||
PATH="/dev/sda1" RM="0" TYPE="part" MOUNTPOINT="/media/zhaominyong/DATA1" TRAN=""
|
||||
PATH="/dev/sdb" RM="1" TYPE="disk" MOUNTPOINT="" TRAN="usb"
|
||||
PATH="/dev/sdb4" RM="1" TYPE="part" MOUNTPOINT="/media/zhaominyong/31 GB" TRAN=""
|
||||
PATH="/dev/sr0" RM="1" TYPE="rom" MOUNTPOINT="/media/zhaominyong/Kylin-Desktop-V10-SP1" TRAN=""
|
||||
*/
|
||||
QStringList items = result.split("\n");
|
||||
QStringList usbDevs;
|
||||
for (QStringList::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) {
|
||||
const QString &line = (*it);
|
||||
if (line.contains(QRegularExpression(" TRAN=\".+\"")) && !line.contains(" TRAN=\"usb\"")) {
|
||||
QStringList storageAttrs = line.split("\" ");
|
||||
if (5 != storageAttrs.size())
|
||||
continue ;
|
||||
QString path = storageAttrs.at(0);
|
||||
path = path.replace("PATH=\"", "");
|
||||
usbDevs.append(path);
|
||||
continue;
|
||||
}
|
||||
if (line.contains(" TYPE=\"part\" ") && line.contains(mountPointPre)) {
|
||||
if (line.contains(" MOUNTPOINT=\"\""))
|
||||
continue;
|
||||
|
||||
// "PATH RM TYPE MOUNTPOINT TRAN" for each row, split by "\" "
|
||||
QStringList storageAttrs = line.split("\" ");
|
||||
if (5 != storageAttrs.size())
|
||||
continue ;
|
||||
QString path = storageAttrs.at(0);
|
||||
path = path.replace("PATH=\"", "");
|
||||
bool isSubPart = false;
|
||||
for (const QString& usbDev : usbDevs) {
|
||||
if (path.contains(usbDev)) {
|
||||
isSubPart = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isSubPart)
|
||||
continue;
|
||||
|
||||
// 默认挂载的内部磁盘不太可能使用中文字符等,暂不考虑挂载路径被转义的场景
|
||||
QString mount_point = storageAttrs.at(3);
|
||||
mount_point = mount_point.replace("MOUNTPOINT=\"", "");
|
||||
localDisks << mount_point;
|
||||
}
|
||||
}
|
||||
|
||||
return localDisks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置安全状态
|
||||
* @param enable——true,开启保护;false,关闭保护
|
||||
|
|
|
@ -110,6 +110,12 @@ public:
|
|||
*/
|
||||
static void excludeSomeHomePath(QStringList &excludes);
|
||||
|
||||
/**
|
||||
* @brief 排除自定义备份路径
|
||||
* @param excludes,存放需要排除的路径
|
||||
*/
|
||||
static void excludeCustomizePath(QStringList &excludes);
|
||||
|
||||
/**
|
||||
* @brief 生成rsync --exclude-from排除路径规则文件
|
||||
* @return
|
||||
|
@ -196,6 +202,12 @@ public:
|
|||
*/
|
||||
static QHash<QString, QString> getRemovableStorages();
|
||||
|
||||
/**
|
||||
* @brief 获取挂接的计算机内部磁盘
|
||||
* @return 内部磁盘挂接路径列表
|
||||
*/
|
||||
static QList<QString> getLocalDisks();
|
||||
|
||||
/**
|
||||
* @brief 设置安全状态
|
||||
* @param enable——true,开启保护;false,关闭保护
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#include "backuppositionselectdialog.h"
|
||||
#include "filefilterproxymodelforbackup.h"
|
||||
#include "../../common/utils.h"
|
||||
|
||||
BackupPositionSelectDialog::BackupPositionSelectDialog(QWidget* parent) :
|
||||
QFileDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Please select a path"));
|
||||
setViewMode(QFileDialog::List);
|
||||
setFileMode(QFileDialog::DirectoryOnly);
|
||||
setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QList<QUrl> siderUrls;
|
||||
siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath());
|
||||
siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath() + "/data");
|
||||
QList<QString> localDisks = Utils::getLocalDisks();
|
||||
for (const QString& path: localDisks) {
|
||||
siderUrls << QUrl::fromLocalFile(path);
|
||||
}
|
||||
setSidebarUrls(siderUrls);
|
||||
FileFilterProxyModeForBackup *proxy = new FileFilterProxyModeForBackup;
|
||||
setProxyModel(proxy);
|
||||
}
|
||||
|
||||
BackupPositionSelectDialog::~BackupPositionSelectDialog()
|
||||
{}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef BACKUPPOSITIONSELECTDIALOG_H
|
||||
#define BACKUPPOSITIONSELECTDIALOG_H
|
||||
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
class BackupPositionSelectDialog : public QFileDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BackupPositionSelectDialog(QWidget* parent = nullptr);
|
||||
virtual ~BackupPositionSelectDialog();
|
||||
};
|
||||
|
||||
|
||||
#endif // BACKUPPOSITIONSELECTDIALOG_H
|
|
@ -1,6 +1,8 @@
|
|||
#include "filefilterproxymodelforbackup.h"
|
||||
#include <QFileSystemModel>
|
||||
#include <QDir>
|
||||
#include "../../common/mydefine.h"
|
||||
#include "../../common/utils.h"
|
||||
|
||||
FileFilterProxyModeForBackup::FileFilterProxyModeForBackup(QObject *parent) :
|
||||
QSortFilterProxyModel(parent)
|
||||
|
@ -31,10 +33,14 @@ bool FileFilterProxyModeForBackup::filterAcceptsRow(int sourceRow, const QModelI
|
|||
return false;
|
||||
|
||||
QString filePath = fileModel->filePath(index0);
|
||||
if (fileName == "backup") {
|
||||
if (Utils::isDirExist(filePath + "/snapshots"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(filePath.endsWith(BACKUP_SNAPSHOTS_PATH) || filePath == BACKUP_PATH);
|
||||
} else {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,3 +27,6 @@ void MyFileSelect::goAccept()
|
|||
{
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
MyFileSelect::~MyFileSelect()
|
||||
{}
|
||||
|
|
|
@ -7,6 +7,7 @@ class MyFileSelect : public QFileDialog {
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit MyFileSelect(QWidget* parent = nullptr);
|
||||
virtual ~MyFileSelect();
|
||||
|
||||
public slots:
|
||||
void goAccept();
|
||||
|
|
|
@ -149,7 +149,7 @@ void DeleteBackupDialog::deleteBackupPoint()
|
|||
QString udiskPrePath = Utils::getSysRootPath();
|
||||
udiskPrePath += "/media";
|
||||
udiskPrePath.replace("//", "/");
|
||||
backupWrapper.m_iPosition = m_backupPonit.m_path.startsWith(udiskPrePath) ? BackupPosition::UDISK : BackupPosition::LOCAL;
|
||||
backupWrapper.m_iPosition = m_backupPonit.m_iPosition;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
backupWrapper.m_gid = getgid();
|
||||
m_pInterface->deleteBackupPoint(backupWrapper);
|
||||
|
|
|
@ -39,6 +39,7 @@ HEADERS += \
|
|||
backup_manager_interface.h \
|
||||
backuppointlistdialog.h \
|
||||
component/backuplistwidget.h \
|
||||
component/backuppositionselectdialog.h \
|
||||
component/circlelabel.h \
|
||||
component/clicklabel.h \
|
||||
component/filefilterproxymodelforbackup.h \
|
||||
|
@ -87,6 +88,7 @@ SOURCES += \
|
|||
backup_manager_interface.cpp \
|
||||
backuppointlistdialog.cpp \
|
||||
component/backuplistwidget.cpp \
|
||||
component/backuppositionselectdialog.cpp \
|
||||
component/circlelabel.cpp \
|
||||
component/clicklabel.cpp \
|
||||
component/filefilterproxymodelforbackup.cpp \
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../component/backuppositionselectdialog.h"
|
||||
#include "../component/clicklabel.h"
|
||||
#include "../component/circlelabel.h"
|
||||
#include "../component/myiconlabel.h"
|
||||
|
@ -29,6 +30,7 @@
|
|||
DataBackup::DataBackup(QWidget *parent /*= nullptr*/) :
|
||||
QStackedWidget(parent),
|
||||
m_udector(new UdiskDetector()),
|
||||
m_isLock(false),
|
||||
m_isLocal(true),
|
||||
m_DataBackupState(DataBackupState::IDEL),
|
||||
m_pInterface(nullptr)
|
||||
|
@ -169,12 +171,10 @@ void DataBackup::initFirstWidget()
|
|||
this->m_uuid = backupPoint.m_uuid;
|
||||
this->m_backupName = backupPoint.m_backupName;
|
||||
this->m_prefixDestPath = backupPoint.m_path;
|
||||
this->m_isLocal = backupPoint.m_iPosition == BackupPosition::UDISK || backupPoint.m_iPosition == BackupPosition::OTHER ? false : true;
|
||||
});
|
||||
|
||||
if (QDialog::Accepted == selectDialog->exec()) {
|
||||
QString udiskFlag = Utils::getSysRootPath() + "/media";
|
||||
udiskFlag.replace("//", "/");
|
||||
this->m_isLocal = this->m_prefixDestPath.startsWith(udiskFlag) ? false : true;
|
||||
this->m_isIncrement = true;
|
||||
this->setCurrentIndex(INC_SELECT_PATH_PAGE);
|
||||
emit this->initIncListWidget();
|
||||
|
@ -277,30 +277,11 @@ void DataBackup::initSecondWidget()
|
|||
hlayoutLine2->addSpacing(40);
|
||||
// 备份路径选择框
|
||||
QComboBox* comboSelect = new QComboBox(second);
|
||||
QPalette palette = comboSelect->palette();
|
||||
comboSelect->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
// 添加本地默认路径、移动设备目录
|
||||
connect(m_udector, &UdiskDetector::udiskListChanged, this, [=](QList<QStorageInfo> diskList) {
|
||||
comboSelect->clear();
|
||||
this->m_udiskPaths.clear();
|
||||
QIcon iconFolder = QIcon::fromTheme("insync-folder.png", QIcon(":/images/folder.png"));
|
||||
|
||||
// 如果有备份分区,则将本地默认分区备份路径放在第一个
|
||||
if (GlobelBackupInfo::inst().hasBackupPartition()) {
|
||||
QString qsLocalDefaultPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH;
|
||||
qsLocalDefaultPath.replace("//", "/");
|
||||
this->m_udiskPaths << "";
|
||||
comboSelect->addItem(iconFolder, tr("local default path : ") + qsLocalDefaultPath);
|
||||
}
|
||||
|
||||
QString qsPreDevPath(tr("removable devices path : "));
|
||||
for (QStorageInfo& disk : diskList) {
|
||||
this->m_udiskPaths << disk.rootPath();
|
||||
comboSelect->addItem(iconFolder, qsPreDevPath + disk.rootPath() + BACKUP_SNAPSHOTS_PATH);
|
||||
}
|
||||
});
|
||||
m_udector->getStorageInfo();
|
||||
QPushButton *buttonBrowse = new QPushButton;
|
||||
buttonBrowse->setText(tr("Browse..."));
|
||||
hlayoutLine2->addWidget(comboSelect);
|
||||
hlayoutLine2->addWidget(buttonBrowse);
|
||||
hlayoutLine2->addSpacing(40);
|
||||
vlayout->addLayout(hlayoutLine2);
|
||||
vlayout->addSpacing(32);
|
||||
|
@ -404,17 +385,6 @@ void DataBackup::initSecondWidget()
|
|||
nextStep->setEnabled(false);
|
||||
nextStep->setAutoRepeat(true);
|
||||
nextStep->setProperty("isImportant", true);
|
||||
connect(nextStep, &MyPushButton::clicked, this, [=]() {
|
||||
// 备份路径选择索引
|
||||
int index = comboSelect->currentIndex();
|
||||
// 第一个选项是本地系统备份
|
||||
this->m_isLocal = GlobelBackupInfo::inst().hasBackupPartition() && (index == 0);
|
||||
this->m_prefixDestPath = this->m_udiskPaths.at(index);
|
||||
this->m_backupPaths.clear();
|
||||
this->m_backupPaths.append(listWidget->getBackupPaths());
|
||||
this->setCurrentIndex(CHECK_ENV_PAGE);
|
||||
emit this->startCheckEnv();
|
||||
});
|
||||
hlayoutLastLine->addWidget(preStep);
|
||||
hlayoutLastLine->addSpacing(16);
|
||||
hlayoutLastLine->addWidget(nextStep);
|
||||
|
@ -425,6 +395,101 @@ void DataBackup::initSecondWidget()
|
|||
vlayout->addSpacing(40);
|
||||
second->setLayout(vlayout);
|
||||
|
||||
addWidget(second);
|
||||
|
||||
// 添加本地默认路径、移动设备目录
|
||||
connect(m_udector, &UdiskDetector::udiskListChanged, this, [=](QList<QStorageInfo> diskList) {
|
||||
m_isLock = true;
|
||||
comboSelect->clear();
|
||||
this->m_udiskPaths.clear();
|
||||
QIcon iconFolder = QIcon::fromTheme("insync-folder.png", QIcon(":/images/folder.png"));
|
||||
|
||||
// 如果有备份分区,则将本地默认分区备份路径放在第一个
|
||||
if (GlobelBackupInfo::inst().hasBackupPartition()) {
|
||||
QString qsLocalDefaultPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH;
|
||||
qsLocalDefaultPath.replace("//", "/");
|
||||
this->m_udiskPaths << "";
|
||||
comboSelect->addItem(iconFolder, tr("local default path : ") + qsLocalDefaultPath);
|
||||
}
|
||||
|
||||
QString qsPreDevPath(tr("removable devices path : "));
|
||||
for (QStorageInfo& disk : diskList) {
|
||||
this->m_udiskPaths << disk.rootPath();
|
||||
comboSelect->addItem(iconFolder, qsPreDevPath + disk.rootPath() + BACKUP_SNAPSHOTS_PATH);
|
||||
}
|
||||
|
||||
if (!this->m_customizePath.isEmpty()) {
|
||||
comboSelect->setCurrentIndex(-1);
|
||||
QLineEdit * edit = new QLineEdit;
|
||||
comboSelect->setLineEdit(edit);
|
||||
edit->setText(tr("customize path : ") + this->m_customizePath + BACKUP_SNAPSHOTS_PATH);
|
||||
edit->setReadOnly(true);
|
||||
} else if (comboSelect->count() > 0 && listWidget->getBackupPaths().size() > 0) {
|
||||
nextStep->setEnabled(true);
|
||||
} else {
|
||||
nextStep->setEnabled(false);
|
||||
}
|
||||
|
||||
m_isLock = false;
|
||||
});
|
||||
connect(comboSelect, QOverload<int>::of(&QComboBox::activated), this, [=]() {
|
||||
if (!m_isLock) {
|
||||
this->m_customizePath = "";
|
||||
if (listWidget->getBackupPaths().size() > 0)
|
||||
nextStep->setEnabled(true);
|
||||
}
|
||||
});
|
||||
m_udector->getStorageInfo();
|
||||
connect(buttonBrowse, &QPushButton::clicked, this, [=](){
|
||||
// 是否自定义路径?自定义路径备份文件不受保护,可能导致备份文件丢失或损坏
|
||||
if (!MessageBoxUtils::QMESSAGE_BOX_WARNING_CANCEL(GlobelBackupInfo::inst().getMainWidget(), QObject::tr("Information"),
|
||||
QObject::tr("Are you sure to continue customizing the path?\nThe custom path backup file is not protected, which may cause the backup file to be lost or damaged"),
|
||||
QObject::tr("Ok"), QObject::tr("Cancel")))
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
BackupPositionSelectDialog fileDialog(this);
|
||||
if (fileDialog.exec() == QDialog::Accepted) {
|
||||
QStringList selectFiles = fileDialog.selectedFiles();
|
||||
if (!selectFiles.isEmpty()) {
|
||||
comboSelect->setCurrentIndex(-1);
|
||||
QString fileName = selectFiles.at(0);
|
||||
this->m_customizePath = fileName;
|
||||
|
||||
QLineEdit * edit = new QLineEdit;
|
||||
comboSelect->setLineEdit(edit);
|
||||
edit->setText(tr("customize path : ") + fileName + BACKUP_SNAPSHOTS_PATH);
|
||||
edit->setReadOnly(true);
|
||||
if (listWidget->getBackupPaths().size() > 0)
|
||||
nextStep->setEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(nextStep, &MyPushButton::clicked, this, [=]() {
|
||||
// 备份路径选择索引
|
||||
int index = comboSelect->currentIndex();
|
||||
if (index >= 0) {
|
||||
// 第一个选项是本地系统备份
|
||||
this->m_isLocal = GlobelBackupInfo::inst().hasBackupPartition() && (index == 0);
|
||||
this->m_prefixDestPath = this->m_udiskPaths.at(index);
|
||||
} else if (this->m_customizePath.isEmpty()) {
|
||||
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::inst().getMainWidget(), QObject::tr("Warning"),
|
||||
QObject::tr("Please select backup position"),
|
||||
QObject::tr("Ok"));
|
||||
return ;
|
||||
} else {
|
||||
// 自定义备份位置的场景
|
||||
this->m_isLocal = true;
|
||||
this->m_prefixDestPath = this->m_customizePath;
|
||||
}
|
||||
|
||||
this->m_backupPaths.clear();
|
||||
this->m_backupPaths.append(listWidget->getBackupPaths());
|
||||
this->setCurrentIndex(CHECK_ENV_PAGE);
|
||||
emit this->startCheckEnv();
|
||||
});
|
||||
|
||||
connect(buttonAdd, &PixmapLabel::clicked, this, [=]() {
|
||||
if (editSelect->text().isEmpty())
|
||||
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"),
|
||||
|
@ -456,17 +521,17 @@ void DataBackup::initSecondWidget()
|
|||
nextStep->setEnabled(false);
|
||||
});
|
||||
connect(listWidget, &BackupListWidget::addedItem, this, [=](){
|
||||
nextStep->setEnabled(true);
|
||||
int index = comboSelect->currentIndex();
|
||||
if (index >= 0 || !this->m_customizePath.isEmpty())
|
||||
nextStep->setEnabled(true);
|
||||
});
|
||||
|
||||
connect(this, &DataBackup::reset, this, [=]() {
|
||||
editSelect->setText("");
|
||||
this->m_backupPaths.clear();
|
||||
listWidget->clearData();
|
||||
comboSelect->setCurrentIndex(0);
|
||||
this->m_udector->getStorageInfo();
|
||||
});
|
||||
|
||||
addWidget(second);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -531,15 +596,6 @@ void DataBackup::initSecondWidget_inc()
|
|||
// 默认备份位置展示
|
||||
MyLabel* labelBackupPosition = new MyLabel(second);
|
||||
labelBackupPosition->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
if (m_isLocal) {
|
||||
QString qsLocalDefaultPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH;
|
||||
qsLocalDefaultPath.replace("//", "/");
|
||||
QString defaultPath = tr("local default path : ") + qsLocalDefaultPath;
|
||||
labelBackupPosition->setDeplayText(defaultPath);
|
||||
} else {
|
||||
QString defaultPath = tr("removable devices path : ") + m_prefixDestPath;
|
||||
labelBackupPosition->setDeplayText(defaultPath);
|
||||
}
|
||||
labelBackupPosition->setEnabled(false);
|
||||
hlayoutLine2->addWidget(labelBackupPosition);
|
||||
hlayoutLine2->addSpacing(40);
|
||||
|
@ -704,6 +760,19 @@ void DataBackup::initSecondWidget_inc()
|
|||
emit this->reset();
|
||||
this->addOldBackupPaths(listWidget);
|
||||
nextStep->setEnabled(true);
|
||||
|
||||
if (this->m_isLocal && !this->m_prefixDestPath.isEmpty()) {
|
||||
QString defaultPath = tr("customize path : ") + this->m_prefixDestPath + BACKUP_SNAPSHOTS_PATH;
|
||||
labelBackupPosition->setDeplayText(defaultPath);
|
||||
} else if (this->m_isLocal) {
|
||||
QString qsLocalDefaultPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH;
|
||||
qsLocalDefaultPath.replace("//", "/");
|
||||
QString defaultPath = tr("local default path : ") + qsLocalDefaultPath;
|
||||
labelBackupPosition->setDeplayText(defaultPath);
|
||||
} else {
|
||||
QString defaultPath = tr("removable devices path : ") + this->m_prefixDestPath + BACKUP_SNAPSHOTS_PATH;
|
||||
labelBackupPosition->setDeplayText(defaultPath);
|
||||
}
|
||||
});
|
||||
|
||||
connect(this, &DataBackup::reset, this, [=]() {
|
||||
|
@ -1016,7 +1085,7 @@ void DataBackup::on_checkEnv_start()
|
|||
BackupWrapper backupWrapper;
|
||||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_type = m_isIncrement ? BackupType::INC_BACKUP_DATA : BackupType::BACKUP_DATA;
|
||||
backupWrapper.m_iPosition = m_isLocal ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_isLocal ? (this->m_prefixDestPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::CUSTOMIZE) : BackupPosition::UDISK;
|
||||
backupWrapper.m_backupPaths << m_backupPaths;
|
||||
backupWrapper.m_prefixDestPath = m_prefixDestPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
|
@ -1511,7 +1580,7 @@ void DataBackup::on_backup_start()
|
|||
backupWrapper.m_backupName = m_backupName;
|
||||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_type = m_isIncrement ? BackupType::INC_BACKUP_DATA : BackupType::BACKUP_DATA;
|
||||
backupWrapper.m_iPosition = m_isLocal ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_isLocal ? (this->m_prefixDestPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::CUSTOMIZE) : BackupPosition::UDISK;
|
||||
backupWrapper.m_backupPaths << m_backupPaths;
|
||||
backupWrapper.m_prefixDestPath = m_prefixDestPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
|
|
|
@ -75,6 +75,8 @@ private:
|
|||
UdiskDetector* m_udector;
|
||||
// U盘挂载路径列表
|
||||
QStringList m_udiskPaths;
|
||||
// U盘列表变化时,先锁定
|
||||
bool m_isLock;
|
||||
// 是否本地备份
|
||||
bool m_isLocal;
|
||||
// 备份路径列表
|
||||
|
@ -83,6 +85,8 @@ private:
|
|||
int m_DataBackupState;
|
||||
// 增量备份选择的备份点uuid
|
||||
QString m_uuid;
|
||||
// 自定义备份路径
|
||||
QString m_customizePath;
|
||||
// 选中的备份目标路径前缀(暂指udisk挂载路径)
|
||||
QString m_prefixDestPath;
|
||||
// dbus接口
|
||||
|
|
|
@ -166,6 +166,7 @@ void DataRestore::on_button_beginRestore_clicked(bool checked)
|
|||
connect(selectRestoreDialog, &SelectRestorePoint::selected, this, [=](ParseBackupList::BackupPoint backupPoint){
|
||||
this->m_uuid = backupPoint.m_uuid;
|
||||
this->m_devPath = backupPoint.m_path;
|
||||
this->m_iPosition = backupPoint.m_iPosition;
|
||||
});
|
||||
|
||||
if (QDialog::Rejected == selectRestoreDialog->exec()) {
|
||||
|
@ -478,7 +479,7 @@ void DataRestore::on_checkEnv_start()
|
|||
|
||||
BackupWrapper backupWrapper;
|
||||
backupWrapper.m_type = BackupType::RESTORE_DATA;
|
||||
backupWrapper.m_iPosition = m_devPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_iPosition;
|
||||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_prefixDestPath = m_devPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
|
@ -671,7 +672,7 @@ void DataRestore::on_restore_start()
|
|||
|
||||
BackupWrapper backupWrapper;
|
||||
backupWrapper.m_type = BackupType::RESTORE_DATA;
|
||||
backupWrapper.m_iPosition = m_devPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_iPosition;
|
||||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_prefixDestPath = m_devPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
|
|
|
@ -60,6 +60,8 @@ private:
|
|||
|
||||
QString m_uuid; // 还原点的UUID
|
||||
QString m_devPath; // 如果是从移动设备进行还原,此中保存移动设备挂载路径
|
||||
// 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份(仅用于业务场景标记,不用于持久化记录);3-自定义路径备份
|
||||
int m_iPosition;
|
||||
// 系统备份状态
|
||||
int m_dataRestoreState;
|
||||
// 是否需要重启
|
||||
|
|
|
@ -31,6 +31,8 @@ GhostImage::GhostImage(QWidget *parent) :
|
|||
m_ghostImageState(GhostImageState::IDEL),
|
||||
m_pInterface(nullptr)
|
||||
{
|
||||
m_iPosition = BackupPosition::LOCAL;
|
||||
|
||||
// 界面手写代码创建,作为练手
|
||||
initFirstWidget();
|
||||
initSecondWidget();
|
||||
|
@ -138,13 +140,11 @@ void GhostImage::initFirstWidget()
|
|||
connect(selectDialog, &SelectRestorePoint::selected, this, [=](ParseBackupList::BackupPoint backupPoint){
|
||||
this->m_uuid = backupPoint.m_uuid;
|
||||
this->m_backupName = backupPoint.m_backupName;
|
||||
this->m_prefixDestPath = backupPoint.m_path;
|
||||
this->m_iPosition = backupPoint.m_iPosition;
|
||||
});
|
||||
|
||||
if (QDialog::Accepted == selectDialog->exec()) {
|
||||
QString udiskFlag = Utils::getSysRootPath() + "/media";
|
||||
udiskFlag.replace("//", "/");
|
||||
this->m_isLocal = this->m_prefixDestPath.startsWith(udiskFlag) ? false : true;
|
||||
this->m_isLocal = true;
|
||||
this->setCurrentIndex(SELECT_PATH_PAGE);
|
||||
}
|
||||
selectDialog->deleteLater();
|
||||
|
@ -543,7 +543,7 @@ void GhostImage::on_checkEnv_start()
|
|||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_type = BackupType::GHOST_IMAGE;
|
||||
backupWrapper.m_backupName = createGhostImageName(m_backupName);
|
||||
backupWrapper.m_iPosition = m_isLocal ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_iPosition;
|
||||
backupWrapper.m_prefixDestPath = m_prefixDestPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
backupWrapper.m_frontUserName = qgetenv("USER");
|
||||
|
@ -839,7 +839,7 @@ void GhostImage::on_ghost_start()
|
|||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_type = BackupType::GHOST_IMAGE;
|
||||
backupWrapper.m_backupName = createGhostImageName(m_backupName);
|
||||
backupWrapper.m_iPosition = m_isLocal ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_iPosition;
|
||||
backupWrapper.m_prefixDestPath = m_prefixDestPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
backupWrapper.m_frontUserName = qgetenv("USER");
|
||||
|
|
|
@ -69,6 +69,8 @@ private:
|
|||
QString m_uuid;
|
||||
// 选中的备份目标路径前缀(暂指udisk挂载路径)
|
||||
QString m_prefixDestPath;
|
||||
// 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份(仅用于业务场景标记,不用于持久化记录);3-自定义路径备份
|
||||
int m_iPosition;
|
||||
// dbus接口
|
||||
ComKylinBackupManagerInterface *m_pInterface;
|
||||
// 备份点名称
|
||||
|
|
|
@ -58,6 +58,9 @@ ManageBackupPointList::ManageBackupPointList(QWidget *parent, BackupPointType ba
|
|||
backupPoint.m_uuid = this->text(curRow, Column_Index::UUID);
|
||||
backupPoint.m_time = this->text(curRow, Column_Index::Backup_Time);
|
||||
backupPoint.m_path = this->text(curRow, Column_Index::Prefix_Path);
|
||||
QString position = this->text(curRow, Column_Index::Backup_Device);
|
||||
backupPoint.m_iPosition = position.startsWith(QObject::tr("Customize:")) ? BackupPosition::CUSTOMIZE :
|
||||
(position.startsWith(QObject::tr("Local Disk:")) ? BackupPosition::LOCAL : BackupPosition::UDISK);
|
||||
m_deleteRow = curRow;
|
||||
|
||||
emit selected(backupPoint);
|
||||
|
@ -136,7 +139,9 @@ void ManageBackupPointList::insertLines(const QList<ParseBackupList::BackupPoint
|
|||
setItem(indexOfRow, Column_Index::Backup_Size, backupPoint.m_size);
|
||||
|
||||
QString prefixPath_to_device;
|
||||
if (backupPoint.m_path.startsWith(preDevPath)) {
|
||||
if (backupPoint.m_iPosition == BackupPosition::CUSTOMIZE) {
|
||||
prefixPath_to_device = QObject::tr("Customize:") + backupPoint.m_path + BACKUP_SNAPSHOTS_PATH;
|
||||
} else if (backupPoint.m_path.startsWith(preDevPath)) {
|
||||
QStorageInfo storage(backupPoint.m_path);
|
||||
QString udiskName = storage.rootPath();
|
||||
prefixPath_to_device = QObject::tr("Udisk Device:") + " " + udiskName + BACKUP_SNAPSHOTS_PATH;
|
||||
|
|
|
@ -60,8 +60,14 @@ SelectRestorePoint::SelectRestorePoint(QWidget *parent, BackupPointType backupTy
|
|||
backupPoint.m_time = this->text(curRow, Column_Index::Backup_Time);
|
||||
backupPoint.m_path = this->text(curRow, Column_Index::Prefix_Path);
|
||||
QString dev = this->text(curRow, Column_Index::Backup_Device);
|
||||
if (dev.startsWith(tr("Udisk Device:")))
|
||||
if (dev.startsWith(tr("Other machine:")))
|
||||
backupPoint.m_iPosition = BackupPosition::OTHER;
|
||||
else if (dev.startsWith(tr("Customize:")))
|
||||
backupPoint.m_iPosition = BackupPosition::CUSTOMIZE;
|
||||
else if (dev.startsWith(tr("Udisk Device:")))
|
||||
backupPoint.m_iPosition = BackupPosition::UDISK;
|
||||
else
|
||||
backupPoint.m_iPosition = BackupPosition::LOCAL;
|
||||
|
||||
emit this->selected(backupPoint);
|
||||
this->accept();
|
||||
|
@ -152,7 +158,9 @@ void SelectRestorePoint::insertLines(const QList<ParseBackupList::BackupPoint> &
|
|||
setItem(indexOfRow, Column_Index::Backup_Size, backupPoint.m_size);
|
||||
|
||||
QString prefixPath_to_device;
|
||||
if (backupPoint.m_path.startsWith(preDevPath)) {
|
||||
if (backupPoint.m_iPosition == BackupPosition::CUSTOMIZE) {
|
||||
prefixPath_to_device = QObject::tr("Customize:") + backupPoint.m_path + BACKUP_SNAPSHOTS_PATH;
|
||||
} else if (backupPoint.m_path.startsWith(preDevPath)) {
|
||||
QStorageInfo storage(backupPoint.m_path);
|
||||
QString udiskName = storage.rootPath();
|
||||
if (isOther)
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#include <QtGlobal>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../component/backuppositionselectdialog.h"
|
||||
#include "../component/clicklabel.h"
|
||||
#include "../component/circlelabel.h"
|
||||
#include "../component/filefilterproxymodelforbackup.h"
|
||||
#include "../component/myiconlabel.h"
|
||||
#include "../component/mylabel.h"
|
||||
#include "../component/mylineedit.h"
|
||||
|
@ -28,6 +28,7 @@
|
|||
SystemBackup::SystemBackup(QWidget *parent /*= nullptr*/) :
|
||||
QStackedWidget(parent),
|
||||
m_udector(new UdiskDetector()),
|
||||
m_isLock(false),
|
||||
m_isLocal(true),
|
||||
m_systemBackupState(SystemBackupState::IDEL),
|
||||
m_pInterface(nullptr)
|
||||
|
@ -230,67 +231,8 @@ void SystemBackup::initSecondWidget()
|
|||
// 备份路径选择框
|
||||
QComboBox* comboSelect = new QComboBox(second);
|
||||
comboSelect->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
// 添加本地默认路径、移动设备目录
|
||||
connect(m_udector, &UdiskDetector::udiskListChanged, this, [=](QList<QStorageInfo> diskList) {
|
||||
comboSelect->clear();
|
||||
this->m_udiskPaths.clear();
|
||||
QIcon iconFolder = QIcon::fromTheme("insync-folder.png", QIcon(":/images/folder.png"));
|
||||
|
||||
// 如果有备份分区,则将本地默认分区备份路径放在第一个
|
||||
if (GlobelBackupInfo::inst().hasBackupPartition()) {
|
||||
QString qsLocalDefaultPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH;
|
||||
qsLocalDefaultPath.replace("//", "/");
|
||||
this->m_udiskPaths << "";
|
||||
comboSelect->addItem(iconFolder, tr("local default path : ") + qsLocalDefaultPath);
|
||||
}
|
||||
|
||||
QString qsPreDevPath(tr("removable devices path : "));
|
||||
for (QStorageInfo& disk : diskList) {
|
||||
this->m_udiskPaths << disk.rootPath();
|
||||
comboSelect->addItem(iconFolder, qsPreDevPath + disk.rootPath() + BACKUP_SNAPSHOTS_PATH);
|
||||
}
|
||||
});
|
||||
connect(comboSelect, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [=]() {
|
||||
this->m_prefixDestPath = "";
|
||||
});
|
||||
m_udector->getStorageInfo();
|
||||
QPushButton *buttonBrowse = new QPushButton;
|
||||
buttonBrowse->setText(tr("Browse..."));
|
||||
connect(buttonBrowse, &QPushButton::clicked, this, [=](){
|
||||
// 是否自定义路径?自定义路径备份文件不受保护,可能导致备份文件丢失或损坏
|
||||
if (!MessageBoxUtils::QMESSAGE_BOX_WARNING_CANCEL(this, QObject::tr("Information"),
|
||||
QObject::tr("Are you sure to continue customizing the path?\n"
|
||||
"The custom path backup file is not protected, which may cause the backup file to be lost or damaged"),
|
||||
QObject::tr("Ok"), QObject::tr("Cancel")))
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
QFileDialog fileDialog(this);
|
||||
fileDialog.setWindowTitle(tr("Please select a path"));
|
||||
fileDialog.setViewMode(QFileDialog::List);
|
||||
fileDialog.setFileMode(QFileDialog::DirectoryOnly);
|
||||
fileDialog.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QList<QUrl> siderUrls;
|
||||
siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath());
|
||||
siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath() + "/data");
|
||||
fileDialog.setSidebarUrls(siderUrls);
|
||||
FileFilterProxyModeForBackup *proxy = new FileFilterProxyModeForBackup;
|
||||
fileDialog.setProxyModel(proxy);
|
||||
if (fileDialog.exec() == QDialog::Accepted) {
|
||||
QStringList selectFiles = fileDialog.selectedFiles();
|
||||
if (!selectFiles.isEmpty()) {
|
||||
comboSelect->setCurrentIndex(-1);
|
||||
QString fileName = selectFiles.at(0);
|
||||
this->m_prefixDestPath = fileName;
|
||||
|
||||
QLineEdit * edit = new QLineEdit;
|
||||
comboSelect->setLineEdit(edit);
|
||||
edit->setText(tr("customize path : ") + fileName + BACKUP_SNAPSHOTS_PATH);
|
||||
edit->setReadOnly(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
hlayoutLine2->addWidget(comboSelect);
|
||||
hlayoutLine2->addWidget(buttonBrowse);
|
||||
hlayoutLine2->addSpacing(40);
|
||||
|
@ -312,23 +254,9 @@ void SystemBackup::initSecondWidget()
|
|||
// 下一步按钮
|
||||
MyPushButton *nextStep = new MyPushButton(second);
|
||||
nextStep->setText(tr("Next"));
|
||||
nextStep->setEnabled(true);
|
||||
nextStep->setEnabled(false);
|
||||
nextStep->setAutoRepeat(true);
|
||||
nextStep->setProperty("isImportant", true);
|
||||
connect(nextStep, &MyPushButton::clicked, this, [=](bool checked) {
|
||||
// 备份路径选择索引
|
||||
int index = comboSelect->currentIndex();
|
||||
if (index >= 0) {
|
||||
// 第一个选项是本地系统备份
|
||||
this->m_isLocal = GlobelBackupInfo::inst().hasBackupPartition() && (index == 0);
|
||||
this->m_prefixDestPath = this->m_udiskPaths.at(index);
|
||||
} else {
|
||||
// 自定义备份位置的场景
|
||||
this->m_isLocal = true;
|
||||
}
|
||||
this->on_next_clicked(checked);
|
||||
emit this->startCheckEnv();
|
||||
});
|
||||
hlayoutLine3->addWidget(nextStep);
|
||||
hlayoutLine3->addStretch();
|
||||
hlayoutLine3->setAlignment(Qt::AlignCenter);
|
||||
|
@ -338,6 +266,94 @@ void SystemBackup::initSecondWidget()
|
|||
second->setLayout(vlayout);
|
||||
|
||||
addWidget(second);
|
||||
|
||||
// 添加本地默认路径、移动设备目录
|
||||
connect(m_udector, &UdiskDetector::udiskListChanged, this, [=](QList<QStorageInfo> diskList) {
|
||||
m_isLock = true;
|
||||
comboSelect->clear();
|
||||
this->m_udiskPaths.clear();
|
||||
QIcon iconFolder = QIcon::fromTheme("insync-folder.png", QIcon(":/images/folder.png"));
|
||||
|
||||
// 如果有备份分区,则将本地默认分区备份路径放在第一个
|
||||
if (GlobelBackupInfo::inst().hasBackupPartition()) {
|
||||
QString qsLocalDefaultPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH;
|
||||
qsLocalDefaultPath.replace("//", "/");
|
||||
this->m_udiskPaths << "";
|
||||
comboSelect->addItem(iconFolder, tr("local default path : ") + qsLocalDefaultPath);
|
||||
}
|
||||
|
||||
QString qsPreDevPath(tr("removable devices path : "));
|
||||
for (QStorageInfo& disk : diskList) {
|
||||
this->m_udiskPaths << disk.rootPath();
|
||||
comboSelect->addItem(iconFolder, qsPreDevPath + disk.rootPath() + BACKUP_SNAPSHOTS_PATH);
|
||||
}
|
||||
|
||||
if (!this->m_customizePath.isEmpty()) {
|
||||
comboSelect->setCurrentIndex(-1);
|
||||
QLineEdit * edit = new QLineEdit;
|
||||
comboSelect->setLineEdit(edit);
|
||||
edit->setText(tr("customize path : ") + this->m_customizePath + BACKUP_SNAPSHOTS_PATH);
|
||||
edit->setReadOnly(true);
|
||||
} else if (comboSelect->count() > 0) {
|
||||
nextStep->setEnabled(true);
|
||||
} else {
|
||||
nextStep->setEnabled(false);
|
||||
}
|
||||
|
||||
m_isLock = false;
|
||||
});
|
||||
connect(comboSelect, QOverload<int>::of(&QComboBox::activated), this, [=]() {
|
||||
if (!m_isLock) {
|
||||
this->m_customizePath = "";
|
||||
nextStep->setEnabled(true);
|
||||
}
|
||||
});
|
||||
m_udector->getStorageInfo();
|
||||
connect(buttonBrowse, &QPushButton::clicked, this, [=](){
|
||||
// 是否自定义路径?自定义路径备份文件不受保护,可能导致备份文件丢失或损坏
|
||||
if (!MessageBoxUtils::QMESSAGE_BOX_WARNING_CANCEL(GlobelBackupInfo::inst().getMainWidget(), QObject::tr("Information"),
|
||||
QObject::tr("Are you sure to continue customizing the path?\nThe custom path backup file is not protected, which may cause the backup file to be lost or damaged"),
|
||||
QObject::tr("Ok"), QObject::tr("Cancel")))
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
BackupPositionSelectDialog fileDialog(this);
|
||||
if (fileDialog.exec() == QDialog::Accepted) {
|
||||
QStringList selectFiles = fileDialog.selectedFiles();
|
||||
if (!selectFiles.isEmpty()) {
|
||||
comboSelect->setCurrentIndex(-1);
|
||||
QString fileName = selectFiles.at(0);
|
||||
this->m_customizePath = fileName;
|
||||
|
||||
QLineEdit * edit = new QLineEdit;
|
||||
comboSelect->setLineEdit(edit);
|
||||
edit->setText(tr("customize path : ") + fileName + BACKUP_SNAPSHOTS_PATH);
|
||||
edit->setReadOnly(true);
|
||||
nextStep->setEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(nextStep, &MyPushButton::clicked, this, [=](bool checked) {
|
||||
// 备份路径选择索引
|
||||
int index = comboSelect->currentIndex();
|
||||
if (index >= 0) {
|
||||
// 第一个选项是本地系统备份
|
||||
this->m_isLocal = GlobelBackupInfo::inst().hasBackupPartition() && (index == 0);
|
||||
this->m_prefixDestPath = this->m_udiskPaths.at(index);
|
||||
} else if (this->m_customizePath.isEmpty()) {
|
||||
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::inst().getMainWidget(), QObject::tr("Warning"),
|
||||
QObject::tr("Please select backup position"),
|
||||
QObject::tr("Ok"));
|
||||
return ;
|
||||
} else {
|
||||
// 自定义备份位置的场景
|
||||
this->m_isLocal = true;
|
||||
this->m_prefixDestPath = this->m_customizePath;
|
||||
}
|
||||
this->on_next_clicked(checked);
|
||||
emit this->startCheckEnv();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -622,6 +638,8 @@ void SystemBackup::on_checkEnv_start()
|
|||
backupWrapper.m_backupPaths << backupPath;
|
||||
backupWrapper.m_prefixDestPath = m_prefixDestPath;
|
||||
backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile());
|
||||
if (backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE)
|
||||
backupWrapper.m_backupExcludePaths << m_prefixDestPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
backupWrapper.m_frontUserName = qgetenv("USER");
|
||||
backupWrapper.m_gid = getgid();
|
||||
|
@ -1126,6 +1144,8 @@ void SystemBackup::on_backup_start()
|
|||
backupWrapper.m_backupPaths << backupPath;
|
||||
backupWrapper.m_prefixDestPath = m_prefixDestPath;
|
||||
backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile());
|
||||
if (backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE)
|
||||
backupWrapper.m_backupExcludePaths << m_prefixDestPath;
|
||||
backupWrapper.m_frontUid = getuid();
|
||||
backupWrapper.m_frontUserName = qgetenv("USER");
|
||||
backupWrapper.m_gid = getgid();
|
||||
|
|
|
@ -62,14 +62,18 @@ public slots:
|
|||
private:
|
||||
// U盘探测
|
||||
UdiskDetector* m_udector;
|
||||
// U盘挂载路径列表
|
||||
QStringList m_udiskPaths;
|
||||
// U盘列表变化时,先锁定
|
||||
bool m_isLock;
|
||||
// 是否本地备份
|
||||
bool m_isLocal;
|
||||
// 系统备份状态
|
||||
int m_systemBackupState;
|
||||
// 自定义路径
|
||||
QString m_customizePath;
|
||||
// 选中的备份目标路径前缀(暂指udisk挂载路径)
|
||||
QString m_prefixDestPath;
|
||||
// U盘挂载路径列表
|
||||
QStringList m_udiskPaths;
|
||||
// dbus接口
|
||||
ComKylinBackupManagerInterface *m_pInterface;
|
||||
// 备份点名称
|
||||
|
|
|
@ -29,6 +29,7 @@ SystemRestore::SystemRestore(QWidget *parent) :
|
|||
m_isRetainUserData = false;
|
||||
m_isFactoryRestore = false;
|
||||
m_pInterface = nullptr;
|
||||
m_iPosition = BackupPosition::LOCAL;
|
||||
|
||||
// 界面手写代码创建,作为练手
|
||||
initFirstWidget();
|
||||
|
@ -196,7 +197,8 @@ void SystemRestore::on_button_beginRestore_clicked(bool checked)
|
|||
this->m_uuid = "";
|
||||
this->m_devPath = "";
|
||||
this->m_backupName = "";
|
||||
this->m_isOtherMachine = "";
|
||||
this->m_isOtherMachine = false;
|
||||
this->m_iPosition = BackupPosition::LOCAL;
|
||||
|
||||
// 出厂还原,不用去选择备份点
|
||||
if (m_isFactoryRestore) {
|
||||
|
@ -220,6 +222,7 @@ void SystemRestore::on_button_beginRestore_clicked(bool checked)
|
|||
this->m_devPath = backupPoint.m_path;
|
||||
this->m_backupName = backupPoint.m_backupName;
|
||||
this->m_isOtherMachine = backupPoint.m_iPosition == BackupPosition::OTHER ? true : false;
|
||||
this->m_iPosition = backupPoint.m_iPosition == BackupPosition::OTHER ? BackupPosition::UDISK : backupPoint.m_iPosition;
|
||||
});
|
||||
|
||||
if (QDialog::Rejected == selectRestoreDialog->exec()) {
|
||||
|
@ -497,7 +500,7 @@ void SystemRestore::on_checkEnv_start()
|
|||
|
||||
BackupWrapper backupWrapper;
|
||||
backupWrapper.m_type = m_isRetainUserData ? BackupType::RESTORE_SYSTEM_WITH_DATA : BackupType::RESTORE_SYSTEM;
|
||||
backupWrapper.m_iPosition = m_devPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_iPosition;
|
||||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_backupName = m_backupName;
|
||||
backupWrapper.m_prefixDestPath = m_devPath;
|
||||
|
@ -697,7 +700,7 @@ void SystemRestore::on_restore_start()
|
|||
|
||||
BackupWrapper backupWrapper;
|
||||
backupWrapper.m_type = m_isRetainUserData ? BackupType::RESTORE_SYSTEM_WITH_DATA : BackupType::RESTORE_SYSTEM;
|
||||
backupWrapper.m_iPosition = m_devPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::UDISK;
|
||||
backupWrapper.m_iPosition = m_iPosition;
|
||||
backupWrapper.m_uuid = m_uuid;
|
||||
backupWrapper.m_backupName = m_backupName;
|
||||
backupWrapper.m_prefixDestPath = m_devPath;
|
||||
|
|
|
@ -64,6 +64,8 @@ private:
|
|||
QString m_devPath; // 如果是从移动设备进行还原,此中保存移动设备挂载路径
|
||||
QString m_backupName; // 还原点的备份点名称
|
||||
bool m_isOtherMachine; // 是否异机备份点还原
|
||||
// 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份(仅用于业务场景标记,不用于持久化记录);3-自定义路径备份
|
||||
int m_iPosition;
|
||||
// 系统备份状态
|
||||
int m_systemRestoreState;
|
||||
};
|
||||
|
|
1022
kybackup/qt_zh_CN.ts
1022
kybackup/qt_zh_CN.ts
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Loading…
Reference in New Issue