summaryrefslogtreecommitdiff
path: root/io
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2016-09-21 15:24:01 +0200
committerFlorian Weimer <fweimer@redhat.com>2016-09-21 15:24:01 +0200
commit85f7554cd97e7f03d8dc66278653045ef63a2221 (patch)
treeda312fa82fef22107c11c5d55dbbaf1931918509 /io
parentfc3e1337be1c6935ab58bd13520f97a535cf70cc (diff)
Add test case for O_TMPFILE handling in open, openat
Also put xasprintf into test-skeleton.c (written in such a way that including <stdarg.h> is not needed).
Diffstat (limited to 'io')
-rw-r--r--io/Makefile3
-rw-r--r--io/tst-open-tmpfile.c216
2 files changed, 218 insertions, 1 deletions
diff --git a/io/Makefile b/io/Makefile
index deb6100156..f5977afef7 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -71,7 +71,8 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-renameat tst-fchownat tst-fchmodat tst-faccessat \
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
- tst-posix_fallocate tst-fts tst-fts-lfs
+ tst-posix_fallocate tst-fts tst-fts-lfs \
+ tst-open-tmpfile
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)ftwtest.out
diff --git a/io/tst-open-tmpfile.c b/io/tst-open-tmpfile.c
new file mode 100644
index 0000000000..9af1875ba2
--- /dev/null
+++ b/io/tst-open-tmpfile.c
@@ -0,0 +1,216 @@
+/* Test open and openat with O_TMPFILE.
+ Copyright (C) 2016 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
+ <http://www.gnu.org/licenses/>. */
+
+/* This test verifies that open and openat work as expected, i.e. they
+ create a deleted file with the requested file mode. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static int do_test (void);
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+#ifdef O_TMPFILE
+typedef int (*wrapper_func) (const char *, int, mode_t);
+
+/* Error-checking wrapper for the open function, compatible with the
+ wrapper_func type. */
+static int
+wrap_open (const char *path, int flags, mode_t mode)
+{
+ int ret = open (path, flags, mode);
+ if (ret < 0)
+ {
+ printf ("error: open (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
+ exit (1);
+ }
+ return ret;
+}
+
+/* Error-checking wrapper for the openat function, compatible with the
+ wrapper_func type. */
+static int
+wrap_openat (const char *path, int flags, mode_t mode)
+{
+ int ret = openat (AT_FDCWD, path, flags, mode);
+ if (ret < 0)
+ {
+ printf ("error: openat (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
+ exit (1);
+ }
+ return ret;
+}
+
+/* Return true if FD is flagged as deleted in /proc/self/fd, false if
+ not. */
+static bool
+is_file_deteted (int fd)
+{
+ char *proc_fd_path = xasprintf ("/proc/self/fd/%d", fd);
+ char file_path[4096];
+ ssize_t file_path_length
+ = readlink (proc_fd_path, file_path, sizeof (file_path));
+ if (file_path_length < 0)
+ {
+ printf ("error: readlink (\"%s\"): %m", proc_fd_path);
+ free (proc_fd_path);
+ exit (1);
+ }
+ free (proc_fd_path);
+ if (file_path_length == sizeof (file_path))
+ {
+ printf ("error: path in /proc resolves to overlong file name: %.*s\n",
+ (int) file_path_length, file_path);
+ exit (1);
+ }
+ const char *deleted = " (deleted)";
+ if (file_path_length < strlen (deleted))
+ {
+ printf ("error: path in /proc is too short: %.*s\n",
+ (int) file_path_length, file_path);
+ exit (1);
+ }
+ return memcmp (file_path + file_path_length - strlen (deleted),
+ deleted, strlen (deleted)) == 0;
+}
+
+/* Check open/openat (as specified by OP and WRAPPER) with a specific
+ PATH/FLAGS/MODE combination. */
+static void
+check_wrapper_flags_mode (const char *op, wrapper_func wrapper,
+ const char *path, int flags, mode_t mode)
+{
+ int fd = wrapper (path, flags | O_TMPFILE, mode);
+ struct stat64 st;
+ if (fstat64 (fd, &st) != 0)
+ {
+ printf ("error: fstat64: %m\n");
+ exit (1);
+ }
+
+ /* Verify that the mode was correctly processed. */
+ int actual_mode = st.st_mode & 0777;
+ if (actual_mode != mode)
+ {
+ printf ("error: unexpected mode; expected 0%03o, actual 0%03o\n",
+ mode, actual_mode);
+ exit (1);
+ }
+
+ /* Check that the file is marked as deleted in /proc. */
+ if (!is_file_deteted (fd))
+ {
+ printf ("error: path in /proc is not marked as deleted\n");
+ exit (1);
+ }
+
+ close (fd);
+}
+
+/* Check OP/WRAPPER with various flags at a specific PATH and
+ MODE. */
+static void
+check_wrapper_mode (const char *op, wrapper_func wrapper,
+ const char *path, mode_t mode)
+{
+ check_wrapper_flags_mode (op, wrapper, path, O_WRONLY, mode);
+ check_wrapper_flags_mode (op, wrapper, path, O_WRONLY | O_EXCL, mode);
+ check_wrapper_flags_mode (op, wrapper, path, O_RDWR, mode);
+ check_wrapper_flags_mode (op, wrapper, path, O_RDWR | O_EXCL, mode);
+}
+
+/* Check open/openat with varying permissions. */
+static void
+check_wrapper (const char *op, wrapper_func wrapper,
+ const char *path)
+{
+ printf ("info: testing %s at: %s\n", op, path);
+ check_wrapper_mode (op, wrapper, path, 0);
+ check_wrapper_mode (op, wrapper, path, 0640);
+ check_wrapper_mode (op, wrapper, path, 0600);
+ check_wrapper_mode (op, wrapper, path, 0755);
+ check_wrapper_mode (op, wrapper, path, 0750);
+}
+
+/* Verify that the directory at PATH supports O_TMPFILE. Exit with
+ status 77 (unsupported) if the kernel does not support O_TMPFILE.
+ Even with kernel support, not all file systems O_TMPFILE, so return
+ true if the directory supports O_TMPFILE, false if not. */
+static bool
+probe_path (const char *path)
+{
+ int fd = openat (AT_FDCWD, path, O_TMPFILE | O_RDWR, 0);
+ if (fd < 0)
+ {
+ if (errno == EISDIR)
+ /* The system does not support O_TMPFILE. */
+ {
+ printf ("info: kernel does not support O_TMPFILE\n");
+ exit (77);
+ }
+ if (errno == EOPNOTSUPP)
+ {
+ printf ("info: path does not support O_TMPFILE: %s\n", path);
+ return false;
+ }
+ printf ("error: openat (\"%s\", O_TMPFILE | O_RDWR): %m\n", path);
+ exit (1);
+ }
+ close (fd);
+ return true;
+}
+
+static int
+do_test (void)
+{
+ umask (0);
+ const char *paths[] = { ".", "/dev/shm", "/tmp",
+ getenv ("TEST_TMPFILE_PATH"),
+ NULL };
+ bool supported = false;
+ for (int i = 0; paths[i] != NULL; ++i)
+ if (probe_path (paths[i]))
+ {
+ supported = true;
+ check_wrapper ("open", wrap_open, paths[i]);
+ check_wrapper ("openat", wrap_openat, paths[i]);
+ }
+
+ if (!supported)
+ return 77;
+
+ return 0;
+}
+
+#else /* !O_TMPFILE */
+
+static int
+do_test (void)
+{
+ return 77;
+}
+
+#endif /* O_TMPFILE */