mirror of
git://sourceware.org/git/glibc.git
synced 2025-03-06 20:58:33 +01:00
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).
This commit is contained in:
parent
fc3e1337be
commit
85f7554cd9
4 changed files with 239 additions and 1 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
2016-09-21 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
|
* test-skeleton.c (xasprintf): Add function.
|
||||||
|
* io/tst-open-tmpfile.c: New test.
|
||||||
|
* io/Makefile (tests): Add it.
|
||||||
|
|
||||||
2016-09-21 Florian Weimer <fweimer@redhat.com>
|
2016-09-21 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
Avoid running $(CXX) during build to obtain header file paths.
|
Avoid running $(CXX) during build to obtain header file paths.
|
||||||
|
|
|
@ -71,7 +71,8 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
|
||||||
tst-renameat tst-fchownat tst-fchmodat tst-faccessat \
|
tst-renameat tst-fchownat tst-fchmodat tst-faccessat \
|
||||||
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
|
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
|
||||||
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
|
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)
|
ifeq ($(run-built-tests),yes)
|
||||||
tests-special += $(objpfx)ftwtest.out
|
tests-special += $(objpfx)ftwtest.out
|
||||||
|
|
216
io/tst-open-tmpfile.c
Normal file
216
io/tst-open-tmpfile.c
Normal file
|
@ -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 */
|
|
@ -33,6 +33,7 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
/* The test function is normally called `do_test' and it is called
|
/* The test function is normally called `do_test' and it is called
|
||||||
with argc and argv as the arguments. We nevertheless provide the
|
with argc and argv as the arguments. We nevertheless provide the
|
||||||
|
@ -115,6 +116,20 @@ xrealloc (void *p, size_t n)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Call asprintf with error checking. */
|
||||||
|
__attribute__ ((always_inline, format (printf, 1, 2)))
|
||||||
|
static __inline__ char *
|
||||||
|
xasprintf (const char *format, ...)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
if (asprintf (&result, format, __builtin_va_arg_pack ()) < 0)
|
||||||
|
{
|
||||||
|
printf ("error: asprintf: %m\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* Write a message to standard output. Can be used in signal
|
/* Write a message to standard output. Can be used in signal
|
||||||
handlers. */
|
handlers. */
|
||||||
static void
|
static void
|
||||||
|
|
Loading…
Add table
Reference in a new issue