/* 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 . */ /* This test verifies that open and openat work as expected, i.e. they create a deleted file with the requested file mode. */ #include #include #include #include #include #include #include #include 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 */