From a8af217a872de6084f9f6f6a42999e1f69717ada Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Wed, 17 Jun 2026 08:39:39 +0800 Subject: [PATCH] fix: improve symlink handling in filename search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Added test cases for symlink directory handling in real-time search 2. Modified real-time strategy to skip recursing into symlink directories while still matching their names 3. Handles three key scenarios: symlink name matching, preventing recursion into symlinks, and avoiding cycles from circular symlinks 4. Tests verify correct behavior for each scenario Log: Improved symlink directory handling in file search Influence: 1. Test searching for files in directories containing symlinks 2. Verify symlink directory names are matched correctly 3. Confirm search doesn't follow symlinks into other directories 4. Check circular symlink detection and handling 5. Test search performance with multiple symlinks fix: 改进文件名搜索中的符号链接处理 1. 为实时搜索中的符号链接目录处理添加测试用例 2. 修改实时策略跳过递归进入符号链接目录,同时仍可匹配其名称 3. 处理三种关键场景:符号链接名称匹配、防止递归进入符号链接、避免环形符 号链接导致的循环 4. 测试验证了每种场景的正确行为 Log: 改进文件搜索中的符号链接目录处理 Influence: 1. 测试包含符号链接目录中的文件搜索 2. 验证符号链接目录名称能被正确匹配 3. 确认搜索不会跟随符号链接进入其他目录 4. 检查环形符号链接的检测和处理 5. 测试多符号链接情况下的搜索性能 --- .../tst_filename_search_engine.cpp | 85 +++++++++++++++++++ .../filenamestrategies/realtimestrategy.cpp | 7 +- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/autotests/dfm-search-tests/tst_filename_search_engine.cpp b/autotests/dfm-search-tests/tst_filename_search_engine.cpp index c599b6c..4860bc8 100644 --- a/autotests/dfm-search-tests/tst_filename_search_engine.cpp +++ b/autotests/dfm-search-tests/tst_filename_search_engine.cpp @@ -200,6 +200,9 @@ private Q_SLOTS: void realtime_sizeAndTimeFilters_applyWithoutIndex(); void realtime_detailedResults_populateAttributes(); void realtime_pinyinOption_doesNotProducePinyinMatches(); + void realtime_symlinkDirName_matchesInResults(); + void realtime_symlinkDir_notRecursedInto(); + void realtime_circularSymlinkDir_deduplicated(); }; void tst_FileNameSearchEngine::search_simpleKeyword_matchesIndexedFilename() @@ -911,6 +914,88 @@ void tst_FileNameSearchEngine::realtime_pinyinOption_doesNotProducePinyinMatches QCOMPARE(expected.value().size(), 0); } +void tst_FileNameSearchEngine::realtime_symlinkDirName_matchesInResults() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const QString rootDir = tempDir.path() + "/docs"; + QVERIFY(QDir().mkpath(rootDir)); + + // 创建一个真实目录和一个指向它的符号链接目录 + const QString realDir = rootDir + "/real-target"; + QVERIFY(QDir().mkpath(realDir)); + QVERIFY(createFileWithSize(realDir + "/inside.txt", 16)); + + // 创建指向真实目录的符号链接 + const QString symlinkDir = rootDir + "/target 快捷方式"; + QVERIFY(QFile::link(realDir, symlinkDir)); + + std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); + engine->setSearchOptions(createRealtimeOptions(rootDir)); + + // 搜索关键词应匹配符号链接目录名称 + const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("快捷方式")); + QVERIFY(expected.hasValue()); + + const QStringList paths = resultPaths(expected); + QCOMPARE(paths.size(), 1); + QCOMPARE(paths.first(), symlinkDir); +} + +void tst_FileNameSearchEngine::realtime_symlinkDir_notRecursedInto() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const QString rootDir = tempDir.path() + "/docs"; + QVERIFY(QDir().mkpath(rootDir)); + + // 真实目录放在搜索根目录之外,确保只有通过 symlink 才能到达 + const QString realDir = tempDir.path() + "/outside-target"; + QVERIFY(QDir().mkpath(realDir)); + + // 创建指向真实目录的符号链接(链接位于搜索根目录内) + const QString symlinkDir = rootDir + "/symlink"; + QVERIFY(QFile::link(realDir, symlinkDir)); + + // 在真实目录内创建一个文件,该文件名不会出现在搜索路径其他位置 + QVERIFY(createFileWithSize(realDir + "/unique-inside-file.txt", 16)); + + std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); + engine->setSearchOptions(createRealtimeOptions(rootDir)); + + // 搜索真实目录内部的文件,应该找不到(因为不会递归进入 symlink) + const SearchResultExpected expected = engine->searchSync( + SearchQuery::createSimpleQuery("unique-inside-file")); + QVERIFY(expected.hasValue()); + QCOMPARE(expected.value().size(), 0); +} + +void tst_FileNameSearchEngine::realtime_circularSymlinkDir_deduplicated() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const QString rootDir = tempDir.path() + "/docs"; + QVERIFY(QDir().mkpath(rootDir)); + + // 创建一个指向自身祖先目录的循环符号链接 + const QString symlinkDir = rootDir + "/loop-link"; + QVERIFY(QFile::link(rootDir, symlinkDir)); + + std::unique_ptr engine(SearchEngine::create(SearchType::FileName)); + engine->setSearchOptions(createRealtimeOptions(rootDir)); + + // 搜索应该正常完成,不会因循环链接而死循环 + const SearchResultExpected expected = engine->searchSync(SearchQuery::createSimpleQuery("loop-link")); + QVERIFY(expected.hasValue()); + + const QStringList paths = resultPaths(expected); + QCOMPARE(paths.size(), 1); + QCOMPARE(paths.first(), symlinkDir); +} + QObject *create_tst_FileNameSearchEngine() { return new tst_FileNameSearchEngine(); diff --git a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp index e5c7060..c634266 100644 --- a/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp +++ b/src/dfm-search/dfm-search-lib/filenamesearch/filenamestrategies/realtimestrategy.cpp @@ -108,11 +108,8 @@ void FileNameRealTimeStrategy::search(const SearchQuery &query) } // 将子目录添加到栈中以便后续处理 - if (info.isDir()) { - // 检查是否为符号链接,避免循环 - if (info.isSymLink()) { - continue; - } + // 符号链接目录不递归进入(防止循环),但其名称仍参与匹配 + if (info.isDir() && !info.isSymLink()) { directoryStack.push(info.filePath()); }