606 lines
17 KiB
C++
Executable File
606 lines
17 KiB
C++
Executable File
#include "parsebackuplist.h"
|
||
#include <QFile>
|
||
#include <QTextStream>
|
||
#include <QXmlStreamReader>
|
||
#include <QDebug>
|
||
#include <algorithm>
|
||
|
||
/*
|
||
<?xml version='1.0'?>
|
||
<backupList>
|
||
<BackupPoint>
|
||
<Comment>21-07-21 14:14:01</Comment>
|
||
<Time>21-07-21 14:14:03</Time>
|
||
<Uuid>{beecb746-561f-4fa1-99ba-19fb849a1ba7}</Uuid>
|
||
<Size>24.26KB</Size>
|
||
<State>backup finished</State>
|
||
<Type>2</Type>
|
||
<Position>0</Position>
|
||
<UserId>1000</UserId>
|
||
<PrefixDestPath>/</PrefixDestPath>
|
||
</BackupPoint>
|
||
</backupList>
|
||
*/
|
||
|
||
#define BACKUPLIST "backupList"
|
||
#define BACKUPPOINT "BackupPoint"
|
||
#define COMMENT "Comment"
|
||
#define TIME "Time"
|
||
#define UUID "Uuid"
|
||
#define SIZE "Size"
|
||
#define STATE "State"
|
||
#define TYPE "Type"
|
||
#define POSITION "Position"
|
||
#define USERID "UserId"
|
||
#define OS "OS"
|
||
#define ARCH "Arch"
|
||
#define ARCHDETECT "ArchDetect"
|
||
#define PREFIXDESTPATH "PrefixDestPath"
|
||
|
||
#define STATUE_BACKUP_FINESHED "backup finished"
|
||
|
||
ParseBackupList::ParseBackupList(const QString& xmlPath)
|
||
: m_xmlPath(xmlPath)
|
||
{
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.exists() || 0 == xmlFile.size()) {
|
||
InitXml();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 初始化xml文件
|
||
* @return
|
||
*/
|
||
bool ParseBackupList::InitXml()
|
||
{
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
|
||
return false;
|
||
|
||
QDomDocument doc;
|
||
|
||
QDomProcessingInstruction ins = doc.createProcessingInstruction("xml", "version=\'1.0\'");
|
||
doc.appendChild(ins);
|
||
|
||
QDomElement root = doc.createElement(BACKUPLIST);
|
||
doc.appendChild(root);
|
||
|
||
QTextStream out(&xmlFile);
|
||
doc.save(out, QDomNode::NodeType::CDATASectionNode);
|
||
|
||
xmlFile.close();
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief 校验xml格式是否正确
|
||
* @return
|
||
*/
|
||
bool ParseBackupList::isXmlCorrect()
|
||
{
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||
qDebug() << "Open file " << m_xmlPath << " failure";
|
||
return false;
|
||
}
|
||
QXmlStreamReader reader(&xmlFile);
|
||
while (!reader.atEnd()) {
|
||
if (reader.isStartElement()) {
|
||
}
|
||
reader.readNext();
|
||
}
|
||
if (reader.hasError()) {
|
||
qDebug() << "xml parse error!";
|
||
xmlFile.close();
|
||
return false;
|
||
}
|
||
xmlFile.close();
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief 读取xml文件
|
||
* @param doc
|
||
* @return
|
||
*/
|
||
bool ParseBackupList::Doc_setContent(QDomDocument& doc)
|
||
{
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.open(QIODevice::ReadOnly)) {
|
||
qDebug() << "open backuplist.xml failed!";
|
||
return false;
|
||
}
|
||
QString errStr;
|
||
int errLine;
|
||
int errCol;
|
||
|
||
if (!doc.setContent(&xmlFile, false, &errStr, &errLine, &errCol)) {
|
||
qDebug() << QString("parse backuplist.xml error at line %1, column %2:%3").arg(errLine).arg(errCol).arg(errStr);
|
||
xmlFile.close();
|
||
return false;
|
||
}
|
||
xmlFile.close();
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief ParseBackupList::BackupPoint::operator <
|
||
* @param other
|
||
* @return
|
||
*/
|
||
bool ParseBackupList::BackupPoint::operator < (const BackupPoint &other)
|
||
{
|
||
return other.m_time > this->m_time;
|
||
}
|
||
|
||
/**
|
||
* @brief 出厂备份修改backuplist.xml,仅保留出厂备份信息
|
||
* @param factoryBackupUuid,出厂备份的uuid
|
||
* @return ParseResult枚举类型
|
||
* @author zhaominyong
|
||
* @since 2021/06/27
|
||
*/
|
||
ParseBackupList::ParseResult ParseBackupList::updateForFactoryRestore(const QString& factoryBackupUuid)
|
||
{
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return XML_PARSE_ERR;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue ;
|
||
QDomElement element = node.toElement();
|
||
QDomNodeList nodes = element.elementsByTagName(UUID);
|
||
if (0 < nodes.count()) {
|
||
QDomElement uuidElement = nodes.at(0).toElement();
|
||
QString tag = uuidElement.tagName();
|
||
QString text = uuidElement.text();
|
||
if (uuidElement.text() != factoryBackupUuid) {
|
||
root.removeChild(node);
|
||
--i;
|
||
}
|
||
}
|
||
}
|
||
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.open(QIODevice::WriteOnly)) {
|
||
qDebug() << "update state failed";
|
||
return FAIL;
|
||
}
|
||
QTextStream out(&xmlFile);
|
||
doc.save(out, QDomNode::NodeType::EntityReferenceNode);
|
||
out.flush();
|
||
xmlFile.close();
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief 根据comment查找uuid
|
||
* @param comment
|
||
* @return uuid
|
||
*/
|
||
QString ParseBackupList::findUuidByComment(const QString& comment)
|
||
{
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return "";
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement eleComment = node.firstChildElement(COMMENT);
|
||
if (eleComment.isNull())
|
||
continue;
|
||
|
||
if (comment != eleComment.text())
|
||
continue;
|
||
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (eleUuid.isNull())
|
||
return "";
|
||
|
||
return eleUuid.text();
|
||
}
|
||
|
||
return "";
|
||
}
|
||
|
||
/**
|
||
* @brief 根据Uuid查找备份点信息
|
||
* @param Uuid
|
||
* @return 备份点信息
|
||
*/
|
||
ParseBackupList::BackupPoint ParseBackupList::findBackupPointByUuid(const QString& Uuid)
|
||
{
|
||
BackupPoint backupPoint;
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return backupPoint;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (eleUuid.isNull() || Uuid != eleUuid.text())
|
||
continue;
|
||
|
||
elementNodeToBackupPoint(node.toElement(), backupPoint);
|
||
return backupPoint;
|
||
}
|
||
|
||
return backupPoint;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取xml文件中备份点uuid和backupname的映射
|
||
* @param uuid_name,xml文件中备份点uuid和backupname的映射
|
||
* @return
|
||
*/
|
||
void ParseBackupList::getXmlUuidNameMap(QMap<QString, QString> &uuid_name)
|
||
{
|
||
BackupPoint backupPoint;
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return ;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (eleUuid.isNull() || eleUuid.text().isEmpty())
|
||
continue;
|
||
|
||
QDomElement eleBackupName = node.firstChildElement(COMMENT);
|
||
if (eleBackupName.isNull() || eleBackupName.text().isEmpty())
|
||
continue;
|
||
|
||
uuid_name.insert(eleUuid.text(), eleBackupName.text());
|
||
}
|
||
|
||
return ;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取自定义备份路径列表
|
||
* @param customizePaths
|
||
*/
|
||
void ParseBackupList::getCustomizePaths(QStringList &customizePaths)
|
||
{
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return ;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement elePrefixPath = node.firstChildElement(PREFIXDESTPATH);
|
||
if (!elePrefixPath.isNull()) {
|
||
QString prefixPath = elePrefixPath.text() + BACKUP_SNAPSHOTS_PATH;
|
||
prefixPath.replace("//", "/");
|
||
if (!customizePaths.contains(prefixPath))
|
||
customizePaths << prefixPath;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 获取最后一次系统备份,排除自动备份点、自定义备份点
|
||
* @return
|
||
* @note 经过测试发现出厂备份点也不能作为基准点进行增量,因为出厂备份的安全标记和其它不同,咨询安全同事说解决不了。所以也排除出厂备份。
|
||
*/
|
||
ParseBackupList::BackupPoint ParseBackupList::getLastSysBackupPoint()
|
||
{
|
||
BackupPoint backupPoint;
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return backupPoint;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement eleType = node.firstChildElement(TYPE);
|
||
if (eleType.isNull() || (BackupType::BACKUP_SYSTEM != eleType.text().toInt() && BackupType::INC_BACKUP_SYSTEM != eleType.text().toInt()))
|
||
continue;
|
||
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (eleUuid.isNull() || eleUuid.text() == AUTO_BACKUP_UUID || eleUuid.text() == FACTORY_BACKUP_UUID)
|
||
continue;
|
||
|
||
QDomElement eleState = node.firstChildElement(STATE);
|
||
if (eleState.isNull() || eleState.text() != QString(STATUE_BACKUP_FINESHED))
|
||
continue;
|
||
|
||
QDomElement elePosition = node.firstChildElement(POSITION);
|
||
if (!elePosition.isNull() && elePosition.text().toInt() == BackupPosition::CUSTOMIZE)
|
||
continue;
|
||
|
||
elementNodeToBackupPoint(node.toElement(), backupPoint);
|
||
}
|
||
|
||
return backupPoint;
|
||
}
|
||
|
||
/**
|
||
* @brief elementNode --> BackupPoint
|
||
* @param node, QDomElement
|
||
* @param backupPoint, BackupPoint
|
||
*/
|
||
void ParseBackupList::elementNodeToBackupPoint(const QDomElement& node, BackupPoint& backupPoint)
|
||
{
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (!eleUuid.isNull())
|
||
backupPoint.m_uuid = eleUuid.text();
|
||
|
||
QDomElement eleComment = node.firstChildElement(COMMENT);
|
||
if (!eleComment.isNull())
|
||
backupPoint.m_backupName = eleComment.text();
|
||
if (backupPoint.m_uuid == FACTORY_BACKUP_UUID)
|
||
backupPoint.m_backupName = QObject::tr("factory backup");
|
||
|
||
QDomElement eleTime = node.firstChildElement(TIME);
|
||
if (!eleTime.isNull())
|
||
backupPoint.m_time = eleTime.text();
|
||
|
||
QDomElement eleSize = node.firstChildElement(SIZE);
|
||
if (!eleSize.isNull())
|
||
backupPoint.m_size = eleSize.text();
|
||
|
||
QDomElement eleState = node.firstChildElement(STATE);
|
||
if (!eleState.isNull())
|
||
backupPoint.m_state = eleState.text();
|
||
|
||
QDomElement eleType = node.firstChildElement(TYPE);
|
||
if (!eleType.isNull())
|
||
backupPoint.m_type = eleType.text().toInt();
|
||
|
||
QDomElement elePosition = node.firstChildElement(POSITION);
|
||
if (!elePosition.isNull() && !elePosition.text().isEmpty())
|
||
backupPoint.m_iPosition = elePosition.text().toInt();
|
||
|
||
QDomElement eleUserId = node.firstChildElement(USERID);
|
||
if (!eleUserId.isNull())
|
||
backupPoint.m_userId = eleUserId.text();
|
||
|
||
QDomElement eleOS = node.firstChildElement(OS);
|
||
if (!eleOS.isNull())
|
||
backupPoint.m_os = eleOS.text();
|
||
|
||
QDomElement eleArch = node.firstChildElement(ARCH);
|
||
if (!eleArch.isNull())
|
||
backupPoint.m_arch = eleArch.text();
|
||
|
||
QDomElement eleArchDetect = node.firstChildElement(ARCHDETECT);
|
||
if (!eleArchDetect.isNull())
|
||
backupPoint.m_archdetect = eleArchDetect.text();
|
||
|
||
QDomElement elePrefixPath = node.firstChildElement(PREFIXDESTPATH);
|
||
if (!elePrefixPath.isNull()) {
|
||
backupPoint.m_path = elePrefixPath.text();
|
||
} else {
|
||
backupPoint.m_path = m_xmlPath;
|
||
backupPoint.m_path.replace(BACKUP_XML_PATH, "");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief backupPoint --> ElementNode
|
||
* @param backupPoint, BackupPoint
|
||
* @param node, QDomElement
|
||
*/
|
||
void ParseBackupList::backupPointToElementNode(const BackupPoint& backupPoint, QDomDocument& doc, QDomNode& node)
|
||
{
|
||
node.appendChild(createTextElement(doc, COMMENT, backupPoint.m_backupName));
|
||
node.appendChild(createTextElement(doc, TIME, backupPoint.m_time));
|
||
node.appendChild(createTextElement(doc, UUID, backupPoint.m_uuid));
|
||
node.appendChild(createTextElement(doc, SIZE, backupPoint.m_size));
|
||
node.appendChild(createTextElement(doc, STATE, backupPoint.m_state));
|
||
node.appendChild(createTextElement(doc, TYPE, QString::number(backupPoint.m_type)));
|
||
node.appendChild(createTextElement(doc, POSITION, QString::number(backupPoint.m_iPosition)));
|
||
if (!backupPoint.m_userId.isEmpty()) {
|
||
node.appendChild(createTextElement(doc, USERID, backupPoint.m_userId));
|
||
}
|
||
if (!backupPoint.m_os.isEmpty()) {
|
||
node.appendChild(createTextElement(doc, OS, backupPoint.m_os));
|
||
}
|
||
if (!backupPoint.m_arch.isEmpty()) {
|
||
node.appendChild(createTextElement(doc, ARCH, backupPoint.m_arch));
|
||
}
|
||
if (!backupPoint.m_archdetect.isEmpty()) {
|
||
node.appendChild(createTextElement(doc, ARCHDETECT, backupPoint.m_archdetect));
|
||
}
|
||
if (!backupPoint.m_path.isEmpty()) {
|
||
node.appendChild(createTextElement(doc, PREFIXDESTPATH, backupPoint.m_path));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief createTextElement
|
||
* @param tagName
|
||
* @param text
|
||
* @return <tagName>text</tagName>
|
||
*/
|
||
QDomElement ParseBackupList::createTextElement(QDomDocument& doc, const QString& tagName, const QString& text)
|
||
{
|
||
QDomElement node = doc.createElement(tagName);
|
||
QDomText textNode = doc.createTextNode(text);
|
||
node.appendChild(textNode);
|
||
|
||
return node;
|
||
}
|
||
|
||
/**
|
||
* @brief 新增备份节点
|
||
* @param backupPoint, 备份点信息
|
||
* @return SUCCESS成功; XML_PARSE_ERR失败
|
||
*/
|
||
ParseBackupList::ParseResult ParseBackupList::addItem(const BackupPoint& backupPoint)
|
||
{
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return XML_PARSE_ERR;
|
||
|
||
QDomElement backupPointNode = doc.createElement(BACKUPPOINT);
|
||
backupPointToElementNode(backupPoint, doc, backupPointNode);
|
||
|
||
QDomElement root = doc.documentElement();
|
||
root.appendChild(backupPointNode);
|
||
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||
return FAIL;
|
||
|
||
QTextStream out(&xmlFile);
|
||
doc.save(out, QDomNode::NodeType::CDATASectionNode);
|
||
xmlFile.close();
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief 更新备份点信息
|
||
* @param backupPoint,新的备份点信息
|
||
* @return SUCCESS成功; XML_PARSE_ERR失败
|
||
*/
|
||
ParseBackupList::ParseResult ParseBackupList::updateItem(const BackupPoint & backupPoint)
|
||
{
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return XML_PARSE_ERR;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
int i = 0;
|
||
for (; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (eleUuid.isNull() || backupPoint.m_uuid != eleUuid.text())
|
||
continue;
|
||
|
||
break ;
|
||
}
|
||
|
||
// 找到了旧节点
|
||
if (i < list.count()) {
|
||
// 移除旧节点,更新后的节点放到最后
|
||
root.removeChild(list.at(i));
|
||
}
|
||
QDomElement newNode = doc.createElement(BACKUPPOINT);
|
||
backupPointToElementNode(backupPoint, doc, newNode);
|
||
root.appendChild(newNode);
|
||
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||
return FAIL;
|
||
}
|
||
|
||
QTextStream out(&xmlFile);
|
||
doc.save(out, QDomNode::NodeType::EntityReferenceNode);
|
||
xmlFile.close();
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief 删除备份点记录
|
||
* @param uuid
|
||
* @return SUCCESS成功; XML_PARSE_ERR失败
|
||
*/
|
||
ParseBackupList::ParseResult ParseBackupList::deleteItem(const QString& uuid)
|
||
{
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return XML_PARSE_ERR;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (eleUuid.isNull() || uuid != eleUuid.text())
|
||
continue;
|
||
|
||
root.removeChild(node);
|
||
|
||
break ;
|
||
}
|
||
|
||
QFile xmlFile(m_xmlPath);
|
||
if (!xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||
return FAIL;
|
||
}
|
||
|
||
QTextStream out(&xmlFile);
|
||
doc.save(out, QDomNode::NodeType::EntityReferenceNode);
|
||
xmlFile.close();
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取备份列表
|
||
* @return
|
||
*/
|
||
QList<ParseBackupList::BackupPoint> ParseBackupList::getBackupPointList()
|
||
{
|
||
QList<ParseBackupList::BackupPoint> backupPointList;
|
||
QDomDocument doc;
|
||
if (!Doc_setContent(doc))
|
||
return backupPointList;
|
||
|
||
QDomElement root = doc.documentElement();
|
||
QDomNodeList list = root.childNodes();
|
||
|
||
for (int i = 0; i < list.count(); i++) {
|
||
QDomNode node = list.at(i);
|
||
if (!node.isElement())
|
||
continue;
|
||
|
||
QDomElement eleUuid = node.firstChildElement(UUID);
|
||
if (eleUuid.isNull())
|
||
continue;
|
||
|
||
BackupPoint backupPoint;
|
||
elementNodeToBackupPoint(node.toElement(), backupPoint);
|
||
backupPointList << backupPoint;
|
||
}
|
||
|
||
// std::sort(backupPointList.begin(), backupPointList.end());
|
||
|
||
return backupPointList;
|
||
}
|