diff --git a/fs/deepin_err_notify.c b/fs/deepin_err_notify.c index f3c6a6f0ce849..761ae19e95e8d 100644 --- a/fs/deepin_err_notify.c +++ b/fs/deepin_err_notify.c @@ -1,27 +1,27 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/deepin_ro_fs_err_notify.c - Deepin read-only filesystem error notification + * Deepin filesystem error notification * - * This module provides notification functionality for read-only filesystem - * errors, specifically targeting overlay filesystems mounted on /usr. + * This module provides notification functionality for filesystem errors, + * especially for read-only filesystem errors. */ -#include -#include +#include +#include +#include +#include #include -#include +#include +#include #include +#include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include "internal.h" @@ -30,13 +30,23 @@ /* Family name (max GENL_NAMSIZ characters, including null terminator) */ #define DEEPIN_ERR_NOTIFY_FAMILY_NAME "DEEPIN_ENOTIFY" +/* Multicast group name for error events (used in Netlink communication) */ +#define MULTICAST_GROUP_ERR_EVENTS "err_events" + /* Define netlink message types and attributes */ enum { DEEPIN_ERR_NOTIFY_ATTR_UNSPEC, - DEEPIN_ERR_NOTIFY_ATTR_FILENAME, /* Filename */ - DEEPIN_ERR_NOTIFY_ATTR_PID, /* Process ID */ - DEEPIN_ERR_NOTIFY_ATTR_COMM, /* Process Name */ - DEEPIN_ERR_NOTIFY_ATTR_FUNC_NAME, /* Function Name */ + DEEPIN_ERR_NOTIFY_ATTR_FILENAME = 1, /* Filename (string) */ + DEEPIN_ERR_NOTIFY_ATTR_INODE = 2, /* Inode (u64) */ + DEEPIN_ERR_NOTIFY_ATTR_INODE_PARENT = + 3, /* Bool: inode from parent (u8) */ + DEEPIN_ERR_NOTIFY_ATTR_PID = 4, /* Process ID (u32) */ + DEEPIN_ERR_NOTIFY_ATTR_PPID = 5, /* Parent Process ID (u32) */ + DEEPIN_ERR_NOTIFY_ATTR_COMM = + 6, /* Process Short Name (string, 15 chars max) */ + DEEPIN_ERR_NOTIFY_ATTR_UID = 7, /* User ID (u32) */ + DEEPIN_ERR_NOTIFY_ATTR_GID = 8, /* Group ID (u32) */ + DEEPIN_ERR_NOTIFY_ATTR_STATUS = 9, /* Enable/Disable Status (u8) */ __DEEPIN_ERR_NOTIFY_ATTR_MAX, }; @@ -44,7 +54,11 @@ enum { enum { DEEPIN_ERR_NOTIFY_CMD_UNSPEC, - DEEPIN_ERR_NOTIFY_CMD_NOTIFY, /* Error Notify Command */ + DEEPIN_ERR_NOTIFY_CMD_ERR_ROFS = + 1, /* Read Only Filesystem Error Notify Command */ + DEEPIN_ERR_NOTIFY_CMD_ENABLE = 2, /* Enable error notification */ + DEEPIN_ERR_NOTIFY_CMD_DISABLE = 3, /* Disable error notification */ + DEEPIN_ERR_NOTIFY_CMD_GET_STATUS = 4, /* Get enable/disable status */ __DEEPIN_ERR_NOTIFY_CMD_MAX, }; @@ -54,68 +68,148 @@ enum { static bool deepin_err_notify_initialized __read_mostly; /* Runtime control variable for deepin error notification */ -static int deepin_err_notify_enable __read_mostly = 0; +static int deepin_err_notify_enable __read_mostly; -int deepin_err_notify_enabled(void) +inline int deepin_err_notify_enabled(void) { return deepin_err_notify_initialized && deepin_err_notify_enable; } -/* Check if overlay filesystem is mounted on /usr and send read only error notification */ -void deepin_check_and_notify_ro_fs_err(const struct path *path, - const char *func_name) +/** + * deepin_err_notify_should_send - Check if error notification should be sent + * + * This function checks both the enable status and rate limiting to determine + * whether an error notification should be sent. + * + * Return: 1 if notification should be sent, 0 otherwise + */ +int deepin_err_notify_should_send(void) { - char *path_buf = NULL; - char *full_path = ""; - /* Rate limiting: allow 100 calls per 5 seconds */ - static DEFINE_RATELIMIT_STATE(deepin_ro_fs_err_ratelimit, - 5 * HZ, /* 5 seconds interval */ - 100); /* 100 calls per interval */ - - /* Check rate limit before proceeding */ - if (!__ratelimit(&deepin_ro_fs_err_ratelimit)) - return; + /* Rate limiting: allow 20 calls per 5 seconds */ + static DEFINE_RATELIMIT_STATE(deepin_err_notify_ratelimit, 5 * HZ, 20); - /* Early return if path or path->mnt is invalid */ - if (!path || !path->mnt || !path->mnt->mnt_sb) - return; + if (!deepin_err_notify_enabled()) + return 0; - /* Use filesystem callback to decide if notification should be sent. - * If filesystem implements the callback, use it. - */ - if (path->mnt->mnt_sb->s_op && - path->mnt->mnt_sb->s_op->deepin_should_notify_error) { - if (!path->mnt->mnt_sb->s_op->deepin_should_notify_error(path->mnt->mnt_sb)) - return; - } else { - /* If filesystem does not implement the callback, return immediately. */ - return; - } + return __ratelimit(&deepin_err_notify_ratelimit); +} - /* Attempt to get the full path. - * Dynamic allocation is used to avoid excessive frame size. - */ - if (path->dentry) { - path_buf = kmalloc(PATH_MAX, GFP_KERNEL); - if (path_buf) { - char *p = NULL; - - p = d_path(path, path_buf, PATH_MAX); - if (!IS_ERR(p)) - full_path = p; - } +/* Define multicast group */ +static const struct genl_multicast_group deepin_err_notify_nl_mcgrps[] = { + { + .name = MULTICAST_GROUP_ERR_EVENTS, + .flags = GENL_UNS_ADMIN_PERM, /* Require CAP_NET_ADMIN */ + }, +}; + +/* Netlink attribute policy */ +static const struct nla_policy + deepin_err_notify_genl_policy[DEEPIN_ERR_NOTIFY_ATTR_MAX + 1] = { + [DEEPIN_ERR_NOTIFY_ATTR_FILENAME] = { .type = NLA_STRING }, + [DEEPIN_ERR_NOTIFY_ATTR_INODE] = { .type = NLA_U64 }, + [DEEPIN_ERR_NOTIFY_ATTR_INODE_PARENT] = { .type = NLA_U8 }, + [DEEPIN_ERR_NOTIFY_ATTR_PID] = { .type = NLA_U32 }, + [DEEPIN_ERR_NOTIFY_ATTR_PPID] = { .type = NLA_U32 }, + [DEEPIN_ERR_NOTIFY_ATTR_COMM] = { .type = NLA_STRING }, + [DEEPIN_ERR_NOTIFY_ATTR_UID] = { .type = NLA_U32 }, + [DEEPIN_ERR_NOTIFY_ATTR_GID] = { .type = NLA_U32 }, + [DEEPIN_ERR_NOTIFY_ATTR_STATUS] = { .type = NLA_U8 }, + }; + +/** + * deepin_err_notify_cmd_enable - Enable error notification via netlink + * @skb: Socket buffer (unused) + * @info: Generic netlink info structure + * + * Returns: 0 on success + */ +static int deepin_err_notify_cmd_enable(struct sk_buff *skb, + struct genl_info *info) +{ + deepin_err_notify_enable = 1; + pr_debug("deepin_err_notify: Error notification enabled via netlink\n"); + return 0; +} + +/** + * deepin_err_notify_cmd_disable - Disable error notification via netlink + * @skb: Socket buffer (unused) + * @info: Generic netlink info structure + * + * Returns: 0 on success + */ +static int deepin_err_notify_cmd_disable(struct sk_buff *skb, + struct genl_info *info) +{ + deepin_err_notify_enable = 0; + pr_debug( + "deepin_err_notify: Error notification disabled via netlink\n"); + return 0; +} + +/* Forward declaration of Generic Netlink family */ +static struct genl_family deepin_err_notify_genl_family; + +/** + * deepin_err_notify_cmd_get_status - Get error notification status via netlink + * @skb: Socket buffer (unused) + * @info: Generic netlink info structure + * + * Returns: 0 on success, negative error code on failure + */ +static int deepin_err_notify_cmd_get_status(struct sk_buff *skb, + struct genl_info *info) +{ + struct sk_buff *msg; + void *hdr; + int ret; + + /* Allocate a new message */ + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + /* Add Generic Netlink header */ + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, + &deepin_err_notify_genl_family, 0, + DEEPIN_ERR_NOTIFY_CMD_GET_STATUS); + if (!hdr) { + ret = -EMSGSIZE; + goto err_free_msg; } - deepin_send_ro_fs_err_notification(full_path, func_name); + /* Add status attribute */ + ret = nla_put_u8(msg, DEEPIN_ERR_NOTIFY_ATTR_STATUS, + deepin_err_notify_enable); + if (ret < 0) + goto err_free_msg; + + genlmsg_end(msg, hdr); - kfree(path_buf); + /* Send unicast reply to the requester */ + return genlmsg_reply(msg, info); + +err_free_msg: + nlmsg_free(msg); + return ret; } -/* Define multicast group */ -static const struct genl_multicast_group deepin_err_notify_nl_mcgrps[] = { +/* Define Generic Netlink operations */ +static const struct genl_ops deepin_err_notify_genl_ops[] = { + { + .cmd = DEEPIN_ERR_NOTIFY_CMD_ENABLE, + .doit = deepin_err_notify_cmd_enable, + .flags = GENL_ADMIN_PERM, /* Require CAP_NET_ADMIN */ + }, + { + .cmd = DEEPIN_ERR_NOTIFY_CMD_DISABLE, + .doit = deepin_err_notify_cmd_disable, + .flags = GENL_ADMIN_PERM, /* Require CAP_NET_ADMIN */ + }, { - .name = "ro_fs_events", - .flags = GENL_UNS_ADMIN_PERM, + .cmd = DEEPIN_ERR_NOTIFY_CMD_GET_STATUS, + .doit = deepin_err_notify_cmd_get_status, + .flags = 0, /* Allow any user to query status */ }, }; @@ -126,93 +220,466 @@ static struct genl_family deepin_err_notify_genl_family __ro_after_init = { .name = DEEPIN_ERR_NOTIFY_FAMILY_NAME, .version = 1, .maxattr = DEEPIN_ERR_NOTIFY_ATTR_MAX, + .policy = deepin_err_notify_genl_policy, + .ops = deepin_err_notify_genl_ops, + .n_ops = ARRAY_SIZE(deepin_err_notify_genl_ops), .mcgrps = deepin_err_notify_nl_mcgrps, .n_mcgrps = ARRAY_SIZE(deepin_err_notify_nl_mcgrps), }; -/* Send read only filesystem error notification */ -void deepin_send_ro_fs_err_notification(const char *filename, - const char *func_name) +/** + * struct deepin_fs_err_event_data - Filesystem error event data for netlink + * @filenames: Array of filenames involved in the error + * @filename_count: Number of filenames in the array (also used for inode count) + * @inodes: Array of inode numbers corresponding to the filenames + * @inode_is_parent: Array of bool flags indicating if inode is from parent dentry + * @pid: Process ID that triggered the error + * @ppid: Parent Process ID + * @comm: Process short name (15 chars max) + * @uid: User ID + * @gid: Group ID + * + * This structure consolidates all the parameters needed for sending + * filesystem error notifications via netlink. + */ +struct deepin_fs_err_event_data { + const char **filenames; + int count; + const ino_t *inodes; + const bool *inode_is_parent; + pid_t pid; + pid_t ppid; + const char *comm; + uid_t uid; + gid_t gid; +}; + +/** + * notify_fs_error - Send filesystem error notification via netlink + * @event: Pointer to filesystem error event data structure + * + * This function constructs and sends a Generic Netlink message with + * filesystem error information to userspace listeners. + * Multiple filenames are sent as separate attributes. + * + * Returns: 0 on success, negative error code on failure + */ +static int notify_fs_error(const struct deepin_fs_err_event_data *event) { - pid_t pid = 0; - const char *comm = NULL; - int msg_size; + int msg_size = 0; struct sk_buff *skb = NULL; void *msg_head = NULL; - int error; + int error = 0; + int i = 0; + + /* Validate input parameters */ + if (!event || !event->filenames || event->count <= 0 || !event->comm) { + pr_debug("deepin_err_notify: Invalid event data (event=%p)\n", + event); + return -EINVAL; + } - pid = current->pid; - comm = current->comm; + /* Calculate message size for all filenames and attributes */ + msg_size = nla_total_size(sizeof(u32)) /* PID */ + + nla_total_size(sizeof(u32)) /* PPID */ + + nla_total_size(strlen(event->comm) + 1) /* COMM */ + + nla_total_size(sizeof(u32)) /* UID */ + + nla_total_size(sizeof(u32)); /* GID */ + + for (i = 0; i < event->count; i++) { + if (event->filenames[i]) + msg_size += + nla_total_size(strlen(event->filenames[i]) + 1); + } - msg_size = nla_total_size(strlen(filename) + 1) + - nla_total_size(sizeof(u32)) + - nla_total_size(strlen(comm) + 1) + - nla_total_size(strlen(func_name) + 1); + /* Add size for inode numbers and parent flags */ + for (i = 0; i < event->count; i++) { + msg_size += nla_total_size(sizeof(u64)); /* INODE */ + msg_size += nla_total_size(sizeof(u8)); /* INODE_PARENT */ + } - /* Use GFP_NOFS to avoid recursion in file system operations. */ - skb = genlmsg_new(msg_size, GFP_NOFS); + /* Use GFP_ATOMIC because we might be called from atomic context */ + skb = genlmsg_new(msg_size, GFP_ATOMIC); if (!skb) { - pr_err("deepin_err_notify: Failed to allocate netlink message\n"); - return; + pr_debug( + "deepin_err_notify: Failed to allocate netlink message\n"); + return -ENOMEM; } msg_head = genlmsg_put(skb, 0, 0, &deepin_err_notify_genl_family, 0, - DEEPIN_ERR_NOTIFY_CMD_NOTIFY); + DEEPIN_ERR_NOTIFY_CMD_ERR_ROFS); if (!msg_head) { - pr_err("deepin_err_notify: Failed to put netlink header\n"); + pr_debug("deepin_err_notify: Failed to put netlink header\n"); + error = -EMSGSIZE; goto err_out; } - error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FILENAME, filename); + /* Add all filenames as separate attributes */ + for (i = 0; i < event->count; i++) { + if (event->filenames[i]) { + error = nla_put_string(skb, + DEEPIN_ERR_NOTIFY_ATTR_FILENAME, + event->filenames[i]); + if (error) + goto attr_err_out; + } + } + + /* Add all inode numbers and parent flags as paired attributes */ + for (i = 0; i < event->count; i++) { + error = nla_put_u64_64bit(skb, DEEPIN_ERR_NOTIFY_ATTR_INODE, + event->inodes[i], 0); + if (error) + goto attr_err_out; + + /* Add corresponding parent flag */ + error = nla_put_u8(skb, DEEPIN_ERR_NOTIFY_ATTR_INODE_PARENT, + event->inode_is_parent[i] ? 1 : 0); + if (error) + goto attr_err_out; + } + + error = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_PID, event->pid); + if (error) + goto attr_err_out; + + error = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_PPID, event->ppid); if (error) goto attr_err_out; - error = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_PID, pid); + error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_COMM, event->comm); if (error) goto attr_err_out; - error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_COMM, comm); + error = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_UID, event->uid); if (error) goto attr_err_out; - error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FUNC_NAME, - func_name); + error = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_GID, event->gid); if (error) goto attr_err_out; genlmsg_end(skb, msg_head); - /* Send multicast message. */ - genlmsg_multicast(&deepin_err_notify_genl_family, skb, 0, 0, GFP_NOFS); - return; + /* + * Send multicast message + * + * IMPORTANT: genlmsg_multicast() consumes the skb on success or when + * returning -ESRCH (no listeners). The skb ownership is transferred + * to the network subsystem in these cases. + * + * For other error codes, the skb is NOT consumed and must be freed + * by the caller. + */ + error = genlmsg_multicast(&deepin_err_notify_genl_family, skb, 0, 0, + GFP_ATOMIC); + if (error) { + if (error == -ESRCH) { + /* + * -ESRCH means no listeners registered. + * The skb has been consumed, so we must NOT free it. + */ + return 0; + } + /* + * Other errors: skb was not consumed, need to free it. + * Jump to err_out for cleanup. + */ + pr_debug("deepin_err_notify: Multicast failed: %d\n", error); + goto err_out; + } + + /* + * Success: skb has been consumed by genlmsg_multicast(). + * Do NOT call kfree_skb() here, as it would cause a double free. + */ + pr_debug( + "deepin_err_notify: Notification sent (files=%d, pid=%d, comm=%s)\n", + event->count, event->pid, event->comm); + return 0; attr_err_out: - pr_err("deepin_err_notify: Failed to add netlink attributes\n"); + pr_debug("deepin_err_notify: Failed to add netlink attributes: %d\n", + error); err_out: kfree_skb(skb); + return error; } -/* sysctl table and initialization */ -#ifdef CONFIG_SYSCTL -static struct ctl_table deepin_err_notify_sysctls[] = { - { - .procname = "deepin-err-notify-enable", - .data = &deepin_err_notify_enable, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, -}; +/** + * combine_path_and_last - Combine base path with last component + * @buffer: Pre-allocated buffer to store the combined path (size PATH_MAX) + * @path: The base path to combine + * @last: The last component to append + * + * This function combines a base path with a last component, constructing + * the full path: base_path + "/" + last. The result is stored in buffer. + * + * Returns: Pointer to the combined path string on success, NULL on failure + */ +static char *combine_path_and_last(char *buffer, const struct path *path, + const char *last) +{ + char *base_path; + size_t base_len, last_len, total_len; + + base_path = d_absolute_path(path, buffer, PATH_MAX); + if (IS_ERR(base_path)) + return NULL; + + /* Construct full path: base_path + "/" + last */ + base_len = strlen(base_path); + last_len = strlen(last); + total_len = base_len + 1 + last_len + 1; + + if (total_len > PATH_MAX) + return NULL; + + /* + * Move base_path to start of buffer if needed. + * d_absolute_path() may return a pointer to the middle of the buffer, + * not the start. We need the path at the beginning so we can + * append "/" + last to it. + */ + if (base_path != buffer) + memmove(buffer, base_path, base_len); + + /* Avoid appending an extra "/" after root directory "/" which would + * result in "//filename". + */ + if (base_len > 0 && buffer[base_len - 1] != '/') { + buffer[base_len] = '/'; + memcpy(buffer + base_len + 1, last, last_len + 1); + } else { + /* base_path is already "/" or ends with "/" (rare case) */ + memcpy(buffer + base_len, last, last_len + 1); + } + + return buffer; +} + +/** + * deepin_put_path_last - Release resources held by deepin_path_last structure + * @path_last: Pointer to the deepin_path_last structure to release + * + * This function releases the path reference and frees the allocated filename + * string in the deepin_path_last structure. + */ +void deepin_put_path_last(struct deepin_path_last *path_last) +{ + if (path_last) { + path_put(&path_last->path); + kfree(path_last->last); + path_last->last = NULL; + } +} + +/** + * prepare_and_notify_fs_error - Prepare and send filesystem error notification + * @path_lasts: Array of struct deepin_path_last that caused the error + * @path_last_count: Number of path_lasts in the array + * + * This function collects process context information, converts paths to + * filenames, and sends a Generic Netlink message with filesystem error + * information to userspace listeners. + * Multiple filenames are sent as separate attributes. + * + * If path_last->last is NULL, it means the complete path was obtained. + * If path_last->last is not NULL, it means the complete path was not obtained, + * and path_last->path + path_last->last does not exist as a file. + * In this case, inode_parent_flags[i] should be false, and inodes[i] should be + * the inode number of the dentry related to path_last->last, but filenames[i] + * should still be the complete path path_last->path + path_last->last. + * + * Returns: 0 on success, negative error code on failure + */ +static int +prepare_and_notify_fs_error(const struct deepin_path_last *path_lasts, + int path_last_count) +{ + /* Process information variables */ + pid_t pid = 0, ppid = 0; + const char *comm = NULL; + uid_t uid = 0; + gid_t gid = 0; + + /* Path conversion variables */ + char *path_bufs[2] = { NULL, NULL }; + const char *filenames[2] = { "", "" }; + int count = 0; + unsigned long inodes[2] = { 0, 0 }; + bool inode_parent_flags[2] = { false, false }; + int i = 0; + int error = 0; + + /* Validate input parameters */ + if (!path_lasts || path_last_count <= 0) { + pr_debug( + "deepin_err_notify: Invalid parameters: path_lasts=%p, path_last_count=%d\n", + path_lasts, path_last_count); + return -EINVAL; + } + + /* Only handle 1 or 2 path_lasts */ + if (path_last_count > 2) { + pr_debug( + "deepin_err_notify: Invalid path_last_count=%d (must be 1 or 2)\n", + path_last_count); + return -EINVAL; + } + + /* Get basic process information from current context */ + pid = current->pid; + ppid = current->real_parent ? current->real_parent->pid : 0; + comm = current->comm; /* Short name, limited to 15 characters */ + + /* Get credentials */ + uid = from_kuid(&init_user_ns, current_uid()); + gid = from_kgid(&init_user_ns, current_gid()); + + /* Convert each path_last to a filename string and extract inode */ + for (i = 0; i < path_last_count; i++) { + if (path_lasts[i].path.dentry) { + const struct deepin_path_last *pl = &path_lasts[i]; + struct dentry *target_dentry = pl->path.dentry; + + path_bufs[i] = kmalloc(PATH_MAX, GFP_ATOMIC); + if (!path_bufs[i]) + continue; + + /* Build the filename */ + if (pl->last) { + /* Complete path not obtained, need to combine path + last */ + char *combined_path = combine_path_and_last( + path_bufs[i], &pl->path, pl->last); + if (!combined_path) { + pr_debug( + "deepin_err_notify: combine_path_and_last failed for path_last[%d]\n", + i); + continue; + } + + filenames[count] = combined_path; + + /* File doesn't exist, use parent dentry's inode if available */ + if (target_dentry && target_dentry->d_inode) + inodes[count] = + target_dentry->d_inode->i_ino; + inode_parent_flags[count] = true; + count++; + } else { + /* Complete path obtained */ + char *p = d_absolute_path( + &pl->path, path_bufs[i], PATH_MAX); + if (!IS_ERR(p)) { + filenames[count] = p; + } else { + pr_debug( + "deepin_err_notify: d_absolute_path failed for path_last[%d]: %ld\n", + i, PTR_ERR(p)); + continue; + } + + /* File exists, use the file's own inode */ + if (target_dentry->d_inode) + inodes[count] = + target_dentry->d_inode->i_ino; + count++; + } + } + } + + /* + * Send the notification if we got at least one valid filename. + * If no valid filenames were extracted, return success anyway + * since this is not a critical error (paths may be unmounted, etc). + */ + if (count > 0) { + struct deepin_fs_err_event_data event = { + .filenames = filenames, + .count = count, + .inodes = inodes, + .inode_is_parent = inode_parent_flags, + .pid = pid, + .ppid = ppid, + .comm = comm, + .uid = uid, + .gid = gid, + }; + + error = notify_fs_error(&event); + if (error < 0) { + pr_debug( + "deepin_err_notify: notify_fs_error failed: %d\n", + error); + goto cleanup; + } + } else { + pr_debug("deepin_err_notify: No valid filenames to send\n"); + } + + /* Success path: set return value to 0 */ + error = 0; + +cleanup: + /* + * Resource cleanup: free all allocated buffers. + * kfree(NULL) is safe, so no need to check before freeing. + */ + for (i = 0; i < 2; i++) + kfree(path_bufs[i]); -static void __init deepin_err_notify_sysctl_init(void) + return error; +} + +/* Check if overlay filesystem is mounted on /usr and send read only error notification */ +void deepin_check_and_notify_ro_fs_err(const struct deepin_path_last *path_last, + const char *func_name) { - register_sysctl_init("fs", deepin_err_notify_sysctls); + int ret; + + /* Early return if path_last is invalid */ + if (!path_last) + return; + + ret = prepare_and_notify_fs_error(path_last, 1); + + if (ret < 0) { + pr_debug( + "deepin_err_notify: Failed to send notification to userspace: %d\n", + ret); + } } -#else -static void __init deepin_err_notify_sysctl_init(void) + +/** + * deepin_notify_rename_ro_fs_err - Notify read-only filesystem errors during rename + * @old_last: qstr for old filename + * @new_last: qstr for new filename + * @old_path: path of old parent directory + * @new_path: path of new parent directory + * + * This function is called when a rename operation fails with EROFS error. + * It attempts to look up the old and new paths and send error notification + * if both paths are valid. + */ +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) { + const struct deepin_path_last path_lasts[2] = { + { .path = *old_path, .last = old_last->name }, + { .path = *new_path, .last = new_last->name } + }; + int ret; + + ret = prepare_and_notify_fs_error(path_lasts, 2); + if (ret < 0) { + pr_debug( + "deepin_err_notify: Failed to send notification to userspace: %d\n", + ret); + } } -#endif /* CONFIG_SYSCTL */ /* Deepin error notify initialization */ static int __init deepin_err_notify_init(void) @@ -222,6 +689,7 @@ static int __init deepin_err_notify_init(void) /* Compile-time check for family name length */ BUILD_BUG_ON(sizeof(DEEPIN_ERR_NOTIFY_FAMILY_NAME) > GENL_NAMSIZ); + /* Register Generic Netlink family */ error = genl_register_family(&deepin_err_notify_genl_family); if (error) { pr_err("deepin_err_notify: Failed to register Generic Netlink family: %d\n", @@ -232,10 +700,10 @@ static int __init deepin_err_notify_init(void) /* Set initialization success flag */ deepin_err_notify_initialized = true; - /* Initialize sysctl interface */ - deepin_err_notify_sysctl_init(); + pr_debug( + "deepin_err_notify: Generic Netlink family '%s' registered successfully\n", + DEEPIN_ERR_NOTIFY_FAMILY_NAME); - pr_info("deepin_err_notify: Generic Netlink family registered successfully\n"); return 0; } diff --git a/fs/internal.h b/fs/internal.h index 09e315210ff3c..15145c526ff64 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -62,14 +62,35 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode); int do_symlinkat(struct filename *from, int newdfd, struct filename *to); int do_linkat(int olddfd, struct filename *old, int newdfd, struct filename *new, int flags); -int deepin_get_path_for_err_notify(int dfd, struct filename *name, struct path *result_path); +struct deepin_path_last { + struct path path; + const char *last; +}; + +int deepin_lookup_path_or_parent(int dfd, struct filename *name, + unsigned int flags, + struct deepin_path_last *result_path_last); /* * deepin_err_notify.c */ int deepin_err_notify_enabled(void); -void deepin_check_and_notify_ro_fs_err(const struct path *path, const char *func_name); -void deepin_send_ro_fs_err_notification(const char *filename, const char *func_name); +int deepin_err_notify_should_send(void); +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); + +#ifdef CONFIG_DEEPIN_ERR_NOTIFY +/* Check if error is EROFS and notification should be sent */ +#define deepin_should_notify_ro_fs_err(error) \ + unlikely((error) == -EROFS && deepin_err_notify_should_send()) +#else +#define deepin_should_notify_ro_fs_err(error) 0 +#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ /* * namespace.c diff --git a/fs/ioctl.c b/fs/ioctl.c index 474bc8eebff94..85260a3598f89 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -903,10 +903,13 @@ SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) if (error == -ENOIOCTLCMD) error = vfs_ioctl(f.file, cmd, arg); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) - deepin_check_and_notify_ro_fs_err(&f.file->f_path, "ioctl"); -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last tmp_path_last = { + .path = f.file->f_path, + .last = NULL + }; + deepin_check_and_notify_ro_fs_err(&tmp_path_last, "ioctl"); + } out: fdput(f); diff --git a/fs/namei.c b/fs/namei.c index 60c9a0c7474b0..132b87e52ee28 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4073,19 +4073,14 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, goto retry; } out1: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - struct path file_path; - int get_path_err; - - get_path_err = - deepin_get_path_for_err_notify(dfd, name, &file_path); - if (!get_path_err) { - deepin_check_and_notify_ro_fs_err(&file_path, "mknod"); - path_put(&file_path); + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last path_last; + + if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) { + deepin_check_and_notify_ro_fs_err(&path_last, "mknod"); + deepin_put_path_last(&path_last); } } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ putname(name); return error; } @@ -4169,19 +4164,14 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode) goto retry; } out_putname: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - struct path file_path; - int get_path_err; - - get_path_err = - deepin_get_path_for_err_notify(dfd, name, &file_path); - if (!get_path_err) { - deepin_check_and_notify_ro_fs_err(&file_path, "mkdir"); - path_put(&file_path); + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last path_last; + + if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) { + deepin_check_and_notify_ro_fs_err(&path_last, "mkdir"); + deepin_put_path_last(&path_last); } } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ putname(name); return error; } @@ -4299,21 +4289,18 @@ int do_rmdir(int dfd, struct filename *name) inode_unlock(path.dentry->d_inode); mnt_drop_write(path.mnt); exit2: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - dentry = lookup_one_qstr_excl(&last, path.dentry, 0); - if (!IS_ERR(dentry)) { - if (d_is_positive(dentry)) { - // dentry is positive, so we can get the path - struct path file_path = { .mnt = path.mnt, - .dentry = dentry }; - deepin_check_and_notify_ro_fs_err(&file_path, + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last path_last; + + if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) { + if (!path_last.last) { + // File exists, notify error + deepin_check_and_notify_ro_fs_err(&path_last, "rmdir"); } - dput(dentry); + deepin_put_path_last(&path_last); } } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -4459,21 +4446,18 @@ int do_unlinkat(int dfd, struct filename *name) } mnt_drop_write(path.mnt); exit2: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - dentry = lookup_one_qstr_excl(&last, path.dentry, 0); - if (!IS_ERR(dentry)) { - if (d_is_positive(dentry)) { - // dentry is positive, so we can get the path - struct path file_path = { .mnt = path.mnt, - .dentry = dentry }; - deepin_check_and_notify_ro_fs_err(&file_path, + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last path_last; + + if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) { + if (!path_last.last) { + // File exists, notify error + deepin_check_and_notify_ro_fs_err(&path_last, "unlink"); } - dput(dentry); + deepin_put_path_last(&path_last); } } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -4574,20 +4558,15 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to) goto retry; } out_putnames: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - struct path file_path; - int get_path_err; - - get_path_err = - deepin_get_path_for_err_notify(newdfd, to, &file_path); - if (!get_path_err) { - deepin_check_and_notify_ro_fs_err(&file_path, + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last path_last; + + if (!deepin_lookup_path_or_parent(newdfd, to, lookup_flags, &path_last)) { + deepin_check_and_notify_ro_fs_err(&path_last, "symlink"); - path_put(&file_path); + deepin_put_path_last(&path_last); } } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ putname(to); putname(from); return error; @@ -4766,19 +4745,14 @@ int do_linkat(int olddfd, struct filename *old, int newdfd, goto retry; } out_putpath: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - struct path file_path; - int get_path_err; - - get_path_err = - deepin_get_path_for_err_notify(newdfd, new, &file_path); - if (!get_path_err) { - deepin_check_and_notify_ro_fs_err(&file_path, "link"); - path_put(&file_path); + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last path_last; + + if (!deepin_lookup_path_or_parent(newdfd, new, how, &path_last)) { + deepin_check_and_notify_ro_fs_err(&path_last, "link"); + deepin_put_path_last(&path_last); } } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ path_put(&old_path); out_putnames: putname(old); @@ -5133,22 +5107,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, } mnt_drop_write(old_path.mnt); exit2: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - old_dentry = - lookup_one_qstr_excl(&old_last, old_path.dentry, 0); - if (!IS_ERR(old_dentry)) { - if (d_is_positive(old_dentry)) { - struct path file_path = { .mnt = old_path.mnt, - .dentry = - old_dentry }; - deepin_check_and_notify_ro_fs_err(&file_path, - "rename"); - } - dput(old_dentry); - } - } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ + if (deepin_should_notify_ro_fs_err(error)) + deepin_notify_rename_ro_fs_err(&old_last, &new_last, &old_path, &new_path); if (retry_estale(error, lookup_flags)) should_retry = true; path_put(&new_path); @@ -5368,37 +5328,76 @@ const struct inode_operations page_symlink_inode_operations = { EXPORT_SYMBOL(page_symlink_inode_operations); #ifdef CONFIG_DEEPIN_ERR_NOTIFY -int deepin_get_path_for_err_notify(int dfd, struct filename *name, - struct path *result_path) +/** + * deepin_lookup_path_or_parent - Prepare path info for read-only FS error notification + * @dfd: Directory file descriptor used as lookup base + * @name: Filename to look up (struct filename) + * @flags: Lookup flags (e.g., LOOKUP_DIRECTORY, LOOKUP_FOLLOW) + * @result_path_last: Output structure carrying the resolved parent path and + * optionally the last component string + * + * Memory/Lifetime management: + * - result_path_last->path: + * The path returned by filename_lookup() or filename_parentat() already + * holds a reference count. You MUST release it after use. Preferred: + * deepin_put_path_last(&pl), which will call path_put() for you. + * Alternatively, call path_put() manually if you manage the string separately. + * + * - result_path_last->last: + * In the -ENOENT (parent found) case, the last component string is duplicated + * via kstrdup() and MUST be freed with kfree(). deepin_put_path_last(&pl) + * will free it for you. If the full path was resolved, this field is set + * to NULL and no extra string free is needed. + * + * Recommended usage pattern: + * struct deepin_path_last pl; + * if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &pl)) { + * deepin_check_and_notify_ro_fs_err(&pl, "op"); + * deepin_put_path_last(&pl); // releases path and frees pl.last if allocated + * } + * + * Return: 0 on success, negative errno on failure. + */ +int deepin_lookup_path_or_parent(int dfd, struct filename *name, + unsigned int flags, + struct deepin_path_last *result_path_last) { + struct path result_path; + struct path parent; struct qstr last; - struct path parent_path; int type; - struct dentry *dentry; int error; - error = filename_parentat(dfd, name, 0, &parent_path, &last, &type); + error = filename_lookup(dfd, name, flags, &result_path, NULL); + if (error == -ENOENT) { + error = filename_parentat(dfd, name, flags, &parent, &last, &type); + if (error) + return error; + if (unlikely(type != LAST_NORM)) { + path_put(&parent); + return -EINVAL; + } + /* Duplicate the filename string to avoid dangling pointer */ + result_path_last->last = kstrdup((const char *)last.name, GFP_KERNEL); + if (!result_path_last->last) { + path_put(&parent); + return -ENOMEM; + } + result_path_last->path = parent; + return 0; + } + if (error) return error; - dentry = lookup_one_qstr_excl(&last, parent_path.dentry, 0); - if (!IS_ERR(dentry)) { - result_path->mnt = parent_path.mnt; - result_path->dentry = dentry; - path_get(result_path); // Increment reference count - dput(dentry); - } else { - // If the file does not exist, use the parent directory - *result_path = parent_path; - path_get(result_path); - } - - path_put(&parent_path); + result_path_last->last = NULL; + result_path_last->path = result_path; return 0; } #else -int deepin_get_path_for_err_notify(int dfd, struct filename *name, - struct path *result_path) +int deepin_lookup_path_or_parent(int dfd, struct filename *name, + unsigned int flags, + struct deepin_path_last *result_path_last) { return -EOPNOTSUPP; } diff --git a/fs/open.c b/fs/open.c index 93620b8fdecba..6712b8d2b3115 100644 --- a/fs/open.c +++ b/fs/open.c @@ -136,10 +136,13 @@ long do_sys_truncate(const char __user *pathname, loff_t length) error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); if (!error) { error = vfs_truncate(&path, length); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) - deepin_check_and_notify_ro_fs_err(&path, "truncate"); -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last tmp_path_last = { + .path = path, + .last = NULL + }; + deepin_check_and_notify_ro_fs_err(&tmp_path_last, "truncate"); + } path_put(&path); } if (retry_estale(error, lookup_flags)) { @@ -717,10 +720,13 @@ static int do_fchmodat(int dfd, const char __user *filename, umode_t mode, error = user_path_at(dfd, filename, lookup_flags, &path); if (!error) { error = chmod_common(&path, mode); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) - deepin_check_and_notify_ro_fs_err(&path, "chmod"); -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last tmp_path_last = { + .path = path, + .last = NULL + }; + deepin_check_and_notify_ro_fs_err(&tmp_path_last, "chmod"); + } path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -846,10 +852,13 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, error = chown_common(&path, user, group); mnt_drop_write(path.mnt); out_release: -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) - deepin_check_and_notify_ro_fs_err(&path, "chown"); -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last tmp_path_last = { + .path = path, + .last = NULL + }; + deepin_check_and_notify_ro_fs_err(&tmp_path_last, "chown"); + } path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -1456,19 +1465,16 @@ static long do_sys_openat2(int dfd, const char __user *filename, if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely(fd == -EROFS) && - deepin_err_notify_enabled()) { - struct path file_path; - int get_path_err; - - get_path_err = deepin_get_path_for_err_notify(dfd, tmp, &file_path); - if (!get_path_err) { - deepin_check_and_notify_ro_fs_err(&file_path, "open"); - path_put(&file_path); + if (deepin_should_notify_ro_fs_err(fd)) { + struct deepin_path_last path_last; + + if (!deepin_lookup_path_or_parent(dfd, tmp, + op.lookup_flags, + &path_last)) { + deepin_check_and_notify_ro_fs_err(&path_last, "open"); + deepin_put_path_last(&path_last); } } -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ } else { fd_install(fd, f); } diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index fcb33a498acc6..d82d2a043da2c 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -19,9 +19,6 @@ struct ovl_config { bool metacopy; bool userxattr; bool ovl_volatile; -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - bool deepin_err_notify; -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ }; struct ovl_sb { diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c index 6ec31723b7268..21d31aaef95d6 100644 --- a/fs/overlayfs/params.c +++ b/fs/overlayfs/params.c @@ -59,9 +59,6 @@ enum ovl_opt { Opt_metacopy, Opt_verity, Opt_volatile, -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - Opt_deepin_err_notify, -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ }; static const struct constant_table ovl_parameter_bool[] = { @@ -162,9 +159,6 @@ const struct fs_parameter_spec ovl_parameter_spec[] = { fsparam_enum("metacopy", Opt_metacopy, ovl_parameter_bool), fsparam_enum("verity", Opt_verity, ovl_parameter_verity), fsparam_flag("volatile", Opt_volatile), -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - fsparam_flag("deepin_err_notify", Opt_deepin_err_notify), -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ {} }; @@ -606,11 +600,6 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param) case Opt_userxattr: config->userxattr = true; break; -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - case Opt_deepin_err_notify: - config->deepin_err_notify = true; - break; -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ default: pr_err("unrecognized mount option \"%s\" or missing value\n", param->key); @@ -1022,9 +1011,5 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry) if (ofs->config.verity_mode != ovl_verity_mode_def()) seq_printf(m, ",verity=%s", ovl_verity_mode(&ofs->config)); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (ofs->config.deepin_err_notify) - seq_puts(m, ",deepin_err_notify"); -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ return 0; } diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 39137dd30184b..953271f1065c4 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -267,16 +267,6 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) return err; } -#ifdef CONFIG_DEEPIN_ERR_NOTIFY -static bool ovl_deepin_should_notify_error(struct super_block *sb) -{ - struct ovl_fs *ofs = OVL_FS(sb); - - /* Return true if deepin_err_notify mount option was set */ - return ofs->config.deepin_err_notify; -} -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ - static const struct super_operations ovl_super_operations = { .alloc_inode = ovl_alloc_inode, .free_inode = ovl_free_inode, @@ -286,9 +276,6 @@ static const struct super_operations ovl_super_operations = { .sync_fs = ovl_sync_fs, .statfs = ovl_statfs, .show_options = ovl_show_options, -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - .deepin_should_notify_error = ovl_deepin_should_notify_error, -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ }; #define OVL_WORKDIR_NAME "work" diff --git a/fs/utimes.c b/fs/utimes.c index 3e74c4c6741f5..51714c41bd0b0 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -99,10 +99,13 @@ static int do_utimes_path(int dfd, const char __user *filename, return error; error = vfs_utimes(&path, times); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) - deepin_check_and_notify_ro_fs_err(&path, "utime"); -#endif /* CONFIG_DEEPIN_ERR_NOTIFY */ + if (deepin_should_notify_ro_fs_err(error)) { + struct deepin_path_last tmp_path_last = { + .path = path, + .last = NULL + }; + deepin_check_and_notify_ro_fs_err(&tmp_path_last, "utime"); + } path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; diff --git a/fs/xattr.c b/fs/xattr.c index 20324eb52e04c..0efd31508cdd5 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -658,10 +658,12 @@ static int path_setxattr(const char __user *pathname, if (!error) { error = do_setxattr(mnt_idmap(path.mnt), path.dentry, &ctx); mnt_drop_write(path.mnt); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - } 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 + }; + deepin_check_and_notify_ro_fs_err(&tmp_path_last, "setxattr"); } path_put(&path); if (retry_estale(error, lookup_flags)) { @@ -932,10 +934,12 @@ static int path_removexattr(const char __user *pathname, if (!error) { error = removexattr(mnt_idmap(path.mnt), path.dentry, kname); mnt_drop_write(path.mnt); -#ifdef CONFIG_DEEPIN_ERR_NOTIFY - } else if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { - deepin_check_and_notify_ro_fs_err(&path, "removexattr"); -#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 + }; + deepin_check_and_notify_ro_fs_err(&tmp_path_last, "removexattr"); } path_put(&path); if (retry_estale(error, lookup_flags)) {