aboutsummaryrefslogtreecommitdiffstats
path: root/test/test_want_conversion.c
blob: bee23cc6eb817ecfaad9d37df36c561d68b72a0c (plain) (blame)
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
#include "util.h"
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)

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

static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
{
	printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 "\n", prefix,
	       conn->want, 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;
}

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_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
}

static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
{
	uint64_t want_ext_default = conn->want_ext;
	uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
	int rc;

	/* High-level init */
	fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);

	conn->want = want_default;

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

	rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
	assert(rc == 0);
}

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;
	conn->capable = fuse_lower_32_bits(conn->capable_ext);
	conn->want_ext = conn->capable_ext;

	print_conn_info("Initial state", conn);

	uint64_t want_ext_default = conn->want_ext;
	uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
	int rc;

	conn->want = want_default;
	conn->capable = fuse_lower_32_bits(conn->capable_ext);

	test_fuse_fs_init(conn, new_style);

	rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
	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_POSIX_LOCKS);
	assert(conn->want_ext & FUSE_CAP_FLOCK_LOCKS);
	assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
	assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
	/* Verify no other flags are set */
	assert(conn->want_ext ==
	       (FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE |
		FUSE_CAP_POSIX_LOCKS | FUSE_CAP_FLOCK_LOCKS |
		FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ));

	print_conn_info("After init", conn);
}

static void test_want_conversion_basic(void)
{
	struct fuse_conn_info conn = { 0 };

	printf("\nTesting basic want conversion:\n");
	test_do_init(&conn, false);
	test_do_init(&conn, true);
	print_conn_info("After init", &conn);
}

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);

	/* Initialize default values like in basic test */
	uint64_t want_ext_default_ll = conn.want_ext;
	uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);

	/* 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 = convert_to_conn_want_ext(&conn, want_ext_default_ll,
				      want_default_ll);
	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);

	uint64_t want_ext_default_ll = conn.want_ext;
	uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);

	rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
				      want_default_ll);
	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;
}