diff --git a/backup-daemon/backup-daemon.pro b/backup-daemon/backup-daemon.pro index fe773dd..a1ba10f 100755 --- a/backup-daemon/backup-daemon.pro +++ b/backup-daemon/backup-daemon.pro @@ -31,6 +31,7 @@ HEADERS += \ ../common/spinlock_mutex.h \ ../common/utils.h \ backupmanager_adaptor.h \ + customizesystembackupproxy.h \ databackupproxy.h \ datarestoreproxy.h \ deletebackupproxy.h \ @@ -59,6 +60,7 @@ SOURCES += \ ../common/reflect.cpp \ ../common/utils.cpp \ backupmanager_adaptor.cpp \ + customizesystembackupproxy.cpp \ databackupproxy.cpp \ datarestoreproxy.cpp \ deletebackupproxy.cpp \ diff --git a/backup-daemon/customizesystembackupproxy.cpp b/backup-daemon/customizesystembackupproxy.cpp index e69de29..92a8661 100644 --- a/backup-daemon/customizesystembackupproxy.cpp +++ b/backup-daemon/customizesystembackupproxy.cpp @@ -0,0 +1,556 @@ +#include "customizesystembackupproxy.h" + +#include +#include +#include +#include +#include +#include "../common/utils.h" +#include "../common/mydusizetool.h" +#include "mymountproxy.h" + +IMPLEMENT_DYNCREATE(CustomizeSystemBackupProxy) + +CustomizeSystemBackupProxy::CustomizeSystemBackupProxy() +{ + m_bSuccess = false; + m_isFinished = false; + m_size = 0; + m_calc = new CalcBackupSize(this); + m_isOnlyCheck = true; + m_mksquashfs = nullptr; + m_isForce = false; + + connect(this, &CustomizeSystemBackupProxy::cancel, this, &CustomizeSystemBackupProxy::cancelEx); +} + +CustomizeSystemBackupProxy::~CustomizeSystemBackupProxy() +{ + delete m_calc; + m_calc = nullptr; + + delete m_mksquashfs; + m_mksquashfs = nullptr; + + QString rm("rm -rf "); + rm += m_imgPath; + QProcess::execute(rm); +} + +/** + * @brief 环境检测 + * @return + */ +bool CustomizeSystemBackupProxy::checkEnvEx() +{ + qDebug() << "CustomizeSystemBackupProxy::checkEnv invoke begin"; + + // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载 + MyMountProxy mountProxy; + if ( MountResult::MOUNTED != mountProxy.mountBackupPartition() ) { + 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 (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(); + + qDebug() << "CustomizeSystemBackupProxy::checkEnv invoke end"; + return true; +} + +/** + * @brief 执行备份逻辑 + */ +void CustomizeSystemBackupProxy::doWorkEx() +{ + qDebug() << "CustomizeSystemBackupProxy::doWorkEx invoke begin"; + + m_isOnlyCheck = false; + // 环境检测 + checkEnvEx(); + + qDebug() << "CustomizeSystemBackupProxy::doWorkEx invoke end"; +} + +void CustomizeSystemBackupProxy::cancelEx() +{ + m_bCancel = true; + if (!m_isFinished) { + emit this->checkResult(int(BackupResult::START_CANCEL)); + + if (m_calc) + m_calc->stop(); + if (m_mksquashfs) + m_mksquashfs->stop(); + + QProcess::execute("sync"); + Utils::wait(5); + deleteFailedData(); + emit this->checkResult(int(BackupResult::CANCEL_SUCCESS)); + } +} + +/** + * @brief 失败则删除相应数据 + */ +void CustomizeSystemBackupProxy::deleteFailedData() +{ + if (m_curUuid.isEmpty()) + return; + + // 1、删除备份目录 + QString destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid; + destPath.replace("//", "/"); + QStringList args; + args << "-rf"; + args << destPath; + QProcess::execute("rm", args); + + // 2、删除xml文件中的备份项 + QString xmlPath = m_backupWrapper.m_prefixDestPath + 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 校验剩余空间是否满足备份 + */ +void CustomizeSystemBackupProxy::checkFreeCapacity(qint64 itotalSize) +{ + qDebug() << "CustomizeSystemBackupProxy::checkFreeCapacity invoke begin"; + + // 如果是取消了操作,则不再发送其它信息 + if (m_bCancel) + return ; + + // 拔掉U盘的场景 + if (m_isForce) { + emit this->checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return ; + } + + // 1、计算待备份数据的大小 + m_size = itotalSize; + // 备份过程中会有一些临时文件产生,会占用一部分空间,故我们预留500M的空间 + itotalSize += 500 * MB; + + // 2、计算备份分区剩余空间大小 + QString backupPath(m_backupWrapper.m_prefixDestPath); + backupPath.replace("//", "/"); + QStorageInfo backupDisk(backupPath); + qint64 freeSize = backupDisk.bytesAvailable(); + + // 3、校验空间是否足够 + if (itotalSize > freeSize) { + emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH)); + } else { + emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); + } + + 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 ; + } + } + } + + // 5、开始制作img或开始备份 + if (m_imgPath.isEmpty()) { + doBackup(); + } else { + doMksqushfs(); + } + + qDebug() << "CustomizeSystemBackupProxy::checkFreeCapacity invoke end"; +} + +/** + * @brief 计算备份所需空间大小 + * @return 计算备份所需空间大小,单位字节 + */ +void CustomizeSystemBackupProxy::calcSizeForBackup() +{ + // 拼接备份源路径和目标路径,测试所需备份空间大小;目标路径为一虚拟路径 + QString srcPath = Utils::getSysRootPath(); + srcPath += "/"; + srcPath.replace("//", "/"); + QString destPath = Utils::getSysRootPath(); + destPath += CHECK_PATH; + destPath.replace("//", "/"); + Utils::mkpath(destPath); + + QStringList args = getRsyncArgs(CustomizeSystemBackupScene::TRY_SYSTEM_BACKUP); + args << srcPath; + args << destPath; + + connect(m_calc, &CalcBackupSize::finished, this, &CustomizeSystemBackupProxy::checkFreeCapacity); + m_calc->start(args, false); +} + +/** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return rsync的参数信息 + */ +QStringList CustomizeSystemBackupProxy::getRsyncArgs(CustomizeSystemBackupScene scene) +{ + QStringList args; + QStringList excludes; + + switch (scene) { + case CustomizeSystemBackupScene::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"; + args << "--ignore-missing-args"; + break ; + case CustomizeSystemBackupScene::MKSQUASHFS : + Utils::excludeFstabBindPath(excludes); + // --exclude=排除路径设置 + for (QString item : m_backupWrapper.m_backupExcludePaths) { + if (excludes.contains(item)) + continue; + if (item.endsWith("/*")) { + item.replace("/*", ""); + } + + args << "-e" << item; + } + args << "-e" << m_imgPath; + return args; + default: + return args; + } + + // --exclude=排除路径设置 + for (const QString & item : m_backupWrapper.m_backupExcludePaths) { + args << QString("--exclude=%1").arg(item); + } + + return args; +} + +/** + * @brief mksqushfs + */ +void CustomizeSystemBackupProxy::doMksqushfs() +{ + qDebug() << "CustomizeSystemBackupProxy::doMksqushfs invoke begin"; + + m_mksquashfs = new MkSquashFSProcess(this); + connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &CustomizeSystemBackupProxy::progress); + connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [=](bool result) { + // 如果是取消了操作,则不再发送其它信息 + if (m_bCancel) + return ; + + if (result && !m_isForce) { + // 开始备份 + doBackup(); + } else { + m_isFinished = true; + emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL)); + } + }); + Utils::mkpath(m_imgPath); + + QString srcPath = Utils::getSysRootPath(); + srcPath += "/"; + srcPath.replace("//", "/"); + QString dstImg = m_imgPath + "/" + UDISK_MKSQUASHFS_IMG_NAME; + QStringList args; + args << srcPath << dstImg; + args.append(getRsyncArgs(CustomizeSystemBackupScene::MKSQUASHFS)); + + if (m_mksquashfs->start(args)) { + emit checkResult(int(BackupResult::MKSQUASHFS_START_SUCCESS)); + } else { + emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL)); + } + + 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,准备失败 + */ +bool CustomizeSystemBackupProxy::doPrepare() +{ + qDebug() << "CustomizeSystemBackupProxy::doPrepare invoke begin"; + + m_bSuccess = false; + + // 1、设置当前备份的Uuid + m_curUuid += Utils::createUuid(); + + // 2、准备备份目录及文件 + m_destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/data"; + m_destPath.replace("//", "/"); + if (!Utils::mkpath(m_destPath)) { + qCritical() << QString("mkdir %1 failed !").arg(m_destPath) ; + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + QString userFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + PATHS_USER_FILE; + userFile.replace("//", "/"); + if (!Utils::writeFileByLines(userFile, m_backupWrapper.m_backupPaths)) { + qCritical() << QString("create file %1 failed !").arg(userFile); + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + QString excludeUserFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + EXCLUDE_PATHS_USER_FILE; + excludeUserFile.replace("//", "/"); + if (!Utils::writeFileByLines(excludeUserFile, m_backupWrapper.m_backupExcludePaths)) { + qCritical() << QString("create file %1 failed !").arg(excludeUserFile); + emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); + return false; + } + + // 3、记录/backup/snapshots/backuplist.xml文件 + if (!recordBackupPoint()) { + qCritical() << "add or update item to backuplist.xml failed !"; + return false; + } + + qDebug() << "CustomizeSystemBackupProxy::doPrepare invoke end"; + return true; +} + +/** + * @brief 记录/backup/snapshots/backuplist.xml文件 + * @return true-成功;false-失败 + */ +bool CustomizeSystemBackupProxy::recordBackupPoint() +{ + m_backupPoint.m_backupName = m_backupWrapper.m_backupName; + m_backupPoint.m_uuid = m_curUuid; + m_backupPoint.m_iPosition = m_backupWrapper.m_iPosition; + m_backupPoint.m_type = m_backupWrapper.m_type; + m_backupPoint.m_size = Utils::StringBySize(m_size); + m_backupPoint.m_time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); + m_backupPoint.m_state = BACKUP_PARSE_STATE_FAIL_STRTING; + m_backupPoint.m_os = SystemInfo::m_os; + m_backupPoint.m_arch = SystemInfo::m_arch; + m_backupPoint.m_archdetect = SystemInfo::m_archDetect; + QString xmlPath = m_backupWrapper.m_prefixDestPath + BACKUP_XML_PATH; + xmlPath.replace("//", "/"); + ParseBackupList parse(xmlPath); + if (m_backupWrapper.m_uuid.isEmpty() || !m_backupWrapper.m_bIncrement) { + if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) { + emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL)); + return false; + } + } else { + if (parse.updateItem(m_backupPoint) != ParseBackupList::SUCCESS) { + emit checkResult(int(BackupResult::WRITE_STORAGEINFO_UPDATE_ITEM_FAIL)); + return false; + } + } + + return true; +} + +/** + * @brief 备份系统 + * @return 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; + ret = kysec_getstatus(); + if (ret > 0) { + QString seFilePath(dstDir + "/.exectl"); + QFile file(seFilePath); + file.open(QIODevice::WriteOnly); + file.close(); + } +} + +/** + * @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 115f4d4..2364ca0 100644 --- a/backup-daemon/customizesystembackupproxy.h +++ b/backup-daemon/customizesystembackupproxy.h @@ -1,4 +1,118 @@ #ifndef CUSTOMIZESYSTEMBACKUPPROXY_H #define CUSTOMIZESYSTEMBACKUPPROXY_H +#include "workerfactory.h" +#include "myprocess/calcbackupsize.h" +#include "myprocess/mksquashfsprocess.h" +#include "parsebackuplist.h" + +class CustomizeSystemBackupProxy : public Worker +{ + Q_OBJECT + DECLARE_DYNCREATE(CustomizeSystemBackupProxy) + +public: + // 系统备份的几种场景 + enum CustomizeSystemBackupScene { + SYSTEM_BACKUP, // 系统备份 + TRY_SYSTEM_BACKUP, // 测试系统备份,可用于计算备份传输数据大小 + MKSQUASHFS, // 生成img文件 + }; + + explicit CustomizeSystemBackupProxy(); + virtual ~CustomizeSystemBackupProxy(); + +public: + // 环境检测 + virtual bool checkEnvEx(); + + // 任务处理 + virtual void doWorkEx(); + +signals: + +private slots: + // 校验剩余空间是否满足备份 + void checkFreeCapacity(qint64 itotalSize); + + // mksqushfs + void doMksqushfs(); + + // 备份 + 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(); + + /** + * @brief 根据场景获取rsync命令参数 + * @param scene,场景 + * @return 组装好的rsync的参数信息 + */ + QStringList getRsyncArgs(CustomizeSystemBackupScene scene); + + /** + * @brief 记录/backup/snapshots/backuplist.xml文件 + * @return true-成功;false-失败 + */ + bool recordBackupPoint(); + + // 备份准备 + bool doPrepare(); + + // 备份系统 + bool backupSystem(); + + // 备份img文件 + bool backupImg(); + + bool backup(const QStringList &args); + + void do_kylin_security(const QString& dstDir); + + // 失败则删除相应数据 + void deleteFailedData(); + + // 计算备份空间大小的进程 + CalcBackupSize *m_calc; + // 压缩进程 + MkSquashFSProcess *m_mksquashfs; + + // 是否完成 + bool m_isFinished; + // 是否备份成功 + bool m_bSuccess; + // 当前备份uuid + QString m_curUuid; + // 当前备份目标目录 + QString m_destPath; + // 当前备份所需空间大小 + qint64 m_size; + // 当前备份节点 + ParseBackupList::BackupPoint m_backupPoint; + // 是否只是检测 + bool m_isOnlyCheck; + // img文件存放路径 + QString m_imgPath; + // 强制结束标志(stop后没反应的情况,系统处于睡眠状态) + bool m_isForce; +}; + #endif // CUSTOMIZESYSTEMBACKUPPROXY_H diff --git a/backup-daemon/parsebackuplist.h b/backup-daemon/parsebackuplist.h index 37db3bc..1d4f9ee 100755 --- a/backup-daemon/parsebackuplist.h +++ b/backup-daemon/parsebackuplist.h @@ -25,7 +25,7 @@ public: QString m_backupName; // 备份时间 QString m_time; - // 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份(仅用于业务场景标记,不用于持久化记录) + // 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份(仅用于业务场景标记,不用于持久化记录);3-自定义路径备份 int m_iPosition = -1; // 操作类型,如:系统备份, 系统还原 int m_type = -1; diff --git a/backup-daemon/udisksystembackupproxy.cpp b/backup-daemon/udisksystembackupproxy.cpp index 139b0cd..02fe69b 100755 --- a/backup-daemon/udisksystembackupproxy.cpp +++ b/backup-daemon/udisksystembackupproxy.cpp @@ -1,6 +1,5 @@ #include "udisksystembackupproxy.h" -#include "systembackupproxy.h" #include #include #include diff --git a/backup-daemon/workerfactory.cpp b/backup-daemon/workerfactory.cpp index 9b5fe82..6f08a4b 100755 --- a/backup-daemon/workerfactory.cpp +++ b/backup-daemon/workerfactory.cpp @@ -54,6 +54,8 @@ Worker * WorkerFactory::createWorker(int type, int position) case BackupType::INC_BACKUP_SYSTEM: if (BackupPosition::UDISK == position) { className = "UDiskSystemBackupProxy"; + } else if (BackupPosition::CUSTOMIZE == position) { + className = "CustomizeSystemBackupProxy"; } else { className = "SystemBackupProxy"; } @@ -62,6 +64,8 @@ Worker * WorkerFactory::createWorker(int type, int position) case BackupType::RESTORE_SYSTEM_WITH_DATA: if (BackupPosition::UDISK == position) { className = "UDiskSystemRestoreProxy"; + } else if (BackupPosition::CUSTOMIZE == position) { + className = "CustomizeSystemRestoreProxy"; } else { className = "SystemRestoreProxy"; } @@ -70,6 +74,8 @@ Worker * WorkerFactory::createWorker(int type, int position) case BackupType::INC_BACKUP_DATA: if (BackupPosition::UDISK == position) { className = "UDiskDataBackupProxy"; + } else if (BackupPosition::CUSTOMIZE == position) { + className = "CustomizeDataBackupProxy"; } else { className = "DataBackupProxy"; } @@ -77,6 +83,8 @@ 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"; } @@ -87,6 +95,8 @@ Worker * WorkerFactory::createWorker(int type, int position) case BackupType::GHOST_IMAGE: if (BackupPosition::UDISK == position) { className = "UDiskGhostImageProxy"; + } else if (BackupPosition::CUSTOMIZE == position) { + className = "CustomizeGhostImageProxy"; } else { className = "GhostImageProxy"; } diff --git a/common/mydefine.h b/common/mydefine.h index e821015..c19e9c5 100755 --- a/common/mydefine.h +++ b/common/mydefine.h @@ -90,7 +90,7 @@ enum BackupType { struct BackupWrapper { // 操作类型,如:系统备份, 系统还原 int m_type = -1; - // 本地备份还是U盘备份: 0-本地备份;1-移动设备备份 + // 本地备份还是U盘备份: 0-本地备份;1-移动设备备份;2-异机备份;3-自定义路径 int m_iPosition = -1; // 备份名称,用于识别备份的,默认是时间 QString m_comment; @@ -163,7 +163,8 @@ enum BackupPosition { LOCAL, // 本地磁盘 UDISK, // 移动设备 - OTHER // 移动设备中的其它机器的备份点 + OTHER, // 移动设备中的其它机器的备份点 + CUSTOMIZE, // 自定义路径 }; /** diff --git a/common/utils.cpp b/common/utils.cpp index f55c51e..d64c9f9 100755 --- a/common/utils.cpp +++ b/common/utils.cpp @@ -457,6 +457,7 @@ bool Utils::generateExcludePathsFile() in << "/var/lib/kmre" << END_LINE; in << "/var/lib/udisks2" << END_LINE; in << "/var/log" << END_LINE; + in << "*/backup/snapshots" << END_LINE; // 系统安装后有的会将/data/home /data/root挂载到的/home /root上,实际文件是存放在/data/home /data/root下面,为了统一标准保留/home /root排除/data/home /data/root QStringList excludes; @@ -511,6 +512,7 @@ QStringList Utils::getFromExcludePathsFile() list << "/var/lib/kmre"; list << "/var/lib/udisks2"; list << "/var/log"; + list << "*/backup/snapshots"; // 系统安装后有的会将/data/home /data/root挂载到的/home /root上,实际文件是存放在/data/home /data/root下面 QStringList excludes; diff --git a/kybackup/component/filefilterproxymodelforbackup.cpp b/kybackup/component/filefilterproxymodelforbackup.cpp index e69de29..bfd4e40 100644 --- a/kybackup/component/filefilterproxymodelforbackup.cpp +++ b/kybackup/component/filefilterproxymodelforbackup.cpp @@ -0,0 +1,40 @@ +#include "filefilterproxymodelforbackup.h" +#include +#include "../../common/mydefine.h" + +FileFilterProxyModeForBackup::FileFilterProxyModeForBackup(QObject *parent) : + QSortFilterProxyModel(parent) +{} + +FileFilterProxyModeForBackup::~FileFilterProxyModeForBackup() +{} + +bool FileFilterProxyModeForBackup::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent); + QFileSystemModel* fileModel = qobject_cast(sourceModel()); + + if (fileModel != nullptr && fileModel->isDir(index0)) { + QString fileName = fileModel->fileName(index0); + // 防命令注入 + // 1、形如:mkdir '`id&>id_bak_test.txt`'中的文件夹名称 + if (fileName.contains(QRegularExpression(".*`.*`.*"))) + return false; + // 2、形如:$()的文件夹名称 + if (fileName.contains(QRegularExpression(".*\\$\\(.*\\).*"))) + return false; + // 3、形如:${}的文件夹名称 + if (fileName.contains(QRegularExpression(".*\\$\\{.*\\}.*"))) + return false; + // 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号 + if (fileName.contains(QRegularExpression("[;&|]+"))) + return false; + + QString filePath = fileModel->filePath(index0); + + return !(filePath.endsWith(BACKUP_SNAPSHOTS_PATH) || filePath == BACKUP_PATH); + } else { + return false; + } +} + diff --git a/kybackup/component/filefilterproxymodelforbackup.h b/kybackup/component/filefilterproxymodelforbackup.h index ba742b3..74c67d8 100644 --- a/kybackup/component/filefilterproxymodelforbackup.h +++ b/kybackup/component/filefilterproxymodelforbackup.h @@ -1,4 +1,17 @@ #ifndef FILEFILTERPROXYMODELFORBACKUP_H #define FILEFILTERPROXYMODELFORBACKUP_H +#include + +class FileFilterProxyModeForBackup : public QSortFilterProxyModel +{ + Q_OBJECT +public: + FileFilterProxyModeForBackup(QObject *parent = nullptr); + ~FileFilterProxyModeForBackup(); + +protected: + virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; +}; + #endif // FILEFILTERPROXYMODELFORBACKUP_H diff --git a/kybackup/component/myfileselect.cpp b/kybackup/component/myfileselect.cpp index aa7004f..70aefed 100755 --- a/kybackup/component/myfileselect.cpp +++ b/kybackup/component/myfileselect.cpp @@ -1,6 +1,7 @@ #include "myfileselect.h" #include #include +#include "filefilterproxymodelforbackup.h" MyFileSelect::MyFileSelect(QWidget* parent) : QFileDialog(parent) @@ -9,6 +10,9 @@ MyFileSelect::MyFileSelect(QWidget* parent) : this->setFileMode(QFileDialog::ExistingFiles); this->setFilter(QDir::System | QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + FileFilterProxyModeForBackup *proxy = new FileFilterProxyModeForBackup; + this->setProxyModel(proxy); + QDialogButtonBox* button = this->findChild("buttonBox"); disconnect(button, SIGNAL(accepted()), this, SLOT(accept())); connect(button, SIGNAL(accepted()), this, SLOT(goAccept())); diff --git a/kybackup/kybackup.pro b/kybackup/kybackup.pro index f040f01..babf1b4 100755 --- a/kybackup/kybackup.pro +++ b/kybackup/kybackup.pro @@ -41,6 +41,7 @@ HEADERS += \ component/backuplistwidget.h \ component/circlelabel.h \ component/clicklabel.h \ + component/filefilterproxymodelforbackup.h \ component/hoverwidget.h \ component/imageutil.h \ component/linelabel.h \ @@ -88,6 +89,7 @@ SOURCES += \ component/backuplistwidget.cpp \ component/circlelabel.cpp \ component/clicklabel.cpp \ + component/filefilterproxymodelforbackup.cpp \ component/hoverwidget.cpp \ component/imageutil.cpp \ component/linelabel.cpp \ diff --git a/kybackup/maindialog.cpp b/kybackup/maindialog.cpp index 66aa2f7..c943c88 100755 --- a/kybackup/maindialog.cpp +++ b/kybackup/maindialog.cpp @@ -288,13 +288,12 @@ void MainDialog::mountBackupPartition() exit(1); } else if (int(MountResult::CANNOT_GET_BACKUPUUID) == reply.value()) { // 没有找到备份分区,只能备份到移动设备中 - MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Information"), - QObject::tr("Please check if the backup partition exists which can be created when you install the Operating System."), - QObject::tr("Ok")); - - // GlobelBackupInfo::inst().setHasBackupPartition(false); - // 此时还未进入应用的事件循环中,故不能使用qApp->quit(); - exit(1); +// MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Information"), +// QObject::tr("Please check if the backup partition exists which can be created when you install the Operating System."), +// QObject::tr("Ok")); +// // 此时还未进入应用的事件循环中,故不能使用qApp->quit(); +// exit(1); + GlobelBackupInfo::inst().setHasBackupPartition(false); } else if (int(MountResult::MOUNTED) != reply.value()) { // 挂载备份分区失败 MessageBoxUtils::QMESSAGE_BOX_CRITICAL(this, QObject::tr("Warning"), diff --git a/kybackup/module/systembackup.cpp b/kybackup/module/systembackup.cpp index 1ec9cc7..455aa44 100755 --- a/kybackup/module/systembackup.cpp +++ b/kybackup/module/systembackup.cpp @@ -5,10 +5,13 @@ #include #include #include +#include +#include #include #include "../component/clicklabel.h" #include "../component/circlelabel.h" +#include "../component/filefilterproxymodelforbackup.h" #include "../component/myiconlabel.h" #include "../component/mylabel.h" #include "../component/mylineedit.h" @@ -246,8 +249,49 @@ void SystemBackup::initSecondWidget() 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); vlayout->addLayout(hlayoutLine2); vlayout->addSpacing(30); @@ -272,9 +316,14 @@ void SystemBackup::initSecondWidget() 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); + 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(); }); @@ -561,7 +610,7 @@ void SystemBackup::on_checkEnv_start() BackupWrapper backupWrapper; backupWrapper.m_backupName = m_backupName; backupWrapper.m_type = BackupType::BACKUP_SYSTEM; - backupWrapper.m_iPosition = m_isLocal ? BackupPosition::LOCAL : BackupPosition::UDISK; + backupWrapper.m_iPosition = m_isLocal ? (this->m_prefixDestPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::CUSTOMIZE) : BackupPosition::UDISK; QString backupPath = Utils::getSysRootPath(); backupPath += "/"; backupPath.replace("//", "/"); @@ -1062,7 +1111,7 @@ void SystemBackup::on_backup_start() BackupWrapper backupWrapper; backupWrapper.m_backupName = m_backupName; backupWrapper.m_type = BackupType::BACKUP_SYSTEM; - backupWrapper.m_iPosition = m_isLocal ? BackupPosition::LOCAL : BackupPosition::UDISK; + backupWrapper.m_iPosition = m_isLocal ? (this->m_prefixDestPath.isEmpty() ? BackupPosition::LOCAL : BackupPosition::CUSTOMIZE) : BackupPosition::UDISK; QString backupPath = Utils::getSysRootPath(); backupPath += "/"; backupPath.replace("//", "/");