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