[LTP] [PATCH V2] syscalls/openat2: New tests

Cyril Hrubis chrubis@suse.cz
Fri Mar 6 15:46:03 CET 2020


Hi!
> +void openat2_supported_by_kernel(void)
> +{
> +	if ((tst_kvercmp(5, 6, 0)) < 0) {
> +		/* Check if the syscall is backported on an older kernel */
> +		TEST(syscall(__NR_openat2, -1, NULL, NULL, 0));
> +		if (TST_RET == -1 && TST_ERR == ENOSYS)
> +			tst_brk(TCONF, "Test not supported on kernel version < v5.2");

The test is for 5.6 but the message says 5.2, so which one is correct?

> +	}
> +}
> +
> +#endif /* OPENAT2_H */
> diff --git a/runtest/syscalls b/runtest/syscalls
> index 14df8d34338e..6a571408b005 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -845,6 +845,10 @@ openat01 openat01
>  openat02 openat02
>  openat03 openat03
>  
> +openat201 openat201
> +openat202 openat202
> +openat203 openat203
> +
>  open_tree01 open_tree01
>  open_tree02 open_tree02
>  
> diff --git a/testcases/kernel/syscalls/openat2/.gitignore b/testcases/kernel/syscalls/openat2/.gitignore
> new file mode 100644
> index 000000000000..5a0843a85229
> --- /dev/null
> +++ b/testcases/kernel/syscalls/openat2/.gitignore
> @@ -0,0 +1,3 @@
> +openat201
> +openat202
> +openat203
> diff --git a/testcases/kernel/syscalls/openat2/Makefile b/testcases/kernel/syscalls/openat2/Makefile
> new file mode 100644
> index 000000000000..18896b6f28c0
> --- /dev/null
> +++ b/testcases/kernel/syscalls/openat2/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +top_srcdir		?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/openat2/openat201.c b/testcases/kernel/syscalls/openat2/openat201.c
> new file mode 100644
> index 000000000000..e2c2456769f4
> --- /dev/null
> +++ b/testcases/kernel/syscalls/openat2/openat201.c
> @@ -0,0 +1,98 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2020 Viresh Kumar <viresh.kumar@linaro.org>
> + *
> + * Basic openat2() test.
> + */
> +#include "tst_test.h"
> +#include "lapi/openat2.h"
> +
> +#define TEST_FILE "test_file"
> +#define TEST_DIR "test_dir"
> +
> +static struct open_how *how;
> +static struct open_how_pad *phow;
> +
> +static int dir_fd = -1, fd_atcwd = AT_FDCWD;
> +
> +static struct tcase {
> +	int *dfd;
> +	const char *pathname;
> +	uint64_t flags;
> +	uint64_t mode;
> +	uint64_t resolve;
> +	struct open_how **how;
> +	size_t size;
> +} tcases[] = {
> +	{&dir_fd, TEST_FILE, O_RDWR, S_IRWXU, 0, &how, sizeof(*how)},
> +	{&dir_fd, TEST_FILE, O_RDONLY, S_IRUSR, 0, &how, sizeof(*how)},
> +	{&dir_fd, TEST_FILE, O_WRONLY, S_IWUSR, 0, &how, sizeof(*how)},
> +	{&dir_fd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_NO_XDEV, &how, sizeof(*how)},
> +	{&dir_fd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_NO_MAGICLINKS, &how, sizeof(*how)},
> +	{&dir_fd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_NO_SYMLINKS, &how, sizeof(*how)},
> +	{&dir_fd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_BENEATH, &how, sizeof(*how)},
> +	{&dir_fd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_IN_ROOT, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_RDWR, S_IRWXU, 0, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_RDONLY, S_IRUSR, 0, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_WRONLY, S_IWUSR, 0, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_NO_XDEV, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_NO_MAGICLINKS, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_NO_SYMLINKS, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_BENEATH, &how, sizeof(*how)},
> +	{&fd_atcwd, TEST_FILE, O_RDWR, S_IRWXU, RESOLVE_IN_ROOT, (struct open_how **)&phow, sizeof(*how) + 1},
                                                                                                         ^
								since the padd is 64bit this should be + 8
> +};
> +
> +static void cleanup(void)
> +{
> +	if (dir_fd != -1)
> +		SAFE_CLOSE(dir_fd);
> +}
> +
> +static void setup(void)
> +{
> +	openat2_supported_by_kernel();
> +
> +	phow->pad = 0x00;
> +	SAFE_MKDIR(TEST_DIR, 0700);
> +	dir_fd = SAFE_OPEN(TEST_DIR, O_DIRECTORY);
> +}
> +
> +static void run(unsigned int n)
> +{
> +	int fd;
> +	struct stat file_stat;
> +	struct tcase *tc = &tcases[n];
> +	struct open_how *myhow = *tc->how;
> +
> +	myhow->flags = tc->flags | O_CREAT;
> +	myhow->mode = tc->mode;
> +	myhow->resolve = tc->resolve;
> +
> +	TEST(fd = openat2(*tc->dfd, tc->pathname, myhow, tc->size));
> +	if (fd < 0) {
> +		tst_res(TFAIL | TTERRNO, "openat2() failed (%d)", n);
> +		return;
> +	}
> +
> +	SAFE_FSTAT(fd, &file_stat);
> +
> +	if (file_stat.st_size == 0)
> +		tst_res(TPASS, "openat2() passed (%d)", n);
> +	else
> +		tst_res(TFAIL, "fstat() didn't work as expected (%d)", n);
> +
> +	SAFE_CLOSE(fd);
> +}
> +
> +static struct tst_test test = {
> +	.tcnt = ARRAY_SIZE(tcases),
> +	.test = run,
> +	.setup = setup,
> +	.cleanup = cleanup,
> +	.needs_tmpdir = 1,
> +	.bufs = (struct tst_buffers []) {
> +		{&how, .size = sizeof(*how)},
> +		{&phow, .size = sizeof(*phow)},
> +		{},
> +	},
> +};
> diff --git a/testcases/kernel/syscalls/openat2/openat202.c b/testcases/kernel/syscalls/openat2/openat202.c
> new file mode 100644
> index 000000000000..504878277f7e
> --- /dev/null
> +++ b/testcases/kernel/syscalls/openat2/openat202.c
> @@ -0,0 +1,86 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2020 Viresh Kumar <viresh.kumar@linaro.org>
> + *
> + * openat2() tests with various resolve flags.
> + */
> +#include "tst_test.h"
> +#include "lapi/openat2.h"
> +
> +#define FOO_SYMLINK "foo_symlink"
> +
> +static struct open_how *how;
> +
> +static struct tcase {
> +	const char *name;
> +	const char *pathname;
> +	uint64_t resolve;
> +	int exp_errno;
> +} tcases[] = {
> +	/* Success cases */
> +	{"open /proc/version", "/proc/version", 0, 0},
> +	{"open magiclinks", "/proc/self/exe", 0, 0},
> +	{"open symlinks", FOO_SYMLINK, 0, 0},

Wouldn't it be easier if we added these to the first test and keep this
one failures only?

> +	/* Failure cases */
> +	{"resolve-no-xdev", "/proc/version", RESOLVE_NO_XDEV, EXDEV},
> +	{"resolve-no-magiclinks", "/proc/self/exe", RESOLVE_NO_MAGICLINKS, ELOOP},
> +	{"resolve-no-symlinks", FOO_SYMLINK, RESOLVE_NO_SYMLINKS, ELOOP},
> +	{"resolve-beneath", "/proc/version", RESOLVE_BENEATH, EXDEV},
> +	{"resolve-no-in-root", "/proc/version", RESOLVE_IN_ROOT, ENOENT},

I guess that given that we can also add "..", RESOLVE_BENEATH case as
well, since .. would escape the AT_FDCWD, right?

> +};
> +
> +static void setup(void)
> +{
> +	openat2_supported_by_kernel();
> +	SAFE_SYMLINK("/proc/version", FOO_SYMLINK);
> +}
> +
> +static void run(unsigned int n)
> +{
> +	struct tcase *tc = &tcases[n];
> +
> +	how->flags = O_RDONLY | O_CREAT;
> +	how->mode = S_IRUSR;
> +	how->resolve = tc->resolve;
> +
> +	TEST(openat2(AT_FDCWD, tc->pathname, how, sizeof(*how)));
> +
> +	if (!tc->exp_errno) {
> +		if (TST_RET < 0) {
> +			tst_res(TFAIL | TTERRNO, "%s: openat2() failed",
> +				tc->name);
> +			return;
> +		}
> +
> +		SAFE_CLOSE(TST_RET);
> +		tst_res(TPASS, "%s: openat2() passed", tc->name);
> +	} else {
> +		if (TST_RET >= 0) {
> +			SAFE_CLOSE(TST_RET);
> +			tst_res(TFAIL, "%s: openat2() passed unexpectedly",
> +				tc->name);
> +			return;
> +		}
> +
> +		if (tc->exp_errno != TST_ERR) {
> +			tst_res(TFAIL | TTERRNO, "%s: openat2() should fail with %s",
> +				tc->name, tst_strerrno(tc->exp_errno));
> +			return;
> +		}
> +
> +		tst_res(TPASS | TTERRNO, "%s: openat2() failed as expected",
> +			tc->name);
> +	}
> +}
> +
> +static struct tst_test test = {
> +	.tcnt = ARRAY_SIZE(tcases),
> +	.test = run,
> +	.setup = setup,
> +	.needs_tmpdir = 1,
> +	.bufs = (struct tst_buffers []) {
> +		{&how, .size = sizeof(*how)},
> +		{},
> +	}
> +};
> diff --git a/testcases/kernel/syscalls/openat2/openat203.c b/testcases/kernel/syscalls/openat2/openat203.c
> new file mode 100644
> index 000000000000..64a109644352
> --- /dev/null
> +++ b/testcases/kernel/syscalls/openat2/openat203.c
> @@ -0,0 +1,80 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2020 Viresh Kumar <viresh.kumar@linaro.org>
> + *
> + * Basic openat2() test to check various failures.
> + */
> +#include "tst_test.h"
> +#include "lapi/openat2.h"
> +
> +#define TEST_FILE "test_file"
> +
> +static struct open_how *how;
> +static struct open_how_pad *phow;
> +
> +static struct tcase {
> +	const char *name;
> +	int dfd;
> +	const char *pathname;
> +	uint64_t flags;
> +	uint64_t mode;
> +	uint64_t resolve;
> +	struct open_how **how;
> +	size_t size;
> +	int exp_errno;
> +} tcases[] = {
> +	{"invalid-dfd", -1, TEST_FILE, O_RDWR | O_CREAT, S_IRWXU, 0, &how, sizeof(*how), EBADF},
> +	{"invalid-pathname", AT_FDCWD, NULL, O_RDONLY | O_CREAT, S_IRUSR, 0, &how, sizeof(*how), EFAULT},
> +	{"invalid-flags", AT_FDCWD, TEST_FILE, O_RDONLY, S_IWUSR, 0, &how, sizeof(*how), EINVAL},
> +	{"invalid-mode", AT_FDCWD, TEST_FILE, O_RDWR | O_CREAT, -1, 0, &how, sizeof(*how), EINVAL},
> +	{"invalid-resolve", AT_FDCWD, TEST_FILE, O_RDWR | O_CREAT, S_IRWXU, -1, &how, sizeof(*how), EINVAL},
> +	{"invalid-size-zero", AT_FDCWD, TEST_FILE, O_RDWR | O_CREAT, S_IRWXU, 0, &how, 0, EINVAL},
> +	{"invalid-size-small", AT_FDCWD, TEST_FILE, O_RDWR | O_CREAT, S_IRWXU, 0, &how, sizeof(*how) - 1, EINVAL},
> +	{"invalid-size-big", AT_FDCWD, TEST_FILE, O_RDWR | O_CREAT, S_IRWXU, 0, &how, sizeof(*how) + 1, EFAULT},
> +	{"invalid-size-big-with-pad", AT_FDCWD, TEST_FILE, O_RDWR | O_CREAT, S_IRWXU, 0, (struct open_how **)&phow, sizeof(*how) + 1, E2BIG},

Here as well +8.

> +};
> +
> +static void setup(void)
> +{
> +	openat2_supported_by_kernel();
> +	phow->pad = 0xDEAD;
> +}
> +
> +static void run(unsigned int n)
> +{
> +	struct tcase *tc = &tcases[n];
> +	struct open_how *myhow = *tc->how;
> +
> +	myhow->flags = tc->flags;
> +	myhow->mode = tc->mode;
> +	myhow->resolve = tc->resolve;
> +
> +	TEST(openat2(tc->dfd, tc->pathname, myhow, tc->size));
> +
> +	if (TST_RET >= 0) {
> +		SAFE_CLOSE(TST_RET);
> +		tst_res(TFAIL, "%s: openat2() passed unexpectedly",
> +			tc->name);
> +		return;
> +	}
> +
> +	if (tc->exp_errno != TST_ERR) {
> +		tst_res(TFAIL | TTERRNO, "%s: openat2() should fail with %s",
> +			tc->name, tst_strerrno(tc->exp_errno));
> +		return;
> +	}
> +
> +	tst_res(TPASS | TTERRNO, "%s: openat2() failed as expected", tc->name);
> +}
> +
> +static struct tst_test test = {
> +	.tcnt = ARRAY_SIZE(tcases),
> +	.test = run,
> +	.setup = setup,
> +	.needs_tmpdir = 1,
> +	.bufs = (struct tst_buffers []) {
> +		{&how, .size = sizeof(*how)},
> +		{&phow, .size = sizeof(*phow)},
> +		{},
> +	}
> +};

Otherwise it looks good.

-- 
Cyril Hrubis
chrubis@suse.cz


More information about the ltp mailing list