Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Author: Aleksa Sarai <cyphar@cyphar.com>
  4 * Copyright (C) 2018-2019 SUSE LLC.
  5 */
  6
  7#define _GNU_SOURCE
 
  8#include <fcntl.h>
  9#include <sched.h>
 10#include <sys/stat.h>
 11#include <sys/types.h>
 12#include <sys/mount.h>
 13#include <stdlib.h>
 14#include <stdbool.h>
 15#include <string.h>
 16
 17#include "../kselftest.h"
 18#include "helpers.h"
 19
 20/*
 21 * O_LARGEFILE is set to 0 by glibc.
 22 * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
 23 */
 24#undef	O_LARGEFILE
 25#ifdef __aarch64__
 26#define	O_LARGEFILE 0x20000
 27#else
 28#define	O_LARGEFILE 0x8000
 29#endif
 30
 31struct open_how_ext {
 32	struct open_how inner;
 33	uint32_t extra1;
 34	char pad1[128];
 35	uint32_t extra2;
 36	char pad2[128];
 37	uint32_t extra3;
 38};
 39
 40struct struct_test {
 41	const char *name;
 42	struct open_how_ext arg;
 43	size_t size;
 44	int err;
 45};
 46
 47#define NUM_OPENAT2_STRUCT_TESTS 7
 48#define NUM_OPENAT2_STRUCT_VARIATIONS 13
 49
 50void test_openat2_struct(void)
 51{
 52	int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
 53
 54	struct struct_test tests[] = {
 55		/* Normal struct. */
 56		{ .name = "normal struct",
 57		  .arg.inner.flags = O_RDONLY,
 58		  .size = sizeof(struct open_how) },
 59		/* Bigger struct, with zeroed out end. */
 60		{ .name = "bigger struct (zeroed out)",
 61		  .arg.inner.flags = O_RDONLY,
 62		  .size = sizeof(struct open_how_ext) },
 63
 64		/* TODO: Once expanded, check zero-padding. */
 65
 66		/* Smaller than version-0 struct. */
 67		{ .name = "zero-sized 'struct'",
 68		  .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL },
 69		{ .name = "smaller-than-v0 struct",
 70		  .arg.inner.flags = O_RDONLY,
 71		  .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL },
 72
 73		/* Bigger struct, with non-zero trailing bytes. */
 74		{ .name = "bigger struct (non-zero data in first 'future field')",
 75		  .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef,
 76		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
 77		{ .name = "bigger struct (non-zero data in middle of 'future fields')",
 78		  .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe,
 79		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
 80		{ .name = "bigger struct (non-zero data at end of 'future fields')",
 81		  .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea,
 82		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
 83	};
 84
 85	BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS);
 86	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS);
 87
 88	for (int i = 0; i < ARRAY_LEN(tests); i++) {
 89		struct struct_test *test = &tests[i];
 90		struct open_how_ext how_ext = test->arg;
 91
 92		for (int j = 0; j < ARRAY_LEN(misalignments); j++) {
 93			int fd, misalign = misalignments[j];
 94			char *fdpath = NULL;
 95			bool failed;
 96			void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
 97
 98			void *copy = NULL, *how_copy = &how_ext;
 99
100			if (!openat2_supported) {
101				ksft_print_msg("openat2(2) unsupported\n");
102				resultfn = ksft_test_result_skip;
103				goto skip;
104			}
105
106			if (misalign) {
107				/*
108				 * Explicitly misalign the structure copying it with the given
109				 * (mis)alignment offset. The other data is set to be non-zero to
110				 * make sure that non-zero bytes outside the struct aren't checked
111				 *
112				 * This is effectively to check that is_zeroed_user() works.
113				 */
114				copy = malloc(misalign + sizeof(how_ext));
115				how_copy = copy + misalign;
116				memset(copy, 0xff, misalign);
117				memcpy(how_copy, &how_ext, sizeof(how_ext));
118			}
119
120			fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size);
121			if (test->err >= 0)
122				failed = (fd < 0);
123			else
124				failed = (fd != test->err);
125			if (fd >= 0) {
126				fdpath = fdreadlink(fd);
127				close(fd);
128			}
129
130			if (failed) {
131				resultfn = ksft_test_result_fail;
132
133				ksft_print_msg("openat2 unexpectedly returned ");
134				if (fdpath)
135					ksft_print_msg("%d['%s']\n", fd, fdpath);
136				else
137					ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
138			}
139
140skip:
141			if (test->err >= 0)
142				resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
143					 test->name, misalign);
144			else
145				resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
146					 test->name, misalign, test->err,
147					 strerror(-test->err));
148
149			free(copy);
150			free(fdpath);
151			fflush(stdout);
152		}
153	}
154}
155
156struct flag_test {
157	const char *name;
158	struct open_how how;
159	int err;
160};
161
162#define NUM_OPENAT2_FLAG_TESTS 25
163
164void test_openat2_flags(void)
165{
166	struct flag_test tests[] = {
167		/* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
168		{ .name = "incompatible flags (O_TMPFILE | O_PATH)",
169		  .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL },
170		{ .name = "incompatible flags (O_TMPFILE | O_CREAT)",
171		  .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL },
172
173		/* O_PATH only permits certain other flags to be set ... */
174		{ .name = "compatible flags (O_PATH | O_CLOEXEC)",
175		  .how.flags = O_PATH | O_CLOEXEC },
176		{ .name = "compatible flags (O_PATH | O_DIRECTORY)",
177		  .how.flags = O_PATH | O_DIRECTORY },
178		{ .name = "compatible flags (O_PATH | O_NOFOLLOW)",
179		  .how.flags = O_PATH | O_NOFOLLOW },
180		/* ... and others are absolutely not permitted. */
181		{ .name = "incompatible flags (O_PATH | O_RDWR)",
182		  .how.flags = O_PATH | O_RDWR, .err = -EINVAL },
183		{ .name = "incompatible flags (O_PATH | O_CREAT)",
184		  .how.flags = O_PATH | O_CREAT, .err = -EINVAL },
185		{ .name = "incompatible flags (O_PATH | O_EXCL)",
186		  .how.flags = O_PATH | O_EXCL, .err = -EINVAL },
187		{ .name = "incompatible flags (O_PATH | O_NOCTTY)",
188		  .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL },
189		{ .name = "incompatible flags (O_PATH | O_DIRECT)",
190		  .how.flags = O_PATH | O_DIRECT, .err = -EINVAL },
191		{ .name = "incompatible flags (O_PATH | O_LARGEFILE)",
192		  .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL },
193
194		/* ->mode must only be set with O_{CREAT,TMPFILE}. */
195		{ .name = "non-zero how.mode and O_RDONLY",
196		  .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL },
197		{ .name = "non-zero how.mode and O_PATH",
198		  .how.flags = O_PATH,   .how.mode = 0600, .err = -EINVAL },
199		{ .name = "valid how.mode and O_CREAT",
200		  .how.flags = O_CREAT,  .how.mode = 0600 },
201		{ .name = "valid how.mode and O_TMPFILE",
202		  .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
203		/* ->mode must only contain 0777 bits. */
204		{ .name = "invalid how.mode and O_CREAT",
205		  .how.flags = O_CREAT,
206		  .how.mode = 0xFFFF, .err = -EINVAL },
207		{ .name = "invalid (very large) how.mode and O_CREAT",
208		  .how.flags = O_CREAT,
209		  .how.mode = 0xC000000000000000ULL, .err = -EINVAL },
210		{ .name = "invalid how.mode and O_TMPFILE",
211		  .how.flags = O_TMPFILE | O_RDWR,
212		  .how.mode = 0x1337, .err = -EINVAL },
213		{ .name = "invalid (very large) how.mode and O_TMPFILE",
214		  .how.flags = O_TMPFILE | O_RDWR,
215		  .how.mode = 0x0000A00000000000ULL, .err = -EINVAL },
216
217		/* ->resolve flags must not conflict. */
218		{ .name = "incompatible resolve flags (BENEATH | IN_ROOT)",
219		  .how.flags = O_RDONLY,
220		  .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
221		  .err = -EINVAL },
222
223		/* ->resolve must only contain RESOLVE_* flags. */
224		{ .name = "invalid how.resolve and O_RDONLY",
225		  .how.flags = O_RDONLY,
226		  .how.resolve = 0x1337, .err = -EINVAL },
227		{ .name = "invalid how.resolve and O_CREAT",
228		  .how.flags = O_CREAT,
229		  .how.resolve = 0x1337, .err = -EINVAL },
230		{ .name = "invalid how.resolve and O_TMPFILE",
231		  .how.flags = O_TMPFILE | O_RDWR,
232		  .how.resolve = 0x1337, .err = -EINVAL },
233		{ .name = "invalid how.resolve and O_PATH",
234		  .how.flags = O_PATH,
235		  .how.resolve = 0x1337, .err = -EINVAL },
236
237		/* currently unknown upper 32 bit rejected. */
238		{ .name = "currently unknown bit (1 << 63)",
239		  .how.flags = O_RDONLY | (1ULL << 63),
240		  .how.resolve = 0, .err = -EINVAL },
241	};
242
243	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
244
245	for (int i = 0; i < ARRAY_LEN(tests); i++) {
246		int fd, fdflags = -1;
247		char *path, *fdpath = NULL;
248		bool failed = false;
249		struct flag_test *test = &tests[i];
250		void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
251
252		if (!openat2_supported) {
253			ksft_print_msg("openat2(2) unsupported\n");
254			resultfn = ksft_test_result_skip;
255			goto skip;
256		}
257
258		path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
259		unlink(path);
260
261		fd = sys_openat2(AT_FDCWD, path, &test->how);
262		if (fd < 0 && fd == -EOPNOTSUPP) {
263			/*
264			 * Skip the testcase if it failed because not supported
265			 * by FS. (e.g. a valid O_TMPFILE combination on NFS)
266			 */
267			ksft_test_result_skip("openat2 with %s fails with %d (%s)\n",
268					      test->name, fd, strerror(-fd));
269			goto next;
270		}
271
272		if (test->err >= 0)
273			failed = (fd < 0);
274		else
275			failed = (fd != test->err);
276		if (fd >= 0) {
277			int otherflags;
278
279			fdpath = fdreadlink(fd);
280			fdflags = fcntl(fd, F_GETFL);
281			otherflags = fcntl(fd, F_GETFD);
282			close(fd);
283
284			E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
285			E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
286
287			/* O_CLOEXEC isn't shown in F_GETFL. */
288			if (otherflags & FD_CLOEXEC)
289				fdflags |= O_CLOEXEC;
290			/* O_CREAT is hidden from F_GETFL. */
291			if (test->how.flags & O_CREAT)
292				fdflags |= O_CREAT;
293			if (!(test->how.flags & O_LARGEFILE))
294				fdflags &= ~O_LARGEFILE;
295			failed |= (fdflags != test->how.flags);
296		}
297
298		if (failed) {
299			resultfn = ksft_test_result_fail;
300
301			ksft_print_msg("openat2 unexpectedly returned ");
302			if (fdpath)
303				ksft_print_msg("%d['%s'] with %X (!= %X)\n",
304					       fd, fdpath, fdflags,
305					       test->how.flags);
306			else
307				ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
308		}
309
310skip:
311		if (test->err >= 0)
312			resultfn("openat2 with %s succeeds\n", test->name);
313		else
314			resultfn("openat2 with %s fails with %d (%s)\n",
315				 test->name, test->err, strerror(-test->err));
316next:
317		free(fdpath);
318		fflush(stdout);
319	}
320}
321
322#define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
323		   NUM_OPENAT2_FLAG_TESTS)
324
325int main(int argc, char **argv)
326{
327	ksft_print_header();
328	ksft_set_plan(NUM_TESTS);
329
330	test_openat2_struct();
331	test_openat2_flags();
332
333	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
334		ksft_exit_fail();
335	else
336		ksft_exit_pass();
337}
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Author: Aleksa Sarai <cyphar@cyphar.com>
  4 * Copyright (C) 2018-2019 SUSE LLC.
  5 */
  6
  7#define _GNU_SOURCE
  8#define __SANE_USERSPACE_TYPES__ // Use ll64
  9#include <fcntl.h>
 10#include <sched.h>
 11#include <sys/stat.h>
 12#include <sys/types.h>
 13#include <sys/mount.h>
 14#include <stdlib.h>
 15#include <stdbool.h>
 16#include <string.h>
 17
 18#include "../kselftest.h"
 19#include "helpers.h"
 20
 21/*
 22 * O_LARGEFILE is set to 0 by glibc.
 23 * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
 24 */
 25#undef	O_LARGEFILE
 26#ifdef __aarch64__
 27#define	O_LARGEFILE 0x20000
 28#else
 29#define	O_LARGEFILE 0x8000
 30#endif
 31
 32struct open_how_ext {
 33	struct open_how inner;
 34	uint32_t extra1;
 35	char pad1[128];
 36	uint32_t extra2;
 37	char pad2[128];
 38	uint32_t extra3;
 39};
 40
 41struct struct_test {
 42	const char *name;
 43	struct open_how_ext arg;
 44	size_t size;
 45	int err;
 46};
 47
 48#define NUM_OPENAT2_STRUCT_TESTS 7
 49#define NUM_OPENAT2_STRUCT_VARIATIONS 13
 50
 51void test_openat2_struct(void)
 52{
 53	int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
 54
 55	struct struct_test tests[] = {
 56		/* Normal struct. */
 57		{ .name = "normal struct",
 58		  .arg.inner.flags = O_RDONLY,
 59		  .size = sizeof(struct open_how) },
 60		/* Bigger struct, with zeroed out end. */
 61		{ .name = "bigger struct (zeroed out)",
 62		  .arg.inner.flags = O_RDONLY,
 63		  .size = sizeof(struct open_how_ext) },
 64
 65		/* TODO: Once expanded, check zero-padding. */
 66
 67		/* Smaller than version-0 struct. */
 68		{ .name = "zero-sized 'struct'",
 69		  .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL },
 70		{ .name = "smaller-than-v0 struct",
 71		  .arg.inner.flags = O_RDONLY,
 72		  .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL },
 73
 74		/* Bigger struct, with non-zero trailing bytes. */
 75		{ .name = "bigger struct (non-zero data in first 'future field')",
 76		  .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef,
 77		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
 78		{ .name = "bigger struct (non-zero data in middle of 'future fields')",
 79		  .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe,
 80		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
 81		{ .name = "bigger struct (non-zero data at end of 'future fields')",
 82		  .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea,
 83		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
 84	};
 85
 86	BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS);
 87	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS);
 88
 89	for (int i = 0; i < ARRAY_LEN(tests); i++) {
 90		struct struct_test *test = &tests[i];
 91		struct open_how_ext how_ext = test->arg;
 92
 93		for (int j = 0; j < ARRAY_LEN(misalignments); j++) {
 94			int fd, misalign = misalignments[j];
 95			char *fdpath = NULL;
 96			bool failed;
 97			void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
 98
 99			void *copy = NULL, *how_copy = &how_ext;
100
101			if (!openat2_supported) {
102				ksft_print_msg("openat2(2) unsupported\n");
103				resultfn = ksft_test_result_skip;
104				goto skip;
105			}
106
107			if (misalign) {
108				/*
109				 * Explicitly misalign the structure copying it with the given
110				 * (mis)alignment offset. The other data is set to be non-zero to
111				 * make sure that non-zero bytes outside the struct aren't checked
112				 *
113				 * This is effectively to check that is_zeroed_user() works.
114				 */
115				copy = malloc(misalign + sizeof(how_ext));
116				how_copy = copy + misalign;
117				memset(copy, 0xff, misalign);
118				memcpy(how_copy, &how_ext, sizeof(how_ext));
119			}
120
121			fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size);
122			if (test->err >= 0)
123				failed = (fd < 0);
124			else
125				failed = (fd != test->err);
126			if (fd >= 0) {
127				fdpath = fdreadlink(fd);
128				close(fd);
129			}
130
131			if (failed) {
132				resultfn = ksft_test_result_fail;
133
134				ksft_print_msg("openat2 unexpectedly returned ");
135				if (fdpath)
136					ksft_print_msg("%d['%s']\n", fd, fdpath);
137				else
138					ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
139			}
140
141skip:
142			if (test->err >= 0)
143				resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
144					 test->name, misalign);
145			else
146				resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
147					 test->name, misalign, test->err,
148					 strerror(-test->err));
149
150			free(copy);
151			free(fdpath);
152			fflush(stdout);
153		}
154	}
155}
156
157struct flag_test {
158	const char *name;
159	struct open_how how;
160	int err;
161};
162
163#define NUM_OPENAT2_FLAG_TESTS 25
164
165void test_openat2_flags(void)
166{
167	struct flag_test tests[] = {
168		/* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
169		{ .name = "incompatible flags (O_TMPFILE | O_PATH)",
170		  .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL },
171		{ .name = "incompatible flags (O_TMPFILE | O_CREAT)",
172		  .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL },
173
174		/* O_PATH only permits certain other flags to be set ... */
175		{ .name = "compatible flags (O_PATH | O_CLOEXEC)",
176		  .how.flags = O_PATH | O_CLOEXEC },
177		{ .name = "compatible flags (O_PATH | O_DIRECTORY)",
178		  .how.flags = O_PATH | O_DIRECTORY },
179		{ .name = "compatible flags (O_PATH | O_NOFOLLOW)",
180		  .how.flags = O_PATH | O_NOFOLLOW },
181		/* ... and others are absolutely not permitted. */
182		{ .name = "incompatible flags (O_PATH | O_RDWR)",
183		  .how.flags = O_PATH | O_RDWR, .err = -EINVAL },
184		{ .name = "incompatible flags (O_PATH | O_CREAT)",
185		  .how.flags = O_PATH | O_CREAT, .err = -EINVAL },
186		{ .name = "incompatible flags (O_PATH | O_EXCL)",
187		  .how.flags = O_PATH | O_EXCL, .err = -EINVAL },
188		{ .name = "incompatible flags (O_PATH | O_NOCTTY)",
189		  .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL },
190		{ .name = "incompatible flags (O_PATH | O_DIRECT)",
191		  .how.flags = O_PATH | O_DIRECT, .err = -EINVAL },
192		{ .name = "incompatible flags (O_PATH | O_LARGEFILE)",
193		  .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL },
194
195		/* ->mode must only be set with O_{CREAT,TMPFILE}. */
196		{ .name = "non-zero how.mode and O_RDONLY",
197		  .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL },
198		{ .name = "non-zero how.mode and O_PATH",
199		  .how.flags = O_PATH,   .how.mode = 0600, .err = -EINVAL },
200		{ .name = "valid how.mode and O_CREAT",
201		  .how.flags = O_CREAT,  .how.mode = 0600 },
202		{ .name = "valid how.mode and O_TMPFILE",
203		  .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
204		/* ->mode must only contain 0777 bits. */
205		{ .name = "invalid how.mode and O_CREAT",
206		  .how.flags = O_CREAT,
207		  .how.mode = 0xFFFF, .err = -EINVAL },
208		{ .name = "invalid (very large) how.mode and O_CREAT",
209		  .how.flags = O_CREAT,
210		  .how.mode = 0xC000000000000000ULL, .err = -EINVAL },
211		{ .name = "invalid how.mode and O_TMPFILE",
212		  .how.flags = O_TMPFILE | O_RDWR,
213		  .how.mode = 0x1337, .err = -EINVAL },
214		{ .name = "invalid (very large) how.mode and O_TMPFILE",
215		  .how.flags = O_TMPFILE | O_RDWR,
216		  .how.mode = 0x0000A00000000000ULL, .err = -EINVAL },
217
218		/* ->resolve flags must not conflict. */
219		{ .name = "incompatible resolve flags (BENEATH | IN_ROOT)",
220		  .how.flags = O_RDONLY,
221		  .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
222		  .err = -EINVAL },
223
224		/* ->resolve must only contain RESOLVE_* flags. */
225		{ .name = "invalid how.resolve and O_RDONLY",
226		  .how.flags = O_RDONLY,
227		  .how.resolve = 0x1337, .err = -EINVAL },
228		{ .name = "invalid how.resolve and O_CREAT",
229		  .how.flags = O_CREAT,
230		  .how.resolve = 0x1337, .err = -EINVAL },
231		{ .name = "invalid how.resolve and O_TMPFILE",
232		  .how.flags = O_TMPFILE | O_RDWR,
233		  .how.resolve = 0x1337, .err = -EINVAL },
234		{ .name = "invalid how.resolve and O_PATH",
235		  .how.flags = O_PATH,
236		  .how.resolve = 0x1337, .err = -EINVAL },
237
238		/* currently unknown upper 32 bit rejected. */
239		{ .name = "currently unknown bit (1 << 63)",
240		  .how.flags = O_RDONLY | (1ULL << 63),
241		  .how.resolve = 0, .err = -EINVAL },
242	};
243
244	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
245
246	for (int i = 0; i < ARRAY_LEN(tests); i++) {
247		int fd, fdflags = -1;
248		char *path, *fdpath = NULL;
249		bool failed = false;
250		struct flag_test *test = &tests[i];
251		void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
252
253		if (!openat2_supported) {
254			ksft_print_msg("openat2(2) unsupported\n");
255			resultfn = ksft_test_result_skip;
256			goto skip;
257		}
258
259		path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
260		unlink(path);
261
262		fd = sys_openat2(AT_FDCWD, path, &test->how);
263		if (fd < 0 && fd == -EOPNOTSUPP) {
264			/*
265			 * Skip the testcase if it failed because not supported
266			 * by FS. (e.g. a valid O_TMPFILE combination on NFS)
267			 */
268			ksft_test_result_skip("openat2 with %s fails with %d (%s)\n",
269					      test->name, fd, strerror(-fd));
270			goto next;
271		}
272
273		if (test->err >= 0)
274			failed = (fd < 0);
275		else
276			failed = (fd != test->err);
277		if (fd >= 0) {
278			int otherflags;
279
280			fdpath = fdreadlink(fd);
281			fdflags = fcntl(fd, F_GETFL);
282			otherflags = fcntl(fd, F_GETFD);
283			close(fd);
284
285			E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
286			E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
287
288			/* O_CLOEXEC isn't shown in F_GETFL. */
289			if (otherflags & FD_CLOEXEC)
290				fdflags |= O_CLOEXEC;
291			/* O_CREAT is hidden from F_GETFL. */
292			if (test->how.flags & O_CREAT)
293				fdflags |= O_CREAT;
294			if (!(test->how.flags & O_LARGEFILE))
295				fdflags &= ~O_LARGEFILE;
296			failed |= (fdflags != test->how.flags);
297		}
298
299		if (failed) {
300			resultfn = ksft_test_result_fail;
301
302			ksft_print_msg("openat2 unexpectedly returned ");
303			if (fdpath)
304				ksft_print_msg("%d['%s'] with %X (!= %llX)\n",
305					       fd, fdpath, fdflags,
306					       test->how.flags);
307			else
308				ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
309		}
310
311skip:
312		if (test->err >= 0)
313			resultfn("openat2 with %s succeeds\n", test->name);
314		else
315			resultfn("openat2 with %s fails with %d (%s)\n",
316				 test->name, test->err, strerror(-test->err));
317next:
318		free(fdpath);
319		fflush(stdout);
320	}
321}
322
323#define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
324		   NUM_OPENAT2_FLAG_TESTS)
325
326int main(int argc, char **argv)
327{
328	ksft_print_header();
329	ksft_set_plan(NUM_TESTS);
330
331	test_openat2_struct();
332	test_openat2_flags();
333
334	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
335		ksft_exit_fail();
336	else
337		ksft_exit_pass();
338}