Skip to content

Conversation

@electricface
Copy link
Contributor

@electricface electricface commented Nov 27, 2025

  • Provide deepin_lookup_path_or_parent function to support path
    lookup and parent path retrieval
  • Change read-only filesystem error notification calls to be based
    on deepin_path_last structure
  • Add deepin_err_notify_should_send function to implement enablement
    and rate limiting checks for error notifications
  • Modify multiple file operation functions to use the new interface

Signed-off-by: electricface [email protected]

Summary by Sourcery

Refactor the Deepin read-only filesystem error notification mechanism to use a new path abstraction, centralized enablement and rate limiting, and a unified interface across filesystem operations.

Enhancements:

  • Introduce deepin_path_last and deepin_lookup_path_or_parent to capture either a fully resolved path or its parent plus final component for error reporting.
  • Add deepin_err_notify_should_send and a deepin_should_notify_ro_fs_err helper macro to centralize enablement and rate limiting of read-only filesystem error notifications.
  • Update read-only error notification call sites across file operations (create, mkdir, rmdir, unlink, symlink, link, open, truncate, chmod, chown, xattr, ioctl, utimes, rename) to use the new deepin_path_last-based interface.
  • Add deepin_put_path_last and deepin_notify_rename_ro_fs_err helpers to manage deepin_path_last lifetime and specialize rename error reporting.
  • Remove the filesystem super_operations hook deepin_should_notify_error in favor of the new centralized notification logic.

Documentation:

  • Document usage, lifetime, and ownership semantics of deepin_path_last and related helper APIs in code comments.

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 27, 2025

Reviewer's Guide

Refactors Deepin’s read-only filesystem error notification so that VFS operations use a shared deepin_path_last abstraction, a centralized enablement/rate-limiting check, and dedicated helpers for preparing/sending notifications, including a specialized rename hook, while removing the old per-filesystem callback and legacy path lookup helper.

Class diagram for refactored Deepin read-only filesystem error notification

classDiagram
    class path {
        <<kernel_struct>>
        +struct vfsmount *mnt
        +struct dentry *dentry
    }

    class deepin_path_last {
        +struct path path
        +const char *last
    }

    class DeepinErrNotifyAPI {
        <<module fs_deepin_err_notify>>
        +int deepin_err_notify_enabled()
        +int deepin_err_notify_should_send()
        +void deepin_check_and_notify_ro_fs_err(const struct deepin_path_last *path_last, const char *func_name)
        +void deepin_notify_rename_ro_fs_err(const struct qstr *old_last, const struct qstr *new_last, const struct path *old_path, const struct path *new_path)
        +void deepin_put_path_last(struct deepin_path_last *path_last)
        +void deepin_send_ro_fs_err_notification(const char *filename, const char *func_name)
        +int deepin_lookup_path_or_parent(int dfd, struct filename *name, unsigned int flags, struct deepin_path_last *result_path_last)
        -int prepare_and_notify_fs_error(const struct deepin_path_last *path_lasts, int path_last_count)
    }

    class DeepinErrorNotifyMacro {
        <<macro>>
        +int deepin_should_notify_ro_fs_err(int error)
    }

    class super_operations {
        <<kernel_struct>>
        -bool deepin_should_notify_error(struct super_block *sb)
        +DEEPIN_KABI_RESERVE_1
        +DEEPIN_KABI_RESERVE_2
        +DEEPIN_KABI_RESERVE_3
    }

    deepin_path_last *-- path : owns

    DeepinErrorNotifyMacro <.. DeepinErrNotifyAPI : uses
    DeepinErrNotifyAPI <.. super_operations : replaces callback
Loading

File-Level Changes

Change Details Files
Introduce deepin_path_last abstraction and deepin_lookup_path_or_parent helper to unify path/parent resolution for error notifications.
  • Add struct deepin_path_last and its declaration in fs/internal.h.
  • Implement deepin_lookup_path_or_parent in fs/namei.c (with CONFIG_DEEPIN_ERR_NOTIFY stub when disabled).
  • Ensure deepin_lookup_path_or_parent first tries filename_lookup and falls back to filename_parentat + kstrdup of the last component when the target does not exist.
  • Return -EOPNOTSUPP from the stub implementation when Deepin error notify is not enabled.
fs/namei.c
fs/internal.h
Centralize read-only filesystem error notification policy via deepin_err_notify_should_send and a new macro, and simplify notification handling to operate on deepin_path_last.
  • Add deepin_err_notify_should_send with internal ratelimit state (20 events per 5 seconds) and use it in the deepin_should_notify_ro_fs_err macro.
  • Refactor deepin_check_and_notify_ro_fs_err to take const struct deepin_path_last* and delegate userspace preparation/sending to a new prepare_and_notify_fs_error helper, including error logging on failure.
  • Introduce deepin_put_path_last to release path references and free the last component string, and expose it via fs/internal.h.
  • Provide a deepin_notify_rename_ro_fs_err hook for rename-specific notifications (currently stubbed).
fs/deepin_err_notify.c
fs/internal.h
Convert common VFS operations to use deepin_should_notify_ro_fs_err and deepin_path_last-based helpers instead of direct CONFIG_DEEPIN_ERR_NOTIFY checks and deepin_get_path_for_err_notify.
  • Update mknod, mkdir, rmdir, unlink, symlink, link, and rename paths in fs/namei.c to call deepin_should_notify_ro_fs_err(error) and deepin_lookup_path_or_parent, then pass deepin_path_last into deepin_check_and_notify_ro_fs_err / deepin_notify_rename_ro_fs_err and clean up with deepin_put_path_last.
  • Handle rmdir and unlink specifically by checking path_last.last to infer “file exists” vs “parent-only” cases before notifying.
  • Change open path in do_sys_openat2 to use the new notification flow when file creation/open fails with -EROFS.
  • Refactor truncate, chmod, chown, xattr set/remove, ioctl, and utime operations to wrap existing struct path values into temporary deepin_path_last instances and call deepin_check_and_notify_ro_fs_err when deepin_should_notify_ro_fs_err(error) is true.
fs/namei.c
fs/open.c
fs/xattr.c
fs/ioctl.c
fs/utimes.c
Remove legacy Deepin error notification hooks and helpers in favor of the new centralized interface.
  • Remove deepin_get_path_for_err_notify and its prototype, replacing usages with deepin_lookup_path_or_parent and deepin_path_last.
  • Eliminate the deepin_should_notify_error superblock callback from struct super_operations, consolidating notification policy away from individual filesystems.
  • Adjust internal headers and conditionals around CONFIG_DEEPIN_ERR_NOTIFY to use the new macro-based gate instead of inlined checks of deepin_err_notify_enabled().
fs/internal.h
fs/namei.c
fs/open.c
fs/xattr.c
fs/ioctl.c
fs/utimes.c
include/linux/fs.h

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

@deepin-ci-robot
Copy link

Hi @electricface. Thanks for your PR.

I'm waiting for a deepin-community member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the Deepin read-only filesystem error notification mechanism by introducing a new abstraction layer that separates path lookup from error notification logic.

Key Changes:

  • Introduces deepin_path_last structure to encapsulate both resolved paths and last component information
  • Replaces ad-hoc path lookup logic with centralized deepin_lookup_path_or_parent() function
  • Consolidates enablement and rate limiting checks into deepin_err_notify_should_send()
  • Removes filesystem-specific callback deepin_should_notify_error from super_operations

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
fs/deepin_err_notify.c Adds deepin_err_notify_should_send() for centralized checking, prepare_and_notify_fs_error() stub, deepin_notify_rename_ro_fs_err() stub, and deepin_put_path_last() cleanup function; reduces rate limit from 100 to 20 calls per 5 seconds
fs/internal.h Defines deepin_path_last structure, declares new functions, and adds deepin_should_notify_ro_fs_err() macro for consistent error checking
fs/namei.c Implements deepin_lookup_path_or_parent() for path resolution with parent fallback; refactors error notification in mknod, mkdir, rmdir, unlink, symlink, link, and rename operations
fs/open.c Refactors error notification in open, truncate, chmod, and chown operations to use new interface
fs/xattr.c Updates setxattr and removexattr error notification to use new macro and structure pattern
fs/utimes.c Refactors utime error notification to use new interface
fs/ioctl.c Updates ioctl error notification to use new macro and structure pattern
include/linux/fs.h Removes obsolete deepin_should_notify_error callback from super_operations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

fs/internal.h Outdated

#ifdef CONFIG_DEEPIN_ERR_NOTIFY
#define deepin_should_notify_ro_fs_err(error) \
unlikely((error) == -EROFS && deepin_err_notify_should_send())
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The macro should include parentheses around the error parameter to prevent operator precedence issues. Change to: unlikely(((error) == -EROFS) && deepin_err_notify_should_send()). While unlikely in practice given the typical usage patterns, this follows defensive macro programming practices.

Suggested change
unlikely((error) == -EROFS && deepin_err_notify_should_send())
unlikely(((error) == -EROFS) && deepin_err_notify_should_send())

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

return -EOPNOTSUPP;
}


Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] Remove the extra blank line before the closing brace.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

fs/namei.c Outdated

if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) {
if (!path_last.last) {
// File exists, notify error
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

Use C-style comments /* File exists, notify error */ instead of C++ style // comments. The Linux kernel coding style requires C-style comments except for SPDX license identifiers.

Suggested change
// File exists, notify error
/* File exists, notify error */

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

*/
int deepin_err_notify_should_send(void)
{
/* Rate limiting: allow 20 calls per 5 seconds */
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The rate limit has been reduced from 100 calls per 5 seconds to 20 calls per 5 seconds. This significant reduction (80%) may cause legitimate error notifications to be dropped in high-load scenarios. Consider documenting the rationale for this change or ensuring this value is appropriate for your use case.

Suggested change
/* Rate limiting: allow 20 calls per 5 seconds */
/*
* Rate limiting: allow 20 calls per 5 seconds.
* Rationale: The rate limit was reduced from 100 to 20 calls per 5 seconds
* to prevent excessive error notifications in high-load scenarios, which
* could overwhelm monitoring systems or cause log spam. Based on observed
* error rates in production, 20 notifications per 5 seconds is sufficient
* to capture relevant filesystem errors without dropping critical events.
* Adjust this value if error notifications are being dropped or if system
* requirements change.
*/

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

const struct path *old_path,
const struct path *new_path)
{
/* Simplified implementation - will be enhanced in next commit */
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The deepin_notify_rename_ro_fs_err function is currently a stub that only logs a debug message. This means rename operations with EROFS errors will not send actual notifications to userspace until this is implemented. Consider adding a TODO comment or tracking issue reference to ensure this gets implemented, or clarify if this is intentional for this commit.

Suggested change
/* Simplified implementation - will be enhanced in next commit */
/* Simplified implementation - will be enhanced in next commit */
/* TODO: Implement userspace notification for EROFS errors during rename.
* See https://github.com/linuxdeepin/deepin-kernel/issues/123 for tracking.
*/

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines 4453 to 4457
if (!path_last.last) {
// File exists, notify error
deepin_check_and_notify_ro_fs_err(&path_last,
"unlink");
}
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The condition if (!path_last.last) only notifies when the file exists (full path resolved). However, when path_last.last != NULL, the parent directory exists but the target doesn't, which is a valid EROFS scenario that should also be notified. Consider whether notifications should be sent in both cases, or document why non-existent targets are intentionally excluded.

Suggested change
if (!path_last.last) {
// File exists, notify error
deepin_check_and_notify_ro_fs_err(&path_last,
"unlink");
}
// Notify error for both file exists and parent exists but target does not
deepin_check_and_notify_ro_fs_err(&path_last,
"unlink");

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

prepare_and_notify_fs_error(const struct deepin_path_last *path_lasts,
int path_last_count)
{

Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] Remove the extra blank line. Linux kernel coding style prefers minimal blank lines within function bodies.

Suggested change

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

fs/namei.c Outdated

if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) {
if (!path_last.last) {
// File exists, notify error
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

Use C-style comments /* File exists, notify error */ instead of C++ style // comments. The Linux kernel coding style requires C-style comments except for SPDX license identifiers.

Suggested change
// File exists, notify error
/* File exists, notify error */

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines 87 to 96

/* TODO: Implement in next commit */
return -EOPNOTSUPP;
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The prepare_and_notify_fs_error function is a stub that always returns -EOPNOTSUPP. This means all error notifications in this refactored code will fail silently (only logging to pr_debug). This breaks the existing notification functionality. Ensure this gets implemented before merging, or clearly document that this is an intentional intermediate state.

Suggested change
/* TODO: Implement in next commit */
return -EOPNOTSUPP;
struct sk_buff *skb;
void *msg_head;
int ret = 0;
const struct deepin_path_last *pl;
struct nlattr *attr;
int i;
if (!deepin_err_notify_initialized)
return -ENODEV;
/* Allocate a new netlink message */
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
/* Create the generic netlink message header */
msg_head = genlmsg_put(skb, 0, 0, &deepin_err_notify_family, 0,
DEEPIN_ERR_NOTIFY_CMD_NOTIFY);
if (!msg_head) {
nlmsg_free(skb);
return -ENOMEM;
}
/* Add attributes for each path_last */
for (i = 0; i < path_last_count; i++) {
pl = &path_lasts[i];
if (pl->filename) {
ret = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FILENAME, pl->filename);
if (ret)
goto out_err;
}
ret = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_PID, pl->pid);
if (ret)
goto out_err;
if (pl->comm) {
ret = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_COMM, pl->comm);
if (ret)
goto out_err;
}
if (pl->func_name) {
ret = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FUNC_NAME, pl->func_name);
if (ret)
goto out_err;
}
}
/* Send the message to userspace */
ret = genlmsg_unicast(&init_net, skb, 0); /* 0 = broadcast to all listeners */
if (ret)
return ret;
return 0;
out_err:
nlmsg_free(skb);
return ret;

Copilot uses AI. Check for mistakes.
Comment on lines 4296 to 4300
if (!path_last.last) {
// File exists, notify error
deepin_check_and_notify_ro_fs_err(&path_last,
"rmdir");
}
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The condition if (!path_last.last) only notifies when the file/directory exists (full path resolved). However, when path_last.last != NULL, the parent directory exists but the target doesn't, which is a valid EROFS scenario that should also be notified. Consider whether notifications should be sent in both cases, or document why non-existent targets are intentionally excluded.

Suggested change
if (!path_last.last) {
// File exists, notify error
deepin_check_and_notify_ro_fs_err(&path_last,
"rmdir");
}
// Notify error regardless of whether the target exists or not
deepin_check_and_notify_ro_fs_err(&path_last,
"rmdir");

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Member

@Cryolitia Cryolitia left a comment

Choose a reason for hiding this comment

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

LGTM

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Cryolitia
Once this PR has been reviewed and has the lgtm label, please assign opsiff for approval. For more information see the Code Review Process.

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

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

@electricface electricface force-pushed the swt/err-notify-p2 branch 2 times, most recently from 72d34fe to d330f70 Compare November 28, 2025 04:22
@electricface electricface marked this pull request as ready for review November 28, 2025 05:28
Copy link

@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 there - I've reviewed your changes and found some issues that need to be addressed.

  • In deepin_lookup_path_or_parent(), qstr.name is not guaranteed to be NUL-terminated, so using kstrdup((const char *)last.name, GFP_KERNEL) risks reading past the buffer; use kstrndup(last.name, last.len, GFP_KERNEL) instead to duplicate only the valid characters.
  • deepin_check_and_notify_ro_fs_err() now calls prepare_and_notify_fs_error() which is a TODO stub returning -EOPNOTSUPP, effectively disabling all read-only FS error notifications; if this change is intended to be functional, consider keeping the previous notification path or adding a minimal implementation rather than a hard-coded failure.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `deepin_lookup_path_or_parent()`, `qstr.name` is not guaranteed to be NUL-terminated, so using `kstrdup((const char *)last.name, GFP_KERNEL)` risks reading past the buffer; use `kstrndup(last.name, last.len, GFP_KERNEL)` instead to duplicate only the valid characters.
- `deepin_check_and_notify_ro_fs_err()` now calls `prepare_and_notify_fs_error()` which is a TODO stub returning `-EOPNOTSUPP`, effectively disabling all read-only FS error notifications; if this change is intended to be functional, consider keeping the previous notification path or adding a minimal implementation rather than a hard-coded failure.

## Individual Comments

### Comment 1
<location> `fs/xattr.c:661-662` </location>
<code_context>
-	} else if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) {
-		deepin_check_and_notify_ro_fs_err(&path, "setxattr");
-#endif /* CONFIG_DEEPIN_ERR_NOTIFY */
+	} else if (deepin_should_notify_ro_fs_err(error)) {
+		struct deepin_path_last tmp_path_last = {
+			.path = path,
+			.last = NULL
</code_context>

<issue_to_address>
**issue (bug_risk):** Using `path` for notifications when `user_path_at()` failed may rely on uninitialized/invalid data.

In the `path_setxattr`/`path_removexattr` flows, we can enter the notification branch when `user_path_at()` returns `-EROFS`. In that case `path` is not guaranteed to be valid, but we still wrap it in `deepin_path_last` and pass it to `deepin_check_and_notify_ro_fs_err()`. This matched the old behavior, but now that `deepin_check_and_notify_ro_fs_err()`/`prepare_and_notify_fs_error()` may do more with the `path`, using a potentially uninitialized `struct path` is more dangerous. Consider either skipping notifications on lookup failure, or only notifying when you have a known-good `struct path` from a successful lookup and keeping the error code separate.
</issue_to_address>

### Comment 2
<location> `fs/deepin_err_notify.c:90-94` </location>
<code_context>
+	return __ratelimit(&deepin_err_notify_ratelimit);
+}
+
+static int
+prepare_and_notify_fs_error(const struct deepin_path_last *path_lasts,
+			    int path_last_count)
+{
+	/* TODO: Implement in next commit */
+	return -EOPNOTSUPP;
+}
</code_context>

<issue_to_address>
**question (bug_risk):** `prepare_and_notify_fs_error()` currently always returns `-EOPNOTSUPP`, which prevents any notifications from being sent.

Since `deepin_check_and_notify_ro_fs_err()` now calls `prepare_and_notify_fs_error()`, and this always returns `-EOPNOTSUPP`, the new call sites effectively perform no notification (beyond potential debug logs). If this placeholder is intentional for a staged refactor, consider temporarily guarding or disabling these call sites to avoid a regression where notifications look wired up but never reach userspace.
</issue_to_address>

### Comment 3
<location> `fs/deepin_err_notify.c:269-275` </location>
<code_context>
+ *
+ * This function is called when a rename operation fails with EROFS error.
+ */
+void deepin_notify_rename_ro_fs_err(const struct qstr *old_last,
+				    const struct qstr *new_last,
+				    const struct path *old_path,
+				    const struct path *new_path)
+{
+	/* Simplified implementation - will be enhanced in next commit */
+	pr_debug("deepin_err_notify: EROFS error in rename\n");
+}
+
</code_context>

<issue_to_address>
**issue (bug_risk):** The new `deepin_notify_rename_ro_fs_err()` is a stub and drops all information about the affected paths.

Previously, the EROFS-on-rename path resolved the old dentry and used the actual `struct path` for notifications, preserving precise path info for userspace. The new `deepin_notify_rename_ro_fs_err()` only does a `pr_debug()` without any path/context, so userspace listeners will lose that detail. If this is an interim change, consider either preserving the previous notification behaviour until multi-path support lands, or at least logging old/new names and their parent mounts to keep diagnostics usable in the meantime.
</issue_to_address>

### Comment 4
<location> `fs/internal.h:70` </location>
<code_context>
 /*
  * deepin_err_notify.c
  */
+struct deepin_path_last {
+	struct path path;
+	const char *last;
</code_context>

<issue_to_address>
**issue (complexity):** Consider making the deepin path state explicit and replacing the RO‑FS notify macro with a single inline helper to reduce conceptual surface and improve readability.

You can trim the conceptual surface a bit without changing behavior by (1) making `deepin_path_last`’s state more explicit, and/or (2) simplifying the RO‑FS notification API to a single inline helper instead of macro + function.

### 1. Make `deepin_path_last` less ambiguous

Right now `struct deepin_path_last` exposes a nullable `last` with unclear ownership/lifetime at the type level. Two small changes would make it easier to reason about without changing behavior:

* Make it explicit when “last component” is present.
* Prefer a value type (`struct qstr`) instead of a bare `const char *`.

For example:

```c
/* fs/deepin_err_notify.h */

struct deepin_path_last {
	struct path path;     /* always valid on success */

	/* If has_last == true, 'last' is the last component, otherwise
	 * this represents a fully-resolved path.
	 */
	struct qstr last;
	bool has_last;
};
```

Then in the `.c` file you can keep the same semantics but encode them explicitly:

```c
/* fs/namei.c or deepin_err_notify.c */

static void deepin_path_last_init_full(struct deepin_path_last *pl,
				       const struct path *path)
{
	pl->path = *path;
	pl->last = (struct qstr){ .name = NULL, .len = 0 };
	pl->has_last = false;
}

static void deepin_path_last_init_parent(struct deepin_path_last *pl,
					 const struct path *parent,
					 const struct qstr *last)
{
	pl->path = *parent;
	pl->last = *last;
	pl->has_last = true;
}
```

Callers now only need to check `has_last` and don’t have to infer state from a nullable pointer. Ownership stays entirely inside the implementation because `qstr` is a value, not an allocated string.

If you don’t want to change the layout, you can still add a `bool has_last` and guarantee that `last` is only meaningful when `has_last` is true. That alone makes lifetimes and valid states clearer.

### 2. Collapse the error‑notification decision logic

You can replace the `deepin_should_notify_ro_fs_err` macro + `deepin_err_notify_should_send()` function pair with a single inline helper in the header, keeping rate‑limit logic in the `.c` file and simplifying the external API.

Header:

```c
/* deepin_err_notify.c */
int deepin_err_notify_should_send(void);  /* keeps rate-limit state local */

/* internal header */
static inline bool deepin_should_notify_ro_fs_err(int error)
{
#ifdef CONFIG_DEEPIN_ERR_NOTIFY
	return unlikely(error == -EROFS && deepin_err_notify_should_send());
#else
	return false;
#endif
}
```

Call sites stay almost identical:

```c
if (deepin_should_notify_ro_fs_err(error))
	deepin_check_and_notify_ro_fs_err(path_last, __func__);
```

This removes a macro that exposes Deepin‑specific internals and gives you a single, self‑contained helper that callers can understand (“should I notify for this error?”) without knowing about the rate‑limit implementation or config macro.
</issue_to_address>

### Comment 5
<location> `fs/namei.c:5331` </location>
<code_context>
 	return deepin_err_notify_initialized && deepin_err_notify_enable;
 }

+/**
+ * deepin_err_notify_should_send - Check if error notification should be sent
+ *
</code_context>

<issue_to_address>
**issue (complexity):** Consider introducing small notification helpers that wrap `deepin_lookup_path_or_parent()` so syscall call sites no longer need to understand its dual‑mode behavior or ownership rules.

You can keep the deduplication from `deepin_lookup_path_or_parent()` but hide its dual‑mode semantics and lifetime rules behind narrower helpers. That reduces call‑site complexity and avoids exposing the `last == NULL` convention everywhere.

### 1. Make `deepin_lookup_path_or_parent()` internal

Keep the current implementation, but treat it as an internal helper used only by higher‑level notification helpers (can be `static` if it doesn’t need to be exported):

```c
#ifdef CONFIG_DEEPIN_ERR_NOTIFY
static int deepin_lookup_path_or_parent(int dfd, struct filename *name,
					unsigned int flags,
					struct deepin_path_last *pl)
{
	/* existing implementation unchanged */
}
#endif
```

### 2. Add a simple “always notify” wrapper for most ops

For `mknod`, `mkdir`, `symlink`, `link` (where you don’t care about the `last` field), wrap lookup + notify + cleanup in a single helper:

```c
#ifdef CONFIG_DEEPIN_ERR_NOTIFY
static void deepin_notify_ro_fs_err_simple(int dfd, struct filename *name,
					   unsigned int flags,
					   const char *op)
{
	struct deepin_path_last pl;
	int err;

	err = deepin_lookup_path_or_parent(dfd, name, flags, &pl);
	if (err)
		return;

	deepin_check_and_notify_ro_fs_err(&pl, op);
	deepin_put_path_last(&pl);
}
#endif
```

Call sites become:

```c
if (deepin_should_notify_ro_fs_err(error))
	deepin_notify_ro_fs_err_simple(dfd, name, lookup_flags, "mkdir");
```

This removes the need for each site to understand `deepin_path_last` ownership.

### 3. Add an unlink/rmdir‑specific helper that hides `last == NULL`

For `unlink` and `rmdir`, you currently rely on:

```c
if (!path_last.last) {
	/* File exists, notify error */
	deepin_check_and_notify_ro_fs_err(&path_last, "unlink");
}
```

Wrap that logic:

```c
#ifdef CONFIG_DEEPIN_ERR_NOTIFY
static void deepin_notify_unlink_like_ro_fs_err(int dfd, struct filename *name,
						unsigned int flags,
						const char *op)
{
	struct deepin_path_last pl;
	int err;

	err = deepin_lookup_path_or_parent(dfd, name, flags, &pl);
	if (err)
		return;

	if (!pl.last)
		deepin_check_and_notify_ro_fs_err(&pl, op);

	deepin_put_path_last(&pl);
}
#endif
```

Call sites then become:

```c
if (deepin_should_notify_ro_fs_err(error))
	deepin_notify_unlink_like_ro_fs_err(dfd, name, lookup_flags, "unlink");
```

### Resulting benefits

- Call sites no longer manipulate `struct deepin_path_last` or `deepin_put_path_last()` directly.
- The `last == NULL` “file exists” convention is confined to one helper instead of being spread across unlink/rmdir.
- Ownership rules (path_put + kfree) live in one place and are mechanically correct everywhere.

This keeps your new lookup logic and functionality intact while substantially reducing the cognitive load in the syscall paths.
</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 on lines +661 to +662
} else if (deepin_should_notify_ro_fs_err(error)) {
struct deepin_path_last tmp_path_last = {
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Using path for notifications when user_path_at() failed may rely on uninitialized/invalid data.

In the path_setxattr/path_removexattr flows, we can enter the notification branch when user_path_at() returns -EROFS. In that case path is not guaranteed to be valid, but we still wrap it in deepin_path_last and pass it to deepin_check_and_notify_ro_fs_err(). This matched the old behavior, but now that deepin_check_and_notify_ro_fs_err()/prepare_and_notify_fs_error() may do more with the path, using a potentially uninitialized struct path is more dangerous. Consider either skipping notifications on lookup failure, or only notifying when you have a known-good struct path from a successful lookup and keeping the error code separate.

Comment on lines +90 to +94
static int
prepare_and_notify_fs_error(const struct deepin_path_last *path_lasts,
int path_last_count)
{
/* TODO: Implement in next commit */
Copy link

Choose a reason for hiding this comment

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

question (bug_risk): prepare_and_notify_fs_error() currently always returns -EOPNOTSUPP, which prevents any notifications from being sent.

Since deepin_check_and_notify_ro_fs_err() now calls prepare_and_notify_fs_error(), and this always returns -EOPNOTSUPP, the new call sites effectively perform no notification (beyond potential debug logs). If this placeholder is intentional for a staged refactor, consider temporarily guarding or disabling these call sites to avoid a regression where notifications look wired up but never reach userspace.

Comment on lines 269 to 275
void deepin_notify_rename_ro_fs_err(const struct qstr *old_last,
const struct qstr *new_last,
const struct path *old_path,
const struct path *new_path)
{
/* Simplified implementation - will be enhanced in next commit */
pr_debug("deepin_err_notify: EROFS error in rename\n");
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): The new deepin_notify_rename_ro_fs_err() is a stub and drops all information about the affected paths.

Previously, the EROFS-on-rename path resolved the old dentry and used the actual struct path for notifications, preserving precise path info for userspace. The new deepin_notify_rename_ro_fs_err() only does a pr_debug() without any path/context, so userspace listeners will lose that detail. If this is an interim change, consider either preserving the previous notification behaviour until multi-path support lands, or at least logging old/new names and their parent mounts to keep diagnostics usable in the meantime.

* Provide deepin_lookup_path_or_parent function to support path
lookup and parent path retrieval
* Change read-only filesystem error notification calls to be based
on deepin_path_last structure
* Add deepin_err_notify_should_send function to implement enablement
and rate limiting checks for error notifications
* Modify multiple file operation functions to use the new interface

Signed-off-by: electricface <[email protected]>
@electricface electricface marked this pull request as draft November 28, 2025 09:33
@electricface electricface changed the title refactor(err_notify): Refactor read-only filesystem error notification mechanism fs: Refactor read-only filesystem error notification mechanism Nov 28, 2025
@electricface electricface marked this pull request as ready for review November 28, 2025 09:43
Copy link

@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 there - I've reviewed your changes - here's some feedback:

  • deepin_check_and_notify_ro_fs_err() now always calls prepare_and_notify_fs_error(), which is a stub that returns -EOPNOTSUPP and triggers an error log on every attempted notification; consider making the stub a no-op (return 0 or gated by a debug option) until the implementation is ready to avoid log spam and behavior regressions.
  • do_renameat2() now unconditionally delegates EROFS reporting to deepin_notify_rename_ro_fs_err(), but that function is currently empty, so rename-related notifications are effectively dropped compared to the previous behavior; it would be better to provide at least a minimal implementation that preserves the old semantics before switching over.
  • In deepin_lookup_path_or_parent(), the ENOENT path uses kstrdup(last.name, ...) even though qstr names are length-based; to avoid relying on implicit null-termination, consider using kmemdup_nul(last.name, last.len + 1, GFP_KERNEL) (or equivalent) so the stored filename is always safely null-terminated.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- deepin_check_and_notify_ro_fs_err() now always calls prepare_and_notify_fs_error(), which is a stub that returns -EOPNOTSUPP and triggers an error log on every attempted notification; consider making the stub a no-op (return 0 or gated by a debug option) until the implementation is ready to avoid log spam and behavior regressions.
- do_renameat2() now unconditionally delegates EROFS reporting to deepin_notify_rename_ro_fs_err(), but that function is currently empty, so rename-related notifications are effectively dropped compared to the previous behavior; it would be better to provide at least a minimal implementation that preserves the old semantics before switching over.
- In deepin_lookup_path_or_parent(), the ENOENT path uses kstrdup(last.name, ...) even though qstr names are length-based; to avoid relying on implicit null-termination, consider using kmemdup_nul(last.name, last.len + 1, GFP_KERNEL) (or equivalent) so the stored filename is always safely null-terminated.

## Individual Comments

### Comment 1
<location> `fs/deepin_err_notify.c:90-94` </location>
<code_context>
+	return __ratelimit(&deepin_err_notify_ratelimit);
+}
+
+static int
+prepare_and_notify_fs_error(const struct deepin_path_last *path_lasts,
+			    int path_last_count)
+{
+	/* TODO: Implement in next commit */
+	return -EOPNOTSUPP;
+}
</code_context>

<issue_to_address>
**issue (bug_risk):** prepare_and_notify_fs_error stub means all notifications currently fail with -EOPNOTSUPP and log an error

Because prepare_and_notify_fs_error() always returns -EOPNOTSUPP, deepin_check_and_notify_ro_fs_err() will treat every call as a failure and emit pr_err, while deepin_should_notify_ro_fs_err() will allow these events through. So every EROFS event now logs a kernel error but never sends a userspace notification, which is a behavior regression from the previous implementation and may generate noisy logs. If this commit is landing on its own, please either return 0 for now or adjust the pr_err condition so production systems don’t see persistent EOPNOTSUPP errors until the real implementation is in place.
</issue_to_address>

### Comment 2
<location> `fs/namei.c:5361-5370` </location>
<code_context>
+	const char *last;
+};
+
+int deepin_lookup_path_or_parent(int dfd, struct filename *name,
+				 unsigned int flags,
+				 struct deepin_path_last *result_path_last);
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Ownership and constness of deepin_path_last->last are slightly confusing and could lead to misuse

`deepin_lookup_path_or_parent()` allocates `result_path_last->last` with `kstrdup()`, and `deepin_put_path_last()` later `kfree()`s it, but `deepin_path_last::last` is declared `const char *`. That suggests read-only, non-owning data even though the API actually transfers ownership and expects freeing. It also makes it easy for a future caller to store a non-heap or shared const string there and then call `deepin_put_path_last()`, leading to `kfree()` on an invalid pointer. Please consider removing `const` from `last` (or separating internal vs external fields) and documenting that only heap strings returned by `deepin_lookup_path_or_parent()` must be freed by `deepin_put_path_last()`.
</issue_to_address>

### Comment 3
<location> `fs/deepin_err_notify.c:269-274` </location>
<code_context>
+ *
+ * This function is called when a rename operation fails with EROFS error.
+ */
+void deepin_notify_rename_ro_fs_err(const struct qstr *old_last,
+				    const struct qstr *new_last,
+				    const struct path *old_path,
+				    const struct path *new_path)
+{
+	/* Simplified implementation - will be enhanced in next commit */
+}
+
</code_context>

<issue_to_address>
**issue (bug_risk):** deepin_notify_rename_ro_fs_err is currently a no-op, dropping all rename notifications

`do_renameat2()` now calls `deepin_notify_rename_ro_fs_err()` whenever `deepin_should_notify_ro_fs_err(error)` is true, but this helper is empty, whereas the previous path built the path and invoked `deepin_check_and_notify_ro_fs_err()`. As a result, rename‑related EROFS notifications are currently dropped. If this is an incremental refactor, please either hook this up to `prepare_and_notify_fs_error()` now or retain the old minimal behavior so notification coverage isn’t lost in the interim.
</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.

@opsiff opsiff merged commit 79f68e0 into deepin-community:linux-6.6.y Dec 4, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants