xref: /linux/tools/testing/selftests/filesystems/fuse/fusectl_test.c (revision 6238729bfce13f94b701766996a5d116d2df8bff)
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 
write_file(struct __test_metadata * const _metadata,const char * path,const char * val)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 
FIXTURE(fusectl)38 FIXTURE(fusectl){
39 	char fuse_mountpoint[sizeof(FUSE_MOUNTPOINT)];
40 	int connection;
41 };
42 
FIXTURE_SETUP(fusectl)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 
FIXTURE_TEARDOWN(fusectl)98 FIXTURE_TEARDOWN(fusectl)
99 {
100 	umount2(self->fuse_mountpoint, MNT_DETACH);
101 	rmdir(self->fuse_mountpoint);
102 }
103 
TEST_F(fusectl,abort)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