1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2015 Fujitsu Ltd.
4f08c3bdfSopenharmony_ci * Author: Guangwen Feng <fenggw-fnst@cn.fujitsu.com>
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*
8f08c3bdfSopenharmony_ci * DESCRIPTION
9f08c3bdfSopenharmony_ci *  Test for feature F_SETLEASE of fcntl(2).
10f08c3bdfSopenharmony_ci *  "F_SETLEASE is used to establish a lease which provides a mechanism:
11f08c3bdfSopenharmony_ci *   When a process (the lease breaker) performs an open(2) or truncate(2)
12f08c3bdfSopenharmony_ci *   that conflicts with the lease, the system call will be blocked by
13f08c3bdfSopenharmony_ci *   kernel, meanwhile the kernel notifies the lease holder by sending
14f08c3bdfSopenharmony_ci *   it a signal (SIGIO by default), after the lease holder successes
15f08c3bdfSopenharmony_ci *   to downgrade or remove the lease, the kernel permits the system
16f08c3bdfSopenharmony_ci *   call of the lease breaker to proceed."
17f08c3bdfSopenharmony_ci */
18f08c3bdfSopenharmony_ci
19f08c3bdfSopenharmony_ci#include <errno.h>
20f08c3bdfSopenharmony_ci
21f08c3bdfSopenharmony_ci#include "tst_test.h"
22f08c3bdfSopenharmony_ci#include "tst_timer.h"
23f08c3bdfSopenharmony_ci#include "tst_safe_macros.h"
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_ci/*
26f08c3bdfSopenharmony_ci * MIN_TIME_LIMIT is defined to 5 senconds as a minimal acceptable
27f08c3bdfSopenharmony_ci * amount of time for the lease breaker waiting for unblock via
28f08c3bdfSopenharmony_ci * lease holder voluntarily downgrade or remove the lease, if the
29f08c3bdfSopenharmony_ci * lease breaker is unblocked within MIN_TIME_LIMIT we may consider
30f08c3bdfSopenharmony_ci * that the feature of the lease mechanism works well.
31f08c3bdfSopenharmony_ci *
32f08c3bdfSopenharmony_ci * The lease-break-time is set to 45 seconds for timeout in kernel.
33f08c3bdfSopenharmony_ci */
34f08c3bdfSopenharmony_ci#define MIN_TIME_LIMIT	5
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_ci#define OP_OPEN_RDONLY	0
37f08c3bdfSopenharmony_ci#define OP_OPEN_WRONLY	1
38f08c3bdfSopenharmony_ci#define OP_OPEN_RDWR	2
39f08c3bdfSopenharmony_ci#define OP_TRUNCATE	3
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_ci#define FILE_MODE	(S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID)
42f08c3bdfSopenharmony_ci#define PATH_LS_BRK_T	"/proc/sys/fs/lease-break-time"
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_cistatic void do_test(unsigned int);
45f08c3bdfSopenharmony_cistatic void do_child(unsigned int);
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_cistatic int fd;
48f08c3bdfSopenharmony_cistatic int ls_brk_t;
49f08c3bdfSopenharmony_cistatic long type;
50f08c3bdfSopenharmony_cistatic sigset_t newset, oldset;
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_ci/* Time limit for lease holder to receive SIGIO. */
53f08c3bdfSopenharmony_cistatic struct timespec timeout = {.tv_sec = 5};
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_cistatic struct test_case_t {
56f08c3bdfSopenharmony_ci	int lease_type;
57f08c3bdfSopenharmony_ci	int op_type;
58f08c3bdfSopenharmony_ci	char *desc;
59f08c3bdfSopenharmony_ci} test_cases[] = {
60f08c3bdfSopenharmony_ci	{F_WRLCK, OP_OPEN_RDONLY,
61f08c3bdfSopenharmony_ci		"open(O_RDONLY) conflicts with fcntl(F_SETLEASE, F_WRLCK)"},
62f08c3bdfSopenharmony_ci	{F_WRLCK, OP_OPEN_WRONLY,
63f08c3bdfSopenharmony_ci		"open(O_WRONLY) conflicts with fcntl(F_SETLEASE, F_WRLCK)"},
64f08c3bdfSopenharmony_ci	{F_WRLCK, OP_OPEN_RDWR,
65f08c3bdfSopenharmony_ci		"open(O_RDWR) conflicts with fcntl(F_SETLEASE, F_WRLCK)"},
66f08c3bdfSopenharmony_ci	{F_WRLCK, OP_TRUNCATE,
67f08c3bdfSopenharmony_ci		"truncate() conflicts with fcntl(F_SETLEASE, F_WRLCK)"},
68f08c3bdfSopenharmony_ci	{F_RDLCK, OP_OPEN_WRONLY,
69f08c3bdfSopenharmony_ci		"open(O_WRONLY) conflicts with fcntl(F_SETLEASE, F_RDLCK)"},
70f08c3bdfSopenharmony_ci	{F_RDLCK, OP_OPEN_RDWR,
71f08c3bdfSopenharmony_ci		"open(O_RDWR) conflicts with fcntl(F_SETLEASE, F_RDLCK)"},
72f08c3bdfSopenharmony_ci	{F_RDLCK, OP_TRUNCATE,
73f08c3bdfSopenharmony_ci		"truncate() conflicts with fcntl(F_SETLEASE, F_RDLCK)"},
74f08c3bdfSopenharmony_ci};
75f08c3bdfSopenharmony_ci
76f08c3bdfSopenharmony_cistatic void setup(void)
77f08c3bdfSopenharmony_ci{
78f08c3bdfSopenharmony_ci	tst_timer_check(CLOCK_MONOTONIC);
79f08c3bdfSopenharmony_ci
80f08c3bdfSopenharmony_ci	/* Backup and set the lease-break-time. */
81f08c3bdfSopenharmony_ci	SAFE_FILE_SCANF(PATH_LS_BRK_T, "%d", &ls_brk_t);
82f08c3bdfSopenharmony_ci	SAFE_FILE_PRINTF(PATH_LS_BRK_T, "%d", 45);
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_ci	SAFE_TOUCH("file", FILE_MODE, NULL);
85f08c3bdfSopenharmony_ci
86f08c3bdfSopenharmony_ci	sigemptyset(&newset);
87f08c3bdfSopenharmony_ci	sigaddset(&newset, SIGIO);
88f08c3bdfSopenharmony_ci
89f08c3bdfSopenharmony_ci	if (sigprocmask(SIG_SETMASK, &newset, &oldset) < 0)
90f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO, "sigprocmask() failed");
91f08c3bdfSopenharmony_ci}
92f08c3bdfSopenharmony_ci
93f08c3bdfSopenharmony_cistatic void do_test(unsigned int i)
94f08c3bdfSopenharmony_ci{
95f08c3bdfSopenharmony_ci	pid_t cpid;
96f08c3bdfSopenharmony_ci
97f08c3bdfSopenharmony_ci	cpid = SAFE_FORK();
98f08c3bdfSopenharmony_ci	if (cpid == 0) {
99f08c3bdfSopenharmony_ci		do_child(i);
100f08c3bdfSopenharmony_ci		return;
101f08c3bdfSopenharmony_ci	}
102f08c3bdfSopenharmony_ci
103f08c3bdfSopenharmony_ci	fd = SAFE_OPEN("file", O_RDONLY);
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci	TEST(fcntl(fd, F_SETLEASE, test_cases[i].lease_type));
106f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
107f08c3bdfSopenharmony_ci		if (type == TST_OVERLAYFS_MAGIC && TST_ERR == EAGAIN) {
108f08c3bdfSopenharmony_ci			tst_res(TINFO | TTERRNO,
109f08c3bdfSopenharmony_ci				"fcntl(F_SETLEASE, F_WRLCK) failed on overlayfs as expected");
110f08c3bdfSopenharmony_ci		} else {
111f08c3bdfSopenharmony_ci			tst_res(TFAIL | TTERRNO, "fcntl() failed to set lease");
112f08c3bdfSopenharmony_ci		}
113f08c3bdfSopenharmony_ci		TST_CHECKPOINT_WAKE(0);
114f08c3bdfSopenharmony_ci		goto exit;
115f08c3bdfSopenharmony_ci	}
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	TST_CHECKPOINT_WAKE(0);
118f08c3bdfSopenharmony_ci	/* Wait for SIGIO caused by lease breaker. */
119f08c3bdfSopenharmony_ci	TEST(sigtimedwait(&newset, NULL, &timeout));
120f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
121f08c3bdfSopenharmony_ci		if (TST_ERR == EAGAIN) {
122f08c3bdfSopenharmony_ci			tst_res(TFAIL | TTERRNO,
123f08c3bdfSopenharmony_ci				"failed to receive SIGIO within %lis",
124f08c3bdfSopenharmony_ci				timeout.tv_sec);
125f08c3bdfSopenharmony_ci			goto exit;
126f08c3bdfSopenharmony_ci		}
127f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO, "sigtimedwait() failed");
128f08c3bdfSopenharmony_ci	}
129f08c3bdfSopenharmony_ci
130f08c3bdfSopenharmony_ci	/* Try to downgrade or remove the lease. */
131f08c3bdfSopenharmony_ci	switch (test_cases[i].lease_type) {
132f08c3bdfSopenharmony_ci	case F_WRLCK:
133f08c3bdfSopenharmony_ci		TEST(fcntl(fd, F_SETLEASE, F_RDLCK));
134f08c3bdfSopenharmony_ci		if (TST_RET == 0) {
135f08c3bdfSopenharmony_ci			if (test_cases[i].op_type == OP_OPEN_RDONLY)
136f08c3bdfSopenharmony_ci				break;
137f08c3bdfSopenharmony_ci
138f08c3bdfSopenharmony_ci			tst_res(TFAIL,
139f08c3bdfSopenharmony_ci				"fcntl() downgraded lease when not read-only");
140f08c3bdfSopenharmony_ci		}
141f08c3bdfSopenharmony_ci
142f08c3bdfSopenharmony_ci		if (test_cases[i].op_type == OP_OPEN_RDONLY) {
143f08c3bdfSopenharmony_ci			tst_res(TFAIL | TTERRNO,
144f08c3bdfSopenharmony_ci				"fcntl() failed to downgrade lease");
145f08c3bdfSopenharmony_ci		}
146f08c3bdfSopenharmony_ci
147f08c3bdfSopenharmony_ci		/* Falls through */
148f08c3bdfSopenharmony_ci	case F_RDLCK:
149f08c3bdfSopenharmony_ci		TEST(fcntl(fd, F_SETLEASE, F_UNLCK));
150f08c3bdfSopenharmony_ci		if (TST_RET == -1) {
151f08c3bdfSopenharmony_ci			tst_res(TFAIL | TTERRNO,
152f08c3bdfSopenharmony_ci				 "fcntl() failed to remove the lease");
153f08c3bdfSopenharmony_ci		}
154f08c3bdfSopenharmony_ci		break;
155f08c3bdfSopenharmony_ci	default:
156f08c3bdfSopenharmony_ci		break;
157f08c3bdfSopenharmony_ci	}
158f08c3bdfSopenharmony_ci
159f08c3bdfSopenharmony_ciexit:
160f08c3bdfSopenharmony_ci	tst_reap_children();
161f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
162f08c3bdfSopenharmony_ci}
163f08c3bdfSopenharmony_ci
164f08c3bdfSopenharmony_cistatic void do_child(unsigned int i)
165f08c3bdfSopenharmony_ci{
166f08c3bdfSopenharmony_ci	long long elapsed_ms;
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_ci	TST_CHECKPOINT_WAIT(0);
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_ci	tst_timer_start(CLOCK_MONOTONIC);
171f08c3bdfSopenharmony_ci
172f08c3bdfSopenharmony_ci	switch (test_cases[i].op_type) {
173f08c3bdfSopenharmony_ci	case OP_OPEN_RDONLY:
174f08c3bdfSopenharmony_ci		SAFE_OPEN("file", O_RDONLY);
175f08c3bdfSopenharmony_ci		break;
176f08c3bdfSopenharmony_ci	case OP_OPEN_WRONLY:
177f08c3bdfSopenharmony_ci		SAFE_OPEN("file", O_WRONLY);
178f08c3bdfSopenharmony_ci		break;
179f08c3bdfSopenharmony_ci	case OP_OPEN_RDWR:
180f08c3bdfSopenharmony_ci		SAFE_OPEN("file", O_RDWR);
181f08c3bdfSopenharmony_ci		break;
182f08c3bdfSopenharmony_ci	case OP_TRUNCATE:
183f08c3bdfSopenharmony_ci		SAFE_TRUNCATE("file", 0);
184f08c3bdfSopenharmony_ci		break;
185f08c3bdfSopenharmony_ci	default:
186f08c3bdfSopenharmony_ci		break;
187f08c3bdfSopenharmony_ci	}
188f08c3bdfSopenharmony_ci
189f08c3bdfSopenharmony_ci	tst_timer_stop();
190f08c3bdfSopenharmony_ci
191f08c3bdfSopenharmony_ci	elapsed_ms = tst_timer_elapsed_ms();
192f08c3bdfSopenharmony_ci
193f08c3bdfSopenharmony_ci	if (elapsed_ms < MIN_TIME_LIMIT * 1000) {
194f08c3bdfSopenharmony_ci		tst_res(TPASS, "%s, unblocked within %ds",
195f08c3bdfSopenharmony_ci			 test_cases[i].desc, MIN_TIME_LIMIT);
196f08c3bdfSopenharmony_ci	} else {
197f08c3bdfSopenharmony_ci		tst_res(TFAIL,
198f08c3bdfSopenharmony_ci			"%s, blocked too long %llims, expected within %ds",
199f08c3bdfSopenharmony_ci			test_cases[i].desc, elapsed_ms, MIN_TIME_LIMIT);
200f08c3bdfSopenharmony_ci	}
201f08c3bdfSopenharmony_ci}
202f08c3bdfSopenharmony_ci
203f08c3bdfSopenharmony_cistatic void cleanup(void)
204f08c3bdfSopenharmony_ci{
205f08c3bdfSopenharmony_ci	if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
206f08c3bdfSopenharmony_ci		tst_res(TWARN | TERRNO, "sigprocmask restore oldset failed");
207f08c3bdfSopenharmony_ci
208f08c3bdfSopenharmony_ci	if (fd > 0)
209f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd);
210f08c3bdfSopenharmony_ci
211f08c3bdfSopenharmony_ci	/* Restore the lease-break-time. */
212f08c3bdfSopenharmony_ci	SAFE_FILE_PRINTF(PATH_LS_BRK_T, "%d", ls_brk_t);
213f08c3bdfSopenharmony_ci}
214f08c3bdfSopenharmony_ci
215f08c3bdfSopenharmony_cistatic struct tst_test test = {
216f08c3bdfSopenharmony_ci	.forks_child = 1,
217f08c3bdfSopenharmony_ci	.needs_root = 1,
218f08c3bdfSopenharmony_ci	.needs_checkpoints = 1,
219f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(test_cases),
220f08c3bdfSopenharmony_ci	.setup = setup,
221f08c3bdfSopenharmony_ci	.test = do_test,
222f08c3bdfSopenharmony_ci	.cleanup = cleanup,
223f08c3bdfSopenharmony_ci	.skip_filesystems = (const char *const []) {
224f08c3bdfSopenharmony_ci		"tmpfs",
225f08c3bdfSopenharmony_ci		"ramfs",
226f08c3bdfSopenharmony_ci		"nfs",
227f08c3bdfSopenharmony_ci		NULL
228f08c3bdfSopenharmony_ci	},
229f08c3bdfSopenharmony_ci};
230