about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/tst-openat2.c
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2024-04-23 16:45:59 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2024-04-23 16:53:42 -0300
commit921efb18ef26cc0dcd024b083a7b22b6d2ccbc0d (patch)
tree98d9659a9195ad5277f66e20a9a449aca2f681dd /sysdeps/unix/sysv/linux/tst-openat2.c
parentf4724843ada64a51d66f65d3199fe431f9d4c254 (diff)
downloadglibc-921efb18ef26cc0dcd024b083a7b22b6d2ccbc0d.tar.gz
glibc-921efb18ef26cc0dcd024b083a7b22b6d2ccbc0d.tar.xz
glibc-921efb18ef26cc0dcd024b083a7b22b6d2ccbc0d.zip
linux: Add openat2 (BZ 31664) azanella/bz31664-openat2
It was added on Linux 5.6, as an extension of openat.  Different
than other open-like function, the kernel only provides the LFS
variant (so files larger than 4GB always succeed, as other functions
with offset larget than off_t).  Similar to other open function,
the new symbol is a cancellable entrypoint.

The testcase added only stress some of the syscall provided
functionalities and it is based on existent kernel selftest.  Since
the prototype does not use variadic arguments, there is no need to
add fortify wrapper to catch wrong usages.

Checked on x86_64-linux-gnu.
Diffstat (limited to 'sysdeps/unix/sysv/linux/tst-openat2.c')
-rw-r--r--sysdeps/unix/sysv/linux/tst-openat2.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/tst-openat2.c b/sysdeps/unix/sysv/linux/tst-openat2.c
new file mode 100644
index 0000000000..6936b9fcef
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-openat2.c
@@ -0,0 +1,259 @@
+/* Linux openat2 tests.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* openat2 is only support in LFS mode.  */
+#define _FILE_OFFSET_BITS 64
+
+#include <array_length.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xunistd.h>
+
+static int dir_fd;
+
+static void
+do_prepare (int argc, char *argv[])
+{
+  char *temp_dir = support_create_temp_directory ("tst-openat2");
+  dir_fd = xopen (temp_dir, O_RDONLY | O_DIRECTORY, 0);
+}
+#define PREPARE do_prepare
+
+static int
+do_test_struct (void)
+{
+  static struct struct_test
+  {
+    struct open_how_ext
+    {
+      struct open_how inner;
+      int extra1;
+      int extra2;
+      int extra3;
+    } arg;
+    size_t size;
+    int err;
+  } tests[] =
+  {
+    {
+      /* Zero size.  */
+      .arg.inner.flags = O_RDONLY,
+      .size = 0,
+      .err = EINVAL,
+    },
+    {
+      /* Normal struct.  */
+      .arg.inner.flags = O_RDONLY,
+      .size = sizeof (struct open_how),
+    },
+    {
+      /* Larger struct, zeroed out the unused values.  */
+      .arg.inner.flags = O_RDONLY,
+      .size = sizeof (struct open_how_ext),
+    },
+    {
+      /* Larger struct, non-zeroed out the unused values.  */
+      .arg.inner.flags = O_RDONLY,
+      .arg.extra1 = 0xdeadbeef,
+      .size = sizeof (struct open_how_ext),
+      .err = E2BIG,
+    },
+    {
+      /* Larger struct, non-zeroed out the unused values.  */
+      .arg.inner.flags = O_RDONLY,
+      .arg.extra2 = 0xdeadbeef,
+      .size = sizeof (struct open_how_ext),
+      .err = E2BIG,
+    },
+  };
+
+  for (struct struct_test *t = tests; t != array_end (tests); t++)
+    {
+      int fd = openat2 (AT_FDCWD, ".", (struct open_how *) &t->arg, t->size);
+      if (fd == -1 && errno == ENOSYS)
+	FAIL_UNSUPPORTED ("openat2 is not supported by the kernel");
+
+      if (t->err != 0)
+	{
+	  TEST_COMPARE (fd, -1);
+	  TEST_COMPARE (errno, t->err);
+	}
+      else
+	TEST_VERIFY (fd >= 0);
+    }
+
+  return 0;
+}
+
+static int
+do_test_flags (void)
+{
+  static struct flag_test
+  {
+    struct open_how how;
+    int err;
+  } tests[] =
+  {
+    /* O_TMPFILE is incompatible with O_PATH and O_CREAT.  */
+    { .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = EINVAL },
+    { .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = EINVAL },
+
+    /* O_PATH only permits certain other flags to be set ...  */
+    { .how.flags = O_PATH | O_CLOEXEC },
+    { .how.flags = O_PATH | O_DIRECTORY },
+    { .how.flags = O_PATH | O_NOFOLLOW },
+    /* ... and others are absolutely not permitted. */
+    { .how.flags = O_PATH | O_RDWR, .err = EINVAL },
+    { .how.flags = O_PATH | O_CREAT, .err = EINVAL },
+    { .how.flags = O_PATH | O_EXCL, .err = EINVAL },
+    { .how.flags = O_PATH | O_NOCTTY, .err = EINVAL },
+    { .how.flags = O_PATH | O_DIRECT, .err = EINVAL },
+
+    /* ->mode must only be set with O_{CREAT,TMPFILE}. */
+    { .how.flags = O_RDONLY, .how.mode = 0600, .err = EINVAL },
+    { .how.flags = O_PATH,   .how.mode = 0600, .err = EINVAL },
+    { .how.flags = O_CREAT,  .how.mode = 0600 },
+    { .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
+    /* ->mode must only contain 0777 bits. */
+    { .how.flags = O_CREAT, .how.mode = 0xFFFF, .err = EINVAL },
+    { .how.flags = O_CREAT, .how.mode = 0xC000000000000000ULL, .err = EINVAL },
+    { .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0x1337, .err = EINVAL },
+    { .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0x0000A00000000000ULL,
+      .err = EINVAL },
+
+    /* ->resolve flags must not conflict. */
+    { .how.flags = O_RDONLY,
+      .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
+      .err = EINVAL },
+
+    /* ->resolve must only contain RESOLVE_* flags.  */
+    { .how.flags = O_RDONLY,
+      .how.resolve = 0x1337, .err = EINVAL },
+    { .how.flags = O_CREAT,
+      .how.resolve = 0x1337, .err = EINVAL },
+    { .how.flags = O_TMPFILE | O_RDWR,
+      .how.resolve = 0x1337, .err = EINVAL },
+    { .how.flags = O_PATH,
+      .how.resolve = 0x1337, .err = EINVAL },
+
+    /* currently unknown upper 32 bit rejected.  */
+    { .how.flags = O_RDONLY | (1ULL << 63),
+      .how.resolve = 0, .err = EINVAL },
+  };
+
+  for (struct flag_test *t = tests; t != array_end (tests); t++)
+    {
+      char *path;
+      if (t->how.flags & O_CREAT)
+	{
+	  int temp_fd = create_temp_file ("tst-openat2.", &path);
+	  TEST_VERIFY_EXIT (temp_fd != -1);
+	  xunlink (path);
+	}
+      else
+	path = (char *) ".";
+
+      int fd = openat2 (AT_FDCWD, path, &t->how, sizeof (struct open_how));
+      if (fd != 0 && errno == EOPNOTSUPP)
+	{
+	  /* Skip the testcase if FS does not support the operation (e.g.
+	     valid O_TMPFILE on NFS).  */
+	  continue;
+	}
+
+      if (t->err != 0)
+	{
+	  TEST_COMPARE (fd, -1);
+	  TEST_COMPARE (errno, t->err);
+	}
+      else
+	TEST_VERIFY (fd >= 0);
+    }
+
+  return 0;
+}
+
+static int
+do_test_basic (void)
+{
+  int fd;
+
+  fd = openat2 (dir_fd,
+		"some-file",
+		&(struct open_how)
+		{
+		  .flags = O_CREAT|O_RDWR|O_EXCL,
+		  .mode = 0666,
+		},
+		sizeof (struct open_how));
+  TEST_VERIFY (fd != -1);
+
+  xwrite (fd, "hello", 5);
+
+  /* Before closing the file, try using this file descriptor to open
+     another file.  This must fail.  */
+  {
+    int fd2 = openat2 (fd,
+		       "should-not-work",
+		       &(struct open_how)
+		       {
+			 .flags = O_CREAT|O_RDWR|O_EXCL,
+			 .mode = 0666,
+		       },
+		       sizeof (struct open_how));
+    TEST_COMPARE (fd2, -1);
+    TEST_COMPARE (errno, ENOTDIR);
+  }
+
+  /* Remove the created file.  */
+  int cwdfd = xopen (".", O_RDONLY | O_DIRECTORY, 0);
+  TEST_COMPARE (fchdir (dir_fd), 0);
+  xunlink ("some-file");
+  TEST_COMPARE (fchdir (cwdfd), 0);
+
+  xclose (dir_fd);
+  xclose (cwdfd);
+
+  fd = openat2 (dir_fd,
+		"some-file",
+		&(struct open_how)
+		{
+		  .flags = O_CREAT|O_RDWR|O_EXCL,
+		  .mode = 0666,
+		},
+		sizeof (struct open_how));
+  TEST_COMPARE (fd, -1);
+  TEST_COMPARE (errno, EBADF);
+
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  do_test_struct ();
+  do_test_flags ();
+  do_test_basic ();
+
+  return 0;
+}
+
+#include <support/test-driver.c>