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