summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/filesystems/fuse/fusectl_test.c
blob: 8d124d1cacb266aef3a713e65571ac3fa1f28480 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Chen Linxuan <chenlinxuan@uniontech.com>

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <dirent.h>
#include <sched.h>
#include <linux/limits.h>

#include "../../kselftest_harness.h"

#define FUSECTL_MOUNTPOINT "/sys/fs/fuse/connections"
#define FUSE_MOUNTPOINT "/tmp/fuse_mnt_XXXXXX"
#define FUSE_DEVICE "/dev/fuse"
#define FUSECTL_TEST_VALUE "1"

static void write_file(struct __test_metadata *const _metadata,
		       const char *path, const char *val)
{
	int fd = open(path, O_WRONLY);
	size_t len = strlen(val);

	ASSERT_GE(fd, 0);
	ASSERT_EQ(write(fd, val, len), len);
	ASSERT_EQ(close(fd), 0);
}

FIXTURE(fusectl){
	char fuse_mountpoint[sizeof(FUSE_MOUNTPOINT)];
	int connection;
};

FIXTURE_SETUP(fusectl)
{
	const char *fuse_mnt_prog = "./fuse_mnt";
	int status, pid;
	struct stat statbuf;
	uid_t uid = getuid();
	gid_t gid = getgid();
	char buf[32];

	/* Setup userns */
	ASSERT_EQ(unshare(CLONE_NEWNS|CLONE_NEWUSER), 0);
	sprintf(buf, "0 %d 1", uid);
	write_file(_metadata, "/proc/self/uid_map", buf);
	write_file(_metadata, "/proc/self/setgroups", "deny");
	sprintf(buf, "0 %d 1", gid);
	write_file(_metadata, "/proc/self/gid_map", buf);
	ASSERT_EQ(mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL), 0);

	strcpy(self->fuse_mountpoint, FUSE_MOUNTPOINT);

	if (!mkdtemp(self->fuse_mountpoint))
		SKIP(return,
		     "Failed to create FUSE mountpoint %s",
		     strerror(errno));

	if (access(FUSECTL_MOUNTPOINT, F_OK))
		SKIP(return,
		     "FUSE control filesystem not mounted");

	pid = fork();
	if (pid < 0)
		SKIP(return,
		     "Failed to fork FUSE daemon process: %s",
		     strerror(errno));

	if (pid == 0) {
		execlp(fuse_mnt_prog, fuse_mnt_prog, self->fuse_mountpoint, NULL);
		exit(errno);
	}

	waitpid(pid, &status, 0);
	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
		SKIP(return,
		     "Failed to start FUSE daemon %s",
		     strerror(WEXITSTATUS(status)));
	}

	if (stat(self->fuse_mountpoint, &statbuf))
		SKIP(return,
		     "Failed to stat FUSE mountpoint %s",
		     strerror(errno));

	self->connection = statbuf.st_dev;
}

FIXTURE_TEARDOWN(fusectl)
{
	umount2(self->fuse_mountpoint, MNT_DETACH);
	rmdir(self->fuse_mountpoint);
}

TEST_F(fusectl, abort)
{
	char path_buf[PATH_MAX];
	int abort_fd, test_fd, ret;

	sprintf(path_buf, "/sys/fs/fuse/connections/%d/abort", self->connection);

	ASSERT_EQ(0, access(path_buf, F_OK));

	abort_fd = open(path_buf, O_WRONLY);
	ASSERT_GE(abort_fd, 0);

	sprintf(path_buf, "%s/test", self->fuse_mountpoint);

	test_fd = open(path_buf, O_RDWR);
	ASSERT_GE(test_fd, 0);

	ret = read(test_fd, path_buf, sizeof(path_buf));
	ASSERT_EQ(ret, 0);

	ret = write(test_fd, "test", sizeof("test"));
	ASSERT_EQ(ret, sizeof("test"));

	ret = lseek(test_fd, 0, SEEK_SET);
	ASSERT_GE(ret, 0);

	ret = write(abort_fd, FUSECTL_TEST_VALUE, sizeof(FUSECTL_TEST_VALUE));
	ASSERT_GT(ret, 0);

	close(abort_fd);

	ret = read(test_fd, path_buf, sizeof(path_buf));
	ASSERT_EQ(ret, -1);
	ASSERT_EQ(errno, ENOTCONN);
}

TEST_HARNESS_MAIN