From b6eaa43087b675d3b5022f9bf48abe35fb5725d5 Mon Sep 17 00:00:00 2001 From: zhaominyong Date: Thu, 13 Jan 2022 14:06:40 +0800 Subject: [PATCH] ghost image --- backup-daemon/backup-daemon.pro | 4 + backup-daemon/databackupproxy.cpp | 2 +- backup-daemon/databackupproxy.h | 0 backup-daemon/datarestoreproxy.cpp | 0 backup-daemon/datarestoreproxy.h | 0 backup-daemon/deletebackupproxy.cpp | 0 backup-daemon/deletebackupproxy.h | 0 backup-daemon/ghostimageproxy.cpp | 127 ++ backup-daemon/ghostimageproxy.h | 42 + backup-daemon/mybackupmanager.cpp | 44 +- backup-daemon/systembackupproxy.cpp | 2 +- backup-daemon/systemrestoreproxy.cpp | 0 backup-daemon/systemrestoreproxy.h | 0 backup-daemon/udiskdatabackupproxy.cpp | 2 +- backup-daemon/udiskdatabackupproxy.h | 0 backup-daemon/udiskdatarestoreproxy.cpp | 0 backup-daemon/udiskdatarestoreproxy.h | 0 backup-daemon/udiskghostImageproxy.cpp | 221 ++++ backup-daemon/udiskghostImageproxy.h | 48 + backup-daemon/udisksystembackupproxy.cpp | 2 +- backup-daemon/udisksystemrestoreproxy.cpp | 0 backup-daemon/udisksystemrestoreproxy.h | 0 backup-daemon/workerfactory.cpp | 4 +- common/mydefine.h | 1 + common/utils.cpp | 23 + common/utils.h | 6 + kybackup/app.qrc | 1 + kybackup/component/backuplistwidget.cpp | 0 kybackup/component/backuplistwidget.h | 0 kybackup/component/myfileselect.cpp | 0 kybackup/component/myfileselect.h | 0 kybackup/deletebackupdialog.cpp | 0 kybackup/deletebackupdialog.h | 0 kybackup/deletebackupdialog.ui | 0 kybackup/kybackup.pro | 2 + kybackup/maindialog.cpp | 5 + kybackup/module/databackup.cpp | 5 +- kybackup/module/databackup.h | 0 kybackup/module/datarestore.cpp | 14 +- kybackup/module/datarestore.h | 0 kybackup/module/ghostimage.cpp | 1100 +++++++++++++++++ kybackup/module/ghostimage.h | 77 ++ kybackup/module/managebackuppointlist.cpp | 2 +- kybackup/module/managebackuppointlist.h | 0 kybackup/module/operationlog.cpp | 0 kybackup/module/operationlog.h | 0 kybackup/module/selectrestorepoint.cpp | 8 +- kybackup/module/selectrestorepoint.h | 2 +- kybackup/module/systembackup.cpp | 5 +- kybackup/module/systemrestore.cpp | 2 + .../symbos/document-open-recent-symbolic.png | Bin 0 -> 319 bytes 51 files changed, 1726 insertions(+), 25 deletions(-) mode change 100644 => 100755 backup-daemon/databackupproxy.cpp mode change 100644 => 100755 backup-daemon/databackupproxy.h mode change 100644 => 100755 backup-daemon/datarestoreproxy.cpp mode change 100644 => 100755 backup-daemon/datarestoreproxy.h mode change 100644 => 100755 backup-daemon/deletebackupproxy.cpp mode change 100644 => 100755 backup-daemon/deletebackupproxy.h create mode 100755 backup-daemon/ghostimageproxy.cpp create mode 100755 backup-daemon/ghostimageproxy.h mode change 100644 => 100755 backup-daemon/systemrestoreproxy.cpp mode change 100644 => 100755 backup-daemon/systemrestoreproxy.h mode change 100644 => 100755 backup-daemon/udiskdatabackupproxy.cpp mode change 100644 => 100755 backup-daemon/udiskdatabackupproxy.h mode change 100644 => 100755 backup-daemon/udiskdatarestoreproxy.cpp mode change 100644 => 100755 backup-daemon/udiskdatarestoreproxy.h create mode 100755 backup-daemon/udiskghostImageproxy.cpp create mode 100755 backup-daemon/udiskghostImageproxy.h mode change 100644 => 100755 backup-daemon/udisksystemrestoreproxy.cpp mode change 100644 => 100755 backup-daemon/udisksystemrestoreproxy.h mode change 100644 => 100755 kybackup/component/backuplistwidget.cpp mode change 100644 => 100755 kybackup/component/backuplistwidget.h mode change 100644 => 100755 kybackup/component/myfileselect.cpp mode change 100644 => 100755 kybackup/component/myfileselect.h mode change 100644 => 100755 kybackup/deletebackupdialog.cpp mode change 100644 => 100755 kybackup/deletebackupdialog.h mode change 100644 => 100755 kybackup/deletebackupdialog.ui mode change 100644 => 100755 kybackup/module/databackup.cpp mode change 100644 => 100755 kybackup/module/databackup.h mode change 100644 => 100755 kybackup/module/datarestore.cpp mode change 100644 => 100755 kybackup/module/datarestore.h create mode 100755 kybackup/module/ghostimage.cpp create mode 100755 kybackup/module/ghostimage.h mode change 100644 => 100755 kybackup/module/managebackuppointlist.cpp mode change 100644 => 100755 kybackup/module/managebackuppointlist.h mode change 100644 => 100755 kybackup/module/operationlog.cpp mode change 100644 => 100755 kybackup/module/operationlog.h mode change 100644 => 100755 kybackup/module/selectrestorepoint.cpp mode change 100644 => 100755 kybackup/module/selectrestorepoint.h create mode 100755 kybackup/resource/symbos/document-open-recent-symbolic.png diff --git a/backup-daemon/backup-daemon.pro b/backup-daemon/backup-daemon.pro index e39ae05..fe773dd 100755 --- a/backup-daemon/backup-daemon.pro +++ b/backup-daemon/backup-daemon.pro @@ -34,6 +34,7 @@ HEADERS += \ databackupproxy.h \ datarestoreproxy.h \ deletebackupproxy.h \ + ghostimageproxy.h \ mybackupmanager.h \ mymountproxy.h \ myprocess/calcbackupsize.h \ @@ -46,6 +47,7 @@ HEADERS += \ systemrestoreproxy.h \ udiskdatabackupproxy.h \ udiskdatarestoreproxy.h \ + udiskghostImageproxy.h \ udisksystembackupproxy.h \ udisksystemrestoreproxy.h \ workerfactory.h @@ -60,6 +62,7 @@ SOURCES += \ databackupproxy.cpp \ datarestoreproxy.cpp \ deletebackupproxy.cpp \ + ghostimageproxy.cpp \ main.cpp \ mybackupmanager.cpp \ mymountproxy.cpp \ @@ -73,6 +76,7 @@ SOURCES += \ systemrestoreproxy.cpp \ udiskdatabackupproxy.cpp \ udiskdatarestoreproxy.cpp \ + udiskghostImageproxy.cpp \ udisksystembackupproxy.cpp \ udisksystemrestoreproxy.cpp \ workerfactory.cpp diff --git a/backup-daemon/databackupproxy.cpp b/backup-daemon/databackupproxy.cpp old mode 100644 new mode 100755 index fb5627d..3f68a0f --- a/backup-daemon/databackupproxy.cpp +++ b/backup-daemon/databackupproxy.cpp @@ -106,7 +106,7 @@ bool DataBackupProxy::checkFreeCapacity() QString backupPath(Utils::getSysRootPath() + BACKUP_PATH); backupPath.replace("//", "/"); QStorageInfo backupDisk(backupPath); - qint64 freeSize = backupDisk.bytesFree(); + qint64 freeSize = backupDisk.bytesAvailable(); // 3、校验空间是否足够 bool result = true; diff --git a/backup-daemon/databackupproxy.h b/backup-daemon/databackupproxy.h old mode 100644 new mode 100755 diff --git a/backup-daemon/datarestoreproxy.cpp b/backup-daemon/datarestoreproxy.cpp old mode 100644 new mode 100755 diff --git a/backup-daemon/datarestoreproxy.h b/backup-daemon/datarestoreproxy.h old mode 100644 new mode 100755 diff --git a/backup-daemon/deletebackupproxy.cpp b/backup-daemon/deletebackupproxy.cpp old mode 100644 new mode 100755 diff --git a/backup-daemon/deletebackupproxy.h b/backup-daemon/deletebackupproxy.h old mode 100644 new mode 100755 diff --git a/backup-daemon/ghostimageproxy.cpp b/backup-daemon/ghostimageproxy.cpp new file mode 100755 index 0000000..0c39aea --- /dev/null +++ b/backup-daemon/ghostimageproxy.cpp @@ -0,0 +1,127 @@ +#include "ghostimageproxy.h" +#include +#include +#include +#include +#include +#include +#include "../common/utils.h" +#include "../common/mydusizetool.h" +#include "mymountproxy.h" +#include "myprocess/calcbackupsize.h" + +IMPLEMENT_DYNCREATE(GhostImageProxy) + +GhostImageProxy::GhostImageProxy() +{ + m_mksquashfs = nullptr; + m_bSuccess = false; +} + +GhostImageProxy::~GhostImageProxy() +{ + delete m_mksquashfs; + m_mksquashfs = nullptr; +} + +/** + * @brief 环境检测 + */ +bool GhostImageProxy::checkEnvEx() +{ + // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载 + MyMountProxy mountProxy; + if ( MountResult::MOUNTED != mountProxy.mountBackupPartition() ) { + emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL)); + return false; + } + + // 2、校验backuppoint.xml中相应的备份节点是否存在 + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList xmlParse(xmlPath); + ParseBackupList::BackupPoint backupPoint = xmlParse.findBackupPointByUuid(m_backupWrapper.m_uuid); + if (backupPoint.m_backupName.isEmpty()) { + emit checkResult(int(BackupResult::GHOST_CANNOT_FIND_BACKUPPOINT)); + return false; + } + + // 3、校验备份数据是否存在 + QString dataPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + dataPath.replace("//", "/"); + if (Utils::isDirEmpty(dataPath)) { + emit checkResult(int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST)); + return false; + } + QFileInfo backup(dataPath); + qint64 itotalSize = backup.size(); + + // 4、校验空间大小是否充足 + m_destPath = Utils::getSysRootPath() + GHOST_PATH; + m_destPath.replace("//", "/"); + Utils::mkpath(m_destPath); + m_kyimg = Utils::getSysRootPath() + GHOST_PATH + "/" + m_backupWrapper.m_backupName; + m_kyimg.replace("//", "/"); + QFile kyimg(m_kyimg); + if (kyimg.exists()) + kyimg.remove(); + QStorageInfo storageInfo(m_destPath); + qint64 sizeAvailable = storageInfo.bytesAvailable(); + if (sizeAvailable < itotalSize / 2) { + emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH)); + return false; + } + + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + return true; +} + +/** + * @brief 任务处理 + */ +void GhostImageProxy::doWorkEx() +{ + if (!checkEnvEx()) + return ; + + doGhostImage(); +} + +/** + * @brief 任务取消 + */ +void GhostImageProxy::cancelEx() +{} + +/** + * @brief ghost镜像 + */ +void GhostImageProxy::doGhostImage() +{ + QStringList args; + // 拼接备份源路径和目标路径 + QString srcPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + srcPath.replace("//", "/"); + args << srcPath; + args << m_kyimg; + + m_mksquashfs = new MkSquashFSProcess(this); + connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &GhostImageProxy::progress); + connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [&](bool result) { + if (result) { + chown(m_kyimg.toLocal8Bit().data(), m_backupWrapper.m_frontUid, m_backupWrapper.m_gid); + QFileInfo fileInfo(m_kyimg); + QString imgSize = Utils::StringBySize(fileInfo.size()); + QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); + Utils::writeBackupLog(time + "," + + m_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + "," + + m_backupWrapper.m_note + "," + imgSize + + ",," + m_backupWrapper.m_backupName); + m_bSuccess = true; + } + emit this->workResult(result); + }); + m_bSuccess = false; + m_mksquashfs->start(args); +} + diff --git a/backup-daemon/ghostimageproxy.h b/backup-daemon/ghostimageproxy.h new file mode 100755 index 0000000..6ee70c4 --- /dev/null +++ b/backup-daemon/ghostimageproxy.h @@ -0,0 +1,42 @@ +#ifndef GHOSTIMAGEPROXY_H +#define GHOSTIMAGEPROXY_H + +#include "workerfactory.h" +#include "myprocess/mksquashfsprocess.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class GhostImageProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(GhostImageProxy) +public: + explicit GhostImageProxy(); + virtual ~GhostImageProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + + // 任务取消 + virtual void cancelEx(); + +private: + void doGhostImage(); + + // 存放.kyimg文件的目录 + QString m_destPath; + // .kyimg文件 + QString m_kyimg; + + // 压缩进程 + MkSquashFSProcess *m_mksquashfs; + + // 是否成功 + bool m_bSuccess; +}; + +#endif // GHOSTIMAGEPROXY_H diff --git a/backup-daemon/mybackupmanager.cpp b/backup-daemon/mybackupmanager.cpp index 2329a42..b547303 100755 --- a/backup-daemon/mybackupmanager.cpp +++ b/backup-daemon/mybackupmanager.cpp @@ -245,8 +245,48 @@ int MyBackupManager::deleteBackupPoint(const BackupWrapper& backupWrapper) */ int MyBackupManager::ghostBackup(const BackupWrapper& backupWrapper) { - Q_UNUSED(backupWrapper) - return 0; + qDebug("MyBackupManager::ghostBackup invoke begin"); + if (m_isActive || !lock(backupWrapper.m_frontUid)) { + emit sendEnvCheckResult(int(BackupResult::LOCK_PROGRAM_FAIL)); + return int(BackupResult::LOCK_PROGRAM_FAIL); + } + + Worker* worker = WorkerFactory::createWorker(backupWrapper.m_type, backupWrapper.m_iPosition); + if (nullptr == worker) { + emit sendEnvCheckResult(int(BackupResult::NO_FOUND_DEALCLASS)); + return int(BackupResult::NO_FOUND_DEALCLASS); + } + + worker->setParam(backupWrapper); + connect(worker, &Worker::checkResult, this, [&](int result) { + emit this->sendEnvCheckResult(result); + + switch (result) { + case int(BackupResult::CHECK_ENV_SUCCESS) : + case int(BackupResult::MKSQUASHFS_START_SUCCESS) : + case int(BackupResult::GHOST_START_SUCCESS) : + break; + default: + this->finished(); + break; + } + }); + connect(worker, &Worker::progress, this, [&](int rate) { + emit this->progress(int(BackupState::WORKING), rate); + }); + connect(worker, &Worker::workResult, this, [&] (bool result) { + emit this->sendGhostBackupResult(result); + this->finished(); + }); + worker->moveToThread(&workerThread); + connect(&workerThread, &MyThread::started, worker, &Worker::doWork); + connect(&workerThread, &MyThread::finished, worker, &Worker::deleteLater); + connect(&workerThread, &MyThread::cancelWork, worker, &Worker::cancel); + + workerThread.start(); + + qDebug("MyBackupManager::ghostBackup invoke end"); + return int(BackupResult::BACKUP_RESULT_INIT); } /** diff --git a/backup-daemon/systembackupproxy.cpp b/backup-daemon/systembackupproxy.cpp index c940b65..2bd5534 100755 --- a/backup-daemon/systembackupproxy.cpp +++ b/backup-daemon/systembackupproxy.cpp @@ -113,7 +113,7 @@ bool SystemBackupProxy::checkFreeCapacity() QString backupPath(Utils::getSysRootPath() + BACKUP_PATH); backupPath.replace("//", "/"); QStorageInfo backupDisk(backupPath); - qint64 freeSize = backupDisk.bytesFree(); + qint64 freeSize = backupDisk.bytesAvailable(); // 3、校验空间是否足够 bool result = true; diff --git a/backup-daemon/systemrestoreproxy.cpp b/backup-daemon/systemrestoreproxy.cpp old mode 100644 new mode 100755 diff --git a/backup-daemon/systemrestoreproxy.h b/backup-daemon/systemrestoreproxy.h old mode 100644 new mode 100755 diff --git a/backup-daemon/udiskdatabackupproxy.cpp b/backup-daemon/udiskdatabackupproxy.cpp old mode 100644 new mode 100755 index cc4c9bc..3006bff --- a/backup-daemon/udiskdatabackupproxy.cpp +++ b/backup-daemon/udiskdatabackupproxy.cpp @@ -105,7 +105,7 @@ bool UDiskDataBackupProxy::checkFreeCapacityToUdisk(qint64 itotalSize) QString backupPath(m_backupWrapper.m_prefixDestPath); backupPath.replace("//", "/"); QStorageInfo backupDisk(backupPath); - qint64 freeSize = backupDisk.bytesFree(); + qint64 freeSize = backupDisk.bytesAvailable(); // 3、校验空间是否足够 bool result = true; diff --git a/backup-daemon/udiskdatabackupproxy.h b/backup-daemon/udiskdatabackupproxy.h old mode 100644 new mode 100755 diff --git a/backup-daemon/udiskdatarestoreproxy.cpp b/backup-daemon/udiskdatarestoreproxy.cpp old mode 100644 new mode 100755 diff --git a/backup-daemon/udiskdatarestoreproxy.h b/backup-daemon/udiskdatarestoreproxy.h old mode 100644 new mode 100755 diff --git a/backup-daemon/udiskghostImageproxy.cpp b/backup-daemon/udiskghostImageproxy.cpp new file mode 100755 index 0000000..f4972a0 --- /dev/null +++ b/backup-daemon/udiskghostImageproxy.cpp @@ -0,0 +1,221 @@ +#include "udiskghostImageproxy.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(UDiskGhostImageProxy) + +UDiskGhostImageProxy::UDiskGhostImageProxy() +{ + m_mksquashfs = nullptr; + m_p = nullptr; + m_bSuccess = false; + m_isFinished = false; +} + +UDiskGhostImageProxy::~UDiskGhostImageProxy() +{ + if (!m_kyimg.isEmpty()) { + QFile kyimg(m_kyimg); + if (kyimg.exists()) + kyimg.remove(); + } +} + +/** + * @brief 环境检测 + */ +bool UDiskGhostImageProxy::checkEnvEx() +{ + // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载 + MyMountProxy mountProxy; + if ( MountResult::MOUNTED != mountProxy.mountBackupPartition() ) { + emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL)); + return false; + } + + // 2、校验backuppoint.xml中相应的备份节点是否存在 + QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList xmlParse(xmlPath); + ParseBackupList::BackupPoint backupPoint = xmlParse.findBackupPointByUuid(m_backupWrapper.m_uuid); + if (backupPoint.m_backupName.isEmpty()) { + emit checkResult(int(BackupResult::GHOST_CANNOT_FIND_BACKUPPOINT)); + return false; + } + + // 3、校验备份数据是否存在 + QString dataPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + dataPath.replace("//", "/"); + if (Utils::isDirEmpty(dataPath)) { + emit checkResult(int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST)); + return false; + } + QFileInfo backup(dataPath); + qint64 itotalSize = backup.size(); + + // 4、校验移动设备情况:空间大小、文件格式、挂载模式等 + 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 (udisk.isReadOnly()) { + // 只读挂载的U盘 + qCritical() << QString("udisk(%s) is readonly filesystem").arg(m_backupWrapper.m_prefixDestPath); + emit checkResult(int(BackupResult::UDISK_FILESYSTEM_IS_READONLY)); + return false; + } + + m_destPath = m_backupWrapper.m_prefixDestPath + GHOST_PATH; + m_destPath.replace("//", "/"); + Utils::mkpath(m_destPath); + m_kyimg = Utils::getSysRootPath() + GHOST_PATH + "/" + m_backupWrapper.m_backupName; + m_kyimg.replace("//", "/"); + QFile kyimg(m_kyimg); + if (kyimg.exists()) + kyimg.remove(); + QStorageInfo storageInfo(m_destPath); + qint64 sizeAvailable = storageInfo.bytesAvailable(); + if (sizeAvailable < itotalSize / 2) { + emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH)); + return false; + } + + // 5、依托本地存储先行压缩再拷贝到移动设备,在此查找空闲分区临时借用 + bool found = false; + QHash hash = Utils::getAvailableSizeOfPartitions(); + for (QHash::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) { + QString path = it.key(); + qint64 leftSize = it.value(); + if (itotalSize < leftSize / 2) { + m_kyimg = path + GHOST_PATH + "/" + m_backupWrapper.m_backupName; + m_kyimg.replace("//", "/"); + QFile kyimg(m_kyimg); + if (kyimg.exists()) + kyimg.remove(); + + found = true; + break ; + } + } + if (!found) { + emit checkResult(int(BackupResult::BACKUP_CAPACITY_FOR_UDISKIMG_IS_NOT_ENOUGH)); + return false; + } + + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + return true; +} + +/** + * @brief 任务处理 + */ +void UDiskGhostImageProxy::doWorkEx() +{ + if (!checkEnvEx()) + return ; + + doGhostImage(); +} + +/** + * @brief 任务取消 + */ +void UDiskGhostImageProxy::cancelEx() +{} + +/** + * @brief ghost镜像 + */ +void UDiskGhostImageProxy::doGhostImage() +{ + QStringList args; + // 拼接备份源路径和目标路径 + QString srcPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; + srcPath.replace("//", "/"); + args << srcPath; + args << m_kyimg; + + m_mksquashfs = new MkSquashFSProcess(this); + connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &UDiskGhostImageProxy::progress); + connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [&](bool result) { + if (result) { + chown(m_kyimg.toLocal8Bit().data(), m_backupWrapper.m_frontUid, m_backupWrapper.m_gid); + + // 同步到U盘 + m_p = new RsyncPathToDirProcess(this); + connect(m_p, &RsyncPathToDirProcess::finished, this, [&](bool resultRsync) { + m_isFinished = true; + if (resultRsync) { + QFileInfo fileInfo(m_kyimg); + QString imgSize = Utils::StringBySize(fileInfo.size()); + QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); + Utils::writeBackupLog(time + "," + + m_backupWrapper.m_uuid + "," + QString::number(m_backupWrapper.m_type) + "," + + m_backupWrapper.m_note + "," + imgSize + + ",," + m_backupWrapper.m_backupName); + m_bSuccess = true; + } + + emit this->workResult(resultRsync); + }); + + QStringList arguments; + arguments << "-a"; + arguments << m_kyimg; + arguments << m_destPath + "/"; + m_p->start(arguments, false); + } else { + m_isFinished = true; + emit this->workResult(false); + } + + }); + m_bSuccess = false; + m_isFinished = false; + m_mksquashfs->start(args); + QTimer::singleShot(1*1000, this, &UDiskGhostImageProxy::checkDestDirExists); +} + +/** + * @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 UDiskGhostImageProxy::checkDestDirExists() +{ + QDir dir(m_backupWrapper.m_prefixDestPath); + if (!dir.exists()) { + qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); + + if (m_mksquashfs) + m_mksquashfs->stop(); + if (m_p) + m_p->stop(); + + return false; + } + + if (!m_isFinished) + { + QTimer::singleShot(1*1000, this, &UDiskGhostImageProxy::checkDestDirExists); + } + + return true; +} diff --git a/backup-daemon/udiskghostImageproxy.h b/backup-daemon/udiskghostImageproxy.h new file mode 100755 index 0000000..02630c6 --- /dev/null +++ b/backup-daemon/udiskghostImageproxy.h @@ -0,0 +1,48 @@ +#ifndef UDISKGHOSTIMAGEPROXY_H +#define UDISKGHOSTIMAGEPROXY_H + +#include "workerfactory.h" +#include "myprocess/mksquashfsprocess.h" +#include "myprocess/rsyncpathtodirprocess.h" +#include "parsebackuplist.h" + +class UDiskGhostImageProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(UDiskGhostImageProxy) +public: + explicit UDiskGhostImageProxy(); + virtual ~UDiskGhostImageProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + + // 任务取消 + virtual void cancelEx(); + +private: + void doGhostImage(); + bool checkDestDirExists(); + + // 存放.kyimg文件的目录 + QString m_destPath; + // .kyimg文件 + QString m_kyimg; + + // 压缩进程 + MkSquashFSProcess *m_mksquashfs; + // 备份进程 + RsyncPathToDirProcess *m_p; + + // 是否成功 + bool m_bSuccess; + // 是否结束 + bool m_isFinished; +}; + + +#endif // UDISKGHOSTIMAGEPROXY_H diff --git a/backup-daemon/udisksystembackupproxy.cpp b/backup-daemon/udisksystembackupproxy.cpp index 5c02038..d02d796 100755 --- a/backup-daemon/udisksystembackupproxy.cpp +++ b/backup-daemon/udisksystembackupproxy.cpp @@ -141,7 +141,7 @@ void UDiskSystemBackupProxy::checkFreeCapacity(qint64 itotalSize) QString backupPath(m_backupWrapper.m_prefixDestPath); backupPath.replace("//", "/"); QStorageInfo backupDisk(backupPath); - qint64 freeSize = backupDisk.bytesFree(); + qint64 freeSize = backupDisk.bytesAvailable(); // 3、校验空间是否足够 if (itotalSize > freeSize) { diff --git a/backup-daemon/udisksystemrestoreproxy.cpp b/backup-daemon/udisksystemrestoreproxy.cpp old mode 100644 new mode 100755 diff --git a/backup-daemon/udisksystemrestoreproxy.h b/backup-daemon/udisksystemrestoreproxy.h old mode 100644 new mode 100755 diff --git a/backup-daemon/workerfactory.cpp b/backup-daemon/workerfactory.cpp index dc478bc..09c39d1 100755 --- a/backup-daemon/workerfactory.cpp +++ b/backup-daemon/workerfactory.cpp @@ -93,9 +93,9 @@ Worker * WorkerFactory::createWorker(int type, int position) break; case BackupType::GHOST_IMAGE: if (BackupPosition::UDISK == position) { - className = "UDiskGhostProxy"; + className = "UDiskGhostImageProxy"; } else { - className = "GhostProxy"; + className = "GhostImageProxy"; } break; default: diff --git a/common/mydefine.h b/common/mydefine.h index 7e3ab73..65bd0ff 100755 --- a/common/mydefine.h +++ b/common/mydefine.h @@ -18,6 +18,7 @@ #define IMGBACKUP_PATH "/imgbackup" #define UDISK_MKSQUASHFS_IMG_NAME "dst.img" #define UDISK_UNIQUE_SETTINGS "/backup/udisk_unique_file" +#define GHOST_PATH "/ghost" #define DATA_PATH "/data" diff --git a/common/utils.cpp b/common/utils.cpp index b91dd19..06b8c44 100755 --- a/common/utils.cpp +++ b/common/utils.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "mylittleparse.h" #include "mydefine.h" @@ -1061,6 +1062,26 @@ QHash Utils::getLeftSizeOfPartitions() return hash; } +/** + * @brief 获取分区可用大小,主要是/和/data分区 + * @return 路径和大小(单位:字节)键值对 + */ +QHash Utils::getAvailableSizeOfPartitions() +{ + QHash hash; + + QString root = Utils::getSysRootPath(); + QStorageInfo rootInfo(root); + hash.insert(root, rootInfo.bytesAvailable()); + + QString data = Utils::getSysRootPath() + DATA_PATH; + data.replace("//", "/"); + QStorageInfo dataInfo(data); + hash.insert(data, dataInfo.bytesAvailable()); + + return hash; +} + /** * @brief 获取文件夹或文件的大小 * @param path 路径 @@ -1140,3 +1161,5 @@ void Utils::wait(uint sec) QTimer::singleShot(1000 * sec, &loop, SLOT(quit())); loop.exec(); } + + diff --git a/common/utils.h b/common/utils.h index 3a8d570..e6bfa29 100755 --- a/common/utils.h +++ b/common/utils.h @@ -267,6 +267,12 @@ public: */ static QHash getLeftSizeOfPartitions(); + /** + * @brief 获取分区可用大小,主要是/和/data分区 + * @return 路径和大小(单位:字节)键值对 + */ + static QHash getAvailableSizeOfPartitions(); + /** * @brief 获取文件夹或文件的大小 * @param path 路径 diff --git a/kybackup/app.qrc b/kybackup/app.qrc index 08f0159..3fcb033 100755 --- a/kybackup/app.qrc +++ b/kybackup/app.qrc @@ -32,5 +32,6 @@ resource/symbos/ukui-dialog-success.png resource/images/empty.png resource/images/empty_dark.png + resource/symbos/document-open-recent-symbolic.png diff --git a/kybackup/component/backuplistwidget.cpp b/kybackup/component/backuplistwidget.cpp old mode 100644 new mode 100755 diff --git a/kybackup/component/backuplistwidget.h b/kybackup/component/backuplistwidget.h old mode 100644 new mode 100755 diff --git a/kybackup/component/myfileselect.cpp b/kybackup/component/myfileselect.cpp old mode 100644 new mode 100755 diff --git a/kybackup/component/myfileselect.h b/kybackup/component/myfileselect.h old mode 100644 new mode 100755 diff --git a/kybackup/deletebackupdialog.cpp b/kybackup/deletebackupdialog.cpp old mode 100644 new mode 100755 diff --git a/kybackup/deletebackupdialog.h b/kybackup/deletebackupdialog.h old mode 100644 new mode 100755 diff --git a/kybackup/deletebackupdialog.ui b/kybackup/deletebackupdialog.ui old mode 100644 new mode 100755 diff --git a/kybackup/kybackup.pro b/kybackup/kybackup.pro index 1d164fc..7fe9411 100755 --- a/kybackup/kybackup.pro +++ b/kybackup/kybackup.pro @@ -61,6 +61,7 @@ HEADERS += \ messageboxutils.h \ module/databackup.h \ module/datarestore.h \ + module/ghostimage.h \ module/managebackuppointlist.h \ module/operationlog.h \ module/selectrestorepoint.h \ @@ -102,6 +103,7 @@ SOURCES += \ messageboxutils.cpp \ module/databackup.cpp \ module/datarestore.cpp \ + module/ghostimage.cpp \ module/managebackuppointlist.cpp \ module/operationlog.cpp \ module/selectrestorepoint.cpp \ diff --git a/kybackup/maindialog.cpp b/kybackup/maindialog.cpp index b6a4530..f750187 100755 --- a/kybackup/maindialog.cpp +++ b/kybackup/maindialog.cpp @@ -13,6 +13,7 @@ #include "module/databackup.h" #include "module/datarestore.h" #include "module/operationlog.h" +#include "module/ghostimage.h" #include "backup_manager_interface.h" #include "globalbackupinfo.h" #include "messageboxutils.h" @@ -227,6 +228,10 @@ void MainDialog::selected(int func_type) m_stackedWidget = new OperationLog(ui->centralwidget); GlobelBackupInfo::inst().setFuncType(FuncTypeConverter::FunType::OPERATION_LOG); break; + case FuncTypeConverter::FunType::GHOST_IMAGE: + m_stackedWidget = new GhostImage(ui->centralwidget); + GlobelBackupInfo::inst().setFuncType(FuncTypeConverter::FunType::GHOST_IMAGE); + break; default: m_stackedWidget = new SystemBackup(ui->centralwidget); GlobelBackupInfo::inst().setFuncType(FuncTypeConverter::FunType::BACKUP_SYSTEM); diff --git a/kybackup/module/databackup.cpp b/kybackup/module/databackup.cpp old mode 100644 new mode 100755 index 64f8799..a8c2b27 --- a/kybackup/module/databackup.cpp +++ b/kybackup/module/databackup.cpp @@ -262,6 +262,7 @@ void DataBackup::initSecondWidget() // 添加本地默认路径、移动设备目录 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")); // 如果有备份分区,则将本地默认分区备份路径放在第一个 @@ -916,6 +917,7 @@ void DataBackup::on_checkEnv_start() backupWrapper.m_backupPaths << m_backupPaths; backupWrapper.m_prefixDestPath = m_prefixDestPath; backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->checkEnv(backupWrapper); } @@ -1323,6 +1325,7 @@ void DataBackup::on_backup_start() backupWrapper.m_backupPaths << m_backupPaths; backupWrapper.m_prefixDestPath = m_prefixDestPath; backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->goBackup(backupWrapper); } @@ -1526,7 +1529,7 @@ void DataBackup::initLastWidget() hlayoutCenterFont5->addWidget(retry); hlayoutCenterFont5->addSpacing(20); // 返回首页 - MyPushButton *homePage = new MyPushButton(last); + MyPushButton *homePage = new MyPushButton(centerFont); homePage->setFixedSize(97, 36); homePage->setText(tr("home page")); homePage->setEnabled(true); diff --git a/kybackup/module/databackup.h b/kybackup/module/databackup.h old mode 100644 new mode 100755 diff --git a/kybackup/module/datarestore.cpp b/kybackup/module/datarestore.cpp old mode 100644 new mode 100755 index 70cc6da..52966fc --- a/kybackup/module/datarestore.cpp +++ b/kybackup/module/datarestore.cpp @@ -398,8 +398,8 @@ void DataRestore::initSecondWidget() resultLogo->setVisible(true); // 检测成功 bigTitle->setDeplayText(tr("Succeeded to check the environment")); - // 还原完成后将自动重启 - labelCheck1->setDeplayText(tr("The system will reboot automatically after the restore is successful")); + // 不要使用电脑,以防数据丢失 + labelCheck1->setDeplayText(tr("Do not use computers in case of data loss")); dot2->setBackgroundColor(COLOR_YELLOW); labelCheck2->setFontColor(COLOR_YELLOW); labelCheck2->setFontWordWrap(true); @@ -472,6 +472,7 @@ void DataRestore::on_checkEnv_start() backupWrapper.m_uuid = m_uuid; backupWrapper.m_prefixDestPath = m_devPath; backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->checkEnv(backupWrapper); } @@ -516,12 +517,6 @@ void DataRestore::on_checkEnv_end(int result) // 备份点可能被损坏 errTip = tr("Backup points may be corrupted"); break; - case int(BackupResult::EFI_RSYNC_FAIL): - // 同步/boot/efi失败 - errMsg = tr("Failed to rsync /boot/efi"); - // 请检查/boot/efi分区挂载方式 - errTip = tr("Check the mounting mode of the /boot/efi partition"); - break; default: bRst = true; break; @@ -671,6 +666,7 @@ void DataRestore::on_restore_start() backupWrapper.m_uuid = m_uuid; backupWrapper.m_prefixDestPath = m_devPath; backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->goRestore(backupWrapper); } @@ -871,7 +867,7 @@ void DataRestore::initLastWidget() // hlayoutCenterFont5->addSpacing(100); hlayoutCenterFont5->addStretch(); // 返回首页 - MyPushButton *homePage = new MyPushButton(last); + MyPushButton *homePage = new MyPushButton(centerFont); homePage->setFixedSize(97, 36); homePage->setText(tr("home page")); homePage->setEnabled(true); diff --git a/kybackup/module/datarestore.h b/kybackup/module/datarestore.h old mode 100644 new mode 100755 diff --git a/kybackup/module/ghostimage.cpp b/kybackup/module/ghostimage.cpp new file mode 100755 index 0000000..8d93c7a --- /dev/null +++ b/kybackup/module/ghostimage.cpp @@ -0,0 +1,1100 @@ +#include "ghostimage.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../component/clicklabel.h" +#include "../component/circlelabel.h" +#include "../component/myiconlabel.h" +#include "../component/mylabel.h" +#include "../component/mylineedit.h" +#include "../component/mypushbutton.h" +#include "../component/linelabel.h" +#include "../component/ringsprogressbar.h" +#include "../component/myfileselect.h" +#include "../../common/utils.h" +#include "../globalbackupinfo.h" +#include "selectrestorepoint.h" +#include "messageboxutils.h" + +GhostImage::GhostImage(QWidget *parent) : + QStackedWidget(parent), + m_udector(new UdiskDetector()), + m_isLocal(true), + m_ghostImageState(GhostImageState::IDEL), + m_pInterface(nullptr) +{ + // 界面手写代码创建,作为练手 + initFirstWidget(); + initSecondWidget(); + initThirdWidget(); + initForthWidget(); + initLastWidget(); +} + +GhostImage::~GhostImage() +{ + delete m_udector; + m_udector = nullptr; + + delete m_pInterface; + m_pInterface = nullptr; +} + +/** + * @brief 初始化第一个页面 + */ +void GhostImage::initFirstWidget() +{ + QWidget *first = new QWidget; + + // 图片 + QLabel *imageGhost_firstPage = new QLabel(first); + imageGhost_firstPage->setGeometry(421, 120, 300, 326); + QPixmap pixmap(":/images/ghost_image.svg"); + imageGhost_firstPage->setPixmap(pixmap); + imageGhost_firstPage->setScaledContents(true); + + // Ghost Image大字提示 + MyLabel *labelGhost_firstPage = new MyLabel(first); + labelGhost_firstPage->setDeplayText(tr("Ghost Image")); + labelGhost_firstPage->setFixedWidth(500); + labelGhost_firstPage->setFixedHeight(48); + labelGhost_firstPage->move(41, 120); + // 默认水平左对齐,上下居中对齐;故不需要设置 + // labelGhost_firstPage->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + // labelGhost_firstPage->setText(tr("System Backup")); + QFont font; + font.setBold(true); + font.setPixelSize(36); + labelGhost_firstPage->setFont(font); + // labelGhost_firstPage->setAttribute(Qt::WA_TranslucentBackground); + labelGhost_firstPage->setScaledContents(true); + labelGhost_firstPage->adjustSize(); + + // Ghost说明 + MyLabel *labelNote_firstPage = new MyLabel(first); + labelNote_firstPage->setFixedWidth(700); + labelNote_firstPage->setFixedHeight(24); + labelNote_firstPage->move(41, 180); + // 必须存在本地系统备份,才能制作镜像文件 + labelNote_firstPage->setDeplayText(tr("A ghost image file can only be created after backup system to local disk")); + font.setBold(false); + font.setPixelSize(18); + labelNote_firstPage->setFont(font); + labelNote_firstPage->setScaledContents(true); + labelNote_firstPage->adjustSize(); + + // 操作简单 + MyIconLabel *iconSimple_firstPage = new MyIconLabel(first); + iconSimple_firstPage->setGeometry(41, 244, 180, 36); + iconSimple_firstPage->setThemeIcon("ukui-bf-simple-symbolic", ":/symbos/ukui-bf-simple-symbolic.png"); + iconSimple_firstPage->setDesplayText(tr("Simple")); + iconSimple_firstPage->setEnabled(false); + + // 快速 + MyIconLabel *iconFast_firstPage = new MyIconLabel(first); + iconFast_firstPage->setGeometry(206, 244, 180, 36); + iconFast_firstPage->setThemeIcon("ukui-bf-fast-symbolic", ":/symbos/ukui-bf-fast-symbolic.png"); + iconFast_firstPage->setDesplayText(tr("Fast")); + iconFast_firstPage->setEnabled(false); + + // 安全 + MyIconLabel *iconSecurity_firstPage = new MyIconLabel(first); + iconSecurity_firstPage->setGeometry(41, 296, 180, 36); + iconSecurity_firstPage->setThemeIcon("ukui-bf-security-symbolic", ":/symbos/ukui-bf-security-symbolic.png"); + iconSecurity_firstPage->setDesplayText(tr("Security")); + iconSecurity_firstPage->setEnabled(false); + + // 节省时间 + MyIconLabel *iconSimple_firstPage1 = new MyIconLabel(first); + iconSimple_firstPage1->setGeometry(206, 296, 180, 36); + iconSimple_firstPage1->setThemeIcon("document-open-recent-symbolic", ":/symbos/document-open-recent-symbolic.png"); + iconSimple_firstPage1->setDesplayText(tr("Timesaving")); + iconSimple_firstPage1->setEnabled(false); + + // 制作镜像按钮 + MyPushButton *beginBackup = new MyPushButton(first); + beginBackup->setGeometry(41, 372, 180, 52); + beginBackup->setText(tr("Start Ghost")); + beginBackup->setEnabled(true); + beginBackup->setAutoRepeat(true); + font.setPixelSize(24); + beginBackup->setFont(font); + connect(beginBackup, &MyPushButton::clicked, this, [=](bool checked) { + Q_UNUSED(checked) + this->m_uuid = ""; + this->m_prefixDestPath = ""; + this->m_backupName = ""; + + SelectRestorePoint * selectDialog = new SelectRestorePoint(this, SelectRestorePoint::DATA, true); + 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; + }); + + if (QDialog::Accepted == selectDialog->exec()) { + QString udiskFlag = Utils::getSysRootPath() + "/media"; + udiskFlag.replace("//", "/"); + this->m_isLocal = this->m_prefixDestPath.startsWith(udiskFlag) ? false : true; + this->setCurrentIndex(SELECT_PATH_PAGE); + } + selectDialog->deleteLater(); + + }); + + addWidget(first); +} + +/** + * @brief “上一步”按钮响应槽 + * @param checked + */ +void GhostImage::on_pre_clicked(bool checked) +{ + Q_UNUSED(checked) + int index = this->currentIndex() - 1; + if (index >= 0) { + this->setCurrentIndex(index); + } +} + +/** + * @brief “开始备份”“下一步”按钮响应槽 + * @param checked + */ +void GhostImage::on_next_clicked(bool checked) +{ + Q_UNUSED(checked) + int index = this->currentIndex() + 1; + if (index < this->count()) { + this->setCurrentIndex(index); + } +} + +/** + * @brief 初始化第二个页面 + */ +void GhostImage::initSecondWidget() +{ + QWidget *second = new QWidget; + + // 请选择存储位置 + MyLabel* labelSelect = new MyLabel(second); + labelSelect->setDeplayText(tr("Please select storage location")); + labelSelect->setFixedWidth(700); + labelSelect->setFixedHeight(27); + labelSelect->move(41, 41); + QFont font; + font.setBold(true); + font.setPixelSize(18); + labelSelect->setFont(font); + labelSelect->setScaledContents(true); + labelSelect->adjustSize(); + + // 备份路径选择框 + QComboBox* comboSelect = new QComboBox(second); + comboSelect->setGeometry(41, 84, 680, 36); + // 添加本地默认路径、移动设备目录 + 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() + GHOST_PATH; + qsLocalDefaultPath.replace("//", "/"); + this->m_prefixDestPath = ""; + 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() + GHOST_PATH); + } + }); + m_udector->getStorageInfo(); + + // 上一步按钮 + MyPushButton *preStep = new MyPushButton(second); + preStep->setGeometry(271, 176, 97, 36); + preStep->setText(tr("back")); + preStep->setEnabled(true); + preStep->setAutoRepeat(true); + connect(preStep, &MyPushButton::clicked, this, &GhostImage::on_pre_clicked); + + // 下一步按钮 + MyPushButton *nextStep = new MyPushButton(second); + nextStep->setGeometry(389, 176, 97, 36); + nextStep->setText(tr("next")); + nextStep->setEnabled(true); + nextStep->setAutoRepeat(true); + connect(nextStep, &MyPushButton::clicked, this, [=](bool checked) { + // 备份路径选择索引 + int index = comboSelect->currentIndex(); + // 第一个选项是本地系统备份 + this->m_isLocal = GlobelBackupInfo::inst().hasBackupPartition() && (index == 0); + this->m_prefixDestPath = this->m_udiskPaths.at(index); + this->on_next_clicked(checked); + emit this->startCheckEnv(); + }); + + addWidget(second); +} + +/** + * @brief 初始化第三个页面 + */ +void GhostImage::initThirdWidget() +{ + QWidget *third = new QWidget; + + // 流程进度提示栏 + CircleLable *one = new CircleLable("1", third, 24, QColor(COLOR_BLUE)); + LineLabel *line1 = new LineLabel(third, QColor(COLOR_BLUE), QSize(200, 24)); + CircleLable *two = new CircleLable("2", third); + LineLabel *line2 = new LineLabel(third, QColor(COLOR_GRAY), QSize(200, 24)); + CircleLable *three = new CircleLable("3", third); + QHBoxLayout *layoutLine1 = new QHBoxLayout; + layoutLine1->addStretch(); + layoutLine1->addWidget(one); + layoutLine1->addWidget(line1); + layoutLine1->addWidget(two); + layoutLine1->addWidget(line2); + layoutLine1->addWidget(three); + layoutLine1->addStretch(); + + MyLabel *label1 = new MyLabel(tr("checking"), third); + label1->setIsOriginal(true); + label1->setFontColor(QColor(COLOR_BLUE)); + MyLabel *label2 = new MyLabel(tr("ghosting"), third); + label2->setIsOriginal(true); + MyLabel *label3 = new MyLabel(tr("finished"), third); + label3->setIsOriginal(true); + QHBoxLayout *layoutLine2 = new QHBoxLayout; + layoutLine2->addSpacing(100); + layoutLine2->addWidget(label1); + layoutLine2->addStretch(); + layoutLine2->addWidget(label2); + layoutLine2->addStretch(); + layoutLine2->addWidget(label3); + layoutLine2->addSpacing(100); + + // ------------ 中部布局begin------------- + QWidget *centerFont = new QWidget(third); + QVBoxLayout *vlayoutCenterFont = new QVBoxLayout; + + // 第一行 + QHBoxLayout *hlayoutCenterFont1 = new QHBoxLayout; + // 检测等待图标 + QLabel *loadingGif = new QLabel(centerFont); + loadingGif->setFixedSize(20,20); + // 环境检测等待动画 + QMovie *movie = new QMovie(":/images/loading.gif", QByteArray(), centerFont); + loadingGif->setMovie(movie); + hlayoutCenterFont1->addWidget(loadingGif); + // 检测结果对错图标 + QLabel *resultLogo = new QLabel(centerFont); + resultLogo->setFixedSize(20,20); + hlayoutCenterFont1->addWidget(resultLogo); + // 检测中大标题 + MyLabel *bigTitle = new MyLabel(centerFont); + bigTitle->setFontSize(24); + bigTitle->setMaximumWidth(700); + hlayoutCenterFont1->addWidget(bigTitle); + hlayoutCenterFont1->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont1); + + // 第二行 + QHBoxLayout *hlayoutCenterFont2 = new QHBoxLayout; + hlayoutCenterFont2->addSpacing(10); + // 检测中的记录:黑点1和文字1 + CircleLable *dot1 = new CircleLable(QString(""), centerFont, 6, Qt::black); + hlayoutCenterFont2->addWidget(dot1); + hlayoutCenterFont2->addSpacing(5); + MyLabel *labelCheck1 = new MyLabel(centerFont); + labelCheck1->setMinimumWidth(400); + labelCheck1->setMaximumWidth(600); + labelCheck1->setIsOriginal(true); + labelCheck1->setWordWrap(true); + labelCheck1->adjustSize(); + hlayoutCenterFont2->addWidget(labelCheck1); + hlayoutCenterFont2->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont2); + + // 第三行 + QHBoxLayout *hlayoutCenterFont3 = new QHBoxLayout; + hlayoutCenterFont3->addSpacing(10); + // 检测中的记录:黑点2和文字2 + CircleLable *dot2 = new CircleLable(QString(""), centerFont, 6, Qt::black); + hlayoutCenterFont3->addWidget(dot2); + hlayoutCenterFont3->addSpacing(5); + MyLabel *labelCheck2 = new MyLabel(centerFont); + labelCheck2->setMinimumWidth(400); + labelCheck2->setMaximumWidth(600); + labelCheck2->setIsOriginal(true); + labelCheck2->setWordWrap(true); + labelCheck2->adjustSize(); + hlayoutCenterFont3->addWidget(labelCheck2); + hlayoutCenterFont3->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont3); + + // 第四行 + vlayoutCenterFont->addSpacing(30); + + // 第五行 + QHBoxLayout *hlayoutCenterFont5 = new QHBoxLayout; + hlayoutCenterFont5->addStretch(); + // 上一步按钮 + MyPushButton *preStep = new MyPushButton(centerFont); + preStep->setFixedSize(97, 36); + preStep->setText(tr("back")); + preStep->setEnabled(true); + preStep->setAutoRepeat(true); + connect(preStep, &MyPushButton::clicked, this, &GhostImage::on_pre_clicked); + hlayoutCenterFont5->addWidget(preStep); + hlayoutCenterFont5->addSpacing(20); + // 下一步按钮 + MyPushButton *nextStep = new MyPushButton(centerFont); + nextStep->setFixedSize(97, 36); + nextStep->setText(tr("next")); + nextStep->setEnabled(true); + nextStep->setAutoRepeat(true); + connect(nextStep, &MyPushButton::clicked, this, [=](bool checked) { + this->on_next_clicked(checked); + emit this->startGhost(); + }); + hlayoutCenterFont5->addWidget(nextStep); + // 重新检测按钮 + MyPushButton *recheck = new MyPushButton(centerFont); + recheck->setFixedSize(97, 36); + recheck->setText(tr("recheck")); + recheck->setEnabled(true); + recheck->setAutoRepeat(true); + hlayoutCenterFont5->addWidget(recheck); + hlayoutCenterFont5->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont5); + + centerFont->setLayout(vlayoutCenterFont); + + // ------------ 中部布局end------------- + + QHBoxLayout *layoutLine3 = new QHBoxLayout; + layoutLine3->addStretch(); + layoutLine3->addWidget(centerFont); + layoutLine3->addStretch(); + + // 布局 + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addSpacing(40); + vlayout->addLayout(layoutLine1); + vlayout->addLayout(layoutLine2); + vlayout->addSpacing(50); + vlayout->addLayout(layoutLine3); + vlayout->addStretch(); + third->setLayout(vlayout); + + // 开始检测 + connect(this, &GhostImage::startCheckEnv, this, [=]() { + this->m_ghostImageState = GhostImageState::CHECKING; + loadingGif->setVisible(true); + movie->start(); + resultLogo->setVisible(false); + // 环境检测中,请等待 + bigTitle->setDeplayText(tr("Checking, wait a moment ...")); + dot1->setBackgroundColor(Qt::black); + dot2->setBackgroundColor(Qt::black); + labelCheck1->setFontColor(Qt::black); + labelCheck2->setFontColor(Qt::black); + // 还原过程中不要做其它操作,以防数据丢失 + labelCheck1->setDeplayText(tr("Check whether the restore environment meets the requirements")); + // 检测还原环境是否满足 + labelCheck2->setDeplayText(tr("Do not perform other operations during restore to avoid data loss")); + preStep->setVisible(false); + nextStep->setVisible(false); + recheck->setVisible(false); + + this->on_checkEnv_start(); + }); + + // 检测结果 + connect(this, &GhostImage::checkEnvResult, this, [=](bool result, const QString &errMsg, const QString &errTip) { + loadingGif->setVisible(false); + movie->stop(); + + if (result) { + QIcon icon = QIcon::fromTheme("ukui-dialog-success", QIcon(":/symbos/ukui-dialog-success.png")); + resultLogo->setPixmap(icon.pixmap(QSize(20,20))); + resultLogo->setVisible(true); + // 检测成功 + bigTitle->setDeplayText(tr("Succeeded to check the environment")); + // 存储空间充足 + labelCheck1->setDeplayText(tr("The storage space is enough")); + dot2->setBackgroundColor(COLOR_YELLOW); + labelCheck2->setFontColor(COLOR_YELLOW); + labelCheck2->setFontWordWrap(true); + // 请确保电脑已连接电源或电量超过60% + labelCheck2->setDeplayText(tr("Make sure the computer is plugged in or the battery level is above 60%")); + dot1->setVisible(true); + dot2->setVisible(true); + labelCheck1->setVisible(true); + labelCheck2->setVisible(true); + nextStep->setVisible(true); + recheck->setVisible(false); + } else { + QIcon icon = QIcon::fromTheme("dialog-error.png", QIcon(":/symbos/dialog-error.png")); + resultLogo->setPixmap(icon.pixmap(QSize(20,20))); + resultLogo->setVisible(true); + // 环境校验失败 + bigTitle->setDeplayText(tr("Failed to check the environment")); + labelCheck1->setDeplayText(errMsg); + labelCheck2->setDeplayText(errTip); + if (errMsg.isEmpty()) { + dot1->setVisible(false); + labelCheck1->setVisible(false); + } else { + dot1->setVisible(true); + labelCheck1->setVisible(true); + } + if (errTip.isEmpty()) { + dot2->setVisible(false); + labelCheck2->setVisible(false); + } else { + dot2->setVisible(true); + labelCheck2->setVisible(true); + } + recheck->setVisible(true); + nextStep->setVisible(false); + } + + preStep->setVisible(true); + }); + + // 重新检查 + connect(recheck, &MyPushButton::clicked, this, [=](bool checked) { + Q_UNUSED(checked) + emit this->startCheckEnv(); + }); + + addWidget(third); +} + +/** + * @brief 开始进行环境检测 + */ +void GhostImage::on_checkEnv_start() +{ + GlobelBackupInfo::inst().setIsBusy(true); + m_pInterface = new ComKylinBackupManagerInterface("com.kylin.backup", "/", QDBusConnection::systemBus(), this); + connect(m_pInterface, &ComKylinBackupManagerInterface::sendEnvCheckResult, this, &GhostImage::on_checkEnv_end); + + // 是否已存在备份、还原等操作 + bool isActive = false; + if(int(BackupState::BACKUP_STATE_INIT) != m_pInterface->getBackupState(isActive)){ + on_checkEnv_end(int(BackupResult::OTHER_BACKUP_OR_RESTORE_RUNNING)); + + return; + } + + BackupWrapper backupWrapper; + 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_prefixDestPath = m_prefixDestPath; + backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); + backupWrapper.m_gid = getgid(); + m_pInterface->checkEnv(backupWrapper); +} + +/** + * @brief 环境检测结束 + * @param result, 环境校验结果 + */ +void GhostImage::on_checkEnv_end(int result) +{ + m_ghostImageState = GhostImageState::IDEL; + bool bRst = false; + QString errMsg, errTip; + switch (result) { + case int(BackupResult::LOCK_PROGRAM_FAIL): + // 程序锁定失败,请重试 + errMsg = tr("Program lock failed, please retry"); + // 可能有其它备份/还原等任务在执行 + errTip = tr("There may be other backups or restores being performed"); + break; + case int(BackupResult::NO_FOUND_DEALCLASS): + // 不支持的任务类型 + errMsg = tr("Unsupported task type"); + // 没有找到相应的处理逻辑 + errTip = tr("No processing logic was found in the service"); + break; + case int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL): + // 备份分区挂载失败 + errMsg = tr("Failed to mount the backup partition"); + // 检查是否有备份分区 + errTip = tr("Check whether there is a backup partition"); + break; + case int(BackupResult::UDISK_FILESYSTEM_TYPE_IS_VFAT): + // 移动设备的文件系统是vfat格式 + errMsg = tr("The filesystem of device is vfat format"); + // 请换成ext4、ntfs等格式 + errTip = tr("Please change filesystem format to ext3、ext4 or ntfs"); + break; + case int(BackupResult::UDISK_FILESYSTEM_IS_READONLY): + // 移动设备是只读的 + errMsg = tr("The device is read only"); + // 请修改为可读写权限的 + errTip = tr("Please chmod to rw"); + break; + case int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH): + // 镜像空间不足 + errMsg = tr("The storage for ghost is not enough"); + // 建议释放空间后重试 + errTip = tr("Retry after release space"); + break; + case int(BackupResult::BACKUP_CAPACITY_FOR_UDISKIMG_IS_NOT_ENOUGH): + // 没有足够的空间存放临时.kyimg文件 + errMsg = tr("There is not enough space for temporary .kyimg file"); + // 建议释放空间后重试 + errTip = tr("Retry after release space"); + break; + case int(BackupResult::OTHER_BACKUP_OR_RESTORE_RUNNING): + // 其它备份还原等操作正在执行 + errMsg = tr("Other backup or restore task is being performed"); + // 请稍后重试 + errTip = tr("Please try again later"); + break; + case int(BackupResult::GHOST_CANNOT_FIND_BACKUPPOINT): + case int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST): + // 备份节点已不存在 + errMsg = tr("The backup node does not exist"); + // 请检查刚刚是否有删除备份点操作 + errTip = tr("Check whether the backup point has been deleted"); + break; + default: + bRst = true; + break; + } + + emit checkEnvResult(bRst, errMsg, errTip); + GlobelBackupInfo::inst().setIsBusy(false); + disconnect(m_pInterface, &ComKylinBackupManagerInterface::sendEnvCheckResult, this, &GhostImage::on_checkEnv_end); + delete m_pInterface; + m_pInterface = nullptr; +} + +/** + * @brief 创建ghost镜像名称 + * @param comment + * @return + */ +QString GhostImage::createGhostImageName(const QString& backupName) +{ + QString localHostName = QHostInfo::localHostName(); + + QRegExp rx = QRegExp("([0-9a-zA-Z_\\-])+"); + QString destFile = "ghost"; + if (rx.exactMatch(localHostName)) + destFile = localHostName; + + //使用uname -m获取体系结构 + QString result; + Utils::executeCMD("uname -m", result); + QString arch = result.trimmed(); + + //用comment + QString mycomment = backupName; + mycomment.replace("-", ""); + mycomment.replace(":", ""); + mycomment.replace(" ", ""); + destFile = destFile + "-" + arch + "-" + mycomment + ".kyimg"; + + return destFile; +} + +/** + * @brief 第四个页面 + */ +void GhostImage::initForthWidget() +{ + QWidget *forth = new QWidget; + + // 流程进度提示栏 + CircleLable *one = new CircleLable("1", forth, 24, QColor(COLOR_BLUE)); + LineLabel *line1 = new LineLabel(forth, QColor(COLOR_BLUE), QSize(200, 24)); + CircleLable *two = new CircleLable("2", forth, 24, QColor(COLOR_BLUE)); + LineLabel *line2 = new LineLabel(forth, QColor(COLOR_BLUE), QSize(200, 24)); + CircleLable *three = new CircleLable("3", forth); + QHBoxLayout *layoutLine1 = new QHBoxLayout; + layoutLine1->addStretch(); + layoutLine1->addWidget(one); + layoutLine1->addWidget(line1); + layoutLine1->addWidget(two); + layoutLine1->addWidget(line2); + layoutLine1->addWidget(three); + layoutLine1->addStretch(); + + MyLabel *label1 = new MyLabel(tr("checking"), forth); + label1->setIsOriginal(true); + label1->setFontColor(QColor(COLOR_BLUE)); + MyLabel *label2 = new MyLabel(tr("ghosting"), forth); + label2->setIsOriginal(true); + label2->setFontColor(QColor(COLOR_BLUE)); + MyLabel *label3 = new MyLabel(tr("finished"), forth); + label3->setIsOriginal(true); + QHBoxLayout *layoutLine2 = new QHBoxLayout; + layoutLine2->addSpacing(100); + layoutLine2->addWidget(label1); + layoutLine2->addStretch(); + layoutLine2->addWidget(label2); + layoutLine2->addStretch(); + layoutLine2->addWidget(label3); + layoutLine2->addSpacing(100); + + // ------------ 中部布局begin------------- + QWidget *centerFont = new QWidget(forth); + QVBoxLayout *vlayoutCenterFont = new QVBoxLayout; + + // 中部第一行 + QHBoxLayout *hlayoutCenterFont1 = new QHBoxLayout; + // 检测等待图标 + QLabel *loadingGif = new QLabel(centerFont); + // 环境检测等待动画 + QMovie *movie = new QMovie(":/images/loading.gif", QByteArray(), centerFont); + loadingGif->setMovie(movie); + loadingGif->setFixedSize(20,20); + // 进度条 + RingsProgressbar *progressBar = new RingsProgressbar(centerFont); + progressBar->setFixedSize(100, 100); + hlayoutCenterFont1->addStretch(); + hlayoutCenterFont1->addWidget(loadingGif); + hlayoutCenterFont1->addWidget(progressBar); + hlayoutCenterFont1->addStretch(); + + // 第二行 + QHBoxLayout *hlayoutCenterFont2 = new QHBoxLayout; + // 提醒 + MyLabel *labelTip = new MyLabel(centerFont); + labelTip->setAlignment(Qt::AlignCenter); + labelTip->setIsOriginal(true); + labelTip->setFontWordWrap(true); + labelTip->setMinimumWidth(700); + // 不要使用电脑,以防数据丢失 + labelTip->setDeplayText(tr("Do not use computers in case of data loss")); + hlayoutCenterFont2->addStretch(); + hlayoutCenterFont2->addWidget(labelTip); + hlayoutCenterFont2->addStretch(); + // 第二行 + QHBoxLayout *hlayoutCenterFont2_1 = new QHBoxLayout; + // 备份过程提醒信息 + MyLabel *labelTip_1 = new MyLabel(centerFont); + labelTip_1->setAlignment(Qt::AlignCenter); + labelTip_1->setIsOriginal(true); + labelTip_1->setFontWordWrap(true); + labelTip_1->setMinimumWidth(700); + labelTip_1->setDeplayText(""); + hlayoutCenterFont2_1->addStretch(); + hlayoutCenterFont2_1->addWidget(labelTip_1); + hlayoutCenterFont2_1->addStretch(); + + // 第三行 + QHBoxLayout *hlayoutCenterFont3 = new QHBoxLayout; + // 取消按钮 + MyPushButton *cancel = new MyPushButton(forth); + cancel->setFixedSize(97, 36); + cancel->setText(tr("cancel")); + cancel->setEnabled(true); + cancel->setAutoRepeat(true); + hlayoutCenterFont3->addStretch(); + hlayoutCenterFont3->addWidget(cancel); + hlayoutCenterFont3->addStretch(); + + vlayoutCenterFont->addLayout(hlayoutCenterFont1); + vlayoutCenterFont->addLayout(hlayoutCenterFont2); + vlayoutCenterFont->addLayout(hlayoutCenterFont2_1); + vlayoutCenterFont->addSpacing(40); + vlayoutCenterFont->addLayout(hlayoutCenterFont3); + vlayoutCenterFont->addStretch(); + centerFont->setLayout(vlayoutCenterFont); + + // ------------ 中部布局end------------- + + QHBoxLayout *layoutLine3 = new QHBoxLayout; + layoutLine3->addStretch(); + layoutLine3->addWidget(centerFont); + layoutLine3->addStretch(); + + // 布局 + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addSpacing(40); + vlayout->addLayout(layoutLine1); + vlayout->addLayout(layoutLine2); + vlayout->addSpacing(50); + vlayout->addLayout(layoutLine3); + vlayout->addStretch(); + forth->setLayout(vlayout); + + // 开始制作ghost镜像 + connect(this, &GhostImage::startGhost, this, [=] { + progressBar->setPersent(0); + movie->start(); + labelTip_1->setVisible(false); + labelTip_1->setDeplayText(""); + + // 开始备份 + this->on_ghost_start(); + }); + + // 进度 + connect(this, &GhostImage::progress, this, [=](int state, int rate) { + Q_UNUSED(state) + progressBar->setPersent(rate); + }); + + // 取消备份 + connect(cancel, &MyPushButton::clicked, this, [=](bool checked) { + Q_UNUSED(checked) + // TODO + }); + + addWidget(forth); +} + +/** + * @brief 开始进行系统备份 + */ +void GhostImage::on_ghost_start() +{ + GlobelBackupInfo::inst().setIsBusy(true); + m_ghostImageState = GhostImageState::GHOSTING; + m_pInterface = new ComKylinBackupManagerInterface("com.kylin.backup", "/", QDBusConnection::systemBus(), this); + connect(m_pInterface, &ComKylinBackupManagerInterface::sendEnvCheckResult, this, &GhostImage::on_checkGhost_end); + connect(m_pInterface, &ComKylinBackupManagerInterface::progress, this, &GhostImage::progress); + connect(m_pInterface, &ComKylinBackupManagerInterface::sendGhostBackupResult, this, &GhostImage::on_ghost_end); + + // 是否已存在备份、还原等操作 + bool isActive = false; + if(int(BackupState::BACKUP_STATE_INIT) != m_pInterface->getBackupState(isActive)){ + on_checkGhost_end(int(BackupResult::OTHER_BACKUP_OR_RESTORE_RUNNING)); + + return; + } + + BackupWrapper backupWrapper; + 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_prefixDestPath = m_prefixDestPath; + backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); + backupWrapper.m_gid = getgid(); + m_pInterface->ghostBackup(backupWrapper); +} + +/** + * @brief 系统备份校验结果处理 + * @param result + */ +void GhostImage::on_checkGhost_end(int result) +{ + bool bRst = false; + QString errMsg, errTip; + switch (result) { + case int(BackupResult::LOCK_PROGRAM_FAIL): + // 程序锁定失败,请重试 + errMsg = tr("Program lock failed, please retry"); + // 可能有其它备份/还原等任务在执行 + errTip = tr("There may be other backups or restores being performed"); + break; + case int(BackupResult::NO_FOUND_DEALCLASS): + // 不支持的任务类型 + errMsg = tr("Unsupported task type"); + // 没有找到相应的处理逻辑 + errTip = tr("No processing logic was found in the service"); + break; + case int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL): + // 备份分区挂载失败 + errMsg = tr("Failed to mount the backup partition"); + // 检查是否有备份分区 + errTip = tr("Check whether there is a backup partition"); + break; + case int(BackupResult::UDISK_FILESYSTEM_TYPE_IS_VFAT): + // 移动设备的文件系统是vfat格式 + errMsg = tr("The filesystem of device is vfat format"); + // 请换成ext4、ntfs等格式 + errTip = tr("Please change filesystem format to ext3、ext4 or ntfs"); + break; + case int(BackupResult::UDISK_FILESYSTEM_IS_READONLY): + // 移动设备是只读的 + errMsg = tr("The device is read only"); + // 请修改为可读写权限的 + errTip = tr("Please chmod to rw"); + break; + case int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH): + // 镜像空间不足 + errMsg = tr("The storage for ghost is not enough"); + // 建议释放空间后重试 + errTip = tr("Retry after release space"); + break; + case int(BackupResult::BACKUP_CAPACITY_FOR_UDISKIMG_IS_NOT_ENOUGH): + // 没有足够的空间存放临时.kyimg文件 + errMsg = tr("There is not enough space for temporary .kyimg file"); + // 建议释放空间后重试 + errTip = tr("Retry after release space"); + break; + case int(BackupResult::OTHER_BACKUP_OR_RESTORE_RUNNING): + // 其它备份还原等操作正在执行 + errMsg = tr("Other backup or restore task is being performed"); + // 请稍后重试 + errTip = tr("Please try again later"); + break; + case int(BackupResult::GHOST_CANNOT_FIND_BACKUPPOINT): + case int(BackupResult::GHOST_SRC_DIRECTORY_IS_NOT_EXIST): + // 备份节点已不存在 + errMsg = tr("The backup node does not exist"); + // 请检查刚刚是否有删除备份点操作 + errTip = tr("Check whether the backup point has been deleted"); + break; + default: + bRst = true; + break; + } + + if (!bRst) { + GlobelBackupInfo::inst().setIsBusy(false); + m_ghostImageState = GhostImageState::IDEL; + this->on_next_clicked(true); + emit this->checkGhostResult(bRst, errMsg, errTip); + disconnect(m_pInterface, &ComKylinBackupManagerInterface::sendEnvCheckResult, this, &GhostImage::on_checkEnv_end); + disconnect(m_pInterface, &ComKylinBackupManagerInterface::progress, this, &GhostImage::progress); + disconnect(m_pInterface, &ComKylinBackupManagerInterface::sendGhostBackupResult, this, &GhostImage::on_ghost_end); + delete m_pInterface; + m_pInterface = nullptr; + } +} + +/** + * @brief 系统备份结束 + * @param result-false 失败; true 成功 + */ +void GhostImage::on_ghost_end(bool result) +{ + m_ghostImageState = GhostImageState::IDEL; + + this->on_next_clicked(true); + if (result) { + emit checkGhostResult(result); + } else { + // 备份过程中出现错误 + QString errMsg = tr("An error occurred during make ghost image"); + // 错误信息参考日志文件:/var/log/backup.log + QString errTip = tr("Error messages refer to log file : /var/log/backup.log"); + emit checkGhostResult(result, errMsg, errTip); + } + + GlobelBackupInfo::inst().setIsBusy(false); + disconnect(m_pInterface, &ComKylinBackupManagerInterface::sendEnvCheckResult, this, &GhostImage::on_checkEnv_end); + disconnect(m_pInterface, &ComKylinBackupManagerInterface::progress, this, &GhostImage::progress); + disconnect(m_pInterface, &ComKylinBackupManagerInterface::sendGhostBackupResult, this, &GhostImage::on_ghost_end); + delete m_pInterface; + m_pInterface = nullptr; +} + +/** + * @brief 最后备份结果界面 + */ +void GhostImage::initLastWidget() +{ + QWidget *last = new QWidget; + + // 流程进度提示栏 + CircleLable *one = new CircleLable("1", last, 24, QColor(COLOR_BLUE)); + LineLabel *line1 = new LineLabel(last, QColor(COLOR_BLUE), QSize(200, 24)); + CircleLable *two = new CircleLable("2", last, 24, QColor(COLOR_BLUE)); + LineLabel *line2 = new LineLabel(last, QColor(COLOR_BLUE), QSize(200, 24)); + CircleLable *three = new CircleLable("3", last, 24, QColor(COLOR_BLUE)); + QHBoxLayout *layoutLine1 = new QHBoxLayout; + layoutLine1->addStretch(); + layoutLine1->addWidget(one); + layoutLine1->addWidget(line1); + layoutLine1->addWidget(two); + layoutLine1->addWidget(line2); + layoutLine1->addWidget(three); + layoutLine1->addStretch(); + + MyLabel *label1 = new MyLabel(tr("checking"), last); + label1->setIsOriginal(true); + label1->setFontColor(QColor(COLOR_BLUE)); + MyLabel *label2 = new MyLabel(tr("ghosting"), last); + label2->setIsOriginal(true); + label2->setFontColor(QColor(COLOR_BLUE)); + MyLabel *label3 = new MyLabel(tr("finished"), last); + label3->setIsOriginal(true); + label3->setFontColor(QColor(COLOR_BLUE)); + QHBoxLayout *layoutLine2 = new QHBoxLayout; + layoutLine2->addSpacing(100); + layoutLine2->addWidget(label1); + layoutLine2->addStretch(); + layoutLine2->addWidget(label2); + layoutLine2->addStretch(); + layoutLine2->addWidget(label3); + layoutLine2->addSpacing(100); + + //------------ 中部布局begin------------- + QWidget *centerFont = new QWidget(last); + QVBoxLayout *vlayoutCenterFont = new QVBoxLayout; + + // 第一行 + QHBoxLayout *hlayoutCenterFont1 = new QHBoxLayout; + // 备份结果对错图标 + QLabel *resultLogo = new QLabel(centerFont); + resultLogo->setFixedSize(20,20); + hlayoutCenterFont1->addWidget(resultLogo); + // 检测中大标题 + MyLabel *bigTitle = new MyLabel(centerFont); + bigTitle->setFontSize(24); + bigTitle->setMaximumWidth(700); + hlayoutCenterFont1->addWidget(bigTitle); + hlayoutCenterFont1->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont1); + + // 第二行 + QHBoxLayout *hlayoutCenterFont2 = new QHBoxLayout; + hlayoutCenterFont2->addSpacing(10); + // 备份结果错误提示:黑点和文字 + CircleLable *dot1 = new CircleLable(QString(""), centerFont, 6, Qt::black); + hlayoutCenterFont2->addWidget(dot1); + hlayoutCenterFont2->addSpacing(5); + MyLabel *labelError1 = new MyLabel(centerFont); + labelError1->setMinimumWidth(400); + labelError1->setMaximumWidth(600); + labelError1->setIsOriginal(true); + labelError1->setWordWrap(true); + labelError1->adjustSize(); + hlayoutCenterFont2->addWidget(labelError1); + hlayoutCenterFont2->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont2); + + // 第三行 + QHBoxLayout *hlayoutCenterFont3 = new QHBoxLayout; + hlayoutCenterFont3->addSpacing(10); + // 检测中的记录:黑点2和文字2 + CircleLable *dot2 = new CircleLable(QString(""), centerFont, 6, Qt::black); + hlayoutCenterFont3->addWidget(dot2); + hlayoutCenterFont3->addSpacing(5); + MyLabel *labelError2 = new MyLabel(centerFont); + labelError2->setMinimumWidth(400); + labelError2->setMaximumWidth(600); + labelError2->setIsOriginal(true); + labelError2->setWordWrap(true); + labelError2->adjustSize(); + hlayoutCenterFont3->addWidget(labelError2); + hlayoutCenterFont3->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont3); + + // 第四行 + vlayoutCenterFont->addSpacing(30); + + // 第五行 + QHBoxLayout *hlayoutCenterFont5 = new QHBoxLayout; + hlayoutCenterFont5->addStretch(); + // 再试一次 + MyPushButton *retry = new MyPushButton(centerFont); + retry->setFixedSize(97, 36); + retry->setText(tr("retry")); + retry->setEnabled(true); + retry->setAutoRepeat(true); + hlayoutCenterFont5->addWidget(retry); + hlayoutCenterFont5->addSpacing(20); + // 返回首页 + MyPushButton *homePage = new MyPushButton(centerFont); + homePage->setFixedSize(97, 36); + homePage->setText(tr("home page")); + homePage->setEnabled(true); + homePage->setAutoRepeat(true); + hlayoutCenterFont5->addWidget(homePage); + hlayoutCenterFont5->addStretch(); + vlayoutCenterFont->addLayout(hlayoutCenterFont5); + + centerFont->setLayout(vlayoutCenterFont); + //------------ 中部布局end------------- + + QHBoxLayout *layoutLine3 = new QHBoxLayout; + layoutLine3->addStretch(); + layoutLine3->addWidget(centerFont); + layoutLine3->addStretch(); + + // 布局 + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addSpacing(40); + vlayout->addLayout(layoutLine1); + vlayout->addLayout(layoutLine2); + vlayout->addSpacing(50); + vlayout->addLayout(layoutLine3); + vlayout->addStretch(); + last->setLayout(vlayout); + + // 检测结果 + connect(this, &GhostImage::checkGhostResult, this, [=](bool result, const QString &errMsg, const QString &errTip) { + if (result) { + QIcon icon = QIcon::fromTheme("ukui-dialog-success", QIcon(":/symbos/ukui-dialog-success.png")); + resultLogo->setPixmap(icon.pixmap(QSize(20,20))); + resultLogo->setVisible(true); + // 镜像制作成功 + bigTitle->setDeplayText(tr("Ghost image creation is successful")); + QString imgPath = m_prefixDestPath + GHOST_PATH; + imgPath.replace("//", "/"); + labelError1->setDeplayText(tr("You can view it in the directory : ") + imgPath); + + dot1->setVisible(true); + dot2->setVisible(false); + labelError1->setVisible(true); + labelError2->setVisible(false); + retry->setVisible(false); + + homePage->setVisible(true); + } else { + QIcon icon = QIcon::fromTheme("dialog-error.png", QIcon(":/symbos/dialog-error.png")); + resultLogo->setPixmap(icon.pixmap(QSize(20,20))); + resultLogo->setVisible(true); + // 镜像制作失败 + bigTitle->setDeplayText(tr("Ghost image creation is failed")); + dot1->setVisible(true); + dot2->setVisible(true); + labelError1->setDeplayText(errMsg); + labelError2->setDeplayText(errTip); + retry->setVisible(true); + + homePage->setVisible(false); + } + }); + + // 再试一次 + connect(retry, &MyPushButton::clicked, this, [=](bool checked) { + Q_UNUSED(checked) + this->setCurrentIndex(GhostImagePage::GHOSTING_PAGE); + emit this->startGhost(); + }); + + // 返回首页 + connect(homePage, &MyPushButton::clicked, this, [=](bool checked) { + Q_UNUSED(checked) + this->setCurrentIndex(GhostImagePage::HOME_PAGE); + }); + + addWidget(last); +} + + diff --git a/kybackup/module/ghostimage.h b/kybackup/module/ghostimage.h new file mode 100755 index 0000000..5c72487 --- /dev/null +++ b/kybackup/module/ghostimage.h @@ -0,0 +1,77 @@ +#ifndef GHOSTIMAGE_H +#define GHOSTIMAGE_H + +#include +#include "udiskdetector.h" +#include "../backup_manager_interface.h" +#include "../backup-daemon/parsebackuplist.h" +#include "../component/backuplistwidget.h" + +class GhostImage : public QStackedWidget +{ + Q_OBJECT +public: + enum GhostImageState + { + IDEL = 0, // 空闲 + CHECKING, // 环境校验中 + GHOSTING // 备份中 + }; + + enum GhostImagePage + { + HOME_PAGE, // 首页 + SELECT_PATH_PAGE, // 选择镜像保存路径页 + CHECK_ENV_PAGE, // 环境检测页 + GHOSTING_PAGE, // 备份中页 + LAST_PAGE, // 结束页 + }; +public: + explicit GhostImage(QWidget *parent = nullptr); + virtual ~GhostImage(); + +private: + void initFirstWidget(); + void initSecondWidget(); + void initThirdWidget(); + void initForthWidget(); + void initLastWidget(); + QString createGhostImageName(const QString& backupName); + +signals: + void startCheckEnv(); + void checkEnvResult(bool result, const QString &errMsg = "", const QString &errTip = ""); + void startGhost(); + void progress(int state, int rate); + void checkGhostResult(bool result, const QString &errMsg = "", const QString &errTip = ""); + +public slots: + void on_pre_clicked(bool checked = false); + void on_next_clicked(bool checked = false); + void on_checkEnv_start(); + void on_checkEnv_end(int result); + void on_ghost_start(); + void on_checkGhost_end(int result); + void on_ghost_end(bool result); + +private: + + // U盘探测 + UdiskDetector* m_udector; + // U盘挂载路径列表 + QStringList m_udiskPaths; + // 是否本地保存镜像 + bool m_isLocal; + // 镜像状态 + int m_ghostImageState; + // 增量备份选择的备份点uuid + QString m_uuid; + // 选中的备份目标路径前缀(暂指udisk挂载路径) + QString m_prefixDestPath; + // dbus接口 + ComKylinBackupManagerInterface *m_pInterface; + // 备份点名称 + QString m_backupName; +}; + +#endif // GHOSTIMAGE_H diff --git a/kybackup/module/managebackuppointlist.cpp b/kybackup/module/managebackuppointlist.cpp old mode 100644 new mode 100755 index c82ed91..dc2e768 --- a/kybackup/module/managebackuppointlist.cpp +++ b/kybackup/module/managebackuppointlist.cpp @@ -124,7 +124,7 @@ void ManageBackupPointList::insertLines(const QList selectList = this->m_tableWidget->selectedItems(); if (selectList.isEmpty()) { MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), QObject::tr("Please select one backup to continue."), QObject::tr("Ok")); - this->reject(); + // this->reject(); return; } @@ -143,7 +143,7 @@ void SelectRestorePoint::insertLines(const QList & else prefixPath_to_device = QObject::tr("Udisk Device:") + " " + udiskName; } else { - prefixPath_to_device = QObject::tr("Local Disk"); + prefixPath_to_device = QObject::tr("Local Disk:") + " " + BACKUP_SNAPSHOTS_PATH; } setItem(indexOfRow, Column_Index::Backup_Device, prefixPath_to_device); setItem(indexOfRow, Column_Index::Backup_State, backupPoint.m_state); diff --git a/kybackup/module/selectrestorepoint.h b/kybackup/module/selectrestorepoint.h old mode 100644 new mode 100755 index ec87496..ccb3de8 --- a/kybackup/module/selectrestorepoint.h +++ b/kybackup/module/selectrestorepoint.h @@ -12,7 +12,7 @@ public: SYSTEM }; - explicit SelectRestorePoint(QWidget *parent = nullptr, BackupPointType backupType = BackupPointType::SYSTEM); + explicit SelectRestorePoint(QWidget *parent = nullptr, BackupPointType backupType = BackupPointType::SYSTEM, bool isOnlyShowLocal = false); virtual ~SelectRestorePoint(); public slots: diff --git a/kybackup/module/systembackup.cpp b/kybackup/module/systembackup.cpp index 3b3a263..29ec0b3 100755 --- a/kybackup/module/systembackup.cpp +++ b/kybackup/module/systembackup.cpp @@ -202,6 +202,7 @@ void SystemBackup::initSecondWidget() // 添加本地默认路径、移动设备目录 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")); // 如果有备份分区,则将本地默认分区备份路径放在第一个 @@ -505,6 +506,7 @@ void SystemBackup::on_checkEnv_start() backupWrapper.m_prefixDestPath = m_prefixDestPath; backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile()); backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->checkEnv(backupWrapper); } @@ -929,6 +931,7 @@ void SystemBackup::on_backup_start() backupWrapper.m_prefixDestPath = m_prefixDestPath; backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile()); backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->goBackup(backupWrapper); } @@ -1142,7 +1145,7 @@ void SystemBackup::initLastWidget() hlayoutCenterFont5->addWidget(retry); hlayoutCenterFont5->addSpacing(20); // 返回首页 - MyPushButton *homePage = new MyPushButton(last); + MyPushButton *homePage = new MyPushButton(centerFont); homePage->setFixedSize(97, 36); homePage->setText(tr("home page")); homePage->setEnabled(true); diff --git a/kybackup/module/systemrestore.cpp b/kybackup/module/systemrestore.cpp index b9fa829..1c1dfbb 100755 --- a/kybackup/module/systemrestore.cpp +++ b/kybackup/module/systemrestore.cpp @@ -490,6 +490,7 @@ void SystemRestore::on_checkEnv_start() backupWrapper.m_prefixDestPath = m_devPath; backupWrapper.m_isOtherMachine = m_isOtherMachine ? 1 : 0; backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->checkEnv(backupWrapper); } @@ -691,6 +692,7 @@ void SystemRestore::on_restore_start() backupWrapper.m_prefixDestPath = m_devPath; backupWrapper.m_isOtherMachine = m_isOtherMachine ? 1 : 0; backupWrapper.m_frontUid = getuid(); + backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_gid = getgid(); m_pInterface->goRestore(backupWrapper); } diff --git a/kybackup/resource/symbos/document-open-recent-symbolic.png b/kybackup/resource/symbos/document-open-recent-symbolic.png new file mode 100755 index 0000000000000000000000000000000000000000..e64e09929a661d886c07b09a54107d5e5d30b5b2 GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GGLLkg|>2BR0px{SO z7sn8b-lf5(c@G)z)GDwgu=stO%Bvx4!1R`_=aS$d4j+b`16Q|iV)PLZImxG1;}z;X zG3ocuqh)VAdjykH!j2ggvnH@71n=Nuh!V7{e7uE&kD-s@8G{rT_P{fd$wrP@5&0V?C-{Ez zeZwY@vgZG>+w2eK&iuO|#x><3