Skip to content

fix(dfm-io): close FTS tree in destructor to prevent memory leak#323

Merged
Johnson-zs merged 1 commit into
linuxdeepin:masterfrom
liujianqiang-niu:master
Jun 25, 2026
Merged

fix(dfm-io): close FTS tree in destructor to prevent memory leak#323
Johnson-zs merged 1 commit into
linuxdeepin:masterfrom
liujianqiang-niu:master

Conversation

@liujianqiang-niu

@liujianqiang-niu liujianqiang-niu commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

When initEnumerator(false) is called, openDirByfts() allocates an FTS tree via fts_open(). However, LocalDirIterator::sortFileInfoList() uses its own opendir()/readdir()/closedir() implementation and never calls fts_close(), leaving the FTS tree (5,279 bytes including indirect allocations) leaked.

Close the FTS tree in the destructor as a safety net so it is always freed regardless of which sortFileInfoList() code path is taken.

Log: Fixed FTS tree memory leak when LocalDirIterator bypasses fts_close().

Assisted-by: deepseek-v4-pro

Influence:

  1. Open a local directory with dde-file-manager - verify no fts_open leak under Valgrind
  2. Test batch directory traversal with sort role set (sortFileInfoList path)
  3. Verify one-by-one enumeration path is unaffected
  4. Test vault and SMB directory traversal paths
  5. Verify rapid open/close of file manager window does not accumulate fts_open leaks

fix(dfm-io): 在析构函数中关闭 FTS 树以防止内存泄露

initEnumerator(false) 调用 openDirByfts() 通过 fts_open() 分配 FTS 树,但 LocalDirIterator::sortFileInfoList() 使用自己的 opendir()/readdir()/closedir() 实现,从未调用 fts_close(), 导致 FTS 树(含间接分配共 5,279 字节)泄露。

在析构函数中添加 fts_close() 作为安全兜底,确保无论走哪个
sortFileInfoList() 代码路径,FTS 树都能被释放。

Log: 修复 LocalDirIterator 绕过 fts_close() 导致的 FTS 树内存泄露。

Influence:

  1. 使用 dde-file-manager 打开本地目录 — Valgrind 下验证 fts_open 无泄露
  2. 测试设置排序角色后的批量目录遍历(sortFileInfoList 路径)
  3. 验证逐个遍历路径不受影响
  4. 测试保险箱和 SMB 目录遍历路径
  5. 验证快速打开/关闭文件管理器窗口不累积 fts_open 泄露

Summary by Sourcery

Bug Fixes:

  • Fix a memory leak by closing any open FTS tree in the DEnumeratorPrivate destructor.

When initEnumerator(false) is called, openDirByfts() allocates an FTS
tree via fts_open().  However, LocalDirIterator::sortFileInfoList()
uses its own opendir()/readdir()/closedir() implementation and never
calls fts_close(), leaving the FTS tree (5,279 bytes including indirect
allocations) leaked.

Close the FTS tree in the destructor as a safety net so it is always
freed regardless of which sortFileInfoList() code path is taken.

Log: Fixed FTS tree memory leak when LocalDirIterator bypasses fts_close().

Assisted-by: deepseek-v4-pro

Influence:
1. Open a local directory with dde-file-manager - verify no fts_open leak under Valgrind
2. Test batch directory traversal with sort role set (sortFileInfoList path)
3. Verify one-by-one enumeration path is unaffected
4. Test vault and SMB directory traversal paths
5. Verify rapid open/close of file manager window does not accumulate fts_open leaks

fix(dfm-io): 在析构函数中关闭 FTS 树以防止内存泄露

initEnumerator(false) 调用 openDirByfts() 通过 fts_open() 分配
FTS 树,但 LocalDirIterator::sortFileInfoList() 使用自己的
opendir()/readdir()/closedir() 实现,从未调用 fts_close(),
导致 FTS 树(含间接分配共 5,279 字节)泄露。

在析构函数中添加 fts_close() 作为安全兜底,确保无论走哪个
sortFileInfoList() 代码路径,FTS 树都能被释放。

Log: 修复 LocalDirIterator 绕过 fts_close() 导致的 FTS 树内存泄露。

Influence:
1. 使用 dde-file-manager 打开本地目录 — Valgrind 下验证 fts_open 无泄露
2. 测试设置排序角色后的批量目录遍历(sortFileInfoList 路径)
3. 验证逐个遍历路径不受影响
4. 测试保险箱和 SMB 目录遍历路径
5. 验证快速打开/关闭文件管理器窗口不累积 fts_open 泄露
@sourcery-ai

sourcery-ai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Ensures the FTS directory traversal tree allocated by initEnumerator(false)/openDirByfts() is always closed by adding an fts_close() safety net in the DEnumeratorPrivate destructor, preventing a memory leak when certain sortFileInfoList() code paths bypass fts_close().

Sequence diagram for FTS tree lifecycle with destructor safety net

sequenceDiagram
    participant DEnumeratorPrivate
    participant openDirByfts
    participant FTS_library

    DEnumeratorPrivate->>openDirByfts: initEnumerator(false)
    openDirByfts->>FTS_library: fts_open
    FTS_library-->>openDirByfts: fts_handle
    openDirByfts-->>DEnumeratorPrivate: store fts_handle

    rect rgb(230,230,230)
        DEnumeratorPrivate-->>DEnumeratorPrivate: [sortFileInfoList path bypasses fts_close]
    end

    DEnumeratorPrivate->>FTS_library: ~DEnumeratorPrivate
    DEnumeratorPrivate->>FTS_library: fts_close
    FTS_library-->>DEnumeratorPrivate: fts_tree_freed
Loading

File-Level Changes

Change Details Files
Add destructor-level cleanup of the FTS traversal tree to prevent leaks when some directory enumeration paths skip explicit fts_close().
  • Extend DEnumeratorPrivate::~DEnumeratorPrivate() to conditionally call fts_close(fts) if an FTS handle exists
  • Reset the fts member to nullptr after closing to avoid dangling pointers and double-close issues
src/dfm-io/dfm-io/denumerator.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 left some high level feedback:

  • Since the destructor now unconditionally closes fts when non-null, please verify any other code paths that call fts_close also set fts to nullptr to avoid a potential double-close if future changes alter ownership assumptions.
  • Consider wrapping the raw fts pointer in a small RAII helper (or a dedicated member type) so that lifetime and ownership are expressed more clearly and future changes cannot forget to close it.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Since the destructor now unconditionally closes `fts` when non-null, please verify any other code paths that call `fts_close` also set `fts` to `nullptr` to avoid a potential double-close if future changes alter ownership assumptions.
- Consider wrapping the raw `fts` pointer in a small RAII helper (or a dedicated member type) so that lifetime and ownership are expressed more clearly and future changes cannot forget to close it.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:100分

■ 【总体评价】

代码准确修复了文件描述符泄露问题,逻辑清晰且符合规范
修复精准无副作用,四个审查维度均无缺陷,满分通过

■ 【详细分析】

  • 1.语法逻辑(完全正确)✓

DEnumeratorPrivate::~DEnumeratorPrivate() 中新增的 if (fts) 判空逻辑与 fts_close(fts) 调用语法正确,有效避免了空指针解引用导致的崩溃风险
建议:保持当前写法,无需额外修改

  • 2.代码质量(良好)✓

新增代码与上方 cancellable 的释放逻辑在缩进、命名和判空模式上保持高度一致,符合原有代码风格,有效落实了 RAII 原则
建议:保持当前风格统一性

  • 3.代码性能(无性能问题)✓

析构阶段仅增加一次指针判空与一次系统调用,开销极小,对整体运行时性能无任何可感知影响
建议:无需优化

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
代码成功关闭了可能泄露的 fts 文件描述符,消除了耗尽系统文件描述符资源的潜在风险,未引入任何新的安全漏洞
建议:无需额外安全加固

■ 【改进建议代码示例】

DEnumeratorPrivate::~DEnumeratorPrivate()
{
    if (cancellable) {
        g_object_unref(cancellable);
        cancellable = nullptr;
    }
    if (fts) {
        fts_close(fts);
        fts = nullptr;
    }
}

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Johnson-zs, liujianqiang-niu, liyigang1

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

@Johnson-zs Johnson-zs merged commit b35ecca into linuxdeepin:master Jun 25, 2026
23 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.

4 participants