#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; connect(this, &CustomizeSystemBackupProxy::cancel, this, &CustomizeSystemBackupProxy::cancelEx); } CustomizeSystemBackupProxy::~CustomizeSystemBackupProxy() { delete m_calc; m_calc = nullptr; delete m_mksquashfs; m_mksquashfs = nullptr; // 如果备份失败,则删除备份数据 if (!m_bSuccess) { deleteFailedData(); } } /** * @brief 环境检测 * @return */ bool CustomizeSystemBackupProxy::checkEnvEx() { qDebug() << "CustomizeSystemBackupProxy::checkEnv invoke begin"; // 1、检查/backup分区是否挂载上(不管是本地磁盘还是u盘设备,都得保证/backup挂载上); 若没挂载,挂载。 MyMountProxy mountProxy; MountResult result = mountProxy.mountBackupPartition(); // 无备份分区 if (MountResult::CANNOT_GET_BACKUPUUID == result) { qInfo() << "There is no backup partition!"; QString snapshotsPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH; snapshotsPath.replace("//", "/"); Utils::mkpath(snapshotsPath); Utils::generateExcludePathsFile(); } else if (MountResult::MOUNTED != result) { emit checkResult(int(BackupResult::BACKUP_PARTITION_MOUNT_FAIL)); return false; } // 防命令注入 // 1、形如:mkdir '`id&>id_bak_test.txt`'中的文件夹名称 // 2、形如:$()的文件夹名称 // 3、形如:${}的文件夹名称 // 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号 if ( m_backupWrapper.m_prefixDestPath.contains(QRegularExpression(".*`.*`.*")) || m_backupWrapper.m_prefixDestPath.contains(QRegularExpression(".*\\$\\(.*\\).*")) || m_backupWrapper.m_prefixDestPath.contains(QRegularExpression(".*\\$\\{.*\\}.*")) || m_backupWrapper.m_prefixDestPath.contains(QRegularExpression("[;&|]+"))) { qCritical () << "The path to backup contains ``,$(),${},;&|,etc"; emit checkResult(int(BackupResult::DU_ERR)); return false; } if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) { qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); Utils::mkpath(m_backupWrapper.m_prefixDestPath); } // 因为有可能是选择的移动设备,故进行校验移动设备情况:空间大小、文件格式、挂载模式等 QString backupPath(m_backupWrapper.m_prefixDestPath); backupPath.replace("//", "/"); QStorageInfo udisk(backupPath); QString udisk_type = udisk.fileSystemType(); qDebug() << "udisk's filesystemtype is " << udisk_type; if (udisk_type == "vfat") { qCritical() << m_backupWrapper.m_prefixDestPath + " udisk's filesystemtype is vfat"; emit checkResult(int(BackupResult::UDISK_FILESYSTEM_TYPE_IS_VFAT)); return false; } if (udisk.isReadOnly()) { // 只读挂载的U盘 qCritical() << QString("udisk(%s) is readonly filesystem").arg(m_backupWrapper.m_prefixDestPath); emit checkResult(int(BackupResult::UDISK_FILESYSTEM_IS_READONLY)); return false; } // 2、计算备份大小 calcSizeForBackup(); qDebug() << "CustomizeSystemBackupProxy::checkEnv invoke end"; return true; } /** * @brief 执行备份逻辑 */ void CustomizeSystemBackupProxy::doWorkEx() { qDebug() << "CustomizeSystemBackupProxy::doWorkEx invoke begin"; m_isOnlyCheck = false; // 环境检测 checkEnvEx(); qDebug() << "CustomizeSystemBackupProxy::doWorkEx invoke end"; } void CustomizeSystemBackupProxy::cancelEx() { m_bCancel = true; if (!m_isFinished) { emit this->checkResult(int(BackupResult::START_CANCEL)); if (m_calc) m_calc->stop(); if (m_mksquashfs) m_mksquashfs->stop(); QProcess::execute("sync"); Utils::wait(5); deleteFailedData(); emit this->checkResult(int(BackupResult::CANCEL_SUCCESS)); } } /** * @brief 失败则删除相应数据 */ void CustomizeSystemBackupProxy::deleteFailedData() { if (m_curUuid.isEmpty()) return; // 1、删除备份目录 QString destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid; destPath.replace("//", "/"); QStringList args; args << "-rf"; args << destPath; QProcess::execute("rm", args); // 2、删除xml文件中的备份项 QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; xmlPath.replace("//", "/"); ParseBackupList parse(xmlPath); parse.deleteItem(m_curUuid); } /** * @brief 校验剩余空间是否满足备份 */ void CustomizeSystemBackupProxy::checkFreeCapacity(qint64 itotalSize) { qDebug() << "CustomizeSystemBackupProxy::checkFreeCapacity invoke begin"; // 如果是取消了操作,则不再发送其它信息 if (m_bCancel) return ; // 1、计算待备份数据的大小 m_size = itotalSize; // 备份过程中会有一些临时文件产生,会占用一部分空间,故我们预留500M的空间 itotalSize += 500 * MB; // 判断是否需要先压缩成img文件,压缩后一般小于原大小的70% itotalSize = itotalSize * 7 / 10; // 2、计算备份分区剩余空间大小 QString backupPath(m_backupWrapper.m_prefixDestPath); backupPath.replace("//", "/"); QStorageInfo backupDisk(backupPath); qint64 freeSize = backupDisk.bytesAvailable(); // 3、校验空间是否足够 if (itotalSize > freeSize) { emit checkResult(int(BackupResult::BACKUP_CAPACITY_IS_NOT_ENOUGH)); return ; } else { emit checkResult(int(BackupResult::CHECK_ENV_SUCCESS)); } if (m_isOnlyCheck) return ; // 4、准备 if (!doPrepare()) return ; // 5、开始制作img doMksqushfs(); qDebug() << "CustomizeSystemBackupProxy::checkFreeCapacity invoke end"; } /** * @brief 计算备份所需空间大小 * @return 计算备份所需空间大小,单位字节 */ void CustomizeSystemBackupProxy::calcSizeForBackup() { // 拼接备份源路径和目标路径,测试所需备份空间大小;目标路径为一虚拟路径 QString srcPath = Utils::getSysRootPath(); srcPath += "/"; srcPath.replace("//", "/"); QString destPath = Utils::getSysRootPath(); destPath += CHECK_PATH; destPath.replace("//", "/"); Utils::mkpath(destPath); QStringList args = getRsyncArgs(CustomizeSystemBackupScene::TRY_SYSTEM_BACKUP); args << srcPath; args << destPath; connect(m_calc, &CalcBackupSize::finished, this, &CustomizeSystemBackupProxy::checkFreeCapacity); m_calc->start(args, false); } /** * @brief 根据场景获取rsync命令参数 * @param scene,场景 * @return rsync的参数信息 */ QStringList CustomizeSystemBackupProxy::getRsyncArgs(CustomizeSystemBackupScene scene) { QStringList args; QStringList excludes; switch (scene) { case CustomizeSystemBackupScene::TRY_SYSTEM_BACKUP : args << "-aAHXrn"; args << "--stats"; args << "--ignore-missing-args"; break ; case CustomizeSystemBackupScene::MKSQUASHFS : // (在mksquashfs时排除bind挂载的任意一方时,都备份不上) Utils::excludeFstabBindPath(excludes); // --exclude=排除路径设置 for (QString item : m_backupWrapper.m_backupExcludePaths) { if (excludes.contains(item)) continue; if (item.endsWith("/*")) { item.replace("/*", ""); } args << "-e" << item; } return args; default: return args; } // --exclude=排除路径设置 for (const QString & item : m_backupWrapper.m_backupExcludePaths) { args << QString("--exclude=%1").arg(item); } return args; } /** * @brief mksqushfs */ void CustomizeSystemBackupProxy::doMksqushfs() { qDebug() << "CustomizeSystemBackupProxy::doMksqushfs invoke begin"; m_mksquashfs = new MkSquashFSProcess(this); connect(m_mksquashfs, &MkSquashFSProcess::progress, this, &CustomizeSystemBackupProxy::progress); connect(m_mksquashfs, &MkSquashFSProcess::finished, this, [=](bool result) { // 如果是取消了操作,则不再发送其它信息 if (m_bCancel) return ; m_isFinished = true; if (result) { m_backupPoint.m_state = BACKUP_PARSE_STATE_SUCCESS_STRTING; m_backupPoint.m_size = Utils::StringBySize(Utils::getDirOrFileSize(m_destPath)); QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; xmlPath.replace("//", "/"); ParseBackupList parse(xmlPath); if (ParseBackupList::ParseResult::SUCCESS != parse.updateItem(m_backupPoint)) { qCritical() << "update backuplist.xml error in sendBackupResult"; result = false; } else { // Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ","+ m_backupWrapper.m_note + "," + m_backupPoint.m_size+ "," + QString::number(m_backupWrapper.m_frontUid)); Utils::writeBackupLog(m_backupPoint.m_time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + "," + m_backupWrapper.m_note + "," + m_backupPoint.m_size + ",," + m_backupWrapper.m_backupName); Utils::update_backup_unique_settings(m_curUuid, m_backupPoint.m_backupName); m_bSuccess = true; } } emit this->workResult(result); }); QString srcPath = Utils::getSysRootPath(); srcPath += "/"; srcPath.replace("//", "/"); QString dstImg = m_destPath + "/" + UDISK_MKSQUASHFS_IMG_NAME; QStringList args; args << srcPath << dstImg; args.append(getRsyncArgs(CustomizeSystemBackupScene::MKSQUASHFS)); if (m_mksquashfs->start(args)) { } else { emit checkResult(int(BackupResult::MKSQUASHFS_DO_FAIL)); } qDebug() << "CustomizeSystemBackupProxy::doMksqushfs invoke end"; } /** * @brief 备份准备 * @return true,准备成功;false,准备失败 */ bool CustomizeSystemBackupProxy::doPrepare() { qDebug() << "CustomizeSystemBackupProxy::doPrepare invoke begin"; m_bSuccess = false; // 1、设置当前备份的Uuid m_curUuid += Utils::createUuid(); // 2、准备备份目录及文件 m_destPath = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/data"; m_destPath.replace("//", "/"); if (!Utils::mkpath(m_destPath)) { qCritical() << QString("mkdir %1 failed !").arg(m_destPath) ; emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); return false; } QString userFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + PATHS_USER_FILE; userFile.replace("//", "/"); if (!Utils::writeFileByLines(userFile, m_backupWrapper.m_backupPaths)) { qCritical() << QString("create file %1 failed !").arg(userFile); emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); return false; } QString excludeUserFile = m_backupWrapper.m_prefixDestPath + BACKUP_SNAPSHOTS_PATH + "/" + m_curUuid + "/" + EXCLUDE_PATHS_USER_FILE; excludeUserFile.replace("//", "/"); if (!Utils::writeFileByLines(excludeUserFile, m_backupWrapper.m_backupExcludePaths)) { qCritical() << QString("create file %1 failed !").arg(excludeUserFile); emit checkResult(int(BackupResult::WRITE_BACKUP_PATHS_TO_USER_FAILED)); return false; } // 3、记录/backup/snapshots/backuplist.xml文件 if (!recordBackupPoint()) { qCritical() << "add or update item to backuplist.xml failed !"; return false; } qDebug() << "CustomizeSystemBackupProxy::doPrepare invoke end"; return true; } /** * @brief 记录/backup/snapshots/backuplist.xml文件 * @return true-成功;false-失败 */ bool CustomizeSystemBackupProxy::recordBackupPoint() { m_backupPoint.m_backupName = m_backupWrapper.m_backupName; m_backupPoint.m_uuid = m_curUuid; m_backupPoint.m_iPosition = m_backupWrapper.m_iPosition; m_backupPoint.m_type = m_backupWrapper.m_type; m_backupPoint.m_size = Utils::StringBySize(m_size); m_backupPoint.m_time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); m_backupPoint.m_state = BACKUP_PARSE_STATE_FAIL_STRTING; m_backupPoint.m_os = SystemInfo::m_os; m_backupPoint.m_arch = SystemInfo::m_arch; m_backupPoint.m_archdetect = SystemInfo::m_archDetect; m_backupPoint.m_path = m_backupWrapper.m_prefixDestPath; QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; xmlPath.replace("//", "/"); ParseBackupList parse(xmlPath); if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) { emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL)); return false; } return true; }