File: test_want_conversion.c

package info (click to toggle)
fuse3 3.17.2-3
  • links: PTS
  • area: main
  • in suites: sid, trixie
  • size: 37,592 kB
  • sloc: ansic: 22,937; perl: 6,044; cpp: 3,853; python: 1,128; sh: 408; javascript: 313; makefile: 59
file content (181 lines) | stat: -rw-r--r-- 5,165 bytes parent folder | download
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)

#include "util.h"
#include "fuse_i.h"
#include "fuse_lowlevel.h"
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <err.h>

static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
{
	struct fuse_session *se = container_of(conn, struct fuse_session, conn);

	printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64
		" want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n",
		prefix, conn->want, conn->want_ext, se->conn_want,
		se->conn_want_ext);
}

static void application_init_old_style(struct fuse_conn_info *conn)
{
	/* Simulate application init the old style */
	conn->want |= FUSE_CAP_ASYNC_READ;
	conn->want &= ~FUSE_CAP_SPLICE_READ;

	/*
	 * Also use new style API, as that might happen through
	 * fuse_apply_conn_info_opts()
	 */
	fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR);
}

static void application_init_new_style(struct fuse_conn_info *conn)
{
	/* Simulate application init the new style */
	fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ);
	fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR);
	fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
}

static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
{
	/* High-level init */
	fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);

	if (new_style)
		application_init_new_style(conn);
	else
		application_init_old_style(conn);
}

static void test_do_init(struct fuse_conn_info *conn, bool new_style)
{
	/* Initial setup */
	conn->capable_ext = FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE |
			    FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS |
			    FUSE_CAP_FLOCK_LOCKS | FUSE_CAP_EXPORT_SUPPORT |
			    FUSE_CAP_ASYNC_READ | FUSE_CAP_IOCTL_DIR;
	conn->capable = fuse_lower_32_bits(conn->capable_ext);

	fuse_set_feature_flag(conn, FUSE_CAP_SPLICE_READ |
				    FUSE_CAP_SPLICE_WRITE |
				    FUSE_CAP_SPLICE_MOVE);

	print_conn_info("Initial state", conn);

	int rc;

	test_fuse_fs_init(conn, new_style);
	print_conn_info("After init", conn);

	rc = fuse_convert_to_conn_want_ext(conn);
	assert(rc == 0);

	/* Verify all expected flags are set */
	assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
	assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
	assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
	assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
	assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
	assert(conn->want_ext & FUSE_CAP_IOCTL_DIR);

	/* Verify no other flags are set */
	assert(conn->want_ext ==
	       (FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE |
		FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ |
		FUSE_CAP_IOCTL_DIR));

	print_conn_info("After init", conn);
}

static void test_want_conversion_basic(void)
{
	const struct fuse_lowlevel_ops ops = { 0 };
	struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
	struct fuse_session *se;
	struct fuse_conn_info *conn;

	/* Add the program name to arg[0] */
	if (fuse_opt_add_arg(&args, "test_signals")) {
		fprintf(stderr, "Failed to add argument\n");
		errx(1, "Failed to add argument");
	}


	se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
	assert(se);
	conn = &se->conn;
	printf("\nTesting basic want conversion, old style:\n");
	test_do_init(conn, false);
	fuse_session_destroy(se);

	se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
	assert(se);
	conn = &se->conn;
	printf("\nTesting basic want conversion, new style:\n");
	test_do_init(conn, true);
	print_conn_info("After init", conn);
	fuse_session_destroy(se);

	fuse_opt_free_args(&args);

}

static void test_want_conversion_conflict(void)
{
	struct fuse_conn_info conn = { 0 };
	int rc;

	printf("\nTesting want conversion conflict:\n");

	/* Test conflicting values */
	/* Initialize like fuse_lowlevel.c does */
	conn.capable_ext = FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE |
			   FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS |
			   FUSE_CAP_FLOCK_LOCKS;
	conn.capable = fuse_lower_32_bits(conn.capable_ext);
	conn.want_ext = conn.capable_ext;
	conn.want = fuse_lower_32_bits(conn.want_ext);
	print_conn_info("Test conflict initial", &conn);

	/* Simulate application init modifying capabilities */
	conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
	conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */

	rc = fuse_convert_to_conn_want_ext(&conn);
	assert(rc == -EINVAL);
	print_conn_info("Test conflict after", &conn);

	printf("Want conversion conflict test passed\n");
}

static void test_want_conversion_high_bits(void)
{
	struct fuse_conn_info conn = { 0 };
	int rc;

	printf("\nTesting want conversion high bits preservation:\n");

	/* Test high bits preservation */
	conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
	conn.want = fuse_lower_32_bits(conn.want_ext);
	print_conn_info("Test high bits initial", &conn);

	rc = fuse_convert_to_conn_want_ext(&conn);
	assert(rc == 0);
	assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
	print_conn_info("Test high bits after", &conn);

	printf("Want conversion high bits test passed\n");
}

int main(void)
{
	test_want_conversion_basic();
	test_want_conversion_conflict();
	test_want_conversion_high_bits();
	return 0;
}