diff --git a/backup-daemon/backup-daemon.pro b/backup-daemon/backup-daemon.pro index a1ba10f..61a9c0d 100755 --- a/backup-daemon/backup-daemon.pro +++ b/backup-daemon/backup-daemon.pro @@ -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 \ diff --git a/backup-daemon/customizedatabackupproxy.cpp b/backup-daemon/customizedatabackupproxy.cpp new file mode 100644 index 0000000..ec9013f --- /dev/null +++ b/backup-daemon/customizedatabackupproxy.cpp @@ -0,0 +1,461 @@ +#include "customizedatabackupproxy.h" +#include +#include +#include +#include +#include "../common/utils.h" +#include "../common/mydusizetool.h" +#include "mymountproxy.h" +#include "myprocess/calcbackupsize.h" + +IMPLEMENT_DYNCREATE(CustomizeDataBackupProxy) + +CustomizeDataBackupProxy::CustomizeDataBackupProxy() +{ + m_isOnlyCheck = true; + m_bSuccess = false; + m_isFinished = false; + m_p = nullptr; + m_size = 0; + m_calc = new CalcBackupSize(this); + + connect(this, &CustomizeDataBackupProxy::cancel, this, &CustomizeDataBackupProxy::cancelEx); +} + +CustomizeDataBackupProxy::~CustomizeDataBackupProxy() +{ + delete m_p; + m_p = nullptr; + + delete m_calc; + m_calc = nullptr; +} + +/** + * @brief 环境检测 + * @return false,检测失败;true,检测成功 + */ +bool CustomizeDataBackupProxy::checkEnvEx() +{ + qDebug() << "CustomizeDataBackupProxy::checkEnv invoke begin"; + + // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载 + MyMountProxy mountProxy; + MountResult result = mountProxy.mountBackupPartition(); + // 无备份分区 + if (MountResult::CANNOT_GET_BACKUPUUID == result) { + qInfo() << "There is no backup partition!"; + + QString snapshotsPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH; + snapshotsPath.replace("//", "/"); + Utils::mkpath(snapshotsPath); + Utils::generateExcludePathsFile(); + } else if (MountResult::MOUNTED != result) { + emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL)); + return false; + } + + if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) { + qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); + Utils::mkpath(m_backupWrapper.m_prefixDestPath); + } + + // 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(); + } +} + + diff --git a/backup-daemon/customizedatabackupproxy.h b/backup-daemon/customizedatabackupproxy.h new file mode 100644 index 0000000..1698290 --- /dev/null +++ b/backup-daemon/customizedatabackupproxy.h @@ -0,0 +1,99 @@ +#ifndef CUSTOMIZEDATABACKUPPROXY_H +#define CUSTOMIZEDATABACKUPPROXY_H + +#include "workerfactory.h" +#include "myprocess/calcbackupsize.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class CustomizeDataBackupProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeDataBackupProxy) +public: + // 系统备份的几种场景 + enum CustomizeDataBackupScene { + DATA_BACKUP, // 系统备份 + INC_DATA_BACKUP, // 增量系统备份 + TRY_DATA_BACKUP, // 测试系统备份,可用于计算备份传输数据大小 + TRY_INC_DATA_BACKUP, // 测试增量系统备份,可用于计算备份传输数据大小 + }; + + explicit CustomizeDataBackupProxy(); + virtual ~CustomizeDataBackupProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + +public slots: + // 任务取消 + virtual void cancelEx(); + +private slots: + // 校验剩余空间是否满足备份 + bool checkFreeCapacity(qint64 itotalSize); + + // 备份 + void doBackup(); + +private: + // 计算备份所需空间大小 + void calcSizeForBackup(); + + /** + * @brief 记录/backup/snapshots/backuplist.xml文件 + * @return true-成功;false-失败 + */ + bool recordBackupPoint(); + + // 备份准备 + bool doPrepare(); + + // 备份系统 + bool backupData(); + +protected: + // 判断是否增量备份 + bool isIncBackup(); + + /** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ + QStringList getRsyncArgs(CustomizeDataBackupScene scene); + + void do_kylin_security(const QString& dstDir); + + // 失败则删除相应数据 + virtual void deleteFailedData(); + + // 计算备份空间大小的进程 + CalcBackupSize *m_calc; + // 是否只是检测 + bool m_isOnlyCheck; + // 是否完成 + bool m_isFinished; + // 是否成功 + bool m_bSuccess; + // 当前备份uuid + QString m_curUuid; + // 当前备份目标目录 + QString m_destPath; + // 当前备份所需空间大小 + qint64 m_size; + // 备份点用户备份路径文件 + QString m_userFile; + // 备份点排除备份路径文件 + QString m_excludeUserFile; + // 备份进程 + RsyncPathToDirProcess *m_p; + // 当前备份节点 + ParseBackupList::BackupPoint m_backupPoint; +}; + +#endif // CUSTOMIZEDATABACKUPPROXY_H diff --git a/backup-daemon/customizeghostImageproxy.cpp b/backup-daemon/customizeghostImageproxy.cpp new file mode 100644 index 0000000..5c0f2ac --- /dev/null +++ b/backup-daemon/customizeghostImageproxy.cpp @@ -0,0 +1,225 @@ +#include "customizeghostImageproxy.h" +#include +#include +#include +#include +#include +#include +#include +#include "../common/utils.h" +#include "../common/mydusizetool.h" +#include "mymountproxy.h" +#include "myprocess/calcbackupsize.h" + +IMPLEMENT_DYNCREATE(CustomizeGhostImageProxy) + +CustomizeGhostImageProxy::CustomizeGhostImageProxy() +{ + m_p = nullptr; + m_bSuccess = false; + m_isFinished = false; + m_isForce = false; + + connect(this, &CustomizeGhostImageProxy::cancel, this, &CustomizeGhostImageProxy::cancelEx); +} + +CustomizeGhostImageProxy::~CustomizeGhostImageProxy() +{ + // 不成功则删除中间文件 + if (!m_bSuccess) { + deleteFailedData(); + } +} + +/** + * @brief 环境检测 + */ +bool CustomizeGhostImageProxy::checkEnvEx() +{ + // 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; +} + diff --git a/backup-daemon/customizeghostImageproxy.h b/backup-daemon/customizeghostImageproxy.h new file mode 100644 index 0000000..b130051 --- /dev/null +++ b/backup-daemon/customizeghostImageproxy.h @@ -0,0 +1,53 @@ +#ifndef CUSTOMIZEGHOSTIMAGEPROXY_H +#define CUSTOMIZEGHOSTIMAGEPROXY_H + +#include "workerfactory.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class CustomizeGhostImageProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeGhostImageProxy) +public: + explicit CustomizeGhostImageProxy(); + virtual ~CustomizeGhostImageProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + + // 任务取消 + virtual void cancelEx(); + +private: + void doGhostImage(); + bool checkDestDirExists(); + void deleteFailedData(); + + ParseBackupList::BackupPoint m_backupPoint; + + // 存放.kyimg文件的目录 + QString m_destPath; + // .kyimg文件 + QString m_kyimg; + // 源文件dst.img文件所在目录 + QString m_srcPath; + // dst.img文件 + QString m_imgDst; + + // 备份进程 + RsyncPathToDirProcess *m_p; + + // 是否成功 + bool m_bSuccess; + // 是否结束 + bool m_isFinished; + // 强制结束标志(stop后没反应的情况,系统处于睡眠状态) + bool m_isForce; +}; + +#endif // CUSTOMIZEGHOSTIMAGEPROXY_H diff --git a/backup-daemon/customizesystembackupproxy.cpp b/backup-daemon/customizesystembackupproxy.cpp index 92a8661..09af0a7 100644 --- a/backup-daemon/customizesystembackupproxy.cpp +++ b/backup-daemon/customizesystembackupproxy.cpp @@ -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 hash = Utils::getLeftSizeOfPartitions(); - for (QHash::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; -} - diff --git a/backup-daemon/customizesystembackupproxy.h b/backup-daemon/customizesystembackupproxy.h index 2364ca0..38964d9 100644 --- a/backup-daemon/customizesystembackupproxy.h +++ b/backup-daemon/customizesystembackupproxy.h @@ -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 diff --git a/backup-daemon/customizesystemrestoreproxy.cpp b/backup-daemon/customizesystemrestoreproxy.cpp new file mode 100644 index 0000000..9dd3eef --- /dev/null +++ b/backup-daemon/customizesystemrestoreproxy.cpp @@ -0,0 +1,315 @@ +#include "customizesystemrestoreproxy.h" +#include +#include +#include +#include +#include +#include "../common/utils.h" +#include "mymountproxy.h" + +IMPLEMENT_DYNCREATE(CustomizeSystemRestoreProxy) + +/** + * @brief 构造函数 + */ +CustomizeSystemRestoreProxy::CustomizeSystemRestoreProxy() +{ + m_bSuccess = false; + m_p = nullptr; +} + +/** + * @brief 析构函数 + */ +CustomizeSystemRestoreProxy::~CustomizeSystemRestoreProxy() +{ + delete m_p; +} + +/** + * @brief 环境检测 + * @return false,检测失败;true,检测成功 + */ +bool CustomizeSystemRestoreProxy::checkEnvEx() +{ + qDebug() << "CustomizeSystemRestoreProxy::checkEnvEx invoke begin"; + + // 1、检测xml中的还原点是否还存在 + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + m_backupPoint = parse.findBackupPointByUuid(m_backupWrapper.m_uuid); + if (m_backupPoint.m_uuid.isEmpty()) { + qCritical("xml中还原点不存在"); + emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR)); + return false; + } + + // 2、检测.user.txt是否存在 + m_userFile = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + PATHS_USER_FILE; + m_userFile.replace("//", "/"); + if (!Utils::filsExists(m_userFile)) { + qCritical(".user.txt文件不存在"); + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + // 3、检测.exclude.user.txt是否存在 + m_excludeUserFile = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/" + EXCLUDE_PATHS_USER_FILE; + m_excludeUserFile.replace("//", "/"); + if (!Utils::filsExists(m_excludeUserFile)) { + qCritical(".exclude.user.txt文件不存在"); + emit checkResult(int(BackupResult::WRITE_EXCLUDE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + // 4、检测还原点是否存在 + m_backupPath = m_backupPoint.m_path + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + m_backupPath.replace("//", "/"); + if (Utils::isDirEmpty(m_backupPath)) { + qCritical("还原点{uuid}/data目录不存在"); + emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR)); + return false; + } + m_imgFileName = m_backupPath + "/" + UDISK_MKSQUASHFS_IMG_NAME; + if (!Utils::filsExists(m_imgFileName)) { + qCritical("还原点{uuid}/data/dst.img文件不存在"); + emit checkResult(int(BackupResult::INC_NOT_FOUND_DIR)); + return false; + } + + m_curUuid = m_backupWrapper.m_uuid; + + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + + qDebug() << "CustomizeSystemRestoreProxy::checkEnvEx invoke end"; + return true; +} + +/** + * @brief 执行还原逻辑 + */ +void CustomizeSystemRestoreProxy::doWorkEx() +{ + qDebug() << "CustomizeSystemRestoreProxy::doWorkEx invoke begin"; + + // 1、校验 + if (!checkEnvEx()) + return ; + + // 2、还原系统 + restoreSystem(); + + qDebug() << "CustomizeSystemRestoreProxy::doWorkEx invoke end"; +} + +/** + * @brief 备份准备 + * @return true,准备成功;false,准备失败 + */ +bool CustomizeSystemRestoreProxy::doPrepare() +{ + qDebug() << "CustomizeSystemRestoreProxy::doPrepare invoke begin"; + + // 1、dst.img文件需要挂载到目录 + if (!mountImg()) + return false; + + // 2、停止安全防护 + QProcess::execute("systemctl stop kysec-init.service"); + + // 3、以读写方式重新挂载boot分区,因为有的机器默认以只读挂载 + remountBoot(); + + // 4、是否有/boot/efi目录,有则认为有efi分区,需重新rw挂载 + QString efiPath = Utils::getSysRootPath() + "/boot/efi"; + efiPath.replace("//", "/"); + if (!Utils::isDirEmpty(efiPath)) { + // 重新rw读写挂载 + remountEfi(); + } + + qDebug() << "CustomizeSystemRestoreProxy::doPrepare invoke end"; + return true; +} + +/** + * @brief 将dst.img文件挂载到/backup/imgbackup目录 + * @return + */ +bool CustomizeSystemRestoreProxy::mountImg() +{ + // 自定义路径系统备份,需要先将dst.img文件挂载到/backup/imgbackup目录 + // 1、检测目录/backup/imgbackup是否存在,不存在则创建此目录 + QString dstImgMountPath = Utils::getSysRootPath() + BACKUP_IMGBACKUP_PATH; + dstImgMountPath.replace("//", "/"); + Utils::mkpath(dstImgMountPath); + + // 2、先卸载/backup/imgbackup上的mount + MountBackupProcess *processMount = new MountBackupProcess(this); + processMount->umount(dstImgMountPath); + + // 3、将img文件挂载到/backup/imgbackup上 + if (!processMount->mount(m_imgFileName, dstImgMountPath)) { + emit checkResult(int(BackupResult::RESTOREDIR_PREPARE_FAILED)); + return false; + } + + m_srcPath = dstImgMountPath; + + return true; +} + +/** + * @brief 重新rw读写挂载efi分区 + */ +void CustomizeSystemRestoreProxy::remountEfi() +{ + QString mountPath = Utils::getSysRootPath() + "/boot/efi"; + mountPath.replace("//", "/"); + QStringList args; + args << "-o" + << "rw,remount" + << mountPath; + QProcess::execute("mount", args); +} + +/** + * @brief 重新rw读写挂载boot分区 + */ +void CustomizeSystemRestoreProxy::remountBoot() +{ + QString mountPath = Utils::getSysRootPath() + "/boot"; + mountPath.replace("//", "/"); + QStringList args; + args << "-o" + << "rw,remount" + << mountPath; + QProcess::execute("mount", args); +} + +/** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ +QStringList CustomizeSystemRestoreProxy::getRsyncArgs(CustomizeSystemRestoreScene scene) +{ + QStringList args; + args << "-avAHXr"; + args << "--info=progress2"; + args << "--no-inc-recursive"; + args << "--ignore-missing-args"; + args << "--delete"; + + QStringList excludes; + // 自定义备份的路径也需要跳过,不进行还原 + Utils::excludeCustomizePath(excludes); + + switch (scene) { + case CustomizeSystemRestoreScene::RESTORE_SYSTEM_WITH_DATA : + args << "--exclude=/home"; + args << "--exclude=/root"; + if (Utils::isHuawei990()) { + args << "--exclude=/data"; + } else { + args << "--exclude=/data/usershare"; + } + // 保留指纹数据,用户密码、角色、权限、生物识别等信息不需要改变 + args << "--exclude=/var/lib/biometric-auth"; + args << "--exclude=/data/sec_storage_data"; + args << "--exclude=/etc/passwd"; + args << "--exclude=/etc/shadow"; + args << "--exclude=/etc/group"; + args << "--exclude=/etc/gshadow"; + args << "--exclude=/etc/sudoers"; + args << "--exclude=/data/home"; + args << "--exclude=/data/root"; + + // 云桌面背景路径属于用户数据 + args << "--exclude=/var/lib/AccountsService"; + // 此处不要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); +} + diff --git a/backup-daemon/customizesystemrestoreproxy.h b/backup-daemon/customizesystemrestoreproxy.h new file mode 100644 index 0000000..45e35b2 --- /dev/null +++ b/backup-daemon/customizesystemrestoreproxy.h @@ -0,0 +1,73 @@ +#ifndef CUSTOMIZESYSTEMRESTOREPROXY_H +#define CUSTOMIZESYSTEMRESTOREPROXY_H + + +#include "workerfactory.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class CustomizeSystemRestoreProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeSystemRestoreProxy) +public: + // 系统还原的几种场景 + enum CustomizeSystemRestoreScene { + SYSTEM_RESTORE, // 系统还原 + RESTORE_SYSTEM_WITH_DATA, // 保留用户数据还原 + }; + + explicit CustomizeSystemRestoreProxy(); + virtual ~CustomizeSystemRestoreProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + +private: + // 将img文件挂载到/backup/imgbackup目录 + bool mountImg(); + // 以读写方式重新挂载efi分区 + void remountEfi(); + // 以读写方式重新挂载boot分区 + void remountBoot(); + // 还原前准备 + bool doPrepare(); + // 系统还原 + void restoreSystem(); + + /** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ + QStringList getRsyncArgs(CustomizeSystemRestoreScene scene); + + // .user.txt文件路径 + QString m_userFile; + // .exclude.user.txt文件路径 + QString m_excludeUserFile; + // 备份数据所在的data目录 + QString m_backupPath; + // 压缩的img文件全名 + QString m_imgFileName; + + // 是否还原成功 + bool m_bSuccess; + // 当前备份uuid + QString m_curUuid; + // 当前还原源目录 + QString m_srcPath; + // 备份进程 + RsyncPathToDirProcess *m_p; + // 当前备份节点 + ParseBackupList::BackupPoint m_backupPoint; + +}; + + +#endif // CUSTOMIZESYSTEMRESTOREPROXY_H + diff --git a/backup-daemon/datarestoreproxy.cpp b/backup-daemon/datarestoreproxy.cpp index 4d76aaf..5eefdc3 100755 --- a/backup-daemon/datarestoreproxy.cpp +++ b/backup-daemon/datarestoreproxy.cpp @@ -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目录不存在"); diff --git a/backup-daemon/datarestoreproxy.h b/backup-daemon/datarestoreproxy.h index 523cebf..0d7c9b3 100755 --- a/backup-daemon/datarestoreproxy.h +++ b/backup-daemon/datarestoreproxy.h @@ -42,6 +42,8 @@ private: QString m_excludeUserFile; // 备份数据所在的data目录 QString m_backupPath; + // 备份点前缀路径 + QString m_prePath; // 是否还原成功 bool m_bSuccess; diff --git a/backup-daemon/deletebackupproxy.cpp b/backup-daemon/deletebackupproxy.cpp index b2a2ece..4b6350d 100755 --- a/backup-daemon/deletebackupproxy.cpp +++ b/backup-daemon/deletebackupproxy.cpp @@ -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; diff --git a/backup-daemon/ghostimageproxy.cpp b/backup-daemon/ghostimageproxy.cpp index 9a55792..d664713 100755 --- a/backup-daemon/ghostimageproxy.cpp +++ b/backup-daemon/ghostimageproxy.cpp @@ -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(); + } } /** diff --git a/backup-daemon/parsebackuplist.cpp b/backup-daemon/parsebackuplist.cpp index 5e7b20d..6208c58 100755 --- a/backup-daemon/parsebackuplist.cpp +++ b/backup-daemon/parsebackuplist.cpp @@ -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 &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)); + } } /** diff --git a/backup-daemon/parsebackuplist.h b/backup-daemon/parsebackuplist.h index 1d4f9ee..37baf25 100755 --- a/backup-daemon/parsebackuplist.h +++ b/backup-daemon/parsebackuplist.h @@ -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, 备份点信息 diff --git a/backup-daemon/systemrestoreproxy.cpp b/backup-daemon/systemrestoreproxy.cpp index 879922e..15dda56 100755 --- a/backup-daemon/systemrestoreproxy.cpp +++ b/backup-daemon/systemrestoreproxy.cpp @@ -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挂载的任意一方时,都备份不上) diff --git a/backup-daemon/udiskghostImageproxy.cpp b/backup-daemon/udiskghostImageproxy.cpp index 11bf2c9..7e98d6e 100755 --- a/backup-daemon/udiskghostImageproxy.cpp +++ b/backup-daemon/udiskghostImageproxy.cpp @@ -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; diff --git a/backup-daemon/udisksystemrestoreproxy.cpp b/backup-daemon/udisksystemrestoreproxy.cpp index b240c90..310a8cf 100755 --- a/backup-daemon/udisksystemrestoreproxy.cpp +++ b/backup-daemon/udisksystemrestoreproxy.cpp @@ -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上 diff --git a/backup-daemon/workerfactory.cpp b/backup-daemon/workerfactory.cpp index 6f08a4b..a78951c 100755 --- a/backup-daemon/workerfactory.cpp +++ b/backup-daemon/workerfactory.cpp @@ -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"; } diff --git a/common/utils.cpp b/common/utils.cpp index d64c9f9..ce09277 100755 --- a/common/utils.cpp +++ b/common/utils.cpp @@ -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 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 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 Utils::getRemovableStorages() return removalbeStorages; } +/** + * @brief 获取挂接的计算机内部磁盘 + * @return 内部磁盘挂接路径列表 + */ +QList Utils::getLocalDisks() +{ + QList 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,关闭保护 diff --git a/common/utils.h b/common/utils.h index 1fcfc2b..ff85639 100755 --- a/common/utils.h +++ b/common/utils.h @@ -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 getRemovableStorages(); + /** + * @brief 获取挂接的计算机内部磁盘 + * @return 内部磁盘挂接路径列表 + */ + static QList getLocalDisks(); + /** * @brief 设置安全状态 * @param enable——true,开启保护;false,关闭保护 diff --git a/kybackup/component/backuppositionselectdialog.cpp b/kybackup/component/backuppositionselectdialog.cpp new file mode 100644 index 0000000..f6b19d8 --- /dev/null +++ b/kybackup/component/backuppositionselectdialog.cpp @@ -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 siderUrls; + siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath()); + siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath() + "/data"); + QList localDisks = Utils::getLocalDisks(); + for (const QString& path: localDisks) { + siderUrls << QUrl::fromLocalFile(path); + } + setSidebarUrls(siderUrls); + FileFilterProxyModeForBackup *proxy = new FileFilterProxyModeForBackup; + setProxyModel(proxy); +} + +BackupPositionSelectDialog::~BackupPositionSelectDialog() +{} + diff --git a/kybackup/component/backuppositionselectdialog.h b/kybackup/component/backuppositionselectdialog.h new file mode 100644 index 0000000..89ddc91 --- /dev/null +++ b/kybackup/component/backuppositionselectdialog.h @@ -0,0 +1,15 @@ +#ifndef BACKUPPOSITIONSELECTDIALOG_H +#define BACKUPPOSITIONSELECTDIALOG_H + + +#include + +class BackupPositionSelectDialog : public QFileDialog { + Q_OBJECT +public: + explicit BackupPositionSelectDialog(QWidget* parent = nullptr); + virtual ~BackupPositionSelectDialog(); +}; + + +#endif // BACKUPPOSITIONSELECTDIALOG_H diff --git a/kybackup/component/filefilterproxymodelforbackup.cpp b/kybackup/component/filefilterproxymodelforbackup.cpp index bfd4e40..904a196 100644 --- a/kybackup/component/filefilterproxymodelforbackup.cpp +++ b/kybackup/component/filefilterproxymodelforbackup.cpp @@ -1,6 +1,8 @@ #include "filefilterproxymodelforbackup.h" #include +#include #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; } } diff --git a/kybackup/component/myfileselect.cpp b/kybackup/component/myfileselect.cpp index 70aefed..4686689 100755 --- a/kybackup/component/myfileselect.cpp +++ b/kybackup/component/myfileselect.cpp @@ -27,3 +27,6 @@ void MyFileSelect::goAccept() { QDialog::accept(); } + +MyFileSelect::~MyFileSelect() +{} diff --git a/kybackup/component/myfileselect.h b/kybackup/component/myfileselect.h index a626a9a..c4008af 100755 --- a/kybackup/component/myfileselect.h +++ b/kybackup/component/myfileselect.h @@ -7,6 +7,7 @@ class MyFileSelect : public QFileDialog { Q_OBJECT public: explicit MyFileSelect(QWidget* parent = nullptr); + virtual ~MyFileSelect(); public slots: void goAccept(); diff --git a/kybackup/deletebackupdialog.cpp b/kybackup/deletebackupdialog.cpp index 96a645c..0d5fdae 100755 --- a/kybackup/deletebackupdialog.cpp +++ b/kybackup/deletebackupdialog.cpp @@ -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); diff --git a/kybackup/kybackup.pro b/kybackup/kybackup.pro index babf1b4..c457626 100755 --- a/kybackup/kybackup.pro +++ b/kybackup/kybackup.pro @@ -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 \ diff --git a/kybackup/module/databackup.cpp b/kybackup/module/databackup.cpp index 2d8243c..cd97dff 100755 --- a/kybackup/module/databackup.cpp +++ b/kybackup/module/databackup.cpp @@ -8,6 +8,7 @@ #include #include +#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 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 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::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(); diff --git a/kybackup/module/databackup.h b/kybackup/module/databackup.h index fa0bb7f..c17dfb4 100755 --- a/kybackup/module/databackup.h +++ b/kybackup/module/databackup.h @@ -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接口 diff --git a/kybackup/module/datarestore.cpp b/kybackup/module/datarestore.cpp index f7b4be9..b5d780c 100755 --- a/kybackup/module/datarestore.cpp +++ b/kybackup/module/datarestore.cpp @@ -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(); diff --git a/kybackup/module/datarestore.h b/kybackup/module/datarestore.h index 4c5f29f..7bfc49d 100755 --- a/kybackup/module/datarestore.h +++ b/kybackup/module/datarestore.h @@ -60,6 +60,8 @@ private: QString m_uuid; // 还原点的UUID QString m_devPath; // 如果是从移动设备进行还原,此中保存移动设备挂载路径 + // 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份(仅用于业务场景标记,不用于持久化记录);3-自定义路径备份 + int m_iPosition; // 系统备份状态 int m_dataRestoreState; // 是否需要重启 diff --git a/kybackup/module/ghostimage.cpp b/kybackup/module/ghostimage.cpp index 3ce668c..deee605 100755 --- a/kybackup/module/ghostimage.cpp +++ b/kybackup/module/ghostimage.cpp @@ -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"); diff --git a/kybackup/module/ghostimage.h b/kybackup/module/ghostimage.h index 71d8d42..d96c0c6 100755 --- a/kybackup/module/ghostimage.h +++ b/kybackup/module/ghostimage.h @@ -69,6 +69,8 @@ private: QString m_uuid; // 选中的备份目标路径前缀(暂指udisk挂载路径) QString m_prefixDestPath; + // 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份(仅用于业务场景标记,不用于持久化记录);3-自定义路径备份 + int m_iPosition; // dbus接口 ComKylinBackupManagerInterface *m_pInterface; // 备份点名称 diff --git a/kybackup/module/managebackuppointlist.cpp b/kybackup/module/managebackuppointlist.cpp index 81288e1..d14b841 100755 --- a/kybackup/module/managebackuppointlist.cpp +++ b/kybackup/module/managebackuppointlist.cpp @@ -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 QListtext(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 & 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) diff --git a/kybackup/module/systembackup.cpp b/kybackup/module/systembackup.cpp index 874ebcf..e49ac08 100755 --- a/kybackup/module/systembackup.cpp +++ b/kybackup/module/systembackup.cpp @@ -9,9 +9,9 @@ #include #include +#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 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::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 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 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::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(); diff --git a/kybackup/module/systembackup.h b/kybackup/module/systembackup.h index 1f82abb..e269355 100755 --- a/kybackup/module/systembackup.h +++ b/kybackup/module/systembackup.h @@ -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; // 备份点名称 diff --git a/kybackup/module/systemrestore.cpp b/kybackup/module/systemrestore.cpp index 75f78c9..d6e7f56 100755 --- a/kybackup/module/systemrestore.cpp +++ b/kybackup/module/systemrestore.cpp @@ -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; diff --git a/kybackup/module/systemrestore.h b/kybackup/module/systemrestore.h index db565fb..47df979 100755 --- a/kybackup/module/systemrestore.h +++ b/kybackup/module/systemrestore.h @@ -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; }; diff --git a/kybackup/qt_zh_CN.ts b/kybackup/qt_zh_CN.ts index dfc1844..594d203 100755 --- a/kybackup/qt_zh_CN.ts +++ b/kybackup/qt_zh_CN.ts @@ -50,80 +50,88 @@ 对话框 - + Backup Name 备份名称 - + UUID 备份标识 - + Backup Time 备份时间 - + Backup Size 备份大小 - + Position 备份位置 - + No Backup 无备份 + + BackupPositionSelectDialog + + + Please select a path + + + DataBackup - + Data Backup 数据备份 - + Only files in the /home, /root, and /data directories can be backed up 仅支持备份/home、/root、/data目录下的文件 - + Only files in the /home, /root, and /data/usershare directories can be backed up 仅支持备份/home、/root、/data/usershare目录下的文件 - + Multi-Spot 多点还原 - + Security 安全可靠 - + Protect Data 防止数据丢失 - + Convenient 便捷快速 - + Start Backup 开始备份 - + Update Backup 备份更新 @@ -133,298 +141,310 @@ 备份管理 >> - + Please select backup position 请选择备份位置 - - + + local default path : 本地默认路径: - - + + removable devices path : 移动设备: - - + + Select backup data 选择备份数据 - - + + Add 添加 - - + + Select 选择 - - + + Please select file to backup 请选择备份文件 - - - - + + + + Back 上一步 - - + + Browse... + 浏览... + + + + Clear 清空 - - - - + + + + Next 下一步 - + + + + customize path : + 自定义路径: + + + Default backup location 默认备份位置 - - - - + + + + checking 环境检测 - - - - + + + + preparing 备份准备 - - - - + + + + backuping 备份中 - - - - + + + + finished 备份完成 - + Recheck 重新检测 - + Checking, wait a moment ... 正在检测,请稍等... - + Do not perform other operations during backup to avoid data loss 备份过程中不要做其它操作,以防数据丢失 - + Check whether the remaining capacity of the backup partition is sufficient - 检测备份分区空间是否充足··· + 检测备份位置空间是否充足··· - + Check whether the remaining capacity of the removable device is sufficient 检测移动设备空间是否充足··· - + Check success 检测成功 - + The storage for backup is enough 备份空间充足 - + Make sure the computer is plugged in or the battery level is above 60% 请确保电脑已连接电源或电量超过60% - + Check failure 检测失败 - - + + Program lock failed, please retry 程序锁定失败,请重试 - - + + There may be other backups or restores being performed 可能有其它备份/还原等任务在执行 - - + + Unsupported task type 不支持的任务类型 - - + + No processing logic was found in the service 没有找到相应的处理逻辑 - - + + Failed to mount the backup partition 备份分区挂载失败 - - + + Check whether there is a backup partition 检查是否有备份分区 - + The filesystem of device is vfat format 移动设备的文件系统是vfat格式 - + Please change filesystem format to ext3、ext4 or ntfs 请换成ext3、ext4、ntfs等文件系统格式 - + The device is read only 移动设备是只读挂载的 - + Please chmod to rw 请修改为读写模式 - - + + The storage for backup is not enough 备份空间不足 - - + + Retry after release space 建议释放空间后重试 - - + + Other backup or restore task is being performed 其它备份还原等操作正在执行 - - + + Please try again later 请稍后重试 - + Backup Name 备份名称 - - + + Name already exists 名称已存在 - + Cancel 取消 - + Do not use computer in case of data loss 请勿使用电脑,以防数据丢失 - + Failed to create the backup point directory 创建备份目录失败 - + Please check backup partition permissions 请检查备份分区权限 - + The backup had been canceled 备份已取消 - + Re-initiate the backup if necessary 如需要可重新进行备份 - + An error occurred during backup 备份期间发生错误 - + Error messages refer to log file : /var/log/backup.log 错误信息请参考日志文件:/var/log/backup.log - + Home Page 返回首页 - + Retry 再试一次 - + The backup is successful 备份成功 - + The backup is failed 备份失败 @@ -467,186 +487,186 @@ 开始还原 - - - + + + checking 环境检测 - - - + + + restoring 还原中 - - - + + + finished 还原完成 - + Back 上一步 - + Next 下一步 - + Recheck 重新检测 - + Checking, wait a moment ... 正在检测,请稍等... - + Check whether the restore environment meets the requirements 检查恢复环境是否符合要求 - + Do not perform other operations during restore to avoid data loss 还原期间不要做其它操作,以防数据丢失 - + Check success 检测成功 - - + + Do not use computer in case of data loss 请勿使用电脑,以防数据丢失 - + Make sure the computer is plugged in or the battery level is above 60% 请确保电脑已连接电源或电量超过60% - + Check failure 检测失败 - - + + Program lock failed, please retry 程序锁定失败,请重试 - - + + There may be other backups or restores being performed 可能有其它备份/还原等任务在执行 - - + + Unsupported task type 不支持的任务类型 - - + + No processing logic was found in the service 没有找到相应的处理逻辑 - - + + The .user.txt file does not exist .user.txt文件不存在 - - - - - - + + + + + + Backup points may be corrupted 备份点可能被损坏 - - + + The .exclude.user.txt file does not exist .exclude.user.txt文件不存在 - - + + The backup point data directory does not exist 备份点数据目录不存在 - + Failed to rsync /boot/efi 同步/boot/efi失败 - + Check the mounting mode of the /boot/efi partition 请检查/boot/efi分区挂载方式 - + Failed to prepare the restore directory 还原目录准备失败 - + Refer to log :/var/log/backup.log for more information 更多信息请参考日志/var/log/backup.log - + An error occurred during restore 还原时发生错误 - + Error messages refer to log file : /var/log/backup.log 错误信息请参考日志文件:/var/log/backup.log - + Home Page 返回首页 - + Retry 再试一次 - + Reboot System 重启系统 - + Successfully restoring the data 还原成功 - + The system needs to reboot. Otherwise, some tools cannot be used. 系统需要重启,否则某些工具可能无法使用 - + Restoring the data failed 还原失败 @@ -675,27 +695,27 @@ 确定 - + Other backup or restore task is being performed 其它备份还原等操作正在执行 - + Program lock failed, please retry 程序锁定失败,请重试 - + Unsupported task type 不支持的任务类型 - + Deleted backup successfully. 删除备份成功。 - + Failed to delete backup. 删除备份失败。 @@ -736,294 +756,294 @@ GhostImage - + Ghost Image Ghost镜像 - + A ghost image file can only be created after backup system to local disk 必须先进行本地系统备份,否则无法创建镜像文件 - + Simple 操作简单 - + Fast 创建速度快 - + Security 安全可靠 - + Timesaving 节省时间 - + Start Ghost 创建镜像 - + Please select storage location 请选择存储位置 - + local default path : 本地默认路径: - + removable devices path : 移动设备: - - + + Back 上一步 - - + + Next 下一步 - - - + + + checking 环境检测 - - - + + + ghosting 创建中 - - - + + + finished 创建完成 - + Recheck 重新检测 - + Checking, wait a moment ... 正在检测,请稍等... - + Check whether the conditions for creating an ghost image are met 检测是否具备制作Ghost镜像条件 - + Do not perform other operations during creating an ghost image to avoid data loss 制作Ghost镜像期间不要做其它操作,以防数据丢失 - + Check success 检测成功 - + The storage space is enough 存储空间充足 - + Make sure the computer is plugged in or the battery level is above 60% 请确保电脑已连接电源或电量超过60% - + Check failure 检测失败 - - + + Program lock failed, please retry 程序锁定失败,请重试 - - + + There may be other backups or restores being performed 可能有其它备份/还原等任务在执行 - - + + Unsupported task type 不支持的任务类型 - - + + No processing logic was found in the service 没有找到相应的处理逻辑 - - + + Failed to mount the backup partition 备份分区挂载失败 - - + + Check whether there is a backup partition 检查是否有备份分区 - - + + The filesystem of device is vfat format 移动设备的文件系统是vfat格式 - - + + Please change filesystem format to ext3、ext4 or ntfs 请换成ext3、ext4、ntfs等文件系统格式 - - + + The device is read only 移动设备是只读挂载的 - - + + Please chmod to rw 请修改为读写模式 - - + + The storage for ghost is not enough Ghost存储空间不足 - - - - + + + + Retry after release space 建议释放空间后重试 - - + + There is not enough space for temporary .kyimg file 没有足够的空间存放临时.kyimg文件 - - + + Other backup or restore task is being performed 其它备份还原等操作正在执行 - - + + Please try again later 请稍后重试 - - + + The backup node does not exist 相应的备份节点不存在 - - + + Check whether the backup point has been deleted 请检查备份点是否已经被删除 - + Cancel 取消 - + Do not use computer in case of data loss 请勿使用电脑,以防数据丢失 - + The data is being compressed to the local disk, please wait patiently... 正压缩数据到本地磁盘,请耐心等待... - + Transferring image file to mobile device, about to be completed... 正在传输image文件到移动设备,即将完成... - + The image creation had been canceled 已取消制作Ghost镜像 - + Re-initiate the image creation if necessary 如需要可以重新进行Ghost镜像制作 - + An error occurred during make ghost image 制作Ghost镜像时发生错误 - + Error messages refer to log file : /var/log/backup.log 错误信息请参考日志文件:/var/log/backup.log - + Home Page 返回首页 - + Retry 再试一次 - + Ghost image creation is successful 创建成功 - + You can view it in the directory : %1 您可以在“%1”目录下查看 - + Ghost image creation is failed 创建失败 @@ -1040,32 +1060,32 @@ MainDialog - + Main menu 主菜单 - + Minimize 最小化 - + Close 关闭 - + Help 帮助 - + About 关于 - + Exit 退出 @@ -1078,32 +1098,32 @@ ManageBackupPointList - + System Backup Information 系统备份信息 - + Data Backup Information 数据备份信息 - + You can delete the backup that does not need, refer operation logs for more details 您可以删除不需要的备份,更多细节请参考“操作日志” - + Delete 删除 - + backup finished 备份完成 - + backup unfinished 备份未完成 @@ -1184,22 +1204,22 @@ QObject - + factory backup 出厂备份 - + Factory Backup 出厂备份 - + Backup State 备份状态 - + PrefixPath @@ -1207,15 +1227,17 @@ - - - - - - - - - + + + + + + + + + + + Warning 警告 @@ -1228,20 +1250,23 @@ - - - - - - - - - - - + + + + + + + + + + + - - + + + + + Ok 确定 @@ -1258,14 +1283,15 @@ 路径:%2不在其中。 - - - - - - - - + + + + + + + + + Information 提示 @@ -1274,107 +1300,130 @@ 此功能只能由系统管理员使用。 - + Another user had opened kybackup, you can not start it again. 其他用户已开启备份还原工具,不能再开启 - + kybackup 备份还原工具 - + An exception occurred when mounting backup partition. 挂载备份分区时发生错误。 - Please check if the backup partition exists which can be created when you install the Operating System. - 请检查备份还原分区是否存在,在安装操作系统时必须创建备份还原分区。 + 请检查备份还原分区是否存在,在安装操作系统时必须创建备份还原分区。 - + Failed to mount backup partition. 挂载备份分区失败。 - - + + It's busy, please wait 系统正忙,请稍等 - - + + + Are you sure to continue customizing the path? +The custom path backup file is not protected, which may cause the backup file to be lost or damaged + 确定自定义路径? +自定义路径备份文件不受保护,可能导致备份文件丢失或损坏 + + + + + Please select backup position + 请选择备份位置 + + + + Please select a backup file or directory 请选择一个备份文件或目录 - - - + + + Are you sure to cancel the operation? 确定取消当前操作? - - - - - - - + + + + + + + + + Cancel 取消 - - - + + + Continue 继续 - + Contains the user's home directory, which need to reboot after restoration. Are you sure to continue? 包含用户家目录,还原完成后需要重启系统。是否继续? - - + + Please select one backup to continue. 请选择一个备份再继续。 - + Are you sure to delete the backup ? 是否确定删除此备份? - - + + + + Customize: + 自定义位置: + + + + Udisk Device: 移动设备: - - + + + Local Disk: 本地磁盘: - + Do you want to continue? 是否继续? - + Other machine: 异机备份: - + Restore factory settings, your system user data will not be retained. Are you sure to continue? 恢复出厂设置,您的系统用户数据都将会消失。是否继续? @@ -1397,7 +1446,17 @@ 确定 - + + Other machine: + 异机备份: + + + + Customize: + 自定义位置: + + + Udisk Device: 移动设备: @@ -1405,320 +1464,331 @@ SystemBackup - + System Backup 系统备份 - + Can be restored when files are damaged or lost 系统原始文件受损或丢失时可以进行还原 - + Multi-Spot 多还原点 - + Small Size 体积小 - + Security 安全保障 - + Simple 操作简单 - + Start Backup 开始备份 - + Backup Management >> 备份管理 >> - + Please select backup position 请选择备份位置 - + local default path : 本地默认路径: - + removable devices path : 移动设备: - - - + + + Back 上一步 - - - + + Browse... + 浏览... + + + + + Next 下一步 - - - - + + + customize path : + 自定义路径: + + + + + + checking 环境检测 - - - - + + + + preparing 备份准备 - - - - + + + + backuping 备份中 - - - - + + + + finished 备份完成 - + Recheck 重新检测 - + Checking, wait a moment ... 正在检测,请稍等... - + Do not perform other operations during backup to avoid data loss 备份期间不要做其它操作,以防数据丢失 - + Check whether the remaining capacity of the backup partition is sufficient - 检测备份分区空间是否充足··· + 检测备份位置空间是否充足··· - + Check whether the remaining capacity of the removable device is sufficient 检测移动设备空间是否充足··· - + Check success 检测成功 - + The storage for backup is enough 备份空间充足 - + Make sure the computer is plugged in or the battery level is above 60% 请确保电脑已连接电源或电量超过60% - + Check failure 检测失败 - - + + Program lock failed, please retry 程序锁定失败,请重试 - - + + There may be other backups or restores being performed 可能有其它备份/还原等任务在执行 - - + + Unsupported task type 不支持的任务类型 - - + + No processing logic was found in the service 没有找到相应的处理逻辑 - - + + Failed to mount the backup partition 备份分区挂载失败 - - + + Check whether there is a backup partition 检查是否有备份分区 - - + + The filesystem of device is vfat format 移动设备的文件系统是vfat格式 - - + + Please change filesystem format to ext3、ext4 or ntfs 请换成ext3、ext4、ntfs等文件系统格式 - - + + The device is read only 移动设备是只读挂载的 - - + + Please chmod to rw 请修改为读写模式 - - + + The storage for backup is not enough 备份空间不足 - - + + Retry after release space 建议释放空间后重试 - - + + Other backup or restore task is being performed 其它备份还原等操作正在执行 - - + + Please try again later 请稍后重试 - + Backup Name 备份名称 - - + + Name already exists 名称已存在 - + factory backup 出厂备份 - + Cancel 取消 - + Do not use computer in case of data loss 请勿使用电脑,以防数据丢失 - + Failed to create the backup point directory 创建备份目录失败 - + Please check backup partition permissions 请检查备份分区权限 - + The system is being compressed to the local disk, please wait patiently... 正压缩系统到本地磁盘,请耐心等待... - + Transferring image file to mobile device, about to be completed... 正在传输image文件到移动设备,即将完成... - + The backup had been canceled 已取消备份 - + Re-initiate the backup if necessary 如需要可重新进行备份 - - + + An error occurred during backup 备份时发生错误 - - + + Error messages refer to log file : /var/log/backup.log 错误信息请参考日志文件:/var/log/backup.log - + Home Page 返回首页 - + Retry 再试一次 - + The backup is successful 备份成功 - + The backup is failed 备份失败 @@ -1726,232 +1796,232 @@ SystemRestore - + System Restore 系统还原 - + You can restore the system to its previous state 在您遇到问题时可将系统还原到以前的状态 - + Simple 操作简单 - + Security 安全可靠 - + Repair 修复系统损坏 - + Independent 自主操作 - + Start Restore 开始还原 - + Factory Restore 出厂还原 - + Retaining User Data 保留用户数据 - - - + + + checking 环境检测 - - - + + + restoring 还原中 - - - + + + finished 还原完成 - + Back 上一步 - + Next 下一步 - + Recheck 重新检测 - + Checking, wait a moment ... 正在检测,请稍等... - + Check whether the restore environment meets the requirements 检查恢复环境是否符合要求 - + Do not perform other operations during restore to avoid data loss 还原期间不要做其它操作,以防数据丢失 - + Check success 检测成功 - + The system will reboot automatically after the restore is successful 还原成功后系统将自动重启 - + Make sure the computer is plugged in or the battery level is above 60% 请确保电脑已连接电源或电量超过60% - + Check failure 检测失败 - - + + Program lock failed, please retry 程序锁定失败,请重试 - - + + There may be other backups or restores being performed 可能有其它备份/还原等任务在执行 - - + + Unsupported task type 不支持的任务类型 - - + + No processing logic was found in the service 没有找到相应的处理逻辑 - - + + The .user.txt file does not exist .user.txt文件不存在 - - - - - - + + + + + + Backup points may be corrupted 备份点可能被损坏 - - + + The .exclude.user.txt file does not exist .exclude.user.txt文件不存在 - - + + The backup point data directory does not exist 备份点数据目录不存在 - - + + Failed to rsync /boot/efi 同步/boot/efi失败 - - + + Check the mounting mode of the /boot/efi partition 请检查/boot/efi分区挂载方式 - + Do not use computer in case of data loss 请勿使用电脑,以防数据丢失 - + Failed to prepare the restore directory 还原目录准备失败 - + Refer to log :/var/log/backup.log for more information 更多信息请参考日志/var/log/backup.log - + An error occurred during restore 还原时发生错误 - + Error messages refer to log file : /var/log/backup.log 错误信息请参考日志文件:/var/log/backup.log - + Home Page 返回首页 - + Retry 再试一次 - + Successfully restoring the system 系统还原成功 - + The system will automatically reboot 系统将自动重启 - + Restoring the system failed 系统还原失败 @@ -1959,7 +2029,7 @@ restore - + system restore diff --git a/kybackup/resource/language/qt_zh_CN.qm b/kybackup/resource/language/qt_zh_CN.qm index f7125fd..0b70a76 100644 Binary files a/kybackup/resource/language/qt_zh_CN.qm and b/kybackup/resource/language/qt_zh_CN.qm differ