Skip to content

Commit 63ef62c

Browse files
committed
fs: Enhance read-only filesystem error notification mechanism
This update introduces a new function, deepin_check_and_notify_ro_fs_err_paths, which allows for error notifications to be sent for multiple paths during filesystem operations. The existing error notification system has been refactored to improve clarity and efficiency, including the integration of a notifier chain for OverlayFS errors. Key changes: - Added support for notifying about read-only filesystem errors during rename operations. - Refactored error notification handling to utilize filesystem callbacks for improved modularity. - Introduced a new header and source file for OverlayFS error notifications, allowing external modules to register for error notifications. This enhancement aims to provide better error handling and notification capabilities for read-only filesystem operations.
1 parent 935caa3 commit 63ef62c

File tree

8 files changed

+340
-155
lines changed

8 files changed

+340
-155
lines changed

fs/deepin_err_notify.c

Lines changed: 78 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,6 @@
2727
#include "internal.h"
2828
#include "mount.h"
2929

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-
5330
/* Track deepin error notification initialization status */
5431
static bool deepin_err_notify_initialized __read_mostly;
5532

@@ -61,134 +38,112 @@ int deepin_err_notify_enabled(void)
6138
return deepin_err_notify_initialized && deepin_err_notify_enable;
6239
}
6340

41+
/* Rate limiting: allow 100 calls per 5 seconds */
42+
static DEFINE_RATELIMIT_STATE(deepin_ro_fs_err_ratelimit,
43+
5 * HZ, /* 5 seconds interval */
44+
100); /* 100 calls per interval */
45+
6446
/* Check if overlay filesystem is mounted on /usr and send read only error notification */
6547
void deepin_check_and_notify_ro_fs_err(const struct path *path,
6648
const char *func_name)
6749
{
6850
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;
51+
const char *filename = "";
7852

79-
/* Early return if path or path->mnt is invalid */
80-
if (!path || !path->mnt || !path->mnt->mnt_sb)
53+
/* Early return if path is invalid or filesystem doesn't implement the callback */
54+
if (!path || !path->mnt || !path->mnt->mnt_sb ||
55+
!path->mnt->mnt_sb->s_op ||
56+
!path->mnt->mnt_sb->s_op->deepin_notify_error)
8157
return;
8258

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. */
59+
/* Check rate limit before proceeding */
60+
if (!__ratelimit(&deepin_ro_fs_err_ratelimit))
9261
return;
93-
}
9462

95-
/* Attempt to get the full path.
96-
* Dynamic allocation is used to avoid excessive frame size.
97-
*/
63+
/* Convert path to filename string */
9864
if (path->dentry) {
9965
path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
10066
if (path_buf) {
101-
char *p = NULL;
102-
103-
p = d_path(path, path_buf, PATH_MAX);
67+
char *p = d_path(path, path_buf, PATH_MAX);
10468
if (!IS_ERR(p))
105-
full_path = p;
69+
filename = p;
10670
}
10771
}
10872

109-
deepin_send_ro_fs_err_notification(full_path, func_name);
110-
73+
/* Use filesystem callback to handle error notification */
74+
path->mnt->mnt_sb->s_op->deepin_notify_error(
75+
path->mnt->mnt_sb, &filename, 1, func_name);
11176
kfree(path_buf);
11277
}
11378

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)
79+
/* Check multiple paths and send read-only error notification */
80+
void deepin_check_and_notify_ro_fs_err_paths(const struct path *path,
81+
const struct path *path_new,
82+
const char *func_name)
13583
{
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-
}
84+
char *path_bufs[2] = { NULL, NULL };
85+
const char *filenames[2] = { "", "" };
86+
int filename_count = 0;
87+
const struct super_operations *s_op = NULL;
88+
struct super_block *sb = NULL;
89+
const struct path *paths[2] = { path, path_new };
90+
int i;
91+
92+
/* First pass: find a filesystem that implements deepin_notify_error
93+
* Check both paths and use the first one that has the callback
94+
*/
95+
for (i = 0; i < 2; i++) {
96+
if (!paths[i] || !paths[i]->mnt || !paths[i]->mnt->mnt_sb)
97+
continue;
15798

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-
}
99+
sb = paths[i]->mnt->mnt_sb;
100+
s_op = sb->s_op;
164101

165-
error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FILENAME, filename);
166-
if (error)
167-
goto attr_err_out;
102+
if (s_op && s_op->deepin_notify_error) {
103+
/* Found a filesystem with the callback */
104+
break;
105+
}
168106

169-
error = nla_put_u32(skb, DEEPIN_ERR_NOTIFY_ATTR_PID, pid);
170-
if (error)
171-
goto attr_err_out;
107+
/* Reset for next iteration */
108+
sb = NULL;
109+
s_op = NULL;
110+
}
172111

173-
error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_COMM, comm);
174-
if (error)
175-
goto attr_err_out;
112+
/* Early return if no filesystem has the callback */
113+
if (!s_op || !s_op->deepin_notify_error)
114+
return;
176115

177-
error = nla_put_string(skb, DEEPIN_ERR_NOTIFY_ATTR_FUNC_NAME,
178-
func_name);
179-
if (error)
180-
goto attr_err_out;
116+
/* Check rate limit before proceeding */
117+
if (!__ratelimit(&deepin_ro_fs_err_ratelimit))
118+
return;
181119

182-
genlmsg_end(skb, msg_head);
120+
/* Second pass: collect all valid filenames from both paths
121+
* Only allocate memory now that we know we'll use it
122+
*/
123+
for (i = 0; i < 2; i++) {
124+
if (!paths[i] || !paths[i]->mnt || !paths[i]->mnt->mnt_sb)
125+
continue;
126+
127+
if (!paths[i]->dentry)
128+
continue;
129+
130+
path_bufs[i] = kmalloc(PATH_MAX, GFP_KERNEL);
131+
if (path_bufs[i]) {
132+
char *p = d_path(paths[i], path_bufs[i], PATH_MAX);
133+
if (!IS_ERR(p))
134+
filenames[filename_count++] = p;
135+
}
136+
}
183137

184-
/* Send multicast message. */
185-
genlmsg_multicast(&deepin_err_notify_genl_family, skb, 0, 0, GFP_NOFS);
186-
return;
138+
/* Call the callback with collected filenames */
139+
if (filename_count == 2) {
140+
s_op->deepin_notify_error(sb, filenames, filename_count,
141+
func_name);
142+
}
187143

188-
attr_err_out:
189-
pr_err("deepin_err_notify: Failed to add netlink attributes\n");
190-
err_out:
191-
kfree_skb(skb);
144+
/* Free allocated buffers */
145+
for (i = 0; i < 2; i++)
146+
kfree(path_bufs[i]);
192147
}
193148

194149
/* sysctl table and initialization */
@@ -216,27 +171,14 @@ static void __init deepin_err_notify_sysctl_init(void)
216171
/* Deepin error notify initialization */
217172
static int __init deepin_err_notify_init(void)
218173
{
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-
231174
/* Set initialization success flag */
232175
deepin_err_notify_initialized = true;
233176

234177
/* Initialize sysctl interface */
235178
deepin_err_notify_sysctl_init();
236-
237-
pr_info("deepin_err_notify: Generic Netlink family registered successfully\n");
238179
return 0;
239180
}
240181

241182
/* Use fs_initcall to ensure initialization before file system operations */
242183
fs_initcall(deepin_err_notify_init);
184+

fs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ int deepin_get_path_for_err_notify(int dfd, struct filename *name, struct path *
6969
*/
7070
int deepin_err_notify_enabled(void);
7171
void deepin_check_and_notify_ro_fs_err(const struct path *path, const char *func_name);
72+
void deepin_check_and_notify_ro_fs_err_paths(const struct path *path, const struct path *path_new, const char *func_name);
7273
void deepin_send_ro_fs_err_notification(const char *filename, const char *func_name);
7374

7475
/*

fs/namei.c

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5005,6 +5005,53 @@ int vfs_rename(struct renamedata *rd)
50055005
}
50065006
EXPORT_SYMBOL(vfs_rename);
50075007

5008+
#ifdef CONFIG_DEEPIN_ERR_NOTIFY
5009+
/*
5010+
* notify_rename_ro_fs_err - Notify read-only filesystem errors during rename
5011+
* @old_last: qstr for old filename
5012+
* @new_last: qstr for new filename
5013+
* @old_path: path of old parent directory
5014+
* @new_path: path of new parent directory
5015+
*
5016+
* This function is called when a rename operation fails with EROFS error.
5017+
* It attempts to look up the old and new paths and send error notification
5018+
* if both paths are valid.
5019+
*/
5020+
static void notify_rename_ro_fs_err(const struct qstr *old_last,
5021+
const struct qstr *new_last,
5022+
const struct path *old_path,
5023+
const struct path *new_path)
5024+
{
5025+
struct path old_file_path = { .mnt = NULL, .dentry = NULL };
5026+
struct path new_file_path = { .mnt = NULL, .dentry = NULL };
5027+
struct dentry *old_dentry_tmp = NULL;
5028+
struct dentry *new_dentry_tmp = NULL;
5029+
5030+
old_dentry_tmp = lookup_one_qstr_excl(old_last, old_path->dentry, 0);
5031+
if (!IS_ERR(old_dentry_tmp) && d_is_positive(old_dentry_tmp)) {
5032+
old_file_path.mnt = old_path->mnt;
5033+
old_file_path.dentry = old_dentry_tmp;
5034+
}
5035+
5036+
new_dentry_tmp = lookup_one_qstr_excl(new_last, new_path->dentry, 0);
5037+
if (!IS_ERR(new_dentry_tmp)) {
5038+
new_file_path.mnt = new_path->mnt;
5039+
new_file_path.dentry = new_dentry_tmp;
5040+
}
5041+
5042+
/* Call notification with both paths only if both are valid */
5043+
if (old_file_path.dentry && new_file_path.dentry) {
5044+
deepin_check_and_notify_ro_fs_err_paths(
5045+
&old_file_path, &new_file_path, "rename");
5046+
}
5047+
5048+
if (!IS_ERR(old_dentry_tmp))
5049+
dput(old_dentry_tmp);
5050+
if (!IS_ERR(new_dentry_tmp))
5051+
dput(new_dentry_tmp);
5052+
}
5053+
#endif /* CONFIG_DEEPIN_ERR_NOTIFY */
5054+
50085055
int do_renameat2(int olddfd, struct filename *from, int newdfd,
50095056
struct filename *to, unsigned int flags)
50105057
{
@@ -5134,20 +5181,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
51345181
mnt_drop_write(old_path.mnt);
51355182
exit2:
51365183
#ifdef CONFIG_DEEPIN_ERR_NOTIFY
5137-
if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) {
5138-
old_dentry =
5139-
lookup_one_qstr_excl(&old_last, old_path.dentry, 0);
5140-
if (!IS_ERR(old_dentry)) {
5141-
if (d_is_positive(old_dentry)) {
5142-
struct path file_path = { .mnt = old_path.mnt,
5143-
.dentry =
5144-
old_dentry };
5145-
deepin_check_and_notify_ro_fs_err(&file_path,
5146-
"rename");
5147-
}
5148-
dput(old_dentry);
5149-
}
5150-
}
5184+
if (unlikely((error == -EROFS) && deepin_err_notify_enabled()))
5185+
notify_rename_ro_fs_err(&old_last, &new_last, &old_path, &new_path);
51515186
#endif /* CONFIG_DEEPIN_ERR_NOTIFY */
51525187
if (retry_estale(error, lookup_flags))
51535188
should_retry = true;

fs/overlayfs/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ obj-$(CONFIG_OVERLAY_FS) += overlay.o
77

88
overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \
99
copy_up.o export.o params.o
10+
11+
ifeq ($(CONFIG_DEEPIN_ERR_NOTIFY),y)
12+
overlay-objs += deepin_ovl_err_notify.o
13+
endif

0 commit comments

Comments
 (0)