Skip to content

fix: Opening a file without permission results in a blank page#451

Merged
lzwind merged 2 commits intolinuxdeepin:masterfrom
JWWTSL:master
Apr 23, 2026
Merged

fix: Opening a file without permission results in a blank page#451
lzwind merged 2 commits intolinuxdeepin:masterfrom
JWWTSL:master

Conversation

@JWWTSL
Copy link
Copy Markdown
Contributor

@JWWTSL JWWTSL commented Apr 23, 2026

log: QFileInfo::isReadable() is unreliable in certain scenarios, causing files without permission to pass the permission check and create a tab, but then failing to open via FileLoadThread
, leaving a blank tab and displaying an error message. Solution: Use QFile::open() to actually attempt to open the file for permission verification, and fix onReadAllocError() to display the correct prompt when a permission error occurs.

pms: bug-339655

Summary by Sourcery

Improve file readability checks to prevent opening tabs for files without read permissions and show accurate error messages when reads fail.

Bug Fixes:

  • Prevent opening an editor tab for files that exist but cannot actually be opened due to missing read permissions.
  • Display a specific permission error message when a read failure is caused by insufficient file permissions instead of a generic read error notice.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 23, 2026

Reviewer's Guide

Reworks file readability checks to use an actual QFile open attempt and improves error messaging so permission issues show a clear, specific prompt instead of leaving a blank/ambiguous error tab.

Sequence diagram for updated file open permission check in Window::addTab

sequenceDiagram
    actor User
    participant Window
    participant QFile
    participant DMessageManager

    User ->> Window: addTab(filepath, activeTab)
    Window ->> Window: QFileInfo(filepath)
    alt File does not exist
        Window ->> Window: Skip permission check
    else File exists
        Window ->> QFile: open(ReadOnly)
        alt Open succeeds
            QFile -->> Window: success
            Window ->> QFile: close()
            Window ->> Window: continue with StartManager and FileLoadThread
        else Open fails (e.g. no permission)
            QFile -->> Window: errorString()
            Window ->> DMessageManager: sendMessage(msgParent, icon, "You do not have permission to open %1")
            DMessageManager -->> User: Permission warning toast/dialog
            Window ->> Window: return (no tab opened)
        end
    end
Loading

Sequence diagram for updated permission-specific read error handling in EditWrapper::onReadAllocError

sequenceDiagram
    participant FileLoadThread
    participant EditWrapper
    participant QFileInfo
    participant WarningNotice
    participant DMessageManager
    participant User

    FileLoadThread ->> EditWrapper: onReadAllocError()
    EditWrapper ->> EditWrapper: set m_bCanBeRestore
    EditWrapper ->> EditWrapper: toggleReadOnlyMode(true)
    EditWrapper ->> QFileInfo: QFileInfo(filePath())
    QFileInfo -->> EditWrapper: exists(), isReadable()
    alt File exists and is not readable
        EditWrapper ->> WarningNotice: setMessage("You do not have permission to open %1")
    else Other read error (too large, damaged, etc.)
        EditWrapper ->> WarningNotice: setMessage("The file cannot be read, which may be too large or has been damaged!")
    end
    EditWrapper ->> WarningNotice: clearBtn()
    EditWrapper ->> WarningNotice: show()
    EditWrapper ->> DMessageManager: sendMessage(m_pTextEdit, WarningNotice)
    DMessageManager -->> User: Display inline warning message
Loading

Updated class diagram for Window and EditWrapper permission handling

classDiagram
    class Window {
        +void addTab(QString filepath, bool activeTab)
        -QWidget *m_editorWidget
    }

    class EditWrapper {
        +void onReadAllocError()
        -QString filePath()
        -QPlainTextEdit *m_pTextEdit
        -WarningNotice *m_pWaringNotices
        -bool m_bCanBeRestore
    }

    class DMessageManager {
        +static DMessageManager* instance()
        +void sendMessage(QWidget *parent, QIcon icon, QString message)
    }

    class WarningNotice {
        +void setMessage(QString message)
        +void clearBtn()
        +void show()
    }

    Window --> DMessageManager : uses
    EditWrapper --> DMessageManager : uses
    EditWrapper --> WarningNotice : manages
    EditWrapper --> QPlainTextEdit : editedWidget
Loading

File-Level Changes

Change Details Files
Make file readability checks reliable by attempting to open the file instead of relying on QFileInfo::isReadable().
  • Replace QFileInfo::isReadable() check with creating a QFile for the target path and calling open(QIODevice::ReadOnly) to validate readability.
  • Log detailed warning including QFile error string when the open attempt fails.
  • Choose a safe parent widget (current editor widget or the window itself) when displaying the permission warning message.
  • Return early to avoid creating a tab when the file cannot be opened due to permission issues.
src/widgets/window.cpp
Improve user-facing error message when a read error is caused by missing permissions.
  • On read allocation error, inspect QFileInfo for the current file path to detect permission issues.
  • Show a specific "You do not have permission to open %1" message when the file exists but is not readable.
  • Fallback to the generic "file cannot be read, may be too large or damaged" message when the problem is not clearly a permissions issue.
  • Preserve existing behavior of toggling read-only mode and displaying the warning notice widget.
src/editor/editwrapper.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

@github-actions
Copy link
Copy Markdown

  • 敏感词检查失败, 检测到1个文件存在敏感词
详情
{
    "src/widgets/window.cpp": [
        {
            "line": "    QString key = \"base/enable\";",
            "line_number": 390,
            "rule": "S106",
            "reason": "Var naming | 64f28539d9"
        }
    ]
}

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

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, and left some high level feedback:

  • Since QFileInfo::isReadable() is known to be unreliable (as noted in the new comment in Window::addTab), consider using the same QFile::open()-based permission check in EditWrapper::onReadAllocError instead of relying on isReadable() there as well.
  • The permission error messages now sometimes use the full path (in Window::addTab) and sometimes only the file name (in EditWrapper::onReadAllocError); consider making this consistent so users see a uniform message format.
  • The logic for checking readability (attempting to open the file and then showing a DMessageManager warning) is now duplicated in multiple places; consider extracting this into a shared helper to keep the behavior and messaging consistent and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Since QFileInfo::isReadable() is known to be unreliable (as noted in the new comment in Window::addTab), consider using the same QFile::open()-based permission check in EditWrapper::onReadAllocError instead of relying on isReadable() there as well.
- The permission error messages now sometimes use the full path (in Window::addTab) and sometimes only the file name (in EditWrapper::onReadAllocError); consider making this consistent so users see a uniform message format.
- The logic for checking readability (attempting to open the file and then showing a DMessageManager warning) is now duplicated in multiple places; consider extracting this into a shared helper to keep the behavior and messaging consistent and easier to maintain.

## Individual Comments

### Comment 1
<location path="src/widgets/window.cpp" line_range="682-687" />
<code_context>
+        // Verify the file is actually readable by attempting to open it.
+        // This is more reliable than QFileInfo::isReadable(), which can be incorrect
+        // in some edge cases (e.g. setuid processes, certain filesystems).
+        if (fileInfo.exists()) {
+            QFile testFile(filepath);
+            if (testFile.open(QIODevice::ReadOnly)) {
+                testFile.close();
+            } else {
+                qWarning() << "No permission to read file:" << filepath << "error:" << testFile.errorString();
+                QWidget *const msgParent = m_editorWidget->currentWidget() ? m_editorWidget->currentWidget() : this;
+                DMessageManager::instance()->sendMessage(msgParent, QIcon(":/images/warning.svg"),
+                                                         QString(tr("You do not have permission to open %1")).arg(filepath));
+                return;
+            }
         }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Distinguish permission errors from other QFile open failures for clearer user feedback.

Right now any `testFile.open()` failure is reported as a permission error. That means unrelated issues (I/O errors, too many open files, etc.) surface the same "You do not have permission" message. Since you already log `testFile.errorString()`, please branch on `testFile.error()` and map only permission-related cases (e.g. `QFileDevice::PermissionError`) to the permission message, and use a more generic failure message for other error types.
</issue_to_address>

### Comment 2
<location path="src/editor/editwrapper.cpp" line_range="1648" />
<code_context>
     }

-    m_pWaringNotices->setMessage(tr("The file cannot be read, which may be too large or has been damaged!"));
+    const QFileInfo fileInfo(filePath());
+    if (fileInfo.exists() && !fileInfo.isReadable()) {
+        m_pWaringNotices->setMessage(tr("You do not have permission to open %1").arg(fileInfo.fileName()));
+    } else {
+        m_pWaringNotices->setMessage(tr("The file cannot be read, which may be too large or has been damaged!"));
+    }
     m_pWaringNotices->clearBtn();
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Permission detection here uses `QFileInfo::isReadable`, which may diverge from the more reliable check in `Window::addTab`.

In `Window::addTab`, readability is determined by actually opening the file with `QFile::open`, specifically because `QFileInfo::isReadable()` can be wrong in some edge cases. This branch reintroduces `fileInfo.isReadable()` for message selection, which can cause it to disagree with the logic that triggered `onReadAllocError()`, leading to inconsistent behavior and confusing messages.

Consider basing this branch on the same error information that caused `onReadAllocError()` (e.g., an error type passed into this handler), or at least reuse the `QFile::open`-based result so the permission message is consistent with the earlier check.

Suggested implementation:

```cpp
    if (permissionDenied) {
        const QFileInfo fileInfo(filePath());
        m_pWaringNotices->setMessage(tr("You do not have permission to open %1").arg(fileInfo.fileName()));
    } else {
        m_pWaringNotices->setMessage(tr("The file cannot be read, which may be too large or has been damaged!"));
    }

```

To fully implement this:

1. Update the signature of the function containing this block (likely `EditWrapper::onReadAllocError`) to accept a `bool permissionDenied` parameter (or a richer error enum, e.g. `FileOpenError error`, from which you derive `permissionDenied` inside this function).
2. At the call site in `Window::addTab` (or wherever this handler is invoked), determine `permissionDenied` based on the `QFile::open` result and `QFile::error()` (e.g. `file.error() == QFileDevice::PermissionError`) and pass that into this handler.
3. Update all other callers of this handler in the codebase to pass an appropriate `permissionDenied` value (likely `false` for non-permission-related errors).
</issue_to_address>

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.

Comment thread src/widgets/window.cpp
Comment thread src/editor/editwrapper.cpp Outdated
}

m_pWaringNotices->setMessage(tr("The file cannot be read, which may be too large or has been damaged!"));
const QFileInfo fileInfo(filePath());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Permission detection here uses QFileInfo::isReadable, which may diverge from the more reliable check in Window::addTab.

In Window::addTab, readability is determined by actually opening the file with QFile::open, specifically because QFileInfo::isReadable() can be wrong in some edge cases. This branch reintroduces fileInfo.isReadable() for message selection, which can cause it to disagree with the logic that triggered onReadAllocError(), leading to inconsistent behavior and confusing messages.

Consider basing this branch on the same error information that caused onReadAllocError() (e.g., an error type passed into this handler), or at least reuse the QFile::open-based result so the permission message is consistent with the earlier check.

Suggested implementation:

    if (permissionDenied) {
        const QFileInfo fileInfo(filePath());
        m_pWaringNotices->setMessage(tr("You do not have permission to open %1").arg(fileInfo.fileName()));
    } else {
        m_pWaringNotices->setMessage(tr("The file cannot be read, which may be too large or has been damaged!"));
    }

To fully implement this:

  1. Update the signature of the function containing this block (likely EditWrapper::onReadAllocError) to accept a bool permissionDenied parameter (or a richer error enum, e.g. FileOpenError error, from which you derive permissionDenied inside this function).
  2. At the call site in Window::addTab (or wherever this handler is invoked), determine permissionDenied based on the QFile::open result and QFile::error() (e.g. file.error() == QFileDevice::PermissionError) and pass that into this handler.
  3. Update all other callers of this handler in the codebase to pass an appropriate permissionDenied value (likely false for non-permission-related errors).

@github-actions
Copy link
Copy Markdown

  • 敏感词检查失败, 检测到1个文件存在敏感词
详情
{
    "src/widgets/window.cpp": [
        {
            "line": "    QString key = \"base/enable\";",
            "line_number": 390,
            "rule": "S106",
            "reason": "Var naming | 64f28539d9"
        }
    ]
}

log: QFileInfo::isReadable() is unreliable in certain scenarios, causing files without permission to pass the permission check and create a tab, but then failing to open via FileLoadThread
, leaving a blank tab and displaying an error message. Solution: Use QFile::open() to actually attempt to open the file for permission verification, and fix onReadAllocError() to display the correct prompt when a permission error occurs.

pms: bug-339655
@github-actions
Copy link
Copy Markdown

  • 敏感词检查失败, 检测到1个文件存在敏感词
详情
{
    "src/widgets/window.cpp": [
        {
            "line": "    QString key = \"base/enable\";",
            "line_number": 390,
            "rule": "S106",
            "reason": "Var naming | 64f28539d9"
        }
    ]
}

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: JWWTSL, lzwind

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

CLA Assistant Lite bot:

如果你是以企业贡献者的身份进行提交,请联系我们签署企业贡献者许可协议
If you submit as corporate contributor, please contact us to sign our Corporate Contributor License Agreement

感谢您的提交,我们非常感谢。 像许多开源项目一样,在接受您的贡献之前,我们要求您签署我们的个人贡献者许可协议。 您只需发布与以下格式相同的评论即可签署个人贡献者许可协议
Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Individual Contributor License Agreement before we can accept your contribution. You can sign the Individual Contributor License Agreement by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA.

You can retrigger this bot by commenting recheck in this Pull Request

@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这段代码的修改主要是为了改进文件可读性检查的可靠性。以下是对这段代码的详细审查和改进意见:

1. 语法逻辑审查

优点:

  • 新代码通过实际尝试打开文件来验证可读性,这确实比单纯依赖 QFileInfo::isReadable() 更可靠,正如注释中提到的,在某些特殊情况下(如 setuid 进程或特定文件系统),isReadable() 可能不准确。
  • 增加了对 testFile.error() 的判断,区分了权限错误和其他类型的错误,这是一个很好的改进。
  • 添加了 msgParent 的空指针检查,提高了代码的健壮性。

潜在问题:

  • else 分支(非权限错误)中,代码仅打印了警告日志,但没有 return,这意味着程序会继续执行后续代码。如果文件无法打开(无论是权限问题还是其他原因),后续操作可能会失败或产生未定义行为。

2. 代码质量审查

优点:

  • 注释清晰,解释了修改的原因。
  • 错误日志更加详细,包含了错误信息字符串。

改进建议:

  • 错误处理不完整:在 else 分支中,如果文件因其他原因(如文件被占用、路径无效等)无法打开,应该同样阻止后续操作。建议在 else 分支中也添加 return
  • 资源管理:虽然 QFile 会在析构时自动关闭文件,但显式调用 close() 是一个好习惯,代码中已经做到了这一点。
  • 魔法值QFileDevice::PermissionsError 是一个枚举值,这里使用是合理的,但可以考虑将其提取为常量以提高可读性(不过在这种情况下可能不是必需的)。

3. 代码性能审查

  • 文件打开开销:新代码通过实际打开文件来验证可读性,这确实比 isReadable() 更耗时,因为涉及到系统调用和可能的 I/O 操作。但在大多数情况下,这种开销是可以接受的,因为准确性更重要。
  • 建议:如果这是一个高频调用的函数(例如在处理大量文件时),可能需要考虑优化。但在当前上下文中(打开一个新标签页),这种性能影响可以忽略不计。

4. 代码安全审查

  • 空指针检查:代码中对 m_editorWidget->currentWidget() 进行了空指针检查,这是好的实践。
  • 错误信息泄露:错误日志中包含了文件路径,这在生产环境中可能需要谨慎处理,尤其是在处理用户提供的路径时,可能存在路径遍历风险。不过,在这个上下文中,路径似乎是可信的(来自 StartManager::checkPath),因此风险较低。
  • 权限检查:通过实际打开文件来验证权限是一个更安全的方法,因为它反映了真实的系统状态。

5. 综合改进建议

以下是改进后的代码,主要修复了错误处理不完整的问题:

// Verify the file is actually readable by attempting to open it.
// This is more reliable than QFileInfo::isReadable(), which can be incorrect
// in some edge cases (e.g. setuid processes, certain filesystems).
if (fileInfo.exists()) {
    QFile testFile(filepath);
    if (!testFile.open(QIODevice::ReadOnly)) {
        if (testFile.error() == QFileDevice::PermissionsError) {
            qWarning() << "No permission to read file:" << filepath << "error:" << testFile.errorString();
        } else {
            qWarning() << "Failed to open file:" << filepath << "error:" << testFile.errorString();
        }
        
        QWidget *const msgParent = m_editorWidget->currentWidget() ? m_editorWidget->currentWidget() : this;
        DMessageManager::instance()->sendMessage(msgParent, QIcon(":/images/warning.svg"),
                                                 QString(tr("You do not have permission to open %1")).arg(filepath));
        return;
    }
    testFile.close();
}

改进点:

  1. 合并了错误处理逻辑,避免重复代码。
  2. 在任何打开失败的情况下都会 return,防止后续操作在无效文件上执行。
  3. 保持了原有的日志记录和用户通知功能。

6. 其他建议

  • 国际化:错误消息使用了 tr(),这是好的实践,确保了国际化支持。
  • 日志级别:使用了 qWarning(),这是合适的,因为文件打开失败是一个需要注意的问题。
  • 测试:建议添加单元测试,覆盖以下场景:
    • 文件存在且可读
    • 文件存在但不可读
    • 文件不存在
    • 文件存在但被其他进程锁定

总的来说,这段代码的修改方向是正确的,主要需要改进的是错误处理的完整性。

@github-actions
Copy link
Copy Markdown

  • 敏感词检查失败, 检测到1个文件存在敏感词
详情
{
    "src/widgets/window.cpp": [
        {
            "line": "    QString key = \"base/enable\";",
            "line_number": 389,
            "rule": "S106",
            "reason": "Var naming | 64f28539d9"
        }
    ]
}

@lzwind lzwind merged commit fe6c41b into linuxdeepin:master Apr 23, 2026
19 of 22 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.

3 participants