Skip to content

fix: detect abnormal exit via sentinel for filename index - #264

Merged
Johnson-zs merged 1 commit into
linuxdeepin:develop/snipe-20260923from
Johnson-zs:develop/snipe-20260923
Sep 29, 2026
Merged

Johnson-zs merged 1 commit into
linuxdeepin:develop/snipe-20260923from
Johnson-zs:develop/snipe-20260923

Conversation

@Johnson-zs

@Johnson-zs Johnson-zs commented Sep 29, 2026 •

Copy link
Copy Markdown

fix: detect abnormal exit via sentinel for filename index

  1. Root cause: when the index service is killed by SIGKILL, crash,
    or OOM while in Clean state with no running tasks, cleanup() is
    never called and the state remains Clean — on restart no recovery
    is triggered, silently losing all file changes during the downtime
  2. Fix: create a sentinel file at startup in the index directory and
    remove it during normal cleanup(); if the sentinel persists across
    a restart, the previous exit was abnormal, so mark state Dirty to
    trigger a compensating full Update via the existing recovery path
  3. Impact: only FileNameIndexDBus is affected; TextIndex and OcrIndex
    are unchanged per the requirement that only filename index needs
    full completeness guarantees

Influence:

  1. Test normal service restart — sentinel file should be removed and
    no false Dirty state on next startup
  2. Test SIGKILL during Clean idle state — sentinel persists, restart
    triggers full Update covering all missed file changes
  3. Verify no regression on existing Dirty state recovery path

fix: 通过哨兵文件检测文件名索引服务异常退出

  1. 根因:索引服务在 Clean 空闲状态下被 SIGKILL、崩溃或 OOM 杀死时,
    cleanup() 未被调用,状态保持 Clean,重启后不触发恢复,退出期间的
    文件变更静默丢失
  2. 方案:启动时在索引目录创建哨兵文件,正常退出时删除;若重启时哨兵
    文件仍在,说明上次异常退出,标记 Dirty 状态触发已有恢复路径的
    全量 Update 补偿丢失的文件变更
  3. 影响:仅修改 FileNameIndexDBus,TextIndex 和 OcrIndex 不变,符合
    仅文件名索引需要完整性保证的需求

Influence:

  1. 测试正常重启场景——哨兵文件应被删除,下次启动不误触发 Dirty
  2. 测试 Clean 空闲状态下 SIGKILL——哨兵文件残留,重启触发全量 Update
  3. 验证已有 Dirty 状态恢复路径无回归

Summary by Sourcery

Detect abnormal filename-index shutdowns and trigger full recovery while making D-Bus service registration failures explicit and safe.

New Features:

  • Add abnormal-exit detection for the filename index using a startup sentinel that triggers recovery after an unclean shutdown.

Bug Fixes:

  • Prevent file changes from being missed when the filename index service is killed or crashes while idle and clean.
  • Make index service startup fail cleanly when any required D-Bus service cannot be registered.

Enhancements:

  • Ensure failed D-Bus registration rolls back previously registered services and improve registration result handling.

Tests:

  • Update registration tests to accept either successful registration or the expected failure result without crashing.

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Johnson-zs

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

FileNameIndexDBus now uses a running sentinel file to detect crashes, SIGKILL, or OOM while idle; a leftover sentinel marks the index Dirty and routes startup through the existing full-update recovery path, while normal cleanup removes it. TextIndex and OcrIndex are unchanged.

Sequence diagram for filename index abnormal-exit recovery

sequenceDiagram
    participant Service as FileNameIndexDBus
    participant FS as IndexDirectory
    participant State as StateStore
    participant Recovery as ExistingRecoveryPath

    Service->>FS: QFile::exists(sentinelPath)
    alt Sentinel remains from previous run
        Service->>State: setIndexState(Dirty)
    end
    Service->>FS: QDir::mkpath(indexDir)
    Service->>FS: QFile::open(WriteOnly)
    Service->>State: getIndexState()
    State-->>Service: Dirty
    Service->>Recovery: handleSilentStart()
    Recovery->>Recovery: Update()

    alt Normal cleanup
        Service->>FS: QFile::remove(sentinelPath)
    end
Loading

File-Level Changes

Change Details Files
Add a startup/shutdown sentinel to distinguish abnormal termination from normal cleanup and force filename-index recovery after an unclean exit.
  • Define a sentinel path under the configured index directory.
  • Detect a leftover sentinel at startup and mark the index Dirty before existing recovery checks run.
  • Create or recreate the sentinel for the current process and remove it during normal cleanup.
  • Log failures to create or remove the sentinel without changing the existing recovery mechanism.
src/index/dbus/filenameindexdbus.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/index/dbus/filenameindexdbus.cpp" line_range="25-28" />
<code_context>
+// when the service starts, the previous run was killed without cleanup.
+inline constexpr char kSentinelFileName[] = ".filename_index_running";
+
+QString sentinelFilePath(const QString &indexDir)
+{
+    return indexDir + QLatin1Char('/') + QLatin1String(kSentinelFileName);
+}
+
 QStringList defaultPathsToProcess()
</code_context>
<issue_to_address>
**issue (bug_risk):** When `indexDir` is empty, `sentinelFilePath()` returns `/.filename_index_running`, which is non-empty. Startup therefore probes the filesystem root for the sentinel and cleanup attempts to remove that root-level path instead of skipping sentinel handling.

**Triggers:** When the filename index directory provider returns an empty path.

**Suggested fix:** Return an empty path from `sentinelFilePath()` when `indexDir` is empty, or guard the existence check with `!indexDir.isEmpty()` as is done for creation.

```suggestion
QString sentinelFilePath(const QString &indexDir)
{
    return indexDir.isEmpty() ? QString() : indexDir + QLatin1Char('/') + QLatin1String(kSentinelFileName);
}
```
</issue_to_address>

### Comment 2
<location path="src/index/dbus/filenameindexdbus.cpp" line_range="69-74" />
<code_context>
+        runtime->stateStore().setIndexState(IndexUtility::IndexState::Dirty);
+    }
+
+    // Create (or recreate) the sentinel file for this run.
+    if (!indexDir.isEmpty()) {
+        QDir().mkpath(indexDir);
+        QFile sentinel(sentinelPath);
+        if (sentinel.open(QIODevice::WriteOnly)) {
+            sentinel.close();
+        } else {
+            qWarning() << "FileNameIndexDBus: Failed to create sentinel file:" << sentinelPath;
</code_context>
<issue_to_address>
**issue (bug_risk):** Two filename-index service instances sharing the same index directory overwrite and remove one another's sentinel: the later startup truncates the shared file, and either instance's normal cleanup removes it while the other is still running. If the remaining instance is then killed, its abnormal exit leaves no sentinel and recovery is skipped.

**Triggers:** When a second service process starts before the first process has exited, including after DBus service registration fails but object construction continues.

**Suggested fix:** Use an ownership-specific/atomic lease sentinel, or refuse to construct the service when DBus name registration fails; cleanup must only remove a sentinel owned by the current process.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/index/dbus/filenameindexdbus.cpp
Comment thread src/index/dbus/filenameindexdbus.cpp Outdated
@Johnson-zs
Johnson-zs force-pushed the develop/snipe-20260923 branch from 9cb34f6 to df79576 Compare September 29, 2026 11:20
1. Root cause: when the index service is killed by SIGKILL, crash,
   or OOM while in Clean state with no running tasks, cleanup() is
   never called and the state remains Clean — on restart no recovery
   is triggered, silently losing all file changes during the downtime
2. Fix: create a sentinel file at startup in the index directory and
   remove it during normal cleanup(); if the sentinel persists across
   a restart, the previous exit was abnormal, so mark state Dirty to
   trigger a compensating full Update via the existing recovery path
3. Harden sentinelFilePath() to return empty when indexDir is empty,
   preventing operations on the filesystem root when indexDir is unset
4. Abort service construction when DBus registerService fails, preventing
   multi-instance scenarios that could corrupt the sentinel file;
   unregister already-registered services on partial failure to avoid
   blocking legitimate instances
5. Check registerIndexServices() return value in main() and exit on
   failure instead of continuing with unregistered DBus services
6. Use open() with O_NOFOLLOW to create the sentinel file, preventing
   symlink-following that could truncate an arbitrary file if an
   attacker pre-placed a symlink in the index directory
7. Guard cleanup() with QFile::exists() before QFile::remove() to
   avoid false warnings when the sentinel file is already absent
8. Impact: FileNameIndexDBus and serviceentry are affected; TextIndex
   and OcrIndex are unchanged per the filename-index completeness need

Influence:
1. Test normal service restart — sentinel file should be removed and
   no false Dirty state on next startup
2. Test SIGKILL during Clean idle state — sentinel persists, restart
   triggers full Update covering all missed file changes
3. Verify no regression on existing Dirty state recovery path
4. Test service startup with empty indexDir — no root-level file ops
5. Test second instance startup — should abort, not corrupt sentinel
6. Test DBus unavailable — main() should exit, not run unregistered
7. Test symlink in indexDir — sentinel creation must not follow it

fix: 通过哨兵文件检测文件名索引服务异常退出

1. 根因:索引服务在 Clean 空闲状态下被 SIGKILL、崩溃或 OOM 杀死时,
   cleanup() 未被调用,状态保持 Clean,重启后不触发恢复,退出期间的
   文件变更静默丢失
2. 方案:启动时在索引目录创建哨兵文件,正常退出时删除;若重启时哨兵
   文件仍在,说明上次异常退出,标记 Dirty 状态触发已有恢复路径的
   全量 Update 补偿丢失的文件变更
3. 加固 sentinelFilePath() 在 indexDir 为空时返回空字符串,避免
   indexDir 未设置时操作文件系统根目录
4. DBus registerService 失败时中止服务构造,防止多实例场景下
   哨兵文件被互相覆盖或删除;部分注册失败时注销已注册的服务,
   避免阻塞合法实例
5. main() 检查 registerIndexServices() 返回值,失败时退出,不再
   在 DBus 服务未注册的情况下继续运行
6. 使用 open() 配合 O_NOFOLLOW 标志创建哨兵文件,防止攻击者在
   索引目录预置符号链接导致任意文件被截断
7. cleanup() 删除哨兵文件前先检查 QFile::exists(),避免文件
   不存在时误报警告
8. 影响:修改 FileNameIndexDBus 和 serviceentry,TextIndex 和
   OcrIndex 不变,符合仅文件名索引需要完整性保证的需求

Influence:
1. 测试正常重启场景——哨兵文件应被删除,下次启动不误触发 Dirty
2. 测试 Clean 空闲状态下 SIGKILL——哨兵文件残留,重启触发全量 Update
3. 验证已有 Dirty 状态恢复路径无回归
4. 测试 indexDir 为空时启动——不应操作根目录文件
5. 测试第二个实例启动——应中止退出,不破坏哨兵文件
6. 测试 DBus 不可用时启动——main() 应退出,不继续运行
7. 测试索引目录存在符号链接——哨兵文件创建不应跟随符号链接
@Johnson-zs
Johnson-zs force-pushed the develop/snipe-20260923 branch from df79576 to 87fc1b3 Compare September 29, 2026 11:38
@Johnson-zs
Johnson-zs merged commit 6595d66 into linuxdeepin:develop/snipe-20260923 Sep 29, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants