|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* |
| 3 | + * fs/deepin_ro_fs_err_notify.c - Deepin read-only filesystem error notification |
| 4 | + * |
| 5 | + * This module provides notification functionality for read-only filesystem |
| 6 | + * errors, specifically targeting overlay filesystems mounted on /usr. |
| 7 | + */ |
| 8 | + |
| 9 | +#include <linux/init.h> |
| 10 | +#include <linux/slab.h> |
| 11 | +#include <linux/fs.h> |
| 12 | +#include <linux/namei.h> |
| 13 | +#include <linux/mount.h> |
| 14 | +#include <linux/path.h> |
| 15 | +#include <linux/dcache.h> |
| 16 | +#include <linux/sched.h> |
| 17 | +#include <linux/printk.h> |
| 18 | +#include <linux/string.h> |
| 19 | +#include <linux/err.h> |
| 20 | +#include <linux/limits.h> |
| 21 | +#include <linux/sysctl.h> |
| 22 | +#include <linux/ratelimit.h> |
| 23 | +#include <linux/build_bug.h> |
| 24 | +#include <net/netlink.h> |
| 25 | +#include <net/genetlink.h> |
| 26 | + |
| 27 | +#include "internal.h" |
| 28 | +#include "mount.h" |
| 29 | + |
| 30 | +/* Family name (max GENL_NAMSIZ characters, including null terminator) */ |
| 31 | +#define DEEPIN_ERR_NOTIFY_FAMILY_NAME "DEEPIN_ENOTIFY" |
| 32 | + |
| 33 | +/* Define netlink message types and attributes */ |
| 34 | +enum { |
| 35 | + DEEPIN_ERR_NOTIFY_ATTR_UNSPEC, |
| 36 | + DEEPIN_ERR_NOTIFY_ATTR_FILENAME, /* Filename */ |
| 37 | + DEEPIN_ERR_NOTIFY_ATTR_PID, /* Process ID */ |
| 38 | + DEEPIN_ERR_NOTIFY_ATTR_COMM, /* Process Name */ |
| 39 | + DEEPIN_ERR_NOTIFY_ATTR_FUNC_NAME, /* Function Name */ |
| 40 | + __DEEPIN_ERR_NOTIFY_ATTR_MAX, |
| 41 | +}; |
| 42 | + |
| 43 | +#define DEEPIN_ERR_NOTIFY_ATTR_MAX (__DEEPIN_ERR_NOTIFY_ATTR_MAX - 1) |
| 44 | + |
| 45 | +enum { |
| 46 | + DEEPIN_ERR_NOTIFY_CMD_UNSPEC, |
| 47 | + DEEPIN_ERR_NOTIFY_CMD_NOTIFY, /* Error Notify Command */ |
| 48 | + __DEEPIN_ERR_NOTIFY_CMD_MAX, |
| 49 | +}; |
| 50 | + |
| 51 | +#define DEEPIN_ERR_NOTIFY_CMD_MAX (__DEEPIN_ERR_NOTIFY_CMD_MAX - 1) |
| 52 | + |
| 53 | +/* Track deepin error notification initialization status */ |
| 54 | +static bool deepin_err_notify_initialized __read_mostly; |
| 55 | + |
| 56 | +/* Runtime control variable for deepin error notification */ |
| 57 | +static int deepin_err_notify_enable __read_mostly = 1; |
| 58 | + |
| 59 | +int deepin_err_notify_enabled(void) |
| 60 | +{ |
| 61 | + return deepin_err_notify_initialized && deepin_err_notify_enable; |
| 62 | +} |
| 63 | + |
| 64 | +/* Check if overlay filesystem is mounted on /usr and send read only error notification */ |
| 65 | +void deepin_check_and_notify_ro_fs_err(const struct path *path, |
| 66 | + const char *func_name) |
| 67 | +{ |
| 68 | + char *path_buf = NULL; |
| 69 | + char *full_path = ""; |
| 70 | + /* Rate limiting: allow 100 calls per 5 seconds */ |
| 71 | + static DEFINE_RATELIMIT_STATE(deepin_ro_fs_err_ratelimit, |
| 72 | + 5 * HZ, /* 5 seconds interval */ |
| 73 | + 100); /* 100 calls per interval */ |
| 74 | + |
| 75 | + /* Check rate limit before proceeding */ |
| 76 | + if (!__ratelimit(&deepin_ro_fs_err_ratelimit)) |
| 77 | + return; |
| 78 | + |
| 79 | + /* Early return if path or path->mnt is invalid */ |
| 80 | + if (!path || !path->mnt || !path->mnt->mnt_sb) |
| 81 | + return; |
| 82 | + |
| 83 | + /* Use filesystem callback to decide if notification should be sent. |
| 84 | + * If filesystem implements the callback, use it. |
| 85 | + */ |
| 86 | + if (path->mnt->mnt_sb->s_op && |
| 87 | + path->mnt->mnt_sb->s_op->deepin_should_notify_error) { |
| 88 | + if (!path->mnt->mnt_sb->s_op->deepin_should_notify_error(path->mnt->mnt_sb)) |
| 89 | + return; |
| 90 | + } else { |
| 91 | + /* If filesystem does not implement the callback, return immediately. */ |
| 92 | + return; |
| 93 | + } |
| 94 | + |
| 95 | + /* Attempt to get the full path. |
| 96 | + * Dynamic allocation is used to avoid excessive frame size. |
| 97 | + */ |
| 98 | + if (path->dentry) { |
| 99 | + path_buf = kmalloc(PATH_MAX, GFP_KERNEL); |
| 100 | + if (path_buf) { |
| 101 | + char *p = NULL; |
| 102 | + |
| 103 | + p = d_path(path, path_buf, PATH_MAX); |
| 104 | + if (!IS_ERR(p)) |
| 105 | + full_path = p; |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + deepin_send_ro_fs_err_notification(full_path, func_name); |
| 110 | + |
| 111 | + kfree(path_buf); |
| 112 | +} |
| 113 | + |
| 114 | +/* Define multicast group */ |
| 115 | +static const struct genl_multicast_group deepin_err_notify_nl_mcgrps[] = { |
| 116 | + { |
| 117 | + .name = "ro_fs_events", |
| 118 | + }, |
| 119 | +}; |
| 120 | + |
| 121 | +/* Define Generic Netlink family */ |
| 122 | +static struct genl_family deepin_err_notify_genl_family __ro_after_init = { |
| 123 | + .module = THIS_MODULE, |
| 124 | + .hdrsize = 0, |
| 125 | + .name = DEEPIN_ERR_NOTIFY_FAMILY_NAME, |
| 126 | + .version = 1, |
| 127 | + .maxattr = DEEPIN_ERR_NOTIFY_ATTR_MAX, |
| 128 | + .mcgrps = deepin_err_notify_nl_mcgrps, |
| 129 | + .n_mcgrps = ARRAY_SIZE(deepin_err_notify_nl_mcgrps), |
| 130 | +}; |
| 131 | + |
| 132 | +/* Send read only filesystem error notification */ |
| 133 | +void deepin_send_ro_fs_err_notification(const char *filename, |
| 134 | + const char *func_name) |
| 135 | +{ |
| 136 | + pid_t pid = 0; |
| 137 | + const char *comm = NULL; |
| 138 | + int msg_size; |
| 139 | + struct sk_buff *skb = NULL; |
| 140 | + void *msg_head = NULL; |
| 141 | + int error; |
| 142 | + |
| 143 | + pid = current->pid; |
| 144 | + comm = current->comm; |
| 145 | + |
| 146 | + msg_size = nla_total_size(strlen(filename) + 1) + |
| 147 | + nla_total_size(sizeof(u32)) + |
| 148 | + nla_total_size(strlen(comm) + 1) + |
| 149 | + nla_total_size(strlen(func_name) + 1); |
| 150 | + |
| 151 | + /* Use GFP_NOFS to avoid recursion in file system operations. */ |
| 152 | + skb = genlmsg_new(msg_size, GFP_NOFS); |
| 153 | + if (!skb) { |
| 154 | + pr_err("deepin_err_notify: Failed to allocate netlink message\n"); |
| 155 | + return; |
| 156 | + } |
| 157 | + |
| 158 | + msg_head = genlmsg_put(skb, 0, 0, &deepin_err_notify_genl_family, 0, |
| 159 | + DEEPIN_ERR_NOTIFY_CMD_NOTIFY); |
| 160 | + if (!msg_head) { |
| 161 | + pr_err("deepin_err_notify: Failed to put netlink header\n"); |
| 162 | + goto err_out; |
| 163 | + } |
| 164 | + |
| 165 | + error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FILENAME, filename); |
| 166 | + if (error) |
| 167 | + goto attr_err_out; |
| 168 | + |
| 169 | + error = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_PID, pid); |
| 170 | + if (error) |
| 171 | + goto attr_err_out; |
| 172 | + |
| 173 | + error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_COMM, comm); |
| 174 | + if (error) |
| 175 | + goto attr_err_out; |
| 176 | + |
| 177 | + error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FUNC_NAME, |
| 178 | + func_name); |
| 179 | + if (error) |
| 180 | + goto attr_err_out; |
| 181 | + |
| 182 | + genlmsg_end(skb, msg_head); |
| 183 | + |
| 184 | + /* Send multicast message. */ |
| 185 | + genlmsg_multicast(&deepin_err_notify_genl_family, skb, 0, 0, GFP_NOFS); |
| 186 | + return; |
| 187 | + |
| 188 | +attr_err_out: |
| 189 | + pr_err("deepin_err_notify: Failed to add netlink attributes\n"); |
| 190 | +err_out: |
| 191 | + kfree_skb(skb); |
| 192 | +} |
| 193 | + |
| 194 | +/* sysctl table and initialization */ |
| 195 | +#ifdef CONFIG_SYSCTL |
| 196 | +static struct ctl_table deepin_err_notify_sysctls[] = { |
| 197 | + { |
| 198 | + .procname = "deepin-err-notify-enable", |
| 199 | + .data = &deepin_err_notify_enable, |
| 200 | + .maxlen = sizeof(int), |
| 201 | + .mode = 0644, |
| 202 | + .proc_handler = proc_dointvec, |
| 203 | + }, |
| 204 | +}; |
| 205 | + |
| 206 | +static void __init deepin_err_notify_sysctl_init(void) |
| 207 | +{ |
| 208 | + register_sysctl_init("fs", deepin_err_notify_sysctls); |
| 209 | +} |
| 210 | +#else |
| 211 | +static void __init deepin_err_notify_sysctl_init(void) |
| 212 | +{ |
| 213 | +} |
| 214 | +#endif /* CONFIG_SYSCTL */ |
| 215 | + |
| 216 | +/* Deepin error notify initialization */ |
| 217 | +static int __init deepin_err_notify_init(void) |
| 218 | +{ |
| 219 | + int error; |
| 220 | + |
| 221 | + /* Compile-time check for family name length */ |
| 222 | + BUILD_BUG_ON(sizeof(DEEPIN_ERR_NOTIFY_FAMILY_NAME) > GENL_NAMSIZ); |
| 223 | + |
| 224 | + error = genl_register_family(&deepin_err_notify_genl_family); |
| 225 | + if (error) { |
| 226 | + pr_err("deepin_err_notify: Failed to register Generic Netlink family: %d\n", |
| 227 | + error); |
| 228 | + return error; |
| 229 | + } |
| 230 | + |
| 231 | + /* Set initialization success flag */ |
| 232 | + deepin_err_notify_initialized = true; |
| 233 | + |
| 234 | + /* Initialize sysctl interface */ |
| 235 | + deepin_err_notify_sysctl_init(); |
| 236 | + |
| 237 | + pr_info("deepin_err_notify: Generic Netlink family registered successfully\n"); |
| 238 | + return 0; |
| 239 | +} |
| 240 | + |
| 241 | +/* Use fs_initcall to ensure initialization before file system operations */ |
| 242 | +fs_initcall(deepin_err_notify_init); |
0 commit comments