!1 close-cd:#I7DFN9 【备份还原】【设计走查】切换至平板模式时,窗口没有最大化 #I7DF5I 【备份还原】【设计走查】窗口选项菜单未翻译成中文 #I72LFX 【备份还原】菜单-退出按钮显示Exit,预期显示退出 #I72P8K 【备份还原】gurb备份/还原系统时,显示乱码

Merge pull request !1 from 李德成/openkylin/yangtze
This commit is contained in:
李德成 2023-09-05 05:54:53 +00:00 committed by Gitee
commit 282af2ad3d
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
57 changed files with 5659 additions and 959 deletions

View File

@ -1,5 +1,11 @@
[Desktop Entry] [Desktop Entry]
Name=backup-daemon Name=backup-daemon
Name[zh_CN]=备份还原守护进程
Name[zh_HK]=備份還原守護進程
Name[bo_CN]=གྲབས་ཉར་སོར་ལོག་ལྟ་སྲུང་འཕེལ་རིམ་
Name[mn]=ᠨᠥᠭᠡᠴᠡ ᠵᠢ ᠡᠬᠡᠬᠦᠯᠵᠤ ᠰᠠᠬᠢᠬᠤ ᠠᠬᠢᠴᠠ
Type=Application
Comment=The daemon process of kybackup. Comment=The daemon process of kybackup.
Exec=/usr/bin/backup-daemon %U
Icon=yhkylin-backup-tools Icon=yhkylin-backup-tools
NoDisplay=true NoDisplay=true

View File

@ -2,6 +2,7 @@
#include <QStorageInfo> #include <QStorageInfo>
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QRegularExpression>
#include "../common/utils.h" #include "../common/utils.h"
#include "../common/mydusizetool.h" #include "../common/mydusizetool.h"
#include "mymountproxy.h" #include "mymountproxy.h"
@ -54,6 +55,21 @@ bool CustomizeDataBackupProxy::checkEnvEx()
return false; 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)) { if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) {
qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath);
Utils::mkpath(m_backupWrapper.m_prefixDestPath); Utils::mkpath(m_backupWrapper.m_prefixDestPath);

View File

@ -4,6 +4,7 @@
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
#include <QRegularExpression>
#include "../common/utils.h" #include "../common/utils.h"
#include "../common/mydusizetool.h" #include "../common/mydusizetool.h"
#include "mymountproxy.h" #include "mymountproxy.h"
@ -60,6 +61,21 @@ bool CustomizeSystemBackupProxy::checkEnvEx()
return false; 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)) { if (Utils::isDirEmpty(m_backupWrapper.m_prefixDestPath)) {
qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath); qCritical() << QString("dstDir %s is not exist!").arg(m_backupWrapper.m_prefixDestPath);
Utils::mkpath(m_backupWrapper.m_prefixDestPath); Utils::mkpath(m_backupWrapper.m_prefixDestPath);
@ -230,6 +246,7 @@ QStringList CustomizeSystemBackupProxy::getRsyncArgs(CustomizeSystemBackupScene
args << "--ignore-missing-args"; args << "--ignore-missing-args";
break ; break ;
case CustomizeSystemBackupScene::MKSQUASHFS : case CustomizeSystemBackupScene::MKSQUASHFS :
// 在mksquashfs时排除bind挂载的任意一方时都备份不上
Utils::excludeFstabBindPath(excludes); Utils::excludeFstabBindPath(excludes);
// --exclude=排除路径设置 // --exclude=排除路径设置
for (QString item : m_backupWrapper.m_backupExcludePaths) { for (QString item : m_backupWrapper.m_backupExcludePaths) {

View File

@ -115,18 +115,20 @@ bool CustomizeSystemRestoreProxy::doPrepare()
if (!mountImg()) if (!mountImg())
return false; return false;
// 2、停止安全防护 // 2、停止安全初始化服务以防过高的CPU占有率因为还原时安全初始化服务会逐个文件打标记造成cpu占有率超高系统卡顿
QProcess::execute("systemctl stop kysec-init.service"); Utils::stopKysecInit();
// 停止网络服务,以防网络更新
Utils::stopNetwork();
// 3、以读写方式重新挂载boot分区因为有的机器默认以只读挂载 // 3、以读写方式重新挂载boot分区因为有的机器默认以只读挂载
remountBoot(); Utils::remountBoot();
// 4、是否有/boot/efi目录有则认为有efi分区需重新rw挂载 // 4、是否有/boot/efi目录有则认为有efi分区需重新rw挂载
QString efiPath = Utils::getSysRootPath() + "/boot/efi"; QString efiPath = Utils::getSysRootPath() + "/boot/efi";
efiPath.replace("//", "/"); efiPath.replace("//", "/");
if (!Utils::isDirEmpty(efiPath)) { if (!Utils::isDirEmpty(efiPath)) {
// 重新rw读写挂载 // 重新rw读写挂载
remountEfi(); Utils::remountEfi();
} }
qDebug() << "CustomizeSystemRestoreProxy::doPrepare invoke end"; qDebug() << "CustomizeSystemRestoreProxy::doPrepare invoke end";

View File

@ -29,16 +29,11 @@ rootpath=$2
m_mountPath=$3 m_mountPath=$3
m_default_uuid=$4 m_default_uuid=$4
BACKUP= m_backuplistPath="${m_mountPath}/snapshots/backuplist.xml"
if [[ -e "${rootpath}/backup/BACKUP/snapshots" ]]; then EXCLUDEFILE="${m_mountPath}/snapshots/.exclude"
BACKUP="/BACKUP" PLOGFILEDIR="${m_mountPath}/log"
fi
m_backuplistPath=${m_mountPath}${BACKUP}"/snapshots/backuplist.xml"
EXCLUDEFILE=${m_mountPath}${BACKUP}"/snapshots/.exclude"
PLOGFILEDIR="${m_mountPath}${BACKUP}/log"
PLOGFILE="$PLOGFILEDIR/log-$(date +%Y%m%d%H%M)" PLOGFILE="$PLOGFILEDIR/log-$(date +%Y%m%d%H%M)"
LOGFILE="${m_mountPath}${BACKUP}/log.txt" #LOGFILE="/tmp/log.txt" LOGFILE="${m_mountPath}/log.txt" #LOGFILE="/tmp/log.txt"
#是否是出厂备份 #是否是出厂备份
m_isFactory=false m_isFactory=false
@ -49,7 +44,7 @@ PERSONAL_BACKUPFILE=".user.txt"
m_isRetainUserData=false m_isRetainUserData=false
#如果/backup不存在则创建该目录 #如果/backup不存在则创建该目录
mkdir -p ${m_mountPath}${BACKUP} mkdir -p ${m_mountPath}
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Could not create /backup in initrd!" echo "Could not create /backup in initrd!"
exit 20 exit 20
@ -111,7 +106,7 @@ getBackupInfo() {
#该函数是对backup-daemon/parsebackuplist.cpp中相应函数的替换 #该函数是对backup-daemon/parsebackuplist.cpp中相应函数的替换
createBackupList() { createBackupList() {
local backuplistDir=${m_mountPath}${BACKUP}"/snapshots/" local backuplistDir="${m_mountPath}/snapshots/"
if [ ! -e "$backuplistDir" ]; then if [ ! -e "$backuplistDir" ]; then
mkdir -p $backuplistDir mkdir -p $backuplistDir
@ -653,8 +648,8 @@ backuping() {
#0:backup 1:restore #0:backup 1:restore
generateExcludeFile 0 generateExcludeFile 0
echo "/" > ${m_mountPath}${BACKUP}/snapshots/$m_uuid/$PERSONAL_BACKUPFILE echo "/" > ${m_mountPath}/snapshots/$m_uuid/$PERSONAL_BACKUPFILE
cp $EXCLUDEFILE ${m_mountPath}${BACKUP}/snapshots/$m_uuid/$PERSONAL_EXCLUDEFILE cp $EXCLUDEFILE ${m_mountPath}/snapshots/$m_uuid/$PERSONAL_EXCLUDEFILE
#echo "Begin to backup efi directory..." >>$PLOGFILE #echo "Begin to backup efi directory..." >>$PLOGFILE
#echo "Begin to backup efi directory..." #echo "Begin to backup efi directory..."
@ -826,8 +821,8 @@ deleteItemByComment() {
} }
DeleteFactoryBackup() { DeleteFactoryBackup() {
if [ -e "/backup${BACKUP}/snapshots/{${factory_uuid}}" ]; then if [ -e "/backup/snapshots/{${factory_uuid}}" ]; then
rm /backup${BACKUP}/snapshots/{${factory_uuid}} -rf rm /backup/snapshots/{${factory_uuid}} -rf
fi fi
local comment=$(findCommentByUuid "{${factory_uuid}}") local comment=$(findCommentByUuid "{${factory_uuid}}")
@ -862,7 +857,7 @@ backupAuto() { #备份
# echo "BYbobbi: m_uuid is $m_uuid" # echo "BYbobbi: m_uuid is $m_uuid"
m_time=$(date "+%y-%m-%d %H:%M:%S" | tr -d "\n") m_time=$(date "+%y-%m-%d %H:%M:%S" | tr -d "\n")
m_comment=$m_time #这个是全局变量 m_comment=$m_time #这个是全局变量
dst="${m_mountPath}${BACKUP}/snapshots/$m_uuid/data" dst="${m_mountPath}/snapshots/$m_uuid/data"
#echo "dst: $dst" #echo "dst: $dst"
mkdir -p $dst mkdir -p $dst
m_size=$global_system_usedDisk m_size=$global_system_usedDisk
@ -942,7 +937,7 @@ backupAuto() { #备份
rm -f $tmpFile rm -f $tmpFile
#写文件 #写文件
metainfo_file="${m_mountPath}${BACKUP}/snapshots/$m_uuid/$METAINFO" metainfo_file="${m_mountPath}/snapshots/$m_uuid/$METAINFO"
#echo "metainfo_file="$metainfo_file #echo "metainfo_file="$metainfo_file
#第1行清空写 #第1行清空写
#这里写的不是xml文件是一个文本文件这时候的状态是backup unfinished #这里写的不是xml文件是一个文本文件这时候的状态是backup unfinished
@ -958,12 +953,13 @@ backupAuto() { #备份
#返回值: #返回值:
getLastUsefulBackupPointUuid() { getLastUsefulBackupPointUuid() {
local xxx currentUuid foundComment local xxx currentUuid foundComment currentComment
currentUuid="" currentUuid=""
currentState=false currentState=false
currentType=true currentType=true
lastUsefulBackupPointUuid="" lastUsefulBackupPointUuid=""
foundComment=0 #是否发现了要修改的comment foundComment=0 #是否发现了要修改的comment
currentComment=""
lastbackupname="" lastbackupname=""
#如果不定义IFS则echo $line会去掉前后空格导致写到文件中去时没有格式 #如果不定义IFS则echo $line会去掉前后空格导致写到文件中去时没有格式
@ -975,7 +971,7 @@ getLastUsefulBackupPointUuid() {
#echo "xxx: $xxx" #echo "xxx: $xxx"
if [[ "$xxx" =~ "<Comment>" ]]; then if [[ "$xxx" =~ "<Comment>" ]]; then
lastbackupname=$xxx currentComment=$xxx
fi fi
if [[ "$xxx" =~ "<Uuid>" ]]; then if [[ "$xxx" =~ "<Uuid>" ]]; then
@ -1004,6 +1000,7 @@ getLastUsefulBackupPointUuid() {
if [[ "$xxx" =~ "</BackupPoint>" ]]; then if [[ "$xxx" =~ "</BackupPoint>" ]]; then
if [ "$currentState" = true -a "$currentType" = true ]; then #"/backup"是不是一个独立的盘 if [ "$currentState" = true -a "$currentType" = true ]; then #"/backup"是不是一个独立的盘
lastUsefulBackupPointUuid=$currentUuid lastUsefulBackupPointUuid=$currentUuid
lastbackupname=$currentComment
fi fi
fi fi
done <"$m_backuplistPath" done <"$m_backuplistPath"
@ -1041,7 +1038,7 @@ restoreAuto() { #还原
local m_time=$(date "+%y-%m-%d %H:%M:%S" | tr -d "\n") local m_time=$(date "+%y-%m-%d %H:%M:%S" | tr -d "\n")
writeLogFile "${m_time},${uuid},4,grub系统还原,,,${lastbackupname}" #grub时只有一键还原没有增量还原 writeLogFile "${m_time},${uuid},4,grub系统还原,,,${lastbackupname}" #grub时只有一键还原没有增量还原
restoreDir="${m_mountPath}${BACKUP}/snapshots/$uuid" restoreDir="${m_mountPath}/snapshots/$uuid"
if [ ! -e "$restoreDir" ]; then if [ ! -e "$restoreDir" ]; then
echo "full restore directory not exists!" echo "full restore directory not exists!"
@ -1165,18 +1162,29 @@ restoreAuto() { #还原
echo "restore other directories end" echo "restore other directories end"
echo "restore other directories end" >>$PLOGFILE echo "restore other directories end" >>$PLOGFILE
sync sync
#还原时清空目录 grub-install
#if [ -e "$rootpath/home/data" ]; then
# rm -r "$rootpath/home/data" ]
#fi
#mkdir -p "$rootpath/home/data" ]
} }
#----------------------------------------------------------------- #-----------------------------------------------------------------
#see backup-auto/autobackup.cpp #see backup-auto/autobackup.cpp
updateBackupAutoFinishedState() { updateBackupAutoFinishedState() {
echo "this is updateBackupAutoFinishedState" echo "this is updateBackupAutoFinishedState"
}
restoreBackupSelf() {
restoreDir="${rootpath}/var/log/yhkylin-backup-tools"
echo $restoreDir
if [ ! -e "$restoreDir" ]; then
echo "restoreBackupSelf error: no files"
return 1
fi
# 还原rsync/kybackup/backup-daemon等程序
rsync -avAXHr ${restoreDir}/backup-daemon ${rootpath}/usr/bin/ >> ${rootpath}/var/log/backup.log 2>&1
rsync -avAXHr ${restoreDir}/kybackup ${rootpath}/usr/bin/ >> ${rootpath}/var/log/backup.log 2>&1
rm -rf ${restoreDir}
} }
#----------------------------------------------------------------- #-----------------------------------------------------------------
@ -1187,13 +1195,16 @@ if [ "${rootpath}" = "/" ]; then
# exit # exit
fi fi
getBackupInfo # 还原自身不需要挂载备份分区
#echo "m_restoreUuid="$m_restoreUuid if [ $backupORrestore != "--restorebackupself" ]; then
#echo "m_enabled="$m_enabled getBackupInfo
#不加引号报错 #echo "m_restoreUuid="$m_restoreUuid
if [ "$m_restoreUuid" = "" ] || [ "$m_enabled" = "" ]; then #echo "m_enabled="$m_enabled
echo "bootinfo file is not correct!" #不加引号报错
exit 3 if [ "$m_restoreUuid" = "" ] || [ "$m_enabled" = "" ]; then
echo "bootinfo file is not correct!"
exit 3
fi
fi fi
mkdir -p ${rootpath}/var/log mkdir -p ${rootpath}/var/log
@ -1241,6 +1252,8 @@ elif [ $backupORrestore = "--factoryrestore" ]; then
#umountBackup #umountBackup
echo "This is factorybackup" echo "This is factorybackup"
elif [ $backupORrestore = "--restorebackupself" ]; then
restoreBackupSelf
else else
echo "Not correct command" echo "Not correct command"
exit 18 exit 18

View File

@ -65,6 +65,7 @@ ExistCopyExec /bin/ls /bin
ExistCopyExec /bin/mount /bin ExistCopyExec /bin/mount /bin
ExistCopyExec /bin/sync /bin ExistCopyExec /bin/sync /bin
ExistCopyExec /sbin/reboot /bin ExistCopyExec /sbin/reboot /bin
ExistCopyExec /sbin/grub-install /bin
#fix wrong timezone in initrd(#) #fix wrong timezone in initrd(#)
#ExistCopyExec /usr/share/zoneinfo/Asia/Shanghai /etc/localtime #ExistCopyExec /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

View File

@ -30,6 +30,7 @@ echo $*
BACKUP_FLAG=backup BACKUP_FLAG=backup
RESTORE_FLAG=restore RESTORE_FLAG=restore
ROLLBACK_FLAG=rollback-backup ROLLBACK_FLAG=rollback-backup
IS_NORMAL=
#. /scripts/security-functions #. /scripts/security-functions
show_text_mesg() show_text_mesg()
@ -133,7 +134,7 @@ is_990_9a0=$(get_is_990_9a0)
factory_restore_warnning() factory_restore_warnning()
{ {
if [ x${is_990_9a0} == x"true" ]; then if [[ x${is_990_9a0} == x"true" && x${FACTORY_RESTORE} == "xy" ]]; then
local is_sure= local is_sure=
while : while :
do do
@ -156,7 +157,7 @@ factory_restore_warnning()
factory_restore_warnning_again() factory_restore_warnning_again()
{ {
if [ x${is_990_9a0} == x"true" ]; then if [[ x${is_990_9a0} == x"true" && x"$FACTORY_RESTORE" = "xy" ]]; then
local is_sure= local is_sure=
while : while :
do do
@ -310,6 +311,7 @@ for x in $(cat /proc/cmdline); do
FACTORY_RESTORE=y FACTORY_RESTORE=y
;; ;;
*) *)
IS_NORMAL=y
;; ;;
esac esac
done done

View File

@ -52,6 +52,7 @@ bool MountBackupProcess::umountBackupPartition()
QString backupPath = Utils::getSysRootPath() + BACKUP_PATH; QString backupPath = Utils::getSysRootPath() + BACKUP_PATH;
backupPath.replace("//","/"); backupPath.replace("//","/");
QStringList arguments; QStringList arguments;
arguments << "-R";
arguments << backupPath; arguments << backupPath;
m_p->start("umount", arguments); m_p->start("umount", arguments);
if (!m_p->waitForStarted()) { if (!m_p->waitForStarted()) {

View File

@ -17,6 +17,7 @@
<Type>2</Type> <Type>2</Type>
<Position>0</Position> <Position>0</Position>
<UserId>1000</UserId> <UserId>1000</UserId>
<PrefixDestPath>/</PrefixDestPath>
</BackupPoint> </BackupPoint>
</backupList> </backupList>
*/ */
@ -299,7 +300,10 @@ void ParseBackupList::getCustomizePaths(QStringList &customizePaths)
QDomElement elePrefixPath = node.firstChildElement(PREFIXDESTPATH); QDomElement elePrefixPath = node.firstChildElement(PREFIXDESTPATH);
if (!elePrefixPath.isNull()) { if (!elePrefixPath.isNull()) {
customizePaths << elePrefixPath.text() + BACKUP_SNAPSHOTS_PATH; QString prefixPath = elePrefixPath.text() + BACKUP_SNAPSHOTS_PATH;
prefixPath.replace("//", "/");
if (!customizePaths.contains(prefixPath))
customizePaths << prefixPath;
} }
} }
} }
@ -512,10 +516,10 @@ ParseBackupList::ParseResult ParseBackupList::updateItem(const BackupPoint & bac
if (i < list.count()) { if (i < list.count()) {
// 移除旧节点,更新后的节点放到最后 // 移除旧节点,更新后的节点放到最后
root.removeChild(list.at(i)); root.removeChild(list.at(i));
QDomElement newNode = doc.createElement(BACKUPPOINT);
backupPointToElementNode(backupPoint, doc, newNode);
root.appendChild(newNode);
} }
QDomElement newNode = doc.createElement(BACKUPPOINT);
backupPointToElementNode(backupPoint, doc, newNode);
root.appendChild(newNode);
QFile xmlFile(m_xmlPath); QFile xmlFile(m_xmlPath);
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {

View File

@ -120,30 +120,36 @@ void SystemBackupProxy::deleteFailedData()
bool SystemBackupProxy::isIncBackup() bool SystemBackupProxy::isIncBackup()
{ {
QString backupPath; QString backupPath;
ParseBackupList::BackupPoint point;
if (m_backupWrapper.m_uuid.isEmpty()) { if (m_backupWrapper.m_uuid.isEmpty()) {
QString xmlPath(Utils::getSysRootPath() + BACKUP_XML_PATH); QString xmlPath(Utils::getSysRootPath() + BACKUP_XML_PATH);
xmlPath.replace("//", "/"); xmlPath.replace("//", "/");
ParseBackupList parser(xmlPath); ParseBackupList parser(xmlPath);
ParseBackupList::BackupPoint point;
point = parser.getLastSysBackupPoint(); point = parser.getLastSysBackupPoint();
if (point.m_uuid.isEmpty()) if (point.m_uuid.isEmpty())
return false; return false;
backupPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + point.m_uuid + "/data"; backupPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + point.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;
}
} else { } else {
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/"); xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath); ParseBackupList parse(xmlPath);
m_backupPoint = parse.findBackupPointByUuid(m_backupWrapper.m_uuid); m_backupPoint = parse.findBackupPointByUuid(m_backupWrapper.m_uuid);
backupPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data"; backupPath = Utils::getSysRootPath() + BACKUP_SNAPSHOTS_PATH + "/" + m_backupWrapper.m_uuid + "/data";
backupPath.replace("//", "/");
if (!m_backupPoint.m_backupName.isEmpty()) {
m_backupWrapper.m_bIncrement = true;
m_backupWrapper.m_type = BackupType::INC_BACKUP_SYSTEM;
return true;
}
} }
backupPath.replace("//", "/");
if (Utils::isDirExist(backupPath) && !point.m_backupName.isEmpty()) {
m_backupWrapper.m_baseUuid = point.m_uuid;
m_backupWrapper.m_bIncrement = true;
m_backupWrapper.m_type = BackupType::INC_BACKUP_SYSTEM;
return true;
}
return false; return false;
} }
@ -399,10 +405,11 @@ bool SystemBackupProxy::recordBackupPoint()
m_backupPoint.m_size = Utils::StringBySize(m_size); m_backupPoint.m_size = Utils::StringBySize(m_size);
m_backupPoint.m_time = QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss"); 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_state = BACKUP_PARSE_STATE_FAIL_STRTING;
m_backupPoint.m_path = m_backupWrapper.m_prefixDestPath;
QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH; QString xmlPath = Utils::getSysRootPath() + BACKUP_XML_PATH;
xmlPath.replace("//", "/"); xmlPath.replace("//", "/");
ParseBackupList parse(xmlPath); ParseBackupList parse(xmlPath);
if (m_backupWrapper.m_uuid.isEmpty() || !m_backupWrapper.m_bIncrement) { if (m_backupWrapper.m_uuid.isEmpty() /*|| !m_backupWrapper.m_bIncrement*/) {
if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) { if (parse.addItem(m_backupPoint) != ParseBackupList::SUCCESS) {
emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL)); emit checkResult(int(BackupResult::WRITE_STORAGEINFO_ADD_ITEM_FAIL));
return false; return false;

View File

@ -113,6 +113,9 @@ bool SystemRestoreProxy::restoreEfi()
{ {
qDebug() << "SystemRestoreProxy::restoreEfi invoke begin"; qDebug() << "SystemRestoreProxy::restoreEfi invoke begin";
// 以读写方式重新挂载boot分区因为有的机器默认以只读挂载
Utils::remountBoot();
// 是否有/boot/efi目录 // 是否有/boot/efi目录
QString efiPath = Utils::getSysRootPath() + "/boot/efi"; QString efiPath = Utils::getSysRootPath() + "/boot/efi";
efiPath.replace("//", "/"); efiPath.replace("//", "/");
@ -122,7 +125,7 @@ bool SystemRestoreProxy::restoreEfi()
sync(); sync();
// 2、重新rw读写挂载 // 2、重新rw读写挂载
remountEfi(); Utils::remountEfi();
// 3、同步efi // 3、同步efi
return rsyncEfi(); return rsyncEfi();
@ -149,34 +152,6 @@ void SystemRestoreProxy::repairEfi()
} }
} }
/**
* @brief rw读写挂载efi分区
*/
void SystemRestoreProxy::remountEfi()
{
QString mountPath = Utils::getSysRootPath() + "/boot/efi";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/**
* @brief rw读写挂载boot分区
*/
void SystemRestoreProxy::remountBoot()
{
QString mountPath = Utils::getSysRootPath() + "/boot";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/** /**
* @brief efi * @brief efi
*/ */
@ -286,7 +261,7 @@ QStringList SystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
args << "--exclude=/usr/bin/rsync"; args << "--exclude=/usr/bin/rsync";
args << "--exclude=/usr/share/rsync"; args << "--exclude=/usr/share/rsync";
args << "--exclude=/usr/share/initramfs-tools/hooks/kybackup-hooks"; args << "--exclude=/usr/share/initramfs-tools/hooks/kybackup-hooks";
args << "--exclude=/usr/share/initramfs-tools/scripts/local-bottom/kybackup"; args << "--exclude=/usr/share/initramfs-tools/scripts/*/kybackup";
// 还原后仍然保持激活状态 // 还原后仍然保持激活状态
args << "--exclude=/etc/LICENSE"; args << "--exclude=/etc/LICENSE";
@ -363,16 +338,15 @@ QStringList SystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
*/ */
void SystemRestoreProxy::restoreSystem() void SystemRestoreProxy::restoreSystem()
{ {
// 停止安全防护 // 停止安全初始化服务以防过高的CPU占有率因为还原时安全初始化服务会逐个文件打标记造成cpu占有率超高系统卡顿
QProcess::execute("systemctl stop kysec-init.service"); Utils::stopKysecInit();
// 停止网络服务,以防网络更新
Utils::stopNetwork();
// 本地系统备份没有img挂载故下面两个路径相等 // 本地系统备份没有img挂载故下面两个路径相等
m_srcPath = m_backupPath; m_srcPath = m_backupPath;
QString destPath = Utils::getSysRootPath(); QString destPath = Utils::getSysRootPath();
// 以读写方式重新挂载boot分区因为有的机器默认以只读挂载
remountBoot();
QStringList args; QStringList args;
// 自动更新的备份还原时保留用户数据 // 自动更新的备份还原时保留用户数据
if (m_curUuid == AUTO_BACKUP_UUID || m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM_WITH_DATA) { if (m_curUuid == AUTO_BACKUP_UUID || m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM_WITH_DATA) {
@ -395,6 +369,15 @@ void SystemRestoreProxy::restoreSystem()
// Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid)); // Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid));
Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,,," + m_backupPoint.m_backupName); Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,,," + m_backupPoint.m_backupName);
// 华为9a0等机型设定1分钟后关机重启原因运行时还原备份还原工具本身可能会造成运行中的备份还原崩溃从而无法执行到后面的自动重启代码故在此预设。
// 不再只华为机型使用此方法,将全部机型都改为此方法,缩短操作链
// if (Utils::isHuawei990())
{
QString msg;
Utils::executeCMD("shutdown -r +1", msg);
qDebug() << msg;
}
Utils::updateSyncFile(); Utils::updateSyncFile();
Utils::wait(2); Utils::wait(2);
QString fileIfSync = Utils::getSysRootPath() + FILE_IF_SYNC; QString fileIfSync = Utils::getSysRootPath() + FILE_IF_SYNC;
@ -417,7 +400,9 @@ void SystemRestoreProxy::restoreSystem()
removeHome(QString(Utils::getSysRootPath() + "/data/home").replace("//", "/")); removeHome(QString(Utils::getSysRootPath() + "/data/home").replace("//", "/"));
} }
QProcess::execute("sync"); // 最后再一次还原home目录以查缺补漏因为系统还原过程中~/.config等目录中的部分文件可能会更新会引起部分bug如任务栏固定图标还原不完整等
this->rsyncAgain();
::sync();
// 2209后新增了一些依赖还原到以前的4.0.13版后缺少依赖无法运行故此最后还得还原backup-daemon和kybackup本身 // 2209后新增了一些依赖还原到以前的4.0.13版后缺少依赖无法运行故此最后还得还原backup-daemon和kybackup本身
// 兼容Qt库从5.12.8升级到5.15,系统还原的最后也将备份还原工具本身还原 // 兼容Qt库从5.12.8升级到5.15,系统还原的最后也将备份还原工具本身还原
// QString version = Utils::getBackupVersion(); // QString version = Utils::getBackupVersion();
@ -425,22 +410,32 @@ void SystemRestoreProxy::restoreSystem()
if (Utils::getSysRootPath() != CASPER_ROOT_PATH) if (Utils::getSysRootPath() != CASPER_ROOT_PATH)
{ {
// initrd.img已经还原为旧状态需要将新版本的backup-auto-efi等脚本文件更新到initrd.img中这样我们修改后的新逻辑才能生效 // initrd.img已经还原为旧状态需要将新版本的backup-auto-efi等脚本文件更新到initrd.img中这样我们修改后的新逻辑才能生效
// QString msg;
// Utils::executeCMD("update-initramfs -u", msg);
// qDebug() << msg;
// 解决bug:182576 【2203自适应升级2309】【2303自适应升级2309】升级成功还原系统失败应该是有更新包修改了UEFI固件查找引导器文件的引导地址
// 故系统还原完成后重新执行下grub-install命令
QString msg; QString msg;
Utils::executeCMD("update-initramfs -u", msg); Utils::executeCMD("grub-install", msg);
qDebug() << msg; qDebug() << msg;
// 写入标记rsync_backup_self:${UUID}到文件/etc/file_if_sync中表示需要还原backup-daemon和kybackup本身 // 写入标记rsync_backup_self:${UUID}到文件/etc/file_if_sync中表示需要还原backup-daemon和kybackup本身
QString line("rsync_backup_self:"); // QString line("rsync_backup_self:");
line += m_curUuid; // line += m_curUuid;
Utils::syncWriteFile(fileIfSync, line); // Utils::syncWriteFile(fileIfSync, line);
// this->undoRestoreData();
// 加一层保险(因为有些机型上使用"update-initramfs -u"命令会失败华为9006c的2203版等
// QString version = Utils::getBackupVersion();
// if (Utils::isHuawei990() && version.contains("4.0.13"))
this->rsyncBackupSelf();
::sync(); ::sync();
// Utils::wait(5);
} }
Utils::wait(5);
emit this->workResult(result); emit this->workResult(result);
Utils::wait(2); Utils::wait(3);
reboot(RB_AUTOBOOT); reboot(RB_AUTOBOOT);
} else { } else {
emit this->workResult(result); emit this->workResult(result);
@ -545,3 +540,78 @@ void SystemRestoreProxy::removeHome(const QString& path)
qDebug() << "SystemRestoreProxy::removeHome invoke end"; qDebug() << "SystemRestoreProxy::removeHome invoke end";
} }
/**
* @brief
* @note
* 1~/.config等目录中的部分文件可能会更新bug
* a.
* b. desktop文件
* c. 1%
*/
void SystemRestoreProxy::rsyncAgain()
{
// 保留用户数据还原直接
if (m_bRetainUserData)
return ;
QString destPath = Utils::getSysRootPath() + "/home/";
destPath.replace("//", "/");
Utils::mkpath(destPath);
RsyncPathToDirProcess * process = new RsyncPathToDirProcess(this);
QStringList args;
args << "-avAHXr";
args << m_srcPath + "/home/";
args << destPath;
process->start(args);
}
/**
* @brief initrd阶段执行备份还原脚本时再进行还原
* @note
*/
void SystemRestoreProxy::undoRestoreData()
{
QString destPath = Utils::getSysRootPath() + "/var/log/yhkylin-backup-tools/";
destPath.replace("//", "/");
Utils::mkpath(destPath);
RsyncPathToDirProcess * process = new RsyncPathToDirProcess(this);
QStringList args;
args << "-avAHXr";
args << m_srcPath + "/usr/bin/backup-daemon";
args << destPath;
process->start(args);
args.clear();
args << "-avAHXr";
args << m_srcPath + "/usr/bin/kybackup";
args << destPath;
process->start(args);
}
/**
* @brief
* @note undoRestoreData基础上增加的一层保险使"update-initramfs -u"9006c的2203版等
* /etc/profile.d//etc/init.d/.sh脚本文件以开机时自动执行
*/
void SystemRestoreProxy::rsyncBackupSelf()
{
QString destPath = Utils::getSysRootPath() + "/usr/bin/";
destPath.replace("//", "/");
Utils::mkpath(destPath);
RsyncPathToDirProcess * process = new RsyncPathToDirProcess(this);
QStringList args;
args << "-avAHXr";
args << m_srcPath + "/usr/bin/kybackup";
args << destPath;
process->start(args);
args.clear();
args << "-avAHXr";
args << m_srcPath + "/usr/bin/backup-daemon";
args << destPath;
process->start(args);
}

View File

@ -32,10 +32,6 @@ private:
bool restoreEfi(); bool restoreEfi();
// 修复efi目录 // 修复efi目录
void repairEfi(); void repairEfi();
// 以读写方式重新挂载efi分区
void remountEfi();
// 以读写方式重新挂载boot分区
void remountBoot();
// 同步efi // 同步efi
bool rsyncEfi(); bool rsyncEfi();
// 系统还原 // 系统还原
@ -45,6 +41,20 @@ private:
// 获取备份点里存在家目录的用户列表 // 获取备份点里存在家目录的用户列表
QStringList getBackupPointUsers(); QStringList getBackupPointUsers();
QStringList getBackupPointUsers(const QString& path); QStringList getBackupPointUsers(const QString& path);
// 待还原数据
void undoRestoreData();
// 还原备份还原工具自身
void rsyncBackupSelf();
/**
* @brief
* @note
* 1~/.config等目录中的部分文件可能会更新bug
* a.
* b. desktop文件
* c. 1%
*/
void rsyncAgain();
/** /**
* @brief rsync命令参数 * @brief rsync命令参数

View File

@ -3,6 +3,7 @@
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
#include <QRegularExpression>
#include "../common/utils.h" #include "../common/utils.h"
#include "../common/mydusizetool.h" #include "../common/mydusizetool.h"
#include "mymountproxy.h" #include "mymountproxy.h"
@ -45,6 +46,21 @@ bool UDiskDataBackupProxy::checkEnvEx()
return false; 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;
}
QString backupPath(m_backupWrapper.m_prefixDestPath); QString backupPath(m_backupWrapper.m_prefixDestPath);
backupPath.replace("//", "/"); backupPath.replace("//", "/");
QStorageInfo udisk(backupPath); QStorageInfo udisk(backupPath);

View File

@ -4,6 +4,7 @@
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
#include <QRegularExpression>
#include "../common/utils.h" #include "../common/utils.h"
#include "../common/mydusizetool.h" #include "../common/mydusizetool.h"
#include "mymountproxy.h" #include "mymountproxy.h"
@ -65,6 +66,21 @@ bool UDiskSystemBackupProxy::checkEnvEx()
return false; 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;
}
QString backupPath(m_backupWrapper.m_prefixDestPath); QString backupPath(m_backupWrapper.m_prefixDestPath);
backupPath.replace("//", "/"); backupPath.replace("//", "/");
QStorageInfo udisk(backupPath); QStorageInfo udisk(backupPath);
@ -293,8 +309,9 @@ QStringList UDiskSystemBackupProxy::getRsyncArgs(UDiskSystemBackupScene scene)
args << "--ignore-missing-args"; args << "--ignore-missing-args";
break ; break ;
case UDiskSystemBackupScene::MKSQUASHFS : case UDiskSystemBackupScene::MKSQUASHFS :
// 在mksquashfs时排除bind挂载的任意一方时都备份不上
Utils::excludeFstabBindPath(excludes); Utils::excludeFstabBindPath(excludes);
// --exclude=排除路径设置 // -e 排除路径设置
for (QString item : m_backupWrapper.m_backupExcludePaths) { for (QString item : m_backupWrapper.m_backupExcludePaths) {
if (excludes.contains(item)) if (excludes.contains(item))
continue; continue;

View File

@ -116,6 +116,9 @@ bool UDiskSystemRestoreProxy::restoreEfi()
{ {
qDebug() << "UDiskSystemRestoreProxy::restoreEfi invoke begin"; qDebug() << "UDiskSystemRestoreProxy::restoreEfi invoke begin";
// 以读写方式重新挂载boot分区因为有的机器默认以只读挂载
Utils::remountBoot();
// 是否有/boot/efi目录 // 是否有/boot/efi目录
QString efiPath = Utils::getSysRootPath() + "/boot/efi"; QString efiPath = Utils::getSysRootPath() + "/boot/efi";
efiPath.replace("//", "/"); efiPath.replace("//", "/");
@ -124,7 +127,7 @@ bool UDiskSystemRestoreProxy::restoreEfi()
repairEfi(); repairEfi();
// 2、重新rw读写挂载 // 2、重新rw读写挂载
remountEfi(); Utils::remountEfi();
// 3、同步efi // 3、同步efi
return rsyncEfi(); return rsyncEfi();
@ -152,34 +155,6 @@ void UDiskSystemRestoreProxy::repairEfi()
} }
} }
/**
* @brief rw读写挂载efi分区
*/
void UDiskSystemRestoreProxy::remountEfi()
{
QString mountPath = Utils::getSysRootPath() + "/boot/efi";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/**
* @brief rw读写挂载boot分区
*/
void UDiskSystemRestoreProxy::remountBoot()
{
QString mountPath = Utils::getSysRootPath() + "/boot";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
QProcess::execute("mount", args);
}
/** /**
* @brief efi * @brief efi
*/ */
@ -244,7 +219,7 @@ QStringList UDiskSystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
args << "--exclude=/data/home"; args << "--exclude=/data/home";
args << "--exclude=/data/root"; args << "--exclude=/data/root";
// 云桌面背景路径属于用户数据 // 云图片作为桌面背景路径属于用户数据
args << "--exclude=/var/lib/AccountsService"; args << "--exclude=/var/lib/AccountsService";
// 域用户相关信息,还原后保持不退域 // 域用户相关信息,还原后保持不退域
@ -289,7 +264,7 @@ QStringList UDiskSystemRestoreProxy::getRsyncArgs(SystemRestoreScene scene)
args << "--exclude=/usr/bin/rsync"; args << "--exclude=/usr/bin/rsync";
args << "--exclude=/usr/share/rsync"; args << "--exclude=/usr/share/rsync";
args << "--exclude=/usr/share/initramfs-tools/hooks/kybackup-hooks"; args << "--exclude=/usr/share/initramfs-tools/hooks/kybackup-hooks";
args << "--exclude=/usr/share/initramfs-tools/scripts/local-bottom/kybackup"; args << "--exclude=/usr/share/initramfs-tools/scripts/*/kybackup";
// 还原后仍然保持激活状态 // 还原后仍然保持激活状态
args << "--exclude=/etc/LICENSE"; args << "--exclude=/etc/LICENSE";
@ -397,9 +372,6 @@ bool UDiskSystemRestoreProxy::doPrepare()
} else } else
m_srcPath = m_backupPath; m_srcPath = m_backupPath;
// 以读写方式重新挂载boot分区因为有的机器默认以只读挂载
remountBoot();
qDebug() << "UDiskSystemRestoreProxy::doPrepare invoke end"; qDebug() << "UDiskSystemRestoreProxy::doPrepare invoke end";
return true; return true;
} }
@ -418,8 +390,10 @@ void UDiskSystemRestoreProxy::restoreSystem()
return ; return ;
} }
// 停止安全防护 // 停止安全初始化服务以防过高的CPU占有率因为还原时安全初始化服务会逐个文件打标记造成cpu占有率超高系统卡顿
QProcess::execute("systemctl stop kysec-init.service"); Utils::stopKysecInit();
// 停止网络服务,以防网络更新
Utils::stopNetwork();
QString destPath = Utils::getSysRootPath(); QString destPath = Utils::getSysRootPath();
@ -427,6 +401,7 @@ void UDiskSystemRestoreProxy::restoreSystem()
// 自动更新的备份还原时保留用户数据 // 自动更新的备份还原时保留用户数据
if (m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM_WITH_DATA) { if (m_backupWrapper.m_type == BackupType::RESTORE_SYSTEM_WITH_DATA) {
args = getRsyncArgs(SystemRestoreScene::RESTORE_SYSTEM_WITH_DATA); args = getRsyncArgs(SystemRestoreScene::RESTORE_SYSTEM_WITH_DATA);
m_bRetainUserData = true;
} else { } else {
args = getRsyncArgs(SystemRestoreScene::SYSTEM_RESTORE); args = getRsyncArgs(SystemRestoreScene::SYSTEM_RESTORE);
} }
@ -445,6 +420,15 @@ void UDiskSystemRestoreProxy::restoreSystem()
// Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid)); // Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,," + QString::number(m_backupWrapper.m_frontUid));
Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,,," + m_backupPoint.m_backupName); Utils::writeBackupLog(time + "," + m_curUuid + "," + QString::number(m_backupWrapper.m_type) + ",,,," + m_backupPoint.m_backupName);
// 华为9a0等机型设定1分钟后关机重启原因运行时还原备份还原工具本身可能会造成运行中的备份还原崩溃从而无法执行到后面的自动重启代码故在此预设。
// 不再只华为机型使用此方法,将全部机型都改为此方法,缩短操作链
// if (Utils::isHuawei990())
{
QString msg;
Utils::executeCMD("shutdown -r +1", msg);
qDebug() << msg;
}
Utils::updateSyncFile(); Utils::updateSyncFile();
Utils::wait(2); Utils::wait(2);
QString fileIfSync = Utils::getSysRootPath() + FILE_IF_SYNC; QString fileIfSync = Utils::getSysRootPath() + FILE_IF_SYNC;
@ -471,6 +455,10 @@ void UDiskSystemRestoreProxy::restoreSystem()
Utils::wait(5); Utils::wait(5);
} }
// 最后再一次还原home目录以查缺补漏因为系统还原过程中~/.config等目录中的部分文件可能会更新会引起部分bug如任务栏固定图标还原不完整等
this->rsyncAgain();
::sync();
// 2209后新增了一些依赖还原到以前的4.0.13版后缺少依赖无法运行故此最后还得还原backup-daemon和kybackup本身 // 2209后新增了一些依赖还原到以前的4.0.13版后缺少依赖无法运行故此最后还得还原backup-daemon和kybackup本身
// 兼容Qt库从5.12.8升级到5.15,系统还原的最后也将备份还原工具本身还原 // 兼容Qt库从5.12.8升级到5.15,系统还原的最后也将备份还原工具本身还原
// QString version = Utils::getBackupVersion(); // QString version = Utils::getBackupVersion();
@ -478,16 +466,28 @@ void UDiskSystemRestoreProxy::restoreSystem()
if (Utils::getSysRootPath() != CASPER_ROOT_PATH) if (Utils::getSysRootPath() != CASPER_ROOT_PATH)
{ {
// initrd.img已经还原为旧状态需要将新版本的backup-auto-efi等脚本文件更新到initrd.img中这样我们修改后的新逻辑才能生效 // initrd.img已经还原为旧状态需要将新版本的backup-auto-efi等脚本文件更新到initrd.img中这样我们修改后的新逻辑才能生效
// QString msg;
// Utils::executeCMD("update-initramfs -u", msg);
// qDebug() << msg;
// 解决bug:182576 【2203自适应升级2309】【2303自适应升级2309】升级成功还原系统失败应该是有更新包修改了UEFI固件查找引导器文件的引导地址
// 故系统还原完成后重新执行下grub-install命令
QString msg; QString msg;
Utils::executeCMD("update-initramfs -u", msg); Utils::executeCMD("grub-install", msg);
qDebug() << msg; qDebug() << msg;
// 写入标记rsync_backup_self:${UUID}到文件/etc/file_if_sync中表示需要还原backup-daemon和kybackup本身 // 写入标记rsync_backup_self:${UUID}到文件/etc/file_if_sync中表示需要还原backup-daemon和kybackup本身
QString line("rsync_backup_self:"); // QString line("rsync_backup_self:");
line += m_curUuid; // line += m_curUuid;
Utils::syncWriteFile(fileIfSync, line); // Utils::syncWriteFile(fileIfSync, line);
// this->undoRestoreData();
// 加一层保险(因为有些机型上使用"update-initramfs -u"命令会失败华为9006c的2203版等
// QString version = Utils::getBackupVersion();
// if (Utils::isHuawei990() && version.contains("4.0.13"))
this->rsyncBackupSelf();
::sync(); ::sync();
Utils::wait(5);
} }
} }
@ -497,7 +497,7 @@ void UDiskSystemRestoreProxy::restoreSystem()
m_isFinished = true; m_isFinished = true;
if (result) { if (result) {
Utils::wait(2); Utils::wait(3);
reboot(RB_AUTOBOOT); reboot(RB_AUTOBOOT);
} }
}); });
@ -567,4 +567,79 @@ bool UDiskSystemRestoreProxy::checkUdiskExists()
return true; return true;
} }
/**
* @brief
* @note
* 1~/.config等目录中的部分文件可能会更新bug
* a.
* b. desktop文件
* c. 1%
*/
void UDiskSystemRestoreProxy::rsyncAgain()
{
// 保留用户数据还原直接
if (m_bRetainUserData)
return ;
QString destPath = Utils::getSysRootPath() + "/home/";
destPath.replace("//", "/");
Utils::mkpath(destPath);
RsyncPathToDirProcess * process = new RsyncPathToDirProcess(this);
QStringList args;
args << "-avAHXr";
args << m_srcPath + "/home/";
args << destPath;
process->start(args);
}
/**
* @brief initrd阶段执行备份还原脚本时再进行还原
* @note
*/
void UDiskSystemRestoreProxy::undoRestoreData()
{
QString destPath = Utils::getSysRootPath() + "/var/log/yhkylin-backup-tools/";
destPath.replace("//", "/");
Utils::mkpath(destPath);
RsyncPathToDirProcess * process = new RsyncPathToDirProcess(this);
QStringList args;
args << "-avAHXr";
args << m_srcPath + "/usr/bin/backup-daemon";
args << destPath;
process->start(args);
args.clear();
args << "-avAHXr";
args << m_srcPath + "/usr/bin/kybackup";
args << destPath;
process->start(args);
}
/**
* @brief
* @note undoRestoreData基础上增加的一层保险使"update-initramfs -u"9006c的2203版等
* /etc/profile.d//etc/init.d/.sh脚本文件以开机时自动执行
*/
void UDiskSystemRestoreProxy::rsyncBackupSelf()
{
QString destPath = Utils::getSysRootPath() + "/usr/bin/";
destPath.replace("//", "/");
Utils::mkpath(destPath);
RsyncPathToDirProcess * process = new RsyncPathToDirProcess(this);
QStringList args;
args << "-avAHXr";
args << m_srcPath + "/usr/bin/kybackup";
args << destPath;
process->start(args);
args.clear();
args << "-avAHXr";
args << m_srcPath + "/usr/bin/backup-daemon";
args << destPath;
process->start(args);
}

View File

@ -35,10 +35,6 @@ private:
bool restoreEfi(); bool restoreEfi();
// 修复efi目录 // 修复efi目录
void repairEfi(); void repairEfi();
// 以读写方式重新挂载efi分区
void remountEfi();
// 以读写方式重新挂载boot分区
void remountBoot();
// 同步efi // 同步efi
bool rsyncEfi(); bool rsyncEfi();
// 系统还原 // 系统还原
@ -47,6 +43,20 @@ private:
bool doPrepare(); bool doPrepare();
// 异机还原时更新grub.cfg中的分区UUID // 异机还原时更新grub.cfg中的分区UUID
void updateGrubUUid(); void updateGrubUUid();
// 待还原数据
void undoRestoreData();
// 还原备份还原工具自身
void rsyncBackupSelf();
/**
* @brief
* @note
* 1~/.config等目录中的部分文件可能会更新bug
* a.
* b. desktop文件
* c. 1%
*/
void rsyncAgain();
/** /**
* @brief rsync命令参数 * @brief rsync命令参数
@ -74,6 +84,8 @@ private:
ParseBackupList::BackupPoint m_backupPoint; ParseBackupList::BackupPoint m_backupPoint;
// 强制结束标志(stop后没反应的情况系统处于睡眠状态) // 强制结束标志(stop后没反应的情况系统处于睡眠状态)
bool m_isForce; bool m_isForce;
// 是否保留用户数据
bool m_bRetainUserData = false;
}; };
#endif // UDISKSYSTEMRESTOREPROXY_H #endif // UDISKSYSTEMRESTOREPROXY_H

View File

@ -758,7 +758,8 @@ bool Utils::writeBackupLog(QString line)
*/ */
bool Utils::syncWriteFile(const QString &fileName, const QString& content) bool Utils::syncWriteFile(const QString &fileName, const QString& content)
{ {
std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(std::fopen(fileName.toStdString().data(), "a+"), std::fclose); qDebug() << fileName << content;
std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(std::fopen(fileName.toStdString().data(), "w"), std::fclose);
if (!fp) { if (!fp) {
std::perror("file opening failed!"); std::perror("file opening failed!");
@ -1521,3 +1522,49 @@ QString Utils::getBackupVersion()
return version; return version;
} }
/**
* @brief rw读写挂载efi分区
*/
void Utils::remountEfi()
{
QString mountPath = Utils::getSysRootPath() + "/boot/efi";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
Utils::executeCmd("mount", args);
}
/**
* @brief rw读写挂载boot分区
*/
void Utils::remountBoot()
{
QString mountPath = Utils::getSysRootPath() + "/boot";
mountPath.replace("//", "/");
QStringList args;
args << "-o"
<< "rw,remount"
<< mountPath;
Utils::executeCmd("mount", args);
}
/**
* @brief
*/
void Utils::stopKysecInit()
{
Utils::executeCmd("systemctl stop kysec-init.service");
}
/**
* @brief
*/
void Utils::stopNetwork()
{
Utils::executeCmd("service network-manager stop");
}

View File

@ -401,6 +401,26 @@ public:
*/ */
static QString getBackupVersion(); static QString getBackupVersion();
/**
* @brief rw读写挂载efi分区
*/
static void remountEfi();
/**
* @brief rw读写挂载boot分区
*/
static void remountBoot();
/**
* @brief
*/
static void stopKysecInit();
/**
* @brief
*/
static void stopNetwork();
/** /**
* @brief * @brief
* @param logfile * @param logfile

View File

@ -78,10 +78,7 @@ Or click "Update Backup" button, pop up data backup list window. As shown in fig
![Fig 15 data backup list -big](image/15.png) ![Fig 15 data backup list -big](image/15.png)
Then, click "Next" button, enter backup checking page --> backup preparing --> backuping --> backup finished. Such as system backup. Then, click "Next" button, enter backup checking page --> backup preparing --> backuping --> backup finished. Such as system backup.
- **Update Data Backup** -- On the basis of a data backup, update the backup data. - **Update Data Backup** -- On the basis of a data backup, update the backup data.
- **Data Recovery** -- Recovery the data to its previous backup state, and the function is similar to system restore. - **Data Recovery** -- Recovery the data to its previous backup state, and the function is similar to system restore.
![Fig 16 Data recovery-big](image/16.png) ![Fig 16 Data recovery-big](image/16.png)

View File

@ -78,9 +78,7 @@
选择好备份路径后,单击“下一步”按钮,进入环境检测-->备份准备-->备份中-->备份完成等环节,和系统备份类似。 选择好备份路径后,单击“下一步”按钮,进入环境检测-->备份准备-->备份中-->备份完成等环节,和系统备份类似。
数据备份首页右下角的“备份管理”,可用来查看数据备份状态,删除无效备份。 数据备份首页右下角的“备份管理”,可用来查看数据备份状态,删除无效备份。
- **数据增量备份** —— 在某个数据备份的基础上,更新备份点的数据。 - **数据增量备份** —— 在某个数据备份的基础上,更新备份点的数据。
- **数据还原** —— 还原到某个数据备份的状态,功能与系统还原相似。 - **数据还原** —— 还原到某个数据备份的状态,功能与系统还原相似。
![图 16 数据还原主界面-big](image/16.png) ![图 16 数据还原主界面-big](image/16.png)
@ -147,7 +145,6 @@ Tips如果制作镜像文件时带有数据分区则在下一步“安装
## 常见问题 ## 常见问题
- 无法使用备份还原 - 无法使用备份还原
在安装操作系统时,必须要选中“创建备份还原分区”,备份还原才能使用。 在安装操作系统时,必须要选中“创建备份还原分区”,备份还原才能使用。
- 备份分区空间不足 - 备份分区空间不足
备份分区(/backup大小是安装系统时固定的一般也不会太大随着系统的使用量逐渐增大会造成备份空间不足无法进行备份的情况。此时可以备份到移动设备上不过备份速度会变慢。 备份分区(/backup大小是安装系统时固定的一般也不会太大随着系统的使用量逐渐增大会造成备份空间不足无法进行备份的情况。此时可以备份到移动设备上不过备份速度会变慢。
建议系统只进行一个初装备份到备份分区,后面的系统备份都备份到移动设备上。比较重要的数据统一放到某个目录中,用数据备份功能进行备份。 建议系统只进行一个初装备份到备份分区,后面的系统备份都备份到移动设备上。比较重要的数据统一放到某个目录中,用数据备份功能进行备份。

12
debian/changelog vendored
View File

@ -1,3 +1,15 @@
yhkylin-backup-tools (4.1.0.1-ok7) yangtze; urgency=medium
* BUG :【备份还原】切换至平板模式时,窗口没有最大化
【备份还原】窗口选项菜单未翻译成中文
【备份还原】菜单-退出按钮显示Exit预期显示退出
【备份还原】gurb备份/还原系统时,显示乱码
* 需求号:无
* 其它改动:无
* 其它影响域:无
-- lidecheng <lidecheng@kylinos.cn> Mon, 04 Sep 2023 18:03:27 +0800
yhkylin-backup-tools (4.1.0.1-ok6) yangtze; urgency=medium yhkylin-backup-tools (4.1.0.1-ok6) yangtze; urgency=medium
* BUG :【备份还原】【次要】删除备份成功的弹窗,关闭按钮无悬浮提示 * BUG :【备份还原】【次要】删除备份成功的弹窗,关闭按钮无悬浮提示

View File

@ -180,6 +180,7 @@ void BackupPointListDialog::keyPressEvent(QKeyEvent* event)
{ {
switch (event->key()) { switch (event->key()) {
case Qt::Key_Escape: case Qt::Key_Escape:
this->close();
case Qt::Key_Up: case Qt::Key_Up:
case Qt::Key_Down: case Qt::Key_Down:
case Qt::Key_Left: case Qt::Key_Left:

View File

@ -315,17 +315,17 @@ bool BackupListWidget::checkPathLimit(const QString &path)
// 2、形如$()的文件夹名称 // 2、形如$()的文件夹名称
// 3、形如${}的文件夹名称 // 3、形如${}的文件夹名称
// 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号 // 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号
// if ( path.contains(QRegularExpression(".*`.*`.*")) if ( path.contains(QRegularExpression(".*`.*`.*"))
// || path.contains(QRegularExpression(".*\\$\\(.*\\).*")) || path.contains(QRegularExpression(".*\\$\\(.*\\).*"))
// || path.contains(QRegularExpression(".*\\$\\{.*\\}.*")) || path.contains(QRegularExpression(".*\\$\\{.*\\}.*"))
// || path.contains(QRegularExpression("[;&|]+"))) { || path.contains(QRegularExpression("[;&|]+"))) {
// MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), QObject::tr("Path can not include symbols that such as : ``,$(),${},;,&,|,etc."), QObject::tr("OK")); MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), QObject::tr("Path can not include symbols that such as : ``,$(),${},;,&,|,etc."), QObject::tr("OK"));
// return false; return false;
// } }
// 1、列表中是否已经存在 // 1、列表中是否已经存在
if (contains(path)) { if (contains(path)) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Warning"),
QObject::tr("Path already exists : ") + path, QObject::tr("Path already exists : ") + path,
QObject::tr("OK")); QObject::tr("OK"));
return false; return false;
@ -334,7 +334,7 @@ bool BackupListWidget::checkPathLimit(const QString &path)
// 2、路径是否存在 // 2、路径是否存在
QFileInfo fileInfo(path); QFileInfo fileInfo(path);
if (!fileInfo.exists()) { if (!fileInfo.exists()) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Warning"),
QObject::tr("The file or directory does not exist : ") + path, QObject::tr("The file or directory does not exist : ") + path,
QObject::tr("OK")); QObject::tr("OK"));
return false; return false;
@ -343,8 +343,9 @@ bool BackupListWidget::checkPathLimit(const QString &path)
// 3、是否是限定路径及其子路径 // 3、是否是限定路径及其子路径
bool blimit = false; bool blimit = false;
QString dirCanBeSelected; QString dirCanBeSelected;
QString abstractPath = fileInfo.absoluteFilePath();
for (const QString &item : m_pathLimit) { for (const QString &item : m_pathLimit) {
if (path.startsWith(item)) { if (abstractPath.startsWith(item)) {
blimit = true; blimit = true;
break; break;
} }
@ -357,7 +358,7 @@ bool BackupListWidget::checkPathLimit(const QString &path)
} }
} }
if (m_pathLimit.size() > 0 && !blimit) { if (m_pathLimit.size() > 0 && !blimit) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Warning"),
QObject::tr("Only data that exists in the follow directorys can be selected: %1.\n Path:%2 is not in them.").arg(dirCanBeSelected).arg(path), QObject::tr("Only data that exists in the follow directorys can be selected: %1.\n Path:%2 is not in them.").arg(dirCanBeSelected).arg(path),
QObject::tr("OK")); QObject::tr("OK"));
return false; return false;

View File

@ -1,6 +1,10 @@
#include "backuppositionselectdialog.h" #include "backuppositionselectdialog.h"
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QPushButton>
#include "filefilterproxymodelforbackup.h" #include "filefilterproxymodelforbackup.h"
#include "../../common/utils.h" #include "../../common/utils.h"
#include "../messageboxutils.h"
BackupPositionSelectDialog::BackupPositionSelectDialog(QWidget* parent) : BackupPositionSelectDialog::BackupPositionSelectDialog(QWidget* parent) :
QFileDialog(parent) QFileDialog(parent)
@ -8,19 +12,85 @@ BackupPositionSelectDialog::BackupPositionSelectDialog(QWidget* parent) :
setWindowTitle(tr("Please select a path")); setWindowTitle(tr("Please select a path"));
setViewMode(QFileDialog::Detail); setViewMode(QFileDialog::Detail);
setFileMode(QFileDialog::DirectoryOnly); setFileMode(QFileDialog::DirectoryOnly);
setFilter(QDir::Dirs | QDir::NoDotAndDotDot); // 主题和文管重构了QFileDialog下面代码已经失效
QList<QUrl> siderUrls; // setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath()); // QList<QUrl> siderUrls;
siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath() + "/data"); // siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath());
QList<QString> localDisks = Utils::getLocalDisks(); // siderUrls << QUrl::fromLocalFile(Utils::getSysRootPath() + "/data");
for (const QString& path: localDisks) { // QList<QString> localDisks = Utils::getLocalDisks();
siderUrls << QUrl::fromLocalFile(path); // for (const QString& path: localDisks) {
} // siderUrls << QUrl::fromLocalFile(path);
setSidebarUrls(siderUrls); // }
FileFilterProxyModeForBackup *proxy = new FileFilterProxyModeForBackup; // setSidebarUrls(siderUrls);
setProxyModel(proxy); // FileFilterProxyModeForBackup *proxy = new FileFilterProxyModeForBackup;
// setProxyModel(proxy);
} }
BackupPositionSelectDialog::~BackupPositionSelectDialog() BackupPositionSelectDialog::~BackupPositionSelectDialog()
{} {}
bool BackupPositionSelectDialog::checkSelectedFiles()
{
QStringList selectFiles = selectedFiles();
for (const QString & fileName : selectFiles) {
// 防命令注入
// 1、形如mkdir '`id&>id_bak_test.txt`'中的文件夹名称
// 2、形如$()的文件夹名称
// 3、形如${}的文件夹名称
// 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号
if ( fileName.contains(QRegularExpression(".*`.*`.*"))
|| fileName.contains(QRegularExpression(".*\\$\\(.*\\).*"))
|| fileName.contains(QRegularExpression(".*\\$\\{.*\\}.*"))
|| fileName.contains(QRegularExpression("[;&|]+"))) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), QObject::tr("Path can not include symbols that such as : ``,$(),${},;,&,|,etc."), QObject::tr("OK"));
return false;
}
// 主题重新实现了QFileDialog后很多功能已经丢失(如:文件夹过滤等),需要再次进行过滤,真难用
QString backupPath = Utils::getSysRootPath() + BACKUP_PATH;
backupPath.replace("//", "/");
if (fileName == backupPath || fileName.contains(BACKUP_SNAPSHOTS_PATH)) {
// 不能嵌套备份,请选择其它目录
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Information"),
QObject::tr("Cannot nest backups, please select another directory."),
QObject::tr("OK"));
return false;
} else if (fileName.endsWith(BACKUP_PATH)) {
QString subfile = fileName + "/snapshots";
if (Utils::isDirExist(subfile)) {
// 不能嵌套备份,请选择其它目录
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Information"),
QObject::tr("Cannot nest backups, please select another directory."),
QObject::tr("OK"));
return false;
}
}
}
return true;
}
void BackupPositionSelectDialog::goAccept()
{
if (checkSelectedFiles())
accept();
}
// 主题和文管重构了QFileDialog需要使用下面代码绑定accept信号
void BackupPositionSelectDialog::showEvent(QShowEvent* event)
{
Q_UNUSED(event)
if (this->isVisible()) {
for (QWidget* widget : this->findChildren<QWidget*>()) {
if (widget->objectName() == "acceptButton") {
QPushButton * btn = qobject_cast<QPushButton*>(widget);
disconnect(btn, &QPushButton::clicked, 0, 0);
connect(btn, &QPushButton::clicked, [=](bool checked){
Q_UNUSED(checked)
this->goAccept();
});
}
}
}
}

View File

@ -9,6 +9,13 @@ class BackupPositionSelectDialog : public QFileDialog {
public: public:
explicit BackupPositionSelectDialog(QWidget* parent = nullptr); explicit BackupPositionSelectDialog(QWidget* parent = nullptr);
virtual ~BackupPositionSelectDialog(); virtual ~BackupPositionSelectDialog();
bool checkSelectedFiles();
public slots:
void goAccept();
protected:
void showEvent(QShowEvent* event) override;
}; };

View File

@ -20,17 +20,17 @@ bool FileFilterProxyModeForBackup::filterAcceptsRow(int sourceRow, const QModelI
QString fileName = fileModel->fileName(index0); QString fileName = fileModel->fileName(index0);
// 防命令注入 // 防命令注入
// 1、形如mkdir '`id&>id_bak_test.txt`'中的文件夹名称 // 1、形如mkdir '`id&>id_bak_test.txt`'中的文件夹名称
// if (fileName.contains(QRegularExpression(".*`.*`.*"))) if (fileName.contains(QRegularExpression(".*`.*`.*")))
// return false; return false;
// // 2、形如$()的文件夹名称 // 2、形如$()的文件夹名称
// if (fileName.contains(QRegularExpression(".*\\$\\(.*\\).*"))) if (fileName.contains(QRegularExpression(".*\\$\\(.*\\).*")))
// return false; return false;
// // 3、形如${}的文件夹名称 // 3、形如${}的文件夹名称
// if (fileName.contains(QRegularExpression(".*\\$\\{.*\\}.*"))) if (fileName.contains(QRegularExpression(".*\\$\\{.*\\}.*")))
// return false; return false;
// // 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号 // 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号
// if (fileName.contains(QRegularExpression("[;&|]+"))) if (fileName.contains(QRegularExpression("[;&|]+")))
// return false; return false;
QString filePath = fileModel->filePath(index0); QString filePath = fileModel->filePath(index0);
if (fileName == "backup") { if (fileName == "backup") {

View File

@ -3,12 +3,13 @@
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton> #include <QPushButton>
#include "filefilterproxymodelforbackup.h" #include "filefilterproxymodelforbackup.h"
#include "../messageboxutils.h"
MyFileSelect::MyFileSelect(QWidget* parent) : MyFileSelect::MyFileSelect(QWidget* parent) :
QFileDialog(parent) QFileDialog(parent)
{ {
this->setFileMode(QFileDialog::ExistingFiles); this->setFileMode(QFileDialog::ExistingFiles);
// sdk和文管重构了QFileDialog下面代码已经失效 // 主题和文管重构了QFileDialog下面代码已经失效
/* /*
this->setViewMode(QFileDialog::Detail); this->setViewMode(QFileDialog::Detail);
this->setFilter(QDir::System | QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); this->setFilter(QDir::System | QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
@ -27,15 +28,37 @@ MyFileSelect::MyFileSelect(QWidget* parent) :
*/ */
} }
bool MyFileSelect::checkSelectedFiles()
{
QStringList selectFiles = selectedFiles();
for (const QString & fileName : selectFiles) {
// 防命令注入
// 1、形如mkdir '`id&>id_bak_test.txt`'中的文件夹名称
// 2、形如$()的文件夹名称
// 3、形如${}的文件夹名称
// 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号
if ( fileName.contains(QRegularExpression(".*`.*`.*"))
|| fileName.contains(QRegularExpression(".*\\$\\(.*\\).*"))
|| fileName.contains(QRegularExpression(".*\\$\\{.*\\}.*"))
|| fileName.contains(QRegularExpression("[;&|]+"))) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), QObject::tr("Path can not include symbols that such as : ``,$(),${},;,&,|,etc."), QObject::tr("OK"));
return false;
}
}
return true;
}
void MyFileSelect::goAccept() void MyFileSelect::goAccept()
{ {
accept(); if (checkSelectedFiles())
accept();
} }
MyFileSelect::~MyFileSelect() MyFileSelect::~MyFileSelect()
{} {}
// sdk和文管重构了QFileDialog需要使用下面代码绑定accept信号 // 主题和文管重构了QFileDialog需要使用下面代码绑定accept信号
void MyFileSelect::showEvent(QShowEvent* event) void MyFileSelect::showEvent(QShowEvent* event)
{ {
Q_UNUSED(event) Q_UNUSED(event)

View File

@ -9,6 +9,7 @@ public:
explicit MyFileSelect(QWidget* parent = nullptr); explicit MyFileSelect(QWidget* parent = nullptr);
virtual ~MyFileSelect(); virtual ~MyFileSelect();
bool checkSelectedFiles();
public slots: public slots:
void goAccept(); void goAccept();

View File

@ -3,13 +3,13 @@
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QPainter> #include <QPainter>
#include <ukuistylehelper/ukuistylehelper.h>
#include <windowmanager/windowmanager.h>
#include <unistd.h> #include <unistd.h>
#include "globalbackupinfo.h" #include "globalbackupinfo.h"
#include "../common/mydefine.h" #include "../common/mydefine.h"
#include "../common/utils.h" #include "../common/utils.h"
#include "messageboxutils.h" #include "messageboxutils.h"
#include <ukuistylehelper/ukuistylehelper.h>
#include <windowmanager/windowmanager.h>
DeleteBackupDialog::DeleteBackupDialog(ParseBackupList::BackupPoint backupPonit, QWidget *parent) : DeleteBackupDialog::DeleteBackupDialog(ParseBackupList::BackupPoint backupPonit, QWidget *parent) :
QDialog(parent), QDialog(parent),

View File

@ -0,0 +1,10 @@
#! /bin/bash
ts_file_list=(`ls *.ts`)
for ts in "${ts_file_list[@]}"
do
lrelease "${ts}"
done
mv *.qm resource/language/

View File

@ -146,14 +146,20 @@ FORMS += \
OTHER_FILES += OTHER_FILES +=
TRANSLATIONS += qt_zh_CN.ts \ TRANSLATIONS += qt_zh_CN.ts \
qt_bo_CN.ts qt_bo_CN.ts \
qt_mn_MN.ts \
qt_zh_HK.ts
system(rm -f qt_zh_CN.qm) message($$system(./generate_translations_qm.sh))
!system(lrelease qt_zh_CN.ts): error("Failed to generate cp qt_zh_CN.qm") #system(rm -f qt_zh_CN.qm)
system(cp qt_zh_CN.qm resource/language/) #!system(lrelease qt_zh_CN.ts): error("Failed to generate cp qt_zh_CN.qm")
system(rm -f qt_bo_CN.qm) #system(cp qt_zh_CN.qm resource/language/)
!system(lrelease qt_bo_CN.ts): error("Failed to generate qt_bo_CN.qm") #system(rm -f qt_bo_CN.qm)
system(cp qt_bo_CN.qm resource/language/) #!system(lrelease qt_bo_CN.ts): error("Failed to generate qt_bo_CN.qm")
#system(cp qt_bo_CN.qm resource/language/)
#system(rm -f qt_mn_MN.qm)
#!system(lrelease qt_mn_MN.ts): error("Failed to generate qt_mn_MN.qm")
#system(cp qt_mn_MN.qm resource/language/)
RESOURCES += \ RESOURCES += \
app.qrc app.qrc

View File

@ -57,16 +57,17 @@ int main(int argc, char *argv[])
GlobelBackupInfo::instance().setIsWayland(true); GlobelBackupInfo::instance().setIsWayland(true);
} }
MainDialog w; MainDialog w;
// 居中窗口
centerToScreen(&w);
a.setWindowIcon(QIcon::fromTheme(THEME_YHKYLIN_BACKUP_TOOLS, QIcon(":/images/yhkylin-backup-tools.png"))); a.setWindowIcon(QIcon::fromTheme(THEME_YHKYLIN_BACKUP_TOOLS, QIcon(":/images/yhkylin-backup-tools.png")));
a.setActivationWindow(&w, true); a.setActivationWindow(&w, true);
a.setApplicationName(QObject::tr("Backup & Restore"));
a.setApplicationVersion("4.1.0.0-0k17.18");
//如果是第一个实例,则绑定,方便下次调用 //如果是第一个实例,则绑定,方便下次调用
QObject::connect(&a,SIGNAL(messageReceived(const QString&)),&w,SLOT(sltMessageReceived(const QString&))); QObject::connect(&a,SIGNAL(messageReceived(const QString&)),&w,SLOT(sltMessageReceived(const QString&)));
w.show(); w.show();
// 居中窗口
centerToScreen(&w);
return a.exec(); return a.exec();
} }
@ -82,53 +83,70 @@ void initApp(QApplication& a)
// 区分中英文 // 区分中英文
QString locale = QLocale::system().name(); QString locale = QLocale::system().name();
// QT自身标准的翻译 // QT自身标准的翻译
#ifndef QT_NO_TRANSLATION #ifndef QT_NO_TRANSLATION
QString translatorFileName = QLatin1String("qt_");
translatorFileName += locale;
QTranslator *selfTransOfQt = new QTranslator(); QTranslator *selfTransOfQt = new QTranslator();
if (selfTransOfQt->load(translatorFileName, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) if (selfTransOfQt->load(QLocale(), QString("qt"), QString("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
a.installTranslator(selfTransOfQt); a.installTranslator(selfTransOfQt);
else else
qDebug() << "load qt translator file failed!"; qDebug() << "load qt translator file failed!";
#endif #endif
// 应用内的翻译
QTranslator *translator = new QTranslator();
if (locale == "zh_CN") {
//中文需要翻译
if (!translator->load(":/language/qt_zh_CN.qm")) //qtcreator启动后看到在资源目录下
qDebug() << "load translator file failed!";
else
a.installTranslator(translator);
} else if (locale == "bo_CN") {
//藏文需要翻译
if (!translator->load(":/language/qt_bo_CN.qm")) //qtcreator启动后看到在资源目录下
qDebug() << "load translator file failed!";
else
a.installTranslator(translator);
}
// 部分sdk控件用到翻译文件 // 部分sdk控件用到翻译文件
QTranslator *transGui = new QTranslator(); QTranslator *transGui = new QTranslator();
if (locale == "zh_CN") if (locale == "zh_CN") {
{ if (transGui->load(":/translations/gui_zh_CN.qm"))
if(transGui->load(":/translations/gui_zh_CN.qm"))
{ {
a.installTranslator(transGui); a.installTranslator(transGui);
} }
} else if (locale == "bo_CN") } else if (locale == "bo_CN") {
{ if (transGui->load(":/translations/gui_bo_CN.qm"))
if(transGui->load(":/translations/gui_bo_CN.qm"))
{ {
a.installTranslator(transGui); a.installTranslator(transGui);
} }
} else if (locale == "mn_MN" || locale == "mn") {
if (transGui->load(":/translations/gui_mn.qm")) {
a.installTranslator(transGui);
}
} else if (locale == "zh_HK") {
if(transGui->load(":/translations/gui_zh_HK.qm")) {
a.installTranslator(transGui);
}
} }
// 命令行参数 // 应用内的翻译
QCoreApplication::setApplicationName(QObject::tr("kybackup")); QTranslator *translator = new QTranslator();
QCoreApplication::setApplicationVersion("4.0.14"); QString translatorResourceName(":/language/qt_");
translatorResourceName += locale;
translatorResourceName += ".qm";
if (!translator->load(translatorResourceName)) //qtcreator启动后看到在资源目录下
qDebug() << translatorResourceName << "load translator file failed!";
else {
a.installTranslator(translator);
qDebug() << translatorResourceName << "load translator file success!";
}
// if (locale == "zh_CN") {
// //中文需要翻译
// if (!translator->load(":/language/qt_zh_CN.qm")) //qtcreator启动后看到在资源目录下
// qDebug() << "load translator file failed!";
// else
// a.installTranslator(translator);
// } else if (locale == "bo_CN") {
// //藏文需要翻译
// if (!translator->load(":/language/qt_bo_CN.qm")) //qtcreator启动后看到在资源目录下
// qDebug() << "load translator file failed!";
// else
// a.installTranslator(translator);
// } else if (locale == "mn_MN") {
// //蒙古文需要翻译
// if (!translator->load(":/language/qt_mn_MN.qm")) //qtcreator启动后看到在资源目录下
// qDebug() << "load translator file failed!";
// else
// a.installTranslator(translator);
// }
// 命令行参数
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("kybackup helper"); parser.setApplicationDescription("kybackup helper");
parser.addHelpOption(); parser.addHelpOption();
@ -219,7 +237,6 @@ void centerToScreen(QWidget* widget)
{ {
if (!widget) if (!widget)
return; return;
int x = widget->width(); int x = widget->width();
int y = widget->height(); int y = widget->height();

View File

@ -17,7 +17,6 @@
#include "aboutdialog.h" #include "aboutdialog.h"
#include "gsettingswrapper.h" #include "gsettingswrapper.h"
#include "component/mywidget.h" #include "component/mywidget.h"
#include "../common/dynamiccreator.h"
MainDialog::MainDialog(QWidget *parent) MainDialog::MainDialog(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
@ -126,7 +125,7 @@ void MainDialog::initTileBar()
m_maxBtn = new QPushButton; m_maxBtn = new QPushButton;
m_closeBtn = new QPushButton; m_closeBtn = new QPushButton;
m_menuOptionBtn->setToolTip(tr("Main menu")); m_menuOptionBtn->setToolTip(tr("Options"));
m_minBtn->setToolTip(tr("Minimize")); m_minBtn->setToolTip(tr("Minimize"));
m_maxBtn->setToolTip(tr("Maximize")); m_maxBtn->setToolTip(tr("Maximize"));
m_closeBtn->setToolTip(tr("Close")); m_closeBtn->setToolTip(tr("Close"));
@ -179,7 +178,7 @@ void MainDialog::initTileBar()
backupMain->addAction(m_backupHelp); backupMain->addAction(m_backupHelp);
m_backupAbout = new QAction(tr("About"), m_titleWidget); m_backupAbout = new QAction(tr("About"), m_titleWidget);
backupMain->addAction(m_backupAbout); backupMain->addAction(m_backupAbout);
m_backupExit = new QAction(tr("Exit"), m_titleWidget); m_backupExit = new QAction(tr("Quit"), m_titleWidget);
backupMain->addAction(m_backupExit); backupMain->addAction(m_backupExit);
m_titleLayout->addStretch(); m_titleLayout->addStretch();

View File

@ -5,6 +5,7 @@
#include "../common/mydefine.h" #include "../common/mydefine.h"
#include "globalsignals.h" #include "globalsignals.h"
#include "globalbackupinfo.h" #include "globalbackupinfo.h"
#include <QPushButton>
MyMessageBox::MyMessageBox(QWidget *parent) : MyMessageBox::MyMessageBox(QWidget *parent) :
@ -48,8 +49,8 @@ void MessageBoxUtils::QMESSAGE_BOX_INFORMATION(QWidget* q_parent, const QString&
box->setMsgIcon(QMessageBox::Information); box->setMsgIcon(QMessageBox::Information);
box->setWindowTitle(typelabel); box->setWindowTitle(typelabel);
box->setText(message); box->setText(message);
box->setStandardButtons(QMessageBox::Ok); QPushButton * okbtn = box->addButton(label, QMessageBox::AcceptRole);
box->setButtonText(QMessageBox::Ok, label); box->setDefaultButton(okbtn);
box->exec(); box->exec();
} }
@ -59,8 +60,8 @@ void MessageBoxUtils::QMESSAGE_BOX_WARNING(QWidget* q_parent, const QString& typ
box->setMsgIcon(QMessageBox::Warning); box->setMsgIcon(QMessageBox::Warning);
box->setWindowTitle(typelabel); box->setWindowTitle(typelabel);
box->setText(message); box->setText(message);
box->setStandardButtons(QMessageBox::Ok); QPushButton * okbtn = box->addButton(label, QMessageBox::AcceptRole);
box->setButtonText(QMessageBox::Ok, label); box->setDefaultButton(okbtn);
box->exec(); box->exec();
} }
@ -70,10 +71,11 @@ bool MessageBoxUtils::QMESSAGE_BOX_WARNING_CANCEL(QWidget *q_parent, const QStri
box->setMsgIcon(QMessageBox::Question); box->setMsgIcon(QMessageBox::Question);
box->setWindowTitle(typelabel); box->setWindowTitle(typelabel);
box->setText(message); box->setText(message);
box->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); QPushButton * okbtn = box->addButton(label_yes, QMessageBox::AcceptRole);
box->setButtonText(QMessageBox::Ok, label_yes); box->addButton(label_no, QMessageBox::RejectRole);
box->setButtonText(QMessageBox::Cancel, label_no); box->setDefaultButton(okbtn);
if (box->exec() != QMessageBox::Ok) { box->exec();
if (box->clickedButton() != okbtn) {
return false; return false;
} }
@ -86,7 +88,7 @@ void MessageBoxUtils::QMESSAGE_BOX_CRITICAL(QWidget* q_parent, const QString& ty
box->setMsgIcon(QMessageBox::Critical); box->setMsgIcon(QMessageBox::Critical);
box->setWindowTitle(typelabel); box->setWindowTitle(typelabel);
box->setText(message); box->setText(message);
box->setStandardButtons(QMessageBox::Ok); QPushButton * okbtn = box->addButton(label, QMessageBox::AcceptRole);
box->setButtonText(QMessageBox::Ok, label); box->setDefaultButton(okbtn);
box->exec(); box->exec();
} }

View File

@ -50,13 +50,13 @@ DataBackup::DataBackup(QWidget *parent /*= nullptr*/) :
m_pInterface(nullptr) m_pInterface(nullptr)
{ {
// 界面手写代码创建,作为练手 // 界面手写代码创建,作为练手
initFirstWidget(); initHomePage();
initSecondWidget(); initSetBackupPathsPage();
initSecondWidget_inc(); initSetIncPathsPage();
initThirdWidget(); initCheckPage();
initForthWidget(); initNameBackupPage();
initFifthWidget(); initBackupingPage();
initLastWidget(); initLastPage();
} }
DataBackup::~DataBackup() DataBackup::~DataBackup()
@ -71,7 +71,7 @@ DataBackup::~DataBackup()
/** /**
* @brief * @brief
*/ */
void DataBackup::initFirstWidget() void DataBackup::initHomePage()
{ {
QWidget *homePage = new QWidget; QWidget *homePage = new QWidget;
QVBoxLayout *vLayout = new QVBoxLayout; QVBoxLayout *vLayout = new QVBoxLayout;
@ -225,6 +225,7 @@ void DataBackup::initFirstWidget()
backupPointManage->setProperty("useButtonPalette", true); backupPointManage->setProperty("useButtonPalette", true);
QPalette pal(backupPointManage->palette()); QPalette pal(backupPointManage->palette());
pal.setColor(QPalette::ButtonText, this->palette().highlight().color()); pal.setColor(QPalette::ButtonText, this->palette().highlight().color());
pal.setColor(QPalette::Text, this->palette().highlight().color());
pal.setColor(QPalette::Button, this->palette().base().color()); pal.setColor(QPalette::Button, this->palette().base().color());
backupPointManage->setPalette(pal); backupPointManage->setPalette(pal);
bottomHBoxLayout->addWidget(backupPointManage); bottomHBoxLayout->addWidget(backupPointManage);
@ -243,15 +244,19 @@ void DataBackup::initFirstWidget()
connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::styleNameChanged, this, [=](bool isDark) { connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::styleNameChanged, this, [=](bool isDark) {
Q_UNUSED(isDark) Q_UNUSED(isDark)
// 深浅主题切换时,因为调色板已经更换,高亮等颜色已经改变,所以要重新加载图标。 // 深浅主题切换时,因为调色板已经更换,高亮等颜色已经改变,所以要重新加载图标。
// from席博文因为按钮的文字颜色换了一个类型因为设计非要什么半透明透明效果的Text就是半透明的buttontext就是不半透明。所以将ButtonText改为Text
QPalette pal(backupPointManage->palette()); QPalette pal(backupPointManage->palette());
pal.setColor(QPalette::ButtonText, this->palette().highlight().color()); pal.setColor(QPalette::ButtonText, this->palette().highlight().color());
pal.setColor(QPalette::Text, this->palette().highlight().color());
pal.setColor(QPalette::Button, this->palette().base().color()); pal.setColor(QPalette::Button, this->palette().base().color());
backupPointManage->setPalette(pal); backupPointManage->setPalette(pal);
}); });
connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::themeColorChanged, this, [=]() { connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::themeColorChanged, this, [=]() {
// 强调色更换,高亮等颜色已经改变,所以要重新加载图标。 // 强调色更换,高亮等颜色已经改变,所以要重新加载图标。
// from席博文因为按钮的文字颜色换了一个类型因为设计非要什么半透明透明效果的Text就是半透明的buttontext就是不半透明。所以将ButtonText改为Text
QPalette pal(backupPointManage->palette()); QPalette pal(backupPointManage->palette());
pal.setColor(QPalette::ButtonText, this->palette().highlight().color()); pal.setColor(QPalette::ButtonText, this->palette().highlight().color());
pal.setColor(QPalette::Text, this->palette().highlight().color());
pal.setColor(QPalette::Button, this->palette().base().color()); pal.setColor(QPalette::Button, this->palette().base().color());
backupPointManage->setPalette(pal); backupPointManage->setPalette(pal);
}); });
@ -288,7 +293,7 @@ void DataBackup::on_next_clicked(bool checked)
/** /**
* @brief * @brief
*/ */
void DataBackup::initSecondWidget() void DataBackup::initSetBackupPathsPage()
{ {
QWidget *selectPathPage = new QWidget; QWidget *selectPathPage = new QWidget;
@ -515,25 +520,7 @@ void DataBackup::initSecondWidget()
if (!selectFiles.isEmpty()) { if (!selectFiles.isEmpty()) {
QString fileName = selectFiles.at(0); QString fileName = selectFiles.at(0);
// sdk重新实现了QFileDialog后很多功能已经丢失(如:文件夹过滤等),需要再次进行过滤,真难用 if (Utils::isSubPath(this->m_udiskPaths, fileName)) {
QString backupPath = Utils::getSysRootPath() + BACKUP_PATH;
backupPath.replace("//", "/");
if (fileName == backupPath || fileName.contains(BACKUP_SNAPSHOTS_PATH)) {
// 不能嵌套备份,请选择其它目录
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"),
QObject::tr("Cannot nest backups, please select another directory."),
QObject::tr("OK"));
return ;
} else if (fileName.endsWith(BACKUP_PATH)) {
QString subfile = fileName + "/snapshots";
if (Utils::isDirExist(subfile)) {
// 不能嵌套备份,请选择其它目录
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"),
QObject::tr("Cannot nest backups, please select another directory."),
QObject::tr("OK"));
return ;
}
} else if (Utils::isSubPath(this->m_udiskPaths, fileName)) {
// 选择了移动设备挂载目录的子孙目录 // 选择了移动设备挂载目录的子孙目录
// 移动设备只能选择挂载目录本身 // 移动设备只能选择挂载目录本身
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"), MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"),
@ -671,7 +658,7 @@ void DataBackup::getPathsLimit(QStringList &pathLimits, QList<QUrl> &siderUrls)
/** /**
* @brief () * @brief ()
*/ */
void DataBackup::initSecondWidget_inc() void DataBackup::initSetIncPathsPage()
{ {
QWidget *incSelectPathPage = new QWidget; QWidget *incSelectPathPage = new QWidget;
@ -935,9 +922,9 @@ void DataBackup::addOldBackupPaths(BackupListWidget *listWidget)
} }
/** /**
* @brief * @brief
*/ */
void DataBackup::initThirdWidget() void DataBackup::initCheckPage()
{ {
QWidget *checkPage = new QWidget; QWidget *checkPage = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;
@ -1335,10 +1322,10 @@ void DataBackup::on_checkEnv_end(int result)
} }
/** /**
* @brief * @brief
*/ */
#define MAX_LEN_BACKUPNAME 64 #define MAX_LEN_BACKUPNAME 64
void DataBackup::initForthWidget() void DataBackup::initNameBackupPage()
{ {
QWidget *nameBackupPage = new QWidget; QWidget *nameBackupPage = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;
@ -1616,9 +1603,9 @@ bool DataBackup::isExistsBackupName(const QString & backupName)
} }
/** /**
* @brief * @brief
*/ */
void DataBackup::initFifthWidget() void DataBackup::initBackupingPage()
{ {
QWidget *backupingPage = new QWidget; QWidget *backupingPage = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;
@ -1669,7 +1656,6 @@ void DataBackup::initFifthWidget()
hlayoutTopLine2->addWidget(label4); hlayoutTopLine2->addWidget(label4);
hlayoutTopLine2->addSpacing(11); hlayoutTopLine2->addSpacing(11);
vlayout->addLayout(hlayoutTopLine2); vlayout->addLayout(hlayoutTopLine2);
vlayout->addSpacing(80); vlayout->addSpacing(80);
// ------------ 中部布局begin------------- // ------------ 中部布局begin-------------
@ -1706,8 +1692,8 @@ void DataBackup::initFifthWidget()
hlayoutCenterFont2->addStretch(); hlayoutCenterFont2->addStretch();
hlayoutCenterFont2->setAlignment(Qt::AlignCenter); hlayoutCenterFont2->setAlignment(Qt::AlignCenter);
vlayout->addLayout(hlayoutCenterFont2); vlayout->addLayout(hlayoutCenterFont2);
vlayout->addSpacing(56); vlayout->addSpacing(56);
// 第三行 // 第三行
QHBoxLayout *hlayoutCenterFont3 = new QHBoxLayout; QHBoxLayout *hlayoutCenterFont3 = new QHBoxLayout;
// 取消按钮 // 取消按钮
@ -1737,6 +1723,10 @@ void DataBackup::initFifthWidget()
// 不要使用电脑,以防数据丢失 // 不要使用电脑,以防数据丢失
labelTip->setDeplayText(tr("Do not use computer in case of data loss")); labelTip->setDeplayText(tr("Do not use computer in case of data loss"));
cancel->setEnabled(true); cancel->setEnabled(true);
if (this->m_isIncrement)
cancel->setVisible(false);
else
cancel->setVisible(true);
// 开始备份 // 开始备份
this->on_backup_start(); this->on_backup_start();
@ -1897,7 +1887,7 @@ void DataBackup::on_backup_end(bool result)
/** /**
* @brief * @brief
*/ */
void DataBackup::initLastWidget() void DataBackup::initLastPage()
{ {
QWidget *last = new QWidget; QWidget *last = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;

View File

@ -37,13 +37,13 @@ public:
~DataBackup(); ~DataBackup();
private: private:
void initFirstWidget(); void initHomePage();
void initSecondWidget(); void initSetBackupPathsPage();
void initSecondWidget_inc(); void initSetIncPathsPage();
void initThirdWidget(); void initCheckPage();
void initForthWidget(); void initNameBackupPage();
void initFifthWidget(); void initBackupingPage();
void initLastWidget(); void initLastPage();
QList<ParseBackupList::BackupPoint> getBackupPointList(); QList<ParseBackupList::BackupPoint> getBackupPointList();
bool isExistsBackupName(const QString & backupName); bool isExistsBackupName(const QString & backupName);

View File

@ -39,10 +39,10 @@ DataRestore::DataRestore(QWidget *parent) :
m_pInterface = nullptr; m_pInterface = nullptr;
// 界面手写代码创建,作为练手 // 界面手写代码创建,作为练手
initFirstWidget(); initHomePage();
initSecondWidget(); initCheckPage();
initThirdWidget(); initRestoringPage();
initLastWidget(); initLastPage();
} }
DataRestore::~DataRestore() DataRestore::~DataRestore()
@ -54,7 +54,7 @@ DataRestore::~DataRestore()
/** /**
* @brief * @brief
*/ */
void DataRestore::initFirstWidget() void DataRestore::initHomePage()
{ {
QWidget *homePage = new QWidget; QWidget *homePage = new QWidget;
QVBoxLayout *vLayout = new QVBoxLayout; QVBoxLayout *vLayout = new QVBoxLayout;
@ -241,7 +241,7 @@ bool DataRestore::checkIsNeedReboot()
/** /**
* @brief -- * @brief --
*/ */
void DataRestore::initSecondWidget() void DataRestore::initCheckPage()
{ {
QWidget *checkPage = new QWidget; QWidget *checkPage = new QWidget;
@ -611,7 +611,7 @@ void DataRestore::on_checkEnv_end(int result)
/** /**
* @brief - * @brief -
*/ */
void DataRestore::initThirdWidget() void DataRestore::initRestoringPage()
{ {
QWidget *restoringPage = new QWidget; QWidget *restoringPage = new QWidget;
@ -847,7 +847,7 @@ void DataRestore::on_restore_end(bool result)
/** /**
* @brief * @brief
*/ */
void DataRestore::initLastWidget() void DataRestore::initLastPage()
{ {
QWidget *last = new QWidget; QWidget *last = new QWidget;

View File

@ -32,10 +32,10 @@ public:
virtual ~DataRestore(); virtual ~DataRestore();
private: private:
void initFirstWidget(); void initHomePage();
void initSecondWidget(); void initCheckPage();
void initThirdWidget(); void initRestoringPage();
void initLastWidget(); void initLastPage();
bool checkIsNeedReboot(); bool checkIsNeedReboot();

View File

@ -44,11 +44,11 @@ GhostImage::GhostImage(QWidget *parent) :
m_iPosition = BackupPosition::LOCAL; m_iPosition = BackupPosition::LOCAL;
// 界面手写代码创建,作为练手 // 界面手写代码创建,作为练手
initFirstWidget(); initHomePage();
initSecondWidget(); initSetSavePathPage();
initThirdWidget(); initCheckPage();
initForthWidget(); initGhostingPage();
initLastWidget(); initLastPage();
} }
GhostImage::~GhostImage() GhostImage::~GhostImage()
@ -63,7 +63,7 @@ GhostImage::~GhostImage()
/** /**
* @brief * @brief
*/ */
void GhostImage::initFirstWidget() void GhostImage::initHomePage()
{ {
QWidget *homePage = new QWidget; QWidget *homePage = new QWidget;
QVBoxLayout *vLayout = new QVBoxLayout; QVBoxLayout *vLayout = new QVBoxLayout;
@ -215,9 +215,9 @@ void GhostImage::on_next_clicked(bool checked)
} }
/** /**
* @brief * @brief
*/ */
void GhostImage::initSecondWidget() void GhostImage::initSetSavePathPage()
{ {
QWidget *savePathPage = new QWidget; QWidget *savePathPage = new QWidget;
// 纵向布局 // 纵向布局
@ -311,9 +311,9 @@ void GhostImage::initSecondWidget()
} }
/** /**
* @brief * @brief
*/ */
void GhostImage::initThirdWidget() void GhostImage::initCheckPage()
{ {
QWidget *checkPage = new QWidget; QWidget *checkPage = new QWidget;
@ -742,9 +742,9 @@ QString GhostImage::createGhostImageName(const QString& backupName)
} }
/** /**
* @brief * @brief
*/ */
void GhostImage::initForthWidget() void GhostImage::initGhostingPage()
{ {
QWidget *ghostingPage = new QWidget; QWidget *ghostingPage = new QWidget;
@ -1064,7 +1064,7 @@ void GhostImage::on_ghost_end(bool result)
/** /**
* @brief * @brief
*/ */
void GhostImage::initLastWidget() void GhostImage::initLastPage()
{ {
QWidget *last = new QWidget; QWidget *last = new QWidget;

View File

@ -35,11 +35,11 @@ public:
virtual ~GhostImage(); virtual ~GhostImage();
private: private:
void initFirstWidget(); void initHomePage();
void initSecondWidget(); void initSetSavePathPage();
void initThirdWidget(); void initCheckPage();
void initForthWidget(); void initGhostingPage();
void initLastWidget(); void initLastPage();
QString createGhostImageName(const QString& backupName); QString createGhostImageName(const QString& backupName);
signals: signals:

View File

@ -150,6 +150,7 @@ void ManageBackupPointList::insertLines(const QList<ParseBackupList::BackupPoint
} else { } else {
prefixPath_to_device = QObject::tr("Local Disk:") + " " + BACKUP_SNAPSHOTS_PATH; prefixPath_to_device = QObject::tr("Local Disk:") + " " + BACKUP_SNAPSHOTS_PATH;
} }
prefixPath_to_device.replace("//", "/");
setItem(indexOfRow, Column_Index::Backup_Device, prefixPath_to_device); setItem(indexOfRow, Column_Index::Backup_Device, prefixPath_to_device);
if (backupPoint.m_state == BACKUP_PARSE_STATE_SUCCESS_STRTING) { if (backupPoint.m_state == BACKUP_PARSE_STATE_SUCCESS_STRTING) {
setItem(indexOfRow, Column_Index::Backup_State, tr("backup finished")); setItem(indexOfRow, Column_Index::Backup_State, tr("backup finished"));

View File

@ -15,7 +15,7 @@ OperationLog::OperationLog(QWidget *parent) :
QStackedWidget(parent) QStackedWidget(parent)
{ {
// 界面手写代码创建,作为练手 // 界面手写代码创建,作为练手
initFirstWidget(); initHomePage();
} }
OperationLog::~OperationLog() OperationLog::~OperationLog()
@ -24,7 +24,7 @@ OperationLog::~OperationLog()
/** /**
* @brief * @brief
*/ */
void OperationLog::initFirstWidget() void OperationLog::initHomePage()
{ {
QWidget *homePage = new QWidget; QWidget *homePage = new QWidget;

View File

@ -24,7 +24,7 @@ public:
virtual ~OperationLog(); virtual ~OperationLog();
private: private:
void initFirstWidget(); void initHomePage();
void initOperationLogs(const QList<BackupWrapper>& list); void initOperationLogs(const QList<BackupWrapper>& list);
void setItem(int row, int column, const QString& text, int alignFlag = Qt::AlignLeft | Qt::AlignVCenter); void setItem(int row, int column, const QString& text, int alignFlag = Qt::AlignLeft | Qt::AlignVCenter);
QString castTypeToString(int type); QString castTypeToString(int type);

View File

@ -173,6 +173,7 @@ void SelectRestorePoint::insertLines(const QList<ParseBackupList::BackupPoint> &
} else { } else {
prefixPath_to_device = QObject::tr("Local Disk:") + " " + BACKUP_SNAPSHOTS_PATH; prefixPath_to_device = QObject::tr("Local Disk:") + " " + BACKUP_SNAPSHOTS_PATH;
} }
prefixPath_to_device.replace("//", "/");
setItem(indexOfRow, Column_Index::Backup_Device, prefixPath_to_device); setItem(indexOfRow, Column_Index::Backup_Device, prefixPath_to_device);
setItem(indexOfRow, Column_Index::Backup_State, backupPoint.m_state); setItem(indexOfRow, Column_Index::Backup_State, backupPoint.m_state);
setItem(indexOfRow, Column_Index::Prefix_Path, backupPoint.m_path); setItem(indexOfRow, Column_Index::Prefix_Path, backupPoint.m_path);

View File

@ -45,12 +45,12 @@ SystemBackup::SystemBackup(QWidget *parent /*= nullptr*/) :
m_pInterface(nullptr) m_pInterface(nullptr)
{ {
// 界面手写代码创建,作为练手 // 界面手写代码创建,作为练手
initFirstWidget(); initHomePage();
initSecondWidget(); initSetBackupPathsPage();
initThirdWidget(); initCheckPage();
initForthWidget(); initNameBackupPage();
initFifthWidget(); initBackupingPage();
initLastWidget(); initLastPage();
} }
SystemBackup::~SystemBackup() SystemBackup::~SystemBackup()
@ -65,7 +65,7 @@ SystemBackup::~SystemBackup()
/** /**
* @brief * @brief
*/ */
void SystemBackup::initFirstWidget() void SystemBackup::initHomePage()
{ {
QWidget *homePage = new QWidget; QWidget *homePage = new QWidget;
QVBoxLayout *vLayout = new QVBoxLayout; QVBoxLayout *vLayout = new QVBoxLayout;
@ -167,6 +167,7 @@ void SystemBackup::initFirstWidget()
backupPointManage->setProperty("useButtonPalette", true); backupPointManage->setProperty("useButtonPalette", true);
QPalette pal(backupPointManage->palette()); QPalette pal(backupPointManage->palette());
pal.setColor(QPalette::ButtonText, this->palette().highlight().color()); pal.setColor(QPalette::ButtonText, this->palette().highlight().color());
pal.setColor(QPalette::Text, this->palette().highlight().color());
pal.setColor(QPalette::Button, this->palette().base().color()); pal.setColor(QPalette::Button, this->palette().base().color());
backupPointManage->setPalette(pal); backupPointManage->setPalette(pal);
bottomHBoxLayout->addWidget(backupPointManage); bottomHBoxLayout->addWidget(backupPointManage);
@ -185,15 +186,19 @@ void SystemBackup::initFirstWidget()
connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::styleNameChanged, this, [=](bool isDark) { connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::styleNameChanged, this, [=](bool isDark) {
Q_UNUSED(isDark) Q_UNUSED(isDark)
// 深浅主题切换时,因为调色板已经更换,高亮等颜色已经改变,所以要重新加载图标。 // 深浅主题切换时,因为调色板已经更换,高亮等颜色已经改变,所以要重新加载图标。
// from席博文因为按钮的文字颜色换了一个类型因为设计非要什么半透明透明效果的Text就是半透明的buttontext就是不半透明。所以将ButtonText改为Text
QPalette pal(backupPointManage->palette()); QPalette pal(backupPointManage->palette());
pal.setColor(QPalette::ButtonText, this->palette().highlight().color()); pal.setColor(QPalette::ButtonText, this->palette().highlight().color());
pal.setColor(QPalette::Text, this->palette().highlight().color());
pal.setColor(QPalette::Button, this->palette().base().color()); pal.setColor(QPalette::Button, this->palette().base().color());
backupPointManage->setPalette(pal); backupPointManage->setPalette(pal);
}); });
connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::themeColorChanged, this, [=]() { connect(GlobelBackupInfo::instance().getGlobalSignals(), &GlobalSignals::themeColorChanged, this, [=]() {
// 强调色更换,高亮等颜色已经改变,所以要重新加载图标。 // 强调色更换,高亮等颜色已经改变,所以要重新加载图标。
// from席博文因为按钮的文字颜色换了一个类型因为设计非要什么半透明透明效果的Text就是半透明的buttontext就是不半透明。所以将ButtonText改为Text
QPalette pal(backupPointManage->palette()); QPalette pal(backupPointManage->palette());
pal.setColor(QPalette::ButtonText, this->palette().highlight().color()); pal.setColor(QPalette::ButtonText, this->palette().highlight().color());
pal.setColor(QPalette::Text, this->palette().highlight().color());
pal.setColor(QPalette::Button, this->palette().base().color()); pal.setColor(QPalette::Button, this->palette().base().color());
backupPointManage->setPalette(pal); backupPointManage->setPalette(pal);
}); });
@ -228,9 +233,9 @@ void SystemBackup::on_next_clicked(bool checked)
} }
/** /**
* @brief * @brief
*/ */
void SystemBackup::initSecondWidget() void SystemBackup::initSetBackupPathsPage()
{ {
QWidget *selectPathPage = new QWidget; QWidget *selectPathPage = new QWidget;
// 纵向布局 // 纵向布局
@ -354,25 +359,7 @@ void SystemBackup::initSecondWidget()
if (!selectFiles.isEmpty()) { if (!selectFiles.isEmpty()) {
QString fileName = selectFiles.at(0); QString fileName = selectFiles.at(0);
// sdk重新实现了QFileDialog后很多功能已经丢失需要再次进行过滤真难用 if (Utils::isSubPath(this->m_udiskPaths, fileName)) {
QString backupPath = Utils::getSysRootPath() + BACKUP_PATH;
backupPath.replace("//", "/");
if (fileName == backupPath || fileName.contains(BACKUP_SNAPSHOTS_PATH)) {
// 不能嵌套备份,请选择其它目录
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"),
QObject::tr("Cannot nest backups, please select another directory."),
QObject::tr("OK"));
return ;
} else if (fileName.endsWith(BACKUP_PATH)) {
QString subfile = fileName + "/snapshots";
if (Utils::isDirExist(subfile)) {
// 不能嵌套备份,请选择其它目录
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"),
QObject::tr("Cannot nest backups, please select another directory."),
QObject::tr("OK"));
return ;
}
} else if (Utils::isSubPath(this->m_udiskPaths, fileName)) {
// 选择了移动设备挂载目录的子孙目录 // 选择了移动设备挂载目录的子孙目录
// 移动设备只能选择挂载目录本身 // 移动设备只能选择挂载目录本身
MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"), MessageBoxUtils::QMESSAGE_BOX_WARNING(GlobelBackupInfo::instance().getMainWidget(), QObject::tr("Information"),
@ -423,9 +410,9 @@ void SystemBackup::initSecondWidget()
} }
/** /**
* @brief * @brief
*/ */
void SystemBackup::initThirdWidget() void SystemBackup::initCheckPage()
{ {
QWidget *checkPage = new QWidget; QWidget *checkPage = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;
@ -748,8 +735,12 @@ void SystemBackup::on_checkEnv_start()
backupWrapper.m_backupPaths << backupPath; backupWrapper.m_backupPaths << backupPath;
backupWrapper.m_prefixDestPath = m_prefixDestPath; backupWrapper.m_prefixDestPath = m_prefixDestPath;
backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile()); backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile());
if (backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE) if (backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE){
backupWrapper.m_backupExcludePaths << m_prefixDestPath; QString prefixPath = m_prefixDestPath;
prefixPath += BACKUP_SNAPSHOTS_PATH;
prefixPath.replace("//", "/");
backupWrapper.m_backupExcludePaths << prefixPath;
}
backupWrapper.m_frontUid = getuid(); backupWrapper.m_frontUid = getuid();
backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_frontUserName = qgetenv("USER");
backupWrapper.m_gid = getgid(); backupWrapper.m_gid = getgid();
@ -822,10 +813,10 @@ void SystemBackup::on_checkEnv_end(int result)
} }
/** /**
* @brief * @brief
*/ */
#define MAX_LEN_BACKUPNAME 64 #define MAX_LEN_BACKUPNAME 64
void SystemBackup::initForthWidget() void SystemBackup::initNameBackupPage()
{ {
QWidget *nameBackupPage = new QWidget; QWidget *nameBackupPage = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;
@ -1098,9 +1089,9 @@ bool SystemBackup::isExistsBackupName(const QString & backupName)
} }
/** /**
* @brief * @brief
*/ */
void SystemBackup::initFifthWidget() void SystemBackup::initBackupingPage()
{ {
QWidget *backupingPage = new QWidget; QWidget *backupingPage = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;
@ -1293,8 +1284,12 @@ void SystemBackup::on_backup_start()
backupWrapper.m_backupPaths << backupPath; backupWrapper.m_backupPaths << backupPath;
backupWrapper.m_prefixDestPath = m_prefixDestPath; backupWrapper.m_prefixDestPath = m_prefixDestPath;
backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile()); backupWrapper.m_backupExcludePaths.append(Utils::getFromExcludePathsFile());
if (backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE) if (backupWrapper.m_iPosition == BackupPosition::CUSTOMIZE){
backupWrapper.m_backupExcludePaths << m_prefixDestPath; QString prefixPath = m_prefixDestPath;
prefixPath += BACKUP_SNAPSHOTS_PATH;
prefixPath.replace("//", "/");
backupWrapper.m_backupExcludePaths << prefixPath;
}
backupWrapper.m_frontUid = getuid(); backupWrapper.m_frontUid = getuid();
backupWrapper.m_frontUserName = qgetenv("USER"); backupWrapper.m_frontUserName = qgetenv("USER");
backupWrapper.m_gid = getgid(); backupWrapper.m_gid = getgid();
@ -1433,7 +1428,7 @@ void SystemBackup::on_backup_end(bool result)
/** /**
* @brief * @brief
*/ */
void SystemBackup::initLastWidget() void SystemBackup::initLastPage()
{ {
QWidget *last = new QWidget; QWidget *last = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout; QVBoxLayout *vlayout = new QVBoxLayout;

View File

@ -35,12 +35,12 @@ public:
~SystemBackup(); ~SystemBackup();
private: private:
void initFirstWidget(); void initHomePage();
void initSecondWidget(); void initSetBackupPathsPage();
void initThirdWidget(); void initCheckPage();
void initForthWidget(); void initNameBackupPage();
void initFifthWidget(); void initBackupingPage();
void initLastWidget(); void initLastPage();
QList<ParseBackupList::BackupPoint> getBackupPointList(); QList<ParseBackupList::BackupPoint> getBackupPointList();
bool isExistsBackupName(const QString & backupName); bool isExistsBackupName(const QString & backupName);

View File

@ -42,10 +42,10 @@ SystemRestore::SystemRestore(QWidget *parent) :
m_iPosition = BackupPosition::LOCAL; m_iPosition = BackupPosition::LOCAL;
// 界面手写代码创建,作为练手 // 界面手写代码创建,作为练手
initFirstWidget(); initHomePage();
initSecondWidget(); initCheckPage();
initThirdWidget(); initRestoringPage();
initLastWidget(); initLastPage();
} }
SystemRestore::~SystemRestore() SystemRestore::~SystemRestore()
@ -57,7 +57,7 @@ SystemRestore::~SystemRestore()
/** /**
* @brief * @brief
*/ */
void SystemRestore::initFirstWidget() void SystemRestore::initHomePage()
{ {
QWidget *homePage = new QWidget; QWidget *homePage = new QWidget;
QVBoxLayout *vLayout = new QVBoxLayout; QVBoxLayout *vLayout = new QVBoxLayout;
@ -259,7 +259,7 @@ void SystemRestore::on_button_beginRestore_clicked(bool checked)
/** /**
* @brief -- * @brief --
*/ */
void SystemRestore::initSecondWidget() void SystemRestore::initCheckPage()
{ {
QWidget *checkPage = new QWidget; QWidget *checkPage = new QWidget;
@ -637,7 +637,7 @@ void SystemRestore::on_checkEnv_end(int result)
/** /**
* @brief - * @brief -
*/ */
void SystemRestore::initThirdWidget() void SystemRestore::initRestoringPage()
{ {
QWidget *restoringPage = new QWidget; QWidget *restoringPage = new QWidget;
@ -874,7 +874,7 @@ void SystemRestore::on_restore_end(bool result)
/** /**
* @brief * @brief
*/ */
void SystemRestore::initLastWidget() void SystemRestore::initLastPage()
{ {
QWidget *last = new QWidget; QWidget *last = new QWidget;

View File

@ -32,10 +32,10 @@ public:
virtual ~SystemRestore(); virtual ~SystemRestore();
private: private:
void initFirstWidget(); void initHomePage();
void initSecondWidget(); void initCheckPage();
void initThirdWidget(); void initRestoringPage();
void initLastWidget(); void initLastPage();
signals: signals:
void startCheckEnv(); void startCheckEnv();

File diff suppressed because it is too large Load Diff

2107
kybackup/qt_mn_MN.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2107
kybackup/qt_zh_HK.ts Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1 @@
<クd<>箆!ソ`。スン

View File

@ -1,13 +1,16 @@
[Desktop Entry] [Desktop Entry]
Name=Backup and Restore Name=Backup & Restore
Name[zh_CN]=备份还原 Name[zh_CN]=备份还原
Name[zh_HK]=備份還原
Name[bo_CN]=རྗེས་གྲབས་དང་སླར་གསོ་བྱེད་དགོས། Name[bo_CN]=རྗེས་གྲབས་དང་སླར་གསོ་བྱེད་དགོས།
Name[mn]=ᠨᠥᠭᠡᠴᠡ ᠵᠢ ᠡᠬᠡᠬᠦᠯᠬᠦ
Comment=Provides system and data backup and restore functions Comment=Provides system and data backup and restore functions
Comment[zh_CN]=提供系统备份还原和数据备份还原等功能 Comment[zh_CN]=提供系统备份还原和数据备份还原等功能
Comment[bo_CN]=རྒྱུད་ཁོངས་གྲབས་ཉར་དང་གཞི་གྲངས་གྲབས་ཉར་སོར་ལོག་མཁོ་འདོན་སོགས་ཀྱི་ནུས་པ་ Comment[bo_CN]=རྒྱུད་ཁོངས་གྲབས་ཉར་དང་གཞི་གྲངས་གྲབས་ཉར་སོར་ལོག་མཁོ་འདོན་སོགས་ཀྱི་ནུས་པ་
Comment[mn]= ᠰᠢᠰᠲ᠋ᠧᠮ ᠤ᠋ᠨ ᠨᠦᠬᠡᠴᠡ ᠵᠢ ᠡᠬᠡᠬᠦᠯᠬᠦ᠂ ᠳ᠋ᠠᠢᠲ᠋ᠠ ᠵᠢᠨ ᠨᠥᠭᠡᠴᠡ ᠵᠢ ᠡᠬᠡᠬᠦᠯᠬᠦ ᠵᠡᠷᠭᠡ ᠴᠢᠳᠠᠪᠬᠢ ᠬᠠᠩᠭᠠᠠ
Keywords=backup Keywords=backup
TryExec=kybackup TryExec=kybackup
Exec=/usr/bin/kybackup %U Exec=kybackup %U
StartupNotify=true StartupNotify=true
Terminal=false Terminal=false
Type=Application Type=Application