1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd
4 *          Author(s): Takahiro Yasui <takahiro.yasui.mp@hitachi.com>,
5 *		       Yumiko Sugita <yumiko.sugita.yf@hitachi.com>,
6 *		       Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp>
7 * Copyright (c) 2016 Linux Test Project
8 */
9
10#include <errno.h>
11#include <mqueue.h>
12#include <pwd.h>
13
14#include "tst_test.h"
15#include "tst_safe_file_ops.h"
16#include "tst_safe_posix_ipc.h"
17
18#define QUEUE_NAME	"/test_mqueue"
19#define QUEUE_INIT	"/init_mqueue"
20
21static uid_t euid;
22static struct passwd *pw;
23static char *qname;
24static struct rlimit rlim;
25
26static mqd_t fd, fd2;
27static mqd_t fd3 = -1;
28static int max_queues;
29
30struct test_case {
31	const char *desc;
32	char *qname;
33	int oflag;
34	struct mq_attr *rq;
35	int ret;
36	int err;
37	void (*setup)(void);
38	void (*cleanup)(void);
39};
40
41#define PROC_MAX_QUEUES "/proc/sys/fs/mqueue/queues_max"
42
43static void create_queue(void);
44static void unlink_queue(void);
45static void set_rlimit(void);
46static void restore_rlimit(void);
47static void set_max_queues(void);
48static void restore_max_queues(void);
49
50static struct test_case tcase[] = {
51	{
52		.desc = "NORMAL",
53		.qname = QUEUE_NAME,
54		.oflag = O_CREAT,
55		.rq = &(struct mq_attr){.mq_maxmsg = 20, .mq_msgsize = 16384},
56		.ret = 0,
57		.err = 0,
58	},
59	{
60		.desc = "NORMAL",
61		.qname = QUEUE_NAME,
62		.oflag = O_CREAT,
63		.ret = 0,
64		.err = 0,
65	},
66	{
67		.desc = "NORMAL",
68		.qname = "/caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
69			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
70			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
71			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
72			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
73			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
74			"aaaaaaaaaaaaaaa",
75		.oflag = O_CREAT,
76		.ret = 0,
77		.err = 0,
78	},
79	{
80		.desc = "NORMAL",
81		.qname = "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
82			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
83			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
84			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
85			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
86			"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
87			"aaaaaaaaaaaaaaaa",
88		.oflag = O_CREAT,
89		.ret = -1,
90		.err = ENAMETOOLONG,
91	},
92
93	{
94		.desc = "NORMAL",
95		.qname = "",
96		.oflag = O_CREAT,
97		.ret = -1,
98		.err = EINVAL,
99	},
100	{
101		.desc = "NORMAL",
102		.qname = QUEUE_NAME,
103		.ret = -1,
104		.err = EACCES,
105		.setup = create_queue,
106		.cleanup = unlink_queue,
107	},
108	{
109		.desc = "NORMAL",
110		.qname = QUEUE_NAME,
111		.oflag = O_CREAT | O_EXCL,
112		.ret = -1,
113		.err = EEXIST,
114		.setup = create_queue,
115		.cleanup = unlink_queue,
116	},
117	{
118		.desc = "NO_FILE",
119		.qname = QUEUE_NAME,
120		.oflag = O_CREAT,
121		.ret = -1,
122		.err = EMFILE,
123		.setup = set_rlimit,
124		.cleanup = restore_rlimit,
125	},
126	{
127		.desc = "NORMAL",
128		.qname = "/notexist",
129		.oflag = 0,
130		.ret = -1,
131		.err = ENOENT,
132	},
133	{
134		.desc = "NO_SPACE",
135		.qname = QUEUE_NAME,
136		.oflag = O_CREAT,
137		.ret = -1,
138		.err = ENOSPC,
139		.setup = set_max_queues,
140		.cleanup = restore_max_queues,
141	}
142};
143
144static void create_queue(void)
145{
146	fd2 = SAFE_MQ_OPEN(QUEUE_NAME, O_CREAT | O_EXCL | O_RDWR, S_IRWXU, NULL);
147
148	SAFE_SETEUID(pw->pw_uid);
149}
150
151static void unlink_queue(void)
152{
153	SAFE_SETEUID(euid);
154	if (fd2 > 0)
155		SAFE_CLOSE(fd2);
156
157	if (mq_unlink(QUEUE_NAME))
158		tst_brk(TBROK | TERRNO, "mq_close(" QUEUE_NAME ") failed");
159}
160
161
162static void set_max_queues(void)
163{
164	SAFE_FILE_SCANF(PROC_MAX_QUEUES, "%d", &max_queues);
165	SAFE_FILE_PRINTF(PROC_MAX_QUEUES, "%d", 1);
166
167	SAFE_SETEUID(pw->pw_uid);
168}
169
170static void restore_max_queues(void)
171{
172	SAFE_SETEUID(euid);
173
174	SAFE_FILE_PRINTF(PROC_MAX_QUEUES, "%d", max_queues);
175}
176
177static void set_rlimit(void)
178{
179	if (rlim.rlim_cur > 0) {
180		struct rlimit r;
181		r.rlim_cur = 0;
182		r.rlim_max = rlim.rlim_max;
183		SAFE_SETRLIMIT(RLIMIT_NOFILE, &r);
184	}
185}
186
187static void restore_rlimit(void)
188{
189	SAFE_SETRLIMIT(RLIMIT_NOFILE, &rlim);
190}
191
192static void setup(void)
193{
194	euid = geteuid();
195	pw = SAFE_GETPWNAM("nobody");
196	SAFE_GETRLIMIT(RLIMIT_NOFILE, &rlim);
197
198	fd3 = SAFE_MQ_OPEN(QUEUE_INIT, O_CREAT | O_EXCL | O_RDWR, S_IRWXU, NULL);
199}
200
201static void cleanup(void)
202{
203	if (fd > 0)
204		mq_close(fd);
205
206	if (fd2 > 0)
207		mq_close(fd2);
208
209	if (fd3 > 0 && mq_close(fd3))
210		tst_res(TWARN | TERRNO, "mq_close(%s) failed", QUEUE_INIT);
211
212	if (mq_unlink(QUEUE_INIT))
213		tst_res(TWARN | TERRNO, "mq_unlink(%s) failed", QUEUE_INIT);
214
215	mq_unlink(qname);
216}
217
218static void do_test(unsigned int i)
219{
220	struct test_case *tc = &tcase[i];
221	struct mq_attr oldattr;
222
223	qname = tc->qname;
224	fd = fd2 = -1;
225
226	tst_res(TINFO, "queue name \"%s\"", qname);
227
228	if (tc->setup)
229		tc->setup();
230
231	TEST(fd = mq_open(qname, tc->oflag, S_IRWXU, tc->rq));
232
233	if (fd > 0 && tc->rq) {
234		if (mq_getattr(fd, &oldattr) < 0) {
235			tst_res(TFAIL | TERRNO, "mq_getattr failed");
236			goto CLEANUP;
237		}
238
239		if (oldattr.mq_maxmsg != tc->rq->mq_maxmsg
240			|| oldattr.mq_msgsize != tc->rq->mq_msgsize) {
241			tst_res(TFAIL, "wrong mq_attr: "
242				"mq_maxmsg expected %ld return %ld, "
243				"mq_msgsize expected %ld return %ld",
244				tc->rq->mq_maxmsg, oldattr.mq_maxmsg, tc->rq->mq_msgsize,
245				oldattr.mq_msgsize);
246			goto CLEANUP;
247		}
248	}
249
250	if (tc->ret == 0) {
251		if (TST_RET < 0) {
252			tst_res(TFAIL | TTERRNO, "%s wrong return code: %ld",
253				tc->desc, TST_RET);
254		} else {
255			tst_res(TPASS | TTERRNO, "%s returned: %ld",
256				tc->desc, TST_RET);
257		}
258
259		goto CLEANUP;
260	}
261
262	if (TST_ERR != tc->err) {
263		tst_res(TFAIL | TTERRNO, "%s expected errno: %d",
264			tc->desc, TST_ERR);
265		goto CLEANUP;
266	}
267
268	if (TST_RET != tc->ret) {
269		tst_res(TFAIL | TTERRNO, "%s wrong return code: %ld",
270			tc->desc, TST_RET);
271	} else {
272		tst_res(TPASS | TTERRNO, "%s returned: %ld",
273			tc->desc, TST_RET);
274	}
275
276CLEANUP:
277	if (tc->cleanup)
278		tc->cleanup();
279
280	if (TST_RET != -1) {
281		if (fd > 0)
282			SAFE_CLOSE(fd);
283		mq_unlink(qname);
284	}
285}
286
287static struct tst_test test = {
288	.tcnt = ARRAY_SIZE(tcase),
289	.test = do_test,
290	.needs_root = 1,
291	.setup = setup,
292	.cleanup = cleanup,
293};
294