Skip to content

Commit 4ac6462

Browse files
electricfaceopsiff
authored andcommitted
fs: Add notification mechanism for read-only filesystem errors
This patch introduces an error notification mechanism specifically targeting OverlayFS, designed to send EROFS error notifications to userspace. Key features: * Adds the CONFIG_DEEPIN_ERR_NOTIFY configuration option, allowing the notification system to be enabled or disabled at compile time. * Implements the notification mechanism based on netlink, using the Generic Netlink protocol family to deliver error details to userspace applications. * Provides runtime control via the /proc/sys/fs/deepin-err-notify-enable sysctl interface. * Integrates EROFS error detection into critical system calls, including: - File operations: open, truncate, chmod, chown, utime - Directory operations: mkdir, rmdir, mknod - Link operations: link, symlink, unlink, rename - Extended attribute operations: setxattr, removexattr - I/O control operations: ioctl * Adds the deepin_err_notify mount option for overlayFS, enabling selective activation of the error notification feature. * Includes rate-limiting mechanisms to prevent notification flooding. * Provides detailed contextual information in notifications, including filename, process ID, process name, and function name. This feature is particularly useful for monitoring failed modification attempts by applications targeting the /usr directory on immutable systems. Signed-off-by: electricface <[email protected]>
1 parent f345660 commit 4ac6462

File tree

17 files changed

+489
-0
lines changed

17 files changed

+489
-0
lines changed

MAINTAINERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5787,6 +5787,11 @@ M: "WangYuli" <[email protected]>
57875787
S: Maintained
57885788
F: lib/fonts/font_cjk*
57895789

5790+
DEEPIN ERR NOTIFY
5791+
M: "electricface" <[email protected]>
5792+
S: Maintained
5793+
F: fs/deepin_err_notify.c
5794+
57905795
DEEPIN KABI-HELPERS
57915796
M: "Lugang He" <[email protected]>
57925797
M: "WangYuli" <[email protected]>

arch/arm64/configs/deepin_arm64_desktop_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4619,3 +4619,4 @@ CONFIG_RCU_CPU_STALL_TIMEOUT=60
46194619
CONFIG_FTRACE_SYSCALLS=y
46204620
# CONFIG_STRICT_DEVMEM is not set
46214621
# CONFIG_RUNTIME_TESTING_MENU is not set
4622+
CONFIG_DEEPIN_ERR_NOTIFY=y

arch/loongarch/configs/deepin_loongarch_desktop_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6027,3 +6027,4 @@ CONFIG_SAMPLE_VFIO_MDEV_MBOCHS=m
60276027
# CONFIG_STRICT_DEVMEM is not set
60286028
CONFIG_UNWINDER_ORC=y
60296029
# CONFIG_RUNTIME_TESTING_MENU is not set
6030+
CONFIG_DEEPIN_ERR_NOTIFY=y

arch/x86/configs/deepin_x86_desktop_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5913,3 +5913,4 @@ CONFIG_IO_DELAY_0XED=y
59135913
# CONFIG_X86_DEBUG_FPU is not set
59145914
CONFIG_UNWINDER_FRAME_POINTER=y
59155915
# CONFIG_RUNTIME_TESTING_MENU is not set
5916+
CONFIG_DEEPIN_ERR_NOTIFY=y

fs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,4 @@ obj-$(CONFIG_EFIVAR_FS) += efivarfs/
128128
obj-$(CONFIG_EROFS_FS) += erofs/
129129
obj-$(CONFIG_VBOXSF_FS) += vboxsf/
130130
obj-$(CONFIG_ZONEFS_FS) += zonefs/
131+
obj-$(CONFIG_DEEPIN_ERR_NOTIFY) += deepin_err_notify.o

fs/deepin_err_notify.c

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
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);

fs/internal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode);
6262
int do_symlinkat(struct filename *from, int newdfd, struct filename *to);
6363
int do_linkat(int olddfd, struct filename *old, int newdfd,
6464
struct filename *new, int flags);
65+
int deepin_get_path_for_err_notify(int dfd, struct filename *name, struct path *result_path);
66+
67+
/*
68+
* deepin_err_notify.c
69+
*/
70+
int deepin_err_notify_enabled(void);
71+
void deepin_check_and_notify_ro_fs_err(const struct path *path, const char *func_name);
72+
void deepin_send_ro_fs_err_notification(const char *filename, const char *func_name);
6573

6674
/*
6775
* namespace.c

fs/ioctl.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,11 @@ SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
903903
if (error == -ENOIOCTLCMD)
904904
error = vfs_ioctl(f.file, cmd, arg);
905905

906+
#ifdef CONFIG_DEEPIN_ERR_NOTIFY
907+
if (unlikely((error == -EROFS) && deepin_err_notify_enabled()))
908+
deepin_check_and_notify_ro_fs_err(&f.file->f_path, "ioctl");
909+
#endif /* CONFIG_DEEPIN_ERR_NOTIFY */
910+
906911
out:
907912
fdput(f);
908913
return error;

0 commit comments

Comments
 (0)