176 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * Copyright (c) Linux Test Project, 2014-2020
 | |
|  *
 | |
|  * errno tests for setns(2) - reassociate thread with a namespace
 | |
|  */
 | |
| #define _GNU_SOURCE
 | |
| #include <sys/stat.h>
 | |
| #include <sys/types.h>
 | |
| #include <errno.h>
 | |
| #include <sched.h>
 | |
| #include <pwd.h>
 | |
| #include <string.h>
 | |
| #include "config.h"
 | |
| #include "tst_test.h"
 | |
| #include "lapi/syscalls.h"
 | |
| #include "setns.h"
 | |
| 
 | |
| static const char nobody_uid[] = "nobody";
 | |
| static struct passwd *ltpuser;
 | |
| static int regular_fd;
 | |
| 
 | |
| struct testcase_t {
 | |
| 	const char *msg;
 | |
| 	int fd;
 | |
| 	int ns_type;
 | |
| 	int exp_ret;
 | |
| 	int exp_errno;
 | |
| 	int skip;
 | |
| 	void (*setup) (struct testcase_t *, int i);
 | |
| 	void (*cleanup) (struct testcase_t *);
 | |
| };
 | |
| 
 | |
| static void setup0(struct testcase_t *t, int i)
 | |
| {
 | |
| 	t->ns_type = ns_types[i];
 | |
| }
 | |
| 
 | |
| static void setup1(struct testcase_t *t, int i)
 | |
| {
 | |
| 	t->ns_type = ns_types[i];
 | |
| 	t->fd = regular_fd;
 | |
| }
 | |
| 
 | |
| static void setup2(struct testcase_t *t, int i)
 | |
| {
 | |
| 	t->fd = ns_fds[i];
 | |
| }
 | |
| 
 | |
| static void setup3(struct testcase_t *t, int i)
 | |
| {
 | |
| 	if (ns_total < 2) {
 | |
| 		t->skip = 1;
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	t->fd = ns_fds[i];
 | |
| 	t->ns_type = ns_types[(i+1) % ns_total];
 | |
| }
 | |
| 
 | |
| static void setup4(struct testcase_t *t, int i)
 | |
| {
 | |
| 	SAFE_SETEUID(ltpuser->pw_uid);
 | |
| 
 | |
| 	t->fd = ns_fds[i];
 | |
| 	t->ns_type = ns_types[i];
 | |
| }
 | |
| 
 | |
| static void cleanup4(LTP_ATTRIBUTE_UNUSED struct testcase_t *t)
 | |
| {
 | |
| 	SAFE_SETEUID(0);
 | |
| }
 | |
| 
 | |
| static struct testcase_t tcases[] = {
 | |
| 	{
 | |
| 		.msg = "invalid fd",
 | |
| 		.fd = -1,
 | |
| 		.exp_ret = -1,
 | |
| 		.exp_errno = EBADF,
 | |
| 		.setup = setup0,
 | |
| 	},
 | |
| 	{
 | |
| 		.msg = "regular file fd",
 | |
| 		.exp_ret = -1,
 | |
| 		.exp_errno = EINVAL,
 | |
| 		.setup = setup1,
 | |
| 	},
 | |
| 	{
 | |
| 		.msg = "invalid ns_type",
 | |
| 		.ns_type = -1,
 | |
| 		.exp_ret = -1,
 | |
| 		.exp_errno = EINVAL,
 | |
| 		.setup = setup2,
 | |
| 	},
 | |
| 	{
 | |
| 		.msg = "mismatch ns_type/fd",
 | |
| 		.exp_ret = -1,
 | |
| 		.exp_errno = EINVAL,
 | |
| 		.setup = setup3,
 | |
| 	},
 | |
| 	{
 | |
| 		.msg = "without CAP_SYS_ADMIN",
 | |
| 		.exp_ret = -1,
 | |
| 		.exp_errno = EPERM,
 | |
| 		.setup = setup4,
 | |
| 		.cleanup = cleanup4,
 | |
| 	}
 | |
| };
 | |
| 
 | |
| static void test_setns(unsigned int testno)
 | |
| {
 | |
| 	int ret, i;
 | |
| 	struct testcase_t *t = &tcases[testno];
 | |
| 
 | |
| 	for (i = 0; i < ns_total; i++) {
 | |
| 		if (t->setup)
 | |
| 			t->setup(t, i);
 | |
| 
 | |
| 		if (t->skip) {
 | |
| 			tst_res(TCONF, "skip %s", t->msg);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		tst_res(TINFO, "setns(%d, 0x%x)", t->fd, t->ns_type);
 | |
| 		ret = tst_syscall(__NR_setns, t->fd, t->ns_type);
 | |
| 		if (ret == t->exp_ret) {
 | |
| 			if (ret == -1 && errno == t->exp_errno) {
 | |
| 				tst_res(TPASS, "%s exp_errno=%d (%s)",
 | |
| 					t->msg,	t->exp_errno,
 | |
| 					tst_strerrno(t->exp_errno));
 | |
| 			} else {
 | |
| 				tst_res(TFAIL|TERRNO, "%s exp_errno=%d (%s)",
 | |
| 					t->msg, t->exp_errno,
 | |
| 					tst_strerrno(t->exp_errno));
 | |
| 			}
 | |
| 		} else {
 | |
| 			tst_res(TFAIL, "%s ret=%d expected=%d",
 | |
| 				t->msg,	ret, t->exp_ret);
 | |
| 		}
 | |
| 
 | |
| 		if (t->cleanup)
 | |
| 			t->cleanup(t);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void setup(void)
 | |
| {
 | |
| 	/* runtime check if syscall is supported */
 | |
| 	tst_syscall(__NR_setns, -1, 0);
 | |
| 
 | |
| 	init_available_ns();
 | |
| 	if (ns_total == 0)
 | |
| 		tst_brk(TCONF, "no ns types/proc entries");
 | |
| 
 | |
| 	ltpuser = SAFE_GETPWNAM(nobody_uid);
 | |
| 	regular_fd = SAFE_OPEN("dummy", O_RDWR|O_CREAT, 0600);
 | |
| 	SAFE_UNLINK("dummy");
 | |
| }
 | |
| 
 | |
| static void cleanup(void)
 | |
| {
 | |
| 	close_ns_fds();
 | |
| 	if (regular_fd)
 | |
| 		SAFE_CLOSE(regular_fd);
 | |
| }
 | |
| 
 | |
| static struct tst_test test = {
 | |
| 	.tcnt = ARRAY_SIZE(tcases),
 | |
| 	.test = test_setns,
 | |
| 	.setup = setup,
 | |
| 	.cleanup = cleanup,
 | |
| 	.needs_root = 1,
 | |
| 	.needs_tmpdir = 1,
 | |
| };
 | |
| 
 |