1 // SPDX-License-Identifier: GPL-2.0-or-later 2 // Copyright (c) 2025 Chen Linxuan <chenlinxuan@uniontech.com> 3 4 #define _GNU_SOURCE 5 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <sys/mount.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 #include <dirent.h> 17 #include <sched.h> 18 #include <linux/limits.h> 19 20 #include "../../kselftest_harness.h" 21 22 #define FUSECTL_MOUNTPOINT "/sys/fs/fuse/connections" 23 #define FUSE_MOUNTPOINT "/tmp/fuse_mnt_XXXXXX" 24 #define FUSE_DEVICE "/dev/fuse" 25 #define FUSECTL_TEST_VALUE "1" 26 27 static void write_file(struct __test_metadata *const _metadata, 28 const char *path, const char *val) 29 { 30 int fd = open(path, O_WRONLY); 31 size_t len = strlen(val); 32 33 ASSERT_GE(fd, 0); 34 ASSERT_EQ(write(fd, val, len), len); 35 ASSERT_EQ(close(fd), 0); 36 } 37 38 FIXTURE(fusectl){ 39 char fuse_mountpoint[sizeof(FUSE_MOUNTPOINT)]; 40 int connection; 41 }; 42 43 FIXTURE_SETUP(fusectl) 44 { 45 const char *fuse_mnt_prog = "./fuse_mnt"; 46 int status, pid; 47 struct stat statbuf; 48 uid_t uid = getuid(); 49 gid_t gid = getgid(); 50 char buf[32]; 51 52 /* Setup userns */ 53 ASSERT_EQ(unshare(CLONE_NEWNS|CLONE_NEWUSER), 0); 54 sprintf(buf, "0 %d 1", uid); 55 write_file(_metadata, "/proc/self/uid_map", buf); 56 write_file(_metadata, "/proc/self/setgroups", "deny"); 57 sprintf(buf, "0 %d 1", gid); 58 write_file(_metadata, "/proc/self/gid_map", buf); 59 ASSERT_EQ(mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL), 0); 60 61 strcpy(self->fuse_mountpoint, FUSE_MOUNTPOINT); 62 63 if (!mkdtemp(self->fuse_mountpoint)) 64 SKIP(return, 65 "Failed to create FUSE mountpoint %s", 66 strerror(errno)); 67 68 if (access(FUSECTL_MOUNTPOINT, F_OK)) 69 SKIP(return, 70 "FUSE control filesystem not mounted"); 71 72 pid = fork(); 73 if (pid < 0) 74 SKIP(return, 75 "Failed to fork FUSE daemon process: %s", 76 strerror(errno)); 77 78 if (pid == 0) { 79 execlp(fuse_mnt_prog, fuse_mnt_prog, self->fuse_mountpoint, NULL); 80 exit(errno); 81 } 82 83 waitpid(pid, &status, 0); 84 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 85 SKIP(return, 86 "Failed to start FUSE daemon %s", 87 strerror(WEXITSTATUS(status))); 88 } 89 90 if (stat(self->fuse_mountpoint, &statbuf)) 91 SKIP(return, 92 "Failed to stat FUSE mountpoint %s", 93 strerror(errno)); 94 95 self->connection = statbuf.st_dev; 96 } 97 98 FIXTURE_TEARDOWN(fusectl) 99 { 100 umount2(self->fuse_mountpoint, MNT_DETACH); 101 rmdir(self->fuse_mountpoint); 102 } 103 104 TEST_F(fusectl, abort) 105 { 106 char path_buf[PATH_MAX]; 107 int abort_fd, test_fd, ret; 108 109 sprintf(path_buf, "/sys/fs/fuse/connections/%d/abort", self->connection); 110 111 ASSERT_EQ(0, access(path_buf, F_OK)); 112 113 abort_fd = open(path_buf, O_WRONLY); 114 ASSERT_GE(abort_fd, 0); 115 116 sprintf(path_buf, "%s/test", self->fuse_mountpoint); 117 118 test_fd = open(path_buf, O_RDWR); 119 ASSERT_GE(test_fd, 0); 120 121 ret = read(test_fd, path_buf, sizeof(path_buf)); 122 ASSERT_EQ(ret, 0); 123 124 ret = write(test_fd, "test", sizeof("test")); 125 ASSERT_EQ(ret, sizeof("test")); 126 127 ret = lseek(test_fd, 0, SEEK_SET); 128 ASSERT_GE(ret, 0); 129 130 ret = write(abort_fd, FUSECTL_TEST_VALUE, sizeof(FUSECTL_TEST_VALUE)); 131 ASSERT_GT(ret, 0); 132 133 close(abort_fd); 134 135 ret = read(test_fd, path_buf, sizeof(path_buf)); 136 ASSERT_EQ(ret, -1); 137 ASSERT_EQ(errno, ENOTCONN); 138 } 139 140 TEST_HARNESS_MAIN 141