xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/fanotify/fanotify22.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2021 Collabora Ltd.
4  *
5  * Author: Gabriel Krisman Bertazi <[email protected]>
6  * Based on previous work by Amir Goldstein <[email protected]>
7  */
8 
9 /*\
10  * [Description]
11  * Check fanotify FAN_ERROR_FS events triggered by intentionally
12  * corrupted filesystems:
13  *
14  * - Generate a broken filesystem
15  * - Start FAN_FS_ERROR monitoring group
16  * - Make the file system notice the error through ordinary operations
17  * - Observe the event generated
18  */
19 
20 #define _GNU_SOURCE
21 #include "config.h"
22 
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/mount.h>
28 #include <sys/syscall.h>
29 #include "tst_test.h"
30 #include <sys/types.h>
31 
32 #ifdef HAVE_SYS_FANOTIFY_H
33 #include "fanotify.h"
34 
35 #ifndef EFSCORRUPTED
36 #define EFSCORRUPTED    EUCLEAN         /* Filesystem is corrupted */
37 #endif
38 
39 #define BUF_SIZE 256
40 
41 #define MOUNT_PATH "test_mnt"
42 #define BASE_DIR "internal_dir"
43 #define BAD_DIR BASE_DIR"/bad_dir"
44 #define BAD_LINK BASE_DIR"/bad_link"
45 
46 #ifdef HAVE_NAME_TO_HANDLE_AT
47 
48 static char event_buf[BUF_SIZE];
49 static int fd_notify;
50 
51 /* These expected FIDs are common to multiple tests */
52 static struct fanotify_fid_t null_fid;
53 static struct fanotify_fid_t bad_file_fid;
54 static struct fanotify_fid_t bad_link_fid;
55 
trigger_fs_abort(void)56 static void trigger_fs_abort(void)
57 {
58 	SAFE_MOUNT(tst_device->dev, MOUNT_PATH, tst_device->fs_type,
59 		   MS_REMOUNT|MS_RDONLY, "abort");
60 }
61 
do_debugfs_request(const char * dev,char * request)62 static void do_debugfs_request(const char *dev, char *request)
63 {
64 	const char *const cmd[] = {"debugfs", "-w", dev, "-R", request, NULL};
65 
66 	SAFE_CMD(cmd, NULL, NULL);
67 }
68 
trigger_bad_file_lookup(void)69 static void trigger_bad_file_lookup(void)
70 {
71 	int ret;
72 
73 	/* SAFE_OPEN cannot be used here because we expect it to fail. */
74 	ret = open(MOUNT_PATH"/"BAD_DIR, O_RDONLY, 0);
75 	if (ret != -1 && errno != EUCLEAN)
76 		tst_res(TFAIL, "Unexpected lookup result(%d) of %s (%d!=%d)",
77 			ret, BAD_DIR, errno, EUCLEAN);
78 }
79 
trigger_bad_link_lookup(void)80 static void trigger_bad_link_lookup(void)
81 {
82 	int ret;
83 
84 	/* SAFE_OPEN cannot be used here because we expect it to fail. */
85 	ret = open(MOUNT_PATH"/"BAD_LINK, O_RDONLY, 0);
86 	if (ret != -1 && errno != EUCLEAN)
87 		tst_res(TFAIL, "Unexpected open result(%d) of %s (%d!=%d)",
88 			ret, BAD_LINK, errno, EUCLEAN);
89 }
90 
91 
tcase3_trigger(void)92 static void tcase3_trigger(void)
93 {
94 	trigger_bad_link_lookup();
95 	trigger_bad_file_lookup();
96 }
97 
tcase4_trigger(void)98 static void tcase4_trigger(void)
99 {
100 	trigger_bad_file_lookup();
101 	trigger_fs_abort();
102 }
103 
104 static struct test_case {
105 	char *name;
106 	int error;
107 	unsigned int error_count;
108 	struct fanotify_fid_t *fid;
109 	void (*trigger_error)(void);
110 } testcases[] = {
111 	{
112 		.name = "Trigger abort",
113 		.trigger_error = &trigger_fs_abort,
114 		.error_count = 1,
115 		.error = ESHUTDOWN,
116 		.fid = &null_fid,
117 	},
118 	{
119 		.name = "Lookup of inode with invalid mode",
120 		.trigger_error = &trigger_bad_file_lookup,
121 		.error_count = 1,
122 		.error = EFSCORRUPTED,
123 		.fid = &bad_file_fid,
124 	},
125 	{
126 		.name = "Multiple error submission",
127 		.trigger_error = &tcase3_trigger,
128 		.error_count = 2,
129 		.error = EFSCORRUPTED,
130 		.fid = &bad_link_fid,
131 	},
132 	{
133 		.name = "Multiple error submission 2",
134 		.trigger_error = &tcase4_trigger,
135 		.error_count = 2,
136 		.error = EFSCORRUPTED,
137 		.fid = &bad_file_fid,
138 	}
139 };
140 
check_error_event_info_fid(struct fanotify_event_info_fid * fid,const struct test_case * ex)141 static int check_error_event_info_fid(struct fanotify_event_info_fid *fid,
142 				 const struct test_case *ex)
143 {
144 	struct file_handle *fh = (struct file_handle *) &fid->handle;
145 
146 	if (memcmp(&fid->fsid, &ex->fid->fsid, sizeof(fid->fsid))) {
147 		tst_res(TFAIL, "%s: Received bad FSID type (%x...!=%x...)",
148 			ex->name, FSID_VAL_MEMBER(fid->fsid, 0),
149 			ex->fid->fsid.val[0]);
150 
151 		return 1;
152 	}
153 	if (fh->handle_type != ex->fid->handle.handle_type) {
154 		tst_res(TFAIL, "%s: Received bad file_handle type (%d!=%d)",
155 			ex->name, fh->handle_type, ex->fid->handle.handle_type);
156 		return 1;
157 	}
158 
159 	if (fh->handle_bytes != ex->fid->handle.handle_bytes) {
160 		tst_res(TFAIL, "%s: Received bad file_handle len (%d!=%d)",
161 			ex->name, fh->handle_bytes, ex->fid->handle.handle_bytes);
162 		return 1;
163 	}
164 
165 	if (memcmp(fh->f_handle, ex->fid->handle.f_handle, fh->handle_bytes)) {
166 		tst_res(TFAIL, "%s: Received wrong handle. "
167 			"Expected (%x...) got (%x...) ", ex->name,
168 			*(int *)ex->fid->handle.f_handle, *(int *)fh->f_handle);
169 		return 1;
170 	}
171 	return 0;
172 }
173 
check_error_event_info_error(struct fanotify_event_info_error * info_error,const struct test_case * ex)174 static int check_error_event_info_error(struct fanotify_event_info_error *info_error,
175 				 const struct test_case *ex)
176 {
177 	int fail = 0;
178 
179 	if (info_error->error_count != ex->error_count) {
180 		tst_res(TFAIL, "%s: Unexpected error_count (%d!=%d)",
181 			ex->name, info_error->error_count, ex->error_count);
182 		fail++;
183 	}
184 
185 	if (info_error->error != ex->error) {
186 		tst_res(TFAIL, "%s: Unexpected error code value (%d!=%d)",
187 			ex->name, info_error->error, ex->error);
188 		fail++;
189 	}
190 
191 	return fail;
192 }
193 
check_error_event_metadata(struct fanotify_event_metadata * event)194 static int check_error_event_metadata(struct fanotify_event_metadata *event)
195 {
196 	int fail = 0;
197 
198 	if (event->mask != FAN_FS_ERROR) {
199 		fail++;
200 		tst_res(TFAIL, "got unexpected event %llx",
201 			(unsigned long long)event->mask);
202 	}
203 
204 	if (event->fd != FAN_NOFD) {
205 		fail++;
206 		tst_res(TFAIL, "Weird FAN_FD %llx",
207 			(unsigned long long)event->mask);
208 	}
209 	return fail;
210 }
211 
check_event(char * buf,size_t len,const struct test_case * ex)212 static void check_event(char *buf, size_t len, const struct test_case *ex)
213 {
214 	struct fanotify_event_metadata *event =
215 		(struct fanotify_event_metadata *) buf;
216 	struct fanotify_event_info_error *info_error;
217 	struct fanotify_event_info_fid *info_fid;
218 	int fail = 0;
219 
220 	if (len < FAN_EVENT_METADATA_LEN) {
221 		tst_res(TFAIL, "No event metadata found");
222 		return;
223 	}
224 
225 	if (check_error_event_metadata(event))
226 		return;
227 
228 	info_error = get_event_info_error(event);
229 	if (info_error)
230 		fail += check_error_event_info_error(info_error, ex);
231 	else {
232 		tst_res(TFAIL, "Generic error record not found");
233 		fail++;
234 	}
235 
236 	info_fid = get_event_info_fid(event);
237 	if (info_fid)
238 		fail += check_error_event_info_fid(info_fid, ex);
239 	else {
240 		tst_res(TFAIL, "FID record not found");
241 		fail++;
242 	}
243 
244 	if (!fail)
245 		tst_res(TPASS, "Successfully received: %s", ex->name);
246 }
247 
do_test(unsigned int i)248 static void do_test(unsigned int i)
249 {
250 	const struct test_case *tcase = &testcases[i];
251 	size_t read_len;
252 
253 	SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_ADD|FAN_MARK_FILESYSTEM,
254 			   FAN_FS_ERROR, AT_FDCWD, MOUNT_PATH);
255 
256 	tcase->trigger_error();
257 
258 	read_len = SAFE_READ(0, fd_notify, event_buf, BUF_SIZE);
259 
260 	SAFE_FANOTIFY_MARK(fd_notify, FAN_MARK_REMOVE|FAN_MARK_FILESYSTEM,
261 			   FAN_FS_ERROR, AT_FDCWD, MOUNT_PATH);
262 
263 	check_event(event_buf, read_len, tcase);
264 	/* Unmount and mount the filesystem to get it out of the error state */
265 	SAFE_UMOUNT(MOUNT_PATH);
266 	SAFE_MOUNT(tst_device->dev, MOUNT_PATH, tst_device->fs_type, 0, NULL);
267 }
268 
pre_corrupt_fs(void)269 static void pre_corrupt_fs(void)
270 {
271 	SAFE_MKDIR(MOUNT_PATH"/"BASE_DIR, 0777);
272 	SAFE_MKDIR(MOUNT_PATH"/"BAD_DIR, 0777);
273 
274 	fanotify_save_fid(MOUNT_PATH"/"BAD_DIR, &bad_file_fid);
275 	fanotify_save_fid(MOUNT_PATH"/"BASE_DIR, &bad_link_fid);
276 
277 	SAFE_UMOUNT(MOUNT_PATH);
278 	do_debugfs_request(tst_device->dev, "sif " BAD_DIR " mode 0xff");
279 	do_debugfs_request(tst_device->dev, "ln <1> " BAD_LINK);
280 	SAFE_MOUNT(tst_device->dev, MOUNT_PATH, tst_device->fs_type, 0, NULL);
281 }
282 
init_null_fid(void)283 static void init_null_fid(void)
284 {
285 	/* Use fanotify_save_fid to fill the fsid and overwrite the
286 	 * file_handler to create a null_fid
287 	 */
288 	fanotify_save_fid(MOUNT_PATH, &null_fid);
289 
290 	null_fid.handle.handle_type = FILEID_INVALID;
291 	null_fid.handle.handle_bytes = 0;
292 }
293 
setup(void)294 static void setup(void)
295 {
296 	REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(FAN_CLASS_NOTIF|FAN_REPORT_FID,
297 						FAN_MARK_FILESYSTEM,
298 						FAN_FS_ERROR, ".");
299 	pre_corrupt_fs();
300 
301 	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF|FAN_REPORT_FID,
302 				       O_RDONLY);
303 
304 	init_null_fid();
305 }
306 
cleanup(void)307 static void cleanup(void)
308 {
309 	if (fd_notify > 0)
310 		SAFE_CLOSE(fd_notify);
311 }
312 
313 static struct tst_test test = {
314 	.test = do_test,
315 	.tcnt = ARRAY_SIZE(testcases),
316 	.setup = setup,
317 	.cleanup = cleanup,
318 	.mount_device = 1,
319 	.mntpoint = MOUNT_PATH,
320 	.needs_root = 1,
321 	.dev_fs_type = "ext4",
322 	.tags = (const struct tst_tag[]) {
323 		{"linux-git", "124e7c61deb2"},
324 		{}
325 	},
326 	.needs_cmds = (const char *[]) {
327 		"debugfs",
328 		NULL
329 	}
330 };
331 
332 #else
333 	TST_TEST_TCONF("system does not have required name_to_handle_at() support");
334 #endif
335 #else
336 	TST_TEST_TCONF("system doesn't have required fanotify support");
337 #endif
338