ukui-search/ukui-search-service-dir-man.../dirwatcher/search-dir.cpp

380 lines
15 KiB
C++
Raw Permalink Normal View History

2023-04-11 10:19:35 +08:00
/*
*
* Copyright (C) 2023, KylinSoft Co., Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Authors: iaom <zhangpengfei@kylinos.cn>
*/
#include "search-dir.h"
2023-04-08 16:54:01 +08:00
#include "config.h"
#include "volume-manager.h"
#include <QFile>
SearchDir::SearchDir(const QString &path, bool generateBlackList)
{
2023-04-08 16:54:01 +08:00
if (!QFile::exists(path)) {
m_error = NotExists;
} else {
m_path = path;
if(generateBlackList) {
this->handleBlackListGenerate();
}
}
}
bool SearchDir::operator ==(const SearchDir &rhs) const
{
return (m_path == rhs.m_path);
}
SearchDir::ErrorInfo SearchDir::error()
{
return m_error;
}
QString SearchDir::errorString()
{
switch (m_error) {
case ErrorInfo::Successful:
2023-04-08 16:54:01 +08:00
return "Add search dir successful";
case ErrorInfo::Duplicated:
2023-04-08 16:54:01 +08:00
return "Path or its parent dir has been added";
case ErrorInfo::UnderBlackList:
2023-04-08 16:54:01 +08:00
return "Path is or under blacklist";
case ErrorInfo::RepeatMount1:
2023-04-08 16:54:01 +08:00
return "Path is in repeat mounted devices and another path which is in the same device has been indexed";
case ErrorInfo::RepeatMount2:
2023-04-08 16:54:01 +08:00
return "Another path which is in the same device has been indexed";
case ErrorInfo::NotExists:
2023-04-08 16:54:01 +08:00
return "Path is not exists";
}
2023-04-08 16:54:01 +08:00
return QString();
}
QString SearchDir::getPath() const
{
return m_path;
}
void SearchDir::setBlackList(const QStringList &blackList)
{
m_blackList = blackList;
}
QStringList SearchDir::getBlackList() const
{
return m_blackList;
}
void SearchDir::generateBlackList()
{
2023-04-08 16:54:01 +08:00
m_blackList.clear();
this->handleBlackListGenerate();
}
QStringList SearchDir::blackListOfDir(const QString &dirPath)
{
//手动刷新挂载信息
VolumeManager::self()->refresh();
2023-04-08 16:54:01 +08:00
QStringList blackListOfDir;
for (const QStringList & mountPoints: VolumeManager::self()->getDuplicates()) {
QString topRepeatedMountPoint;
for (const QString &mountPoint : mountPoints) {
//该目录下是否有两个文件夹(有可能为父子关系)是重复挂载的关系
if (mountPoint.startsWith(dirPath + "/") or mountPoint == dirPath) {
if (topRepeatedMountPoint.isEmpty()) {
topRepeatedMountPoint = mountPoint;
continue;
}
//重复挂载时保留最上层的挂载点
if (topRepeatedMountPoint.startsWith(mountPoint + "/")) {
blackListOfDir.append(topRepeatedMountPoint);
topRepeatedMountPoint = mountPoint;
} else {
blackListOfDir.append(mountPoint);
}
}
}
}
blackListOfDir << handleSubVolumes4SingleDir(dirPath);
2023-04-08 16:54:01 +08:00
return blackListOfDir;
}
void SearchDir::handleBlackListGenerate()
{
//手动刷新挂载信息
VolumeManager::self()->refresh();
2023-04-08 16:54:01 +08:00
QStringList searchDirs = Config::self()->searchDirs();
//目录已被索引(根目录被添加过直接返回)
for (const QString searchDir : searchDirs) {
if (searchDir == m_path || searchDir == "/") {
m_error = Duplicated;
return;
}
2023-04-08 16:54:01 +08:00
}
//根目录特殊处理
if (m_path == "/") {
m_blackList << Config::self()->globalBlackList() << searchDirs;
for (const QStringList &mountPoints: VolumeManager::self()->getDuplicates()) {
QStringList repeatMountPoints = mountPoints;
if (mountPoints.contains("/")) {
repeatMountPoints.removeAll("/");
m_blackList << repeatMountPoints;
continue;
}
bool excludeAll(false);
for (const QString &mountPoint : mountPoints) {
for (const QString &searchDir : searchDirs) {
//之前已索引重复挂载设备挂载点或其子目录,则排除其他目录
if (searchDir.startsWith(mountPoint + "/") || searchDir == mountPoint) {
repeatMountPoints.removeAll(mountPoint);
break;
}
//重复挂载点在已索引目录下,该挂载点全排除
if (mountPoint.startsWith(searchDir + "/")) {
excludeAll = true;
break;
}
}
if (excludeAll) {
m_blackList << repeatMountPoints;
break;
}
}
//不需要全排除且没有需要特别保留的挂载点,默认留第一个
if (!excludeAll && repeatMountPoints == mountPoints) {
repeatMountPoints.removeFirst();
}
m_blackList << repeatMountPoints;
}
m_blackList.append(handleSubVolumes4SingleDir("/"));
2023-04-08 16:54:01 +08:00
m_blackList.removeDuplicates();
return;
}
//处理要添加索引的路径与全局黑名单中路径为父子关系的情况
for (const QString& blackListPath : Config::self()->globalBlackList()) {
if (m_path.startsWith(blackListPath + "/") or m_path == blackListPath) {
m_error = UnderBlackList;
return;
}
if (blackListPath.startsWith(m_path + "/")) {
m_blackList.append(blackListPath);
}
}
//重复挂载情况
for (const QStringList & mountPoints: VolumeManager::self()->getDuplicates()) {
QString topRepeatedMountPoint;
for (const QString &mountPoint : mountPoints) {
if (mountPoint.startsWith(m_path + "/") or mountPoint == m_path) {
if (topRepeatedMountPoint.isEmpty()) {
topRepeatedMountPoint = mountPoint;
continue;
} else if (topRepeatedMountPoint.startsWith(mountPoint)) {
m_blackList.append(topRepeatedMountPoint);
topRepeatedMountPoint = mountPoint;
} else {
m_blackList.append(mountPoint);
}
}
}
//排除要添加的目录为某设备的重复挂载目录,并且之前已索引过该设备其他挂载目录或其父目录的情况
bool pathToBeAddedIsRepeatedDevice = false;
bool pathToBeAddedHasRepeatedDevice = false;
bool addedPathIsRepeatedDevice = false;
bool addedPathHasRepeatedDevice = false;
QString addedRelativeDir;
QString repeatedDir;
for (const QString &addedPath : searchDirs) {
for (const QString &mountPoint : mountPoints) {
//要添加索引路径在重复挂载设备路径下(1)
if (m_path.startsWith(mountPoint + "/") or mountPoint == m_path) {
repeatedDir = mountPoint;
pathToBeAddedIsRepeatedDevice = true;
}
//重复挂载设备路径在要添加索引路径下(2)
if (mountPoint.startsWith(m_path + "/")) {
repeatedDir = mountPoint;
pathToBeAddedHasRepeatedDevice = true;
}
//已索引路径在重复挂载设备路径下(3)
if (addedPath.startsWith(mountPoint + "/") or mountPoint == addedPath) {
addedRelativeDir = addedPath;
addedRelativeDir.remove(0, mountPoint.length());
addedPathIsRepeatedDevice = true;
}
//重复挂载设备路径在已索引路径下(4)
if (mountPoint.startsWith(addedPath + "/")) {
addedPathHasRepeatedDevice = true;
}
//(1)(4)直接返回
if (pathToBeAddedIsRepeatedDevice and addedPathHasRepeatedDevice) {
m_error = RepeatMount1;
return;
}
//(2)(4)将要添加索引目录相应的重复挂载路径添加到黑名单
if (pathToBeAddedHasRepeatedDevice and addedPathHasRepeatedDevice) {
m_blackList.append(repeatedDir);
break;
}
//(1)(3)将已索引路径的前缀替换为要添加路径的前缀前缀为mountPoint判断替换后路径是否在要索引路径下如果是则返回否则将替换后路径添加到黑名单
if (pathToBeAddedIsRepeatedDevice and addedPathIsRepeatedDevice) {
QString pathAfterReplace = repeatedDir + addedRelativeDir;
if (m_path.startsWith(pathAfterReplace) or m_path == pathAfterReplace) {
m_error = RepeatMount2;
return;
} else {
m_blackList.append(pathAfterReplace);
break;
}
}
//(2)(3)将替换前缀后的已索引路径添加到黑名单
if (pathToBeAddedHasRepeatedDevice and addedPathIsRepeatedDevice) {
m_blackList.append(repeatedDir + addedRelativeDir);
break;
}
}
}
}
//处理自动挂载子卷下的目录
for (const Volume &volume : VolumeManager::self()->volumesHaveSubVolumes()) {
QMap<QString, QString> subVolumeInfo = volume.subVolumes();
for (auto it = subVolumeInfo.constBegin(); it != subVolumeInfo.constEnd(); it++) {
QString subMountPoint = it.key();
for (const QString &duplicateMountPoint : volume.mountPoints()) {
if (subVolumeInfo.keys().contains(duplicateMountPoint)) {
continue;
}
QString spec = duplicateMountPoint + it.value(); //子卷对应目录
//要添加目录下存在子卷(要添加/data但挂到/home的/data/home是子卷若添加了/home则将/data/home排除
if (spec.startsWith(m_path + "/")) {
for (QString &searchDir : searchDirs) {
if (searchDir == subMountPoint || subMountPoint.startsWith(searchDir + "/")) {
m_blackList << spec;
}
if (searchDir.startsWith(subMountPoint + "/")) {
m_blackList << searchDir.replace(0, subMountPoint.length(), spec);
}
}
}
//要添加的目录是子卷或在子卷下(/data/home or /data/home/xxx
if (m_path.startsWith(spec + "/") || m_path == spec) {
for (QString &searchDir : searchDirs) {
//已添加挂载点或其上层目录
if (subMountPoint.startsWith(searchDir + "/") || searchDir == subMountPoint) {
m_error = RepeatMount1;
return;
}
//已添加挂载点下其他目录
if (searchDir.startsWith(subMountPoint + "/")) {
QString tmp = searchDir;
tmp.replace(0, subMountPoint.length(), spec);
if (tmp == m_path) {
m_error = RepeatMount2;
return;
} else if (tmp.startsWith(m_path + "/")) {//已添加的子卷子目录替换前缀后在要添加目录下
m_blackList << tmp;
}
}
}
}
//要添加的目录是挂载点或在挂载点下(/home or /home/xxx
if (m_path.startsWith(subMountPoint + "/") || m_path == subMountPoint) {
for (QString &searchDir : searchDirs) {
//已添加子卷或其上层目录
if (spec.startsWith(searchDir + "/") || searchDir == spec) {
m_error = RepeatMount1;
return;
}
//已添加子卷下其他目录
if (searchDir.startsWith(spec + "/")) {
QString tmp = searchDir;
tmp.replace(0, spec.length(), subMountPoint);
if (tmp == m_path) {
m_error = RepeatMount2;
return;
} else if (tmp.startsWith(m_path + "/")) {//已添加的子卷子目录替换前缀后在要添加目录下
m_blackList << tmp;
}
}
}
}
}
}
}
//要添加目录下存在已索引目录
for (const QString &searchDir : searchDirs) {
if (m_path.startsWith(searchDir + "/")) {
m_error = Duplicated;
return;
}
if (searchDir.startsWith(m_path + "/")) {
m_blackList.append(searchDir);
}
}
m_blackList.removeDuplicates();
}
QStringList SearchDir::handleSubVolumes4SingleDir(const QString &searchDir) {
QStringList blackList;
for (const Volume &volume : VolumeManager::self()->volumesHaveSubVolumes()) {
QMap<QString, QString> subVolumeInfo = volume.subVolumes();
for (auto it = subVolumeInfo.constBegin(); it != subVolumeInfo.constEnd(); it++) {
QString subMountPoint = it.key();
for (const QString &duplicateMountPoint : volume.mountPoints()) {
if (subVolumeInfo.keys().contains(duplicateMountPoint)) {
continue;
}
QString spec = duplicateMountPoint + it.value(); //子卷对应目录
if (searchDir == "/") {
blackList << spec;
} else {
//排除搜索列表指定多个目录时子卷会重复包含的情况,比如同时指定/home和/data/home
QString tmp = searchDir;
if (searchDir.startsWith(subMountPoint + "/")) {
blackList << tmp.replace(0, subMountPoint.length(), spec);
}
//要添加目录下存在子卷(要添加/data但挂到/home的/data/home是子卷若添加了/home则将/data/home排除
if (searchDir.startsWith(spec + "/")) {
blackList << tmp.replace(0, spec.length(), subMountPoint);
}
if (spec.startsWith(searchDir + "/")) {
if (searchDir == subMountPoint || subMountPoint.startsWith(searchDir + "/")) {
blackList << spec;
}
}
}
}
}
}
return blackList;
}