Skip to content

fix: distinguish ENOENT from lock in isFileLocked - #770

Open
add-uos wants to merge 1 commit into
linuxdeepin:masterfrom
add-uos:agent/pms-bug-bot/69744d83dbdd
Open

add-uos wants to merge 1 commit into
linuxdeepin:masterfrom
add-uos:agent/pms-bug-bot/69744d83dbdd

Conversation

@add-uos

@add-uos add-uos commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Root Cause Analysis

isFileLocked() in utils.cpp returned true for all open() failures (fd < 0), conflating "file not found" (ENOENT) with "file is locked". When the dpkg lock file (/var/lib/dpkg/lock) was absent, callers in aptinstaller.cpp:124 and debinstaller.cpp:153 retried until timeout and reported a misleading "Dpkg locked, time out" error, when the actual problem was a missing lock file.

Fix

Added #include <errno.h> and an errno == ENOENT check in the fd < 0 branch of isFileLocked(): return false for ENOENT (file not found = not locked), keep returning true for other open() failures (defensive conservative behavior). API signature unchanged, backward compatible.

Change Safety Assessment

Code Safety

  • Risk Level: Low
  • isFileLocked() fd < 0 branch logic was unchanged since initial implementation; this is the first adjustment to that branch, not reverting any prior bug fix
  • Both callers (aptinstaller.cpp, debinstaller.cpp) benefit from the more precise return value with no negative impact

Business Impact Scope

Affects package install/uninstall flows in deepin-devicemanager that check the dpkg lock file. When the lock file is missing (system anomaly), the fix prevents false "Dpkg locked" errors and unnecessary retry timeouts. Normal scenarios (lock file present, dpkg running or idle) are unaffected.

Verification Suggestion

Test package uninstall when the dpkg lock file exists and is held by a running dpkg process, and when the lock file is absent. Verify normal install/uninstall still works.


根因分析

utils.cppisFileLocked() 对所有 open() 失败(fd < 0)统一返回 true,将"文件不存在"(ENOENT)与"文件被锁定"混淆。当 dpkg 锁文件(/var/lib/dpkg/lock)缺失时,aptinstaller.cpp:124debinstaller.cpp:153 的调用方反复重试至超时,报出误导性的"Dpkg locked, time out"错误,而实际问题仅为锁文件不存在。

修复方案

新增 #include <errno.h>,在 isFileLocked()fd < 0 分支增加 errno == ENOENT 判断:ENOENT 返回 false(未锁定),其他 open() 失败保持返回 true(防御性保守处理)。API 签名不变,向后兼容。

改动安全评估

代码安全评估

  • 风险等级: 低
  • isFileLocked()fd < 0 分支自初始实现未变,本次为首次调整,非撤销历史修复
  • 两个调用点(aptinstaller.cppdebinstaller.cpp)均受益于更精确的返回值,无负面影响

业务影响范围

影响 deepin-devicemanager 中检查 dpkg 锁文件的安装/卸载流程。锁文件缺失(系统异常)时,修复避免了误报"Dpkg locked"和不必要的重试超时。正常场景(锁文件存在、dpkg 运行或空闲)不受影响。

验证建议

测试 dpkg 锁文件存在且被占用时的卸载操作,以及锁文件不存在时的卸载操作。验证正常安装/卸载功能不受影响。

Summary by Sourcery

Bug Fixes:

  • Distinguish missing dpkg lock files from other file-opening failures to prevent false lock reports and unnecessary installation or uninstallation timeouts.

1. Root cause: isFileLocked() returned true for all open()
   failures, conflating "file not found" (ENOENT) with "file
   is locked", causing misleading "Dpkg locked" timeout errors
2. Fix: add errno check after open() failure, return false for
   ENOENT (file not found = not locked), keep true for other
   failures (defensive conservative behavior)
3. Impact: callers aptinstaller.cpp and debinstaller.cpp no
   longer retry indefinitely when dpkg lock file is absent

Log: fix incorrect Dpkg locked error when lock file is missing

Influence:
1. Test package uninstall when dpkg lock file exists and is locked
2. Test package uninstall when dpkg lock file does not exist
3. Verify normal package install/uninstall works correctly

fix: 区分文件不存在与文件锁定状态

1. 根因:isFileLocked() 对所有 open() 失败统一返回 true,将
   文件不存在(ENOENT)与文件被锁定混淆,导致锁文件缺失时
   误报 Dpkg locked 超时错误
2. 方案:open() 失败后检查 errno,ENOENT 返回 false(未锁定),
   其他错误保持返回 true(保守视为已锁定)
3. 影响:aptinstaller.cpp 和 debinstaller.cpp 调用方在锁文件
   不存在时不再无限重试

Log: 修复锁文件不存在时误报 Dpkg locked 的问题

Influence:
1. 测试 dpkg 锁文件存在且被锁定时的卸载操作
2. 测试 dpkg 锁文件不存在时的卸载操作
3. 验证正常的安装卸载功能不受影响

PMS: DEFECT-001
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos

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 17, 2026

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

Reviewer's Guide

Updates isFileLocked() to return false specifically for ENOENT while retaining conservative locked behavior for other open errors, preventing absent dpkg lock files from causing misleading retry timeouts without changing the API or normal lock handling.

Flow diagram for distinguishing missing and locked dpkg files

flowchart TD
    A["isFileLocked(filepath, bread)"] --> B["open(filepath, opentype)"]
    B --> C{open succeeds?}
    C -->|No| D{errno == ENOENT?}
    D -->|Yes| E[return false]
    D -->|No| F[return true]
    C -->|Yes| G["fCntl(fd, F_SETLK, &fl)"]
    G --> H[Existing lock handling]
Loading

File-Level Changes

Change Details Files
Refine lock detection to distinguish a missing file from other open failures.
  • Include errno definitions.
  • Return false when opening the path fails with ENOENT.
  • Continue treating other open failures as locked for conservative behavior.
deepin-devicemanager-server/deepin-devicecontrol/src/drivercontrol/utils.cpp
Prevent misleading dpkg lock timeouts when the lock file is absent.
  • Allow existing apt and deb installer checks to proceed when the dpkg lock path does not exist.
  • Preserve behavior for existing locks and non-ENOENT failures.
deepin-devicemanager-server/deepin-devicecontrol/src/drivercontrol/utils.cpp
deepin-devicemanager-server/deepin-devicecontrol/src/drivercontrol/aptinstaller.cpp
deepin-devicemanager-server/deepin-devicecontrol/src/drivercontrol/debinstaller.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 reviewed your changes and they look great!


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

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 100 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 总体评分 100 分,大于 70 分通过阈值,代码质量符合要求。本次提交修复了 isFileLocked 函数中文件打开失败时的处理逻辑,正确区分文件不存在(ENOENT)与其他错误,代码实现清晰、注释完整、无安全漏洞。

🔍 详细分析

1. 语法逻辑 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: 代码语法正确,逻辑清晰。正确添加了 #include <errno.h> 头文件以使用 errno 和 ENOENT 宏。在 open() 系统调用失败后立即检查 errno,中间无其他函数调用,不会导致 errno 被覆盖。逻辑分支清晰:ENOENT 返回 false(文件不存在即未锁定),其他错误返回 true(保守视为已锁定)。


2. 代码质量 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: 代码结构清晰,注释完整。注释准确描述了代码意图:"文件打开失败,区分文件不存在与其他错误"、"文件不存在,未被锁定"、"其他原因无法打开文件,保守起见视为已锁定"。无重复代码,无残留调试代码。


3. 代码性能 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: 性能良好,资源使用合理。新增的 errno 检查开销可忽略不计。无不必要的内存分配或系统调用。


4. 代码安全 🔒

评价: 优秀 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: 存在0个安全漏洞。errno 是线程局部存储(TLS),在多线程环境下使用安全。文件路径通过 toStdString().c_str() 转换,无缓冲区溢出风险。无命令注入、路径遍历等安全风险。


💡 改进建议代码示例

// 本次修改已完善,无需额外修复示例
// 修改后的 isFileLocked 函数正确处理了 ENOENT 情况:

bool Utils::isFileLocked(const QString &filepath, bool bread)
{
    // ... 前置代码 ...
    int fd = open(filepath.toStdString().c_str(), opentype);
    // 文件打开失败,区分文件不存在与其他错误
    if (fd < 0) {
        if (errno == ENOENT) {
            // 文件不存在,未被锁定
            return false;
        }
        // 其他原因无法打开文件,保守起见视为已锁定
        return true;
    }
    // ... 后续代码 ...
}

本报告由 AI 代码审查工具自动生成

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