|
Packit |
6c4009 |
/* Test open and openat with O_TMPFILE.
|
|
Packit |
6c4009 |
Copyright (C) 2016-2018 Free Software Foundation, Inc.
|
|
Packit |
6c4009 |
This file is part of the GNU C Library.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
The GNU C Library is free software; you can redistribute it and/or
|
|
Packit |
6c4009 |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6c4009 |
License as published by the Free Software Foundation; either
|
|
Packit |
6c4009 |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
The GNU C Library is distributed in the hope that it will be useful,
|
|
Packit |
6c4009 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6c4009 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6c4009 |
Lesser General Public License for more details.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6c4009 |
License along with the GNU C Library; if not, see
|
|
Packit |
6c4009 |
<http://www.gnu.org/licenses/>. */
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* This test verifies that open and openat work as expected, i.e. they
|
|
Packit |
6c4009 |
create a deleted file with the requested file mode. */
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#include <errno.h>
|
|
Packit |
6c4009 |
#include <fcntl.h>
|
|
Packit |
6c4009 |
#include <stdbool.h>
|
|
Packit |
6c4009 |
#include <stdio.h>
|
|
Packit |
6c4009 |
#include <stdlib.h>
|
|
Packit |
6c4009 |
#include <string.h>
|
|
Packit |
6c4009 |
#include <sys/stat.h>
|
|
Packit |
6c4009 |
#include <unistd.h>
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#include <support/support.h>
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#ifdef O_TMPFILE
|
|
Packit |
6c4009 |
typedef int (*wrapper_func) (const char *, int, mode_t);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Error-checking wrapper for the open function, compatible with the
|
|
Packit |
6c4009 |
wrapper_func type. */
|
|
Packit |
6c4009 |
static int
|
|
Packit |
6c4009 |
wrap_open (const char *path, int flags, mode_t mode)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
int ret = open (path, flags, mode);
|
|
Packit |
6c4009 |
if (ret < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: open (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
return ret;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Error-checking wrapper for the openat function, compatible with the
|
|
Packit |
6c4009 |
wrapper_func type. */
|
|
Packit |
6c4009 |
static int
|
|
Packit |
6c4009 |
wrap_openat (const char *path, int flags, mode_t mode)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
int ret = openat (AT_FDCWD, path, flags, mode);
|
|
Packit |
6c4009 |
if (ret < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: openat (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
return ret;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Error-checking wrapper for the open64 function, compatible with the
|
|
Packit |
6c4009 |
wrapper_func type. */
|
|
Packit |
6c4009 |
static int
|
|
Packit |
6c4009 |
wrap_open64 (const char *path, int flags, mode_t mode)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
int ret = open64 (path, flags, mode);
|
|
Packit |
6c4009 |
if (ret < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: open64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
return ret;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Error-checking wrapper for the openat64 function, compatible with the
|
|
Packit |
6c4009 |
wrapper_func type. */
|
|
Packit |
6c4009 |
static int
|
|
Packit |
6c4009 |
wrap_openat64 (const char *path, int flags, mode_t mode)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
int ret = openat64 (AT_FDCWD, path, flags, mode);
|
|
Packit |
6c4009 |
if (ret < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: openat64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
return ret;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Return true if FD is flagged as deleted in /proc/self/fd, false if
|
|
Packit |
6c4009 |
not. */
|
|
Packit |
6c4009 |
static bool
|
|
Packit |
6c4009 |
is_file_deteted (int fd)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
char *proc_fd_path = xasprintf ("/proc/self/fd/%d", fd);
|
|
Packit |
6c4009 |
char file_path[4096];
|
|
Packit |
6c4009 |
ssize_t file_path_length
|
|
Packit |
6c4009 |
= readlink (proc_fd_path, file_path, sizeof (file_path));
|
|
Packit |
6c4009 |
if (file_path_length < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: readlink (\"%s\"): %m", proc_fd_path);
|
|
Packit |
6c4009 |
free (proc_fd_path);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
free (proc_fd_path);
|
|
Packit |
6c4009 |
if (file_path_length == sizeof (file_path))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: path in /proc resolves to overlong file name: %.*s\n",
|
|
Packit |
6c4009 |
(int) file_path_length, file_path);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
const char *deleted = " (deleted)";
|
|
Packit |
6c4009 |
if (file_path_length < strlen (deleted))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: path in /proc is too short: %.*s\n",
|
|
Packit |
6c4009 |
(int) file_path_length, file_path);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
return memcmp (file_path + file_path_length - strlen (deleted),
|
|
Packit |
6c4009 |
deleted, strlen (deleted)) == 0;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Obtain a file name which is difficult to guess. */
|
|
Packit |
6c4009 |
static char *
|
|
Packit |
6c4009 |
get_random_name (void)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
unsigned long long bytes[2];
|
|
Packit |
6c4009 |
int random_device = open ("/dev/urandom", O_RDONLY);
|
|
Packit |
6c4009 |
if (random_device < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: open (\"/dev/urandom\"): %m\n");
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
ssize_t ret = read (random_device, bytes, sizeof (bytes));
|
|
Packit |
6c4009 |
if (ret < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: read (\"/dev/urandom\"): %m\n");
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
if (ret != sizeof (bytes))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: short read from /dev/urandom: %zd\n", ret);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
close (random_device);
|
|
Packit |
6c4009 |
return xasprintf ("tst-open-tmpfile-%08llx%08llx.tmp", bytes[0], bytes[1]);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check open/openat (as specified by OP and WRAPPER) with a specific
|
|
Packit |
6c4009 |
PATH/FLAGS/MODE combination. */
|
|
Packit |
6c4009 |
static void
|
|
Packit |
6c4009 |
check_wrapper_flags_mode (const char *op, wrapper_func wrapper,
|
|
Packit |
6c4009 |
const char *path, int flags, mode_t mode)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
int fd = wrapper (path, flags | O_TMPFILE, mode);
|
|
Packit |
6c4009 |
struct stat64 st;
|
|
Packit |
6c4009 |
if (fstat64 (fd, &st) != 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: fstat64: %m\n");
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Verify that the mode was correctly processed. */
|
|
Packit |
6c4009 |
int actual_mode = st.st_mode & 0777;
|
|
Packit |
6c4009 |
if (actual_mode != mode)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: unexpected mode; expected 0%03o, actual 0%03o\n",
|
|
Packit |
6c4009 |
mode, actual_mode);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check that the file is marked as deleted in /proc. */
|
|
Packit |
6c4009 |
if (!is_file_deteted (fd))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: path in /proc is not marked as deleted\n");
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check that the file can be turned into a regular file with
|
|
Packit |
6c4009 |
linkat. Open a file descriptor for the directory at PATH. Use
|
|
Packit |
6c4009 |
AT_FDCWD if PATH is ".", to exercise that functionality as
|
|
Packit |
6c4009 |
well. */
|
|
Packit |
6c4009 |
int path_fd;
|
|
Packit |
6c4009 |
if (strcmp (path, ".") == 0)
|
|
Packit |
6c4009 |
path_fd = AT_FDCWD;
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
path_fd = open (path, O_RDONLY | O_DIRECTORY);
|
|
Packit |
6c4009 |
if (path_fd < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: open (\"%s\"): %m\n", path);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Use a hard-to-guess name for the new directory entry. */
|
|
Packit |
6c4009 |
char *new_name = get_random_name ();
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* linkat does not require privileges if the path in /proc/self/fd
|
|
Packit |
6c4009 |
is used. */
|
|
Packit |
6c4009 |
char *proc_fd_path = xasprintf ("/proc/self/fd/%d", fd);
|
|
Packit |
6c4009 |
if (linkat (AT_FDCWD, proc_fd_path, path_fd, new_name,
|
|
Packit |
6c4009 |
AT_SYMLINK_FOLLOW) == 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (unlinkat (path_fd, new_name, 0) != 0 && errno != ENOENT)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: unlinkat (\"%s/%s\"): %m\n", path, new_name);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* linkat failed. This is expected if O_EXCL was specified. */
|
|
Packit |
6c4009 |
if ((flags & O_EXCL) == 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("error: linkat failed after %s (\"%s\", 0x%x, 0%03o): %m\n",
|
|
Packit |
6c4009 |
op, path, flags, mode);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
free (proc_fd_path);
|
|
Packit |
6c4009 |
free (new_name);
|
|
Packit |
6c4009 |
if (path_fd != AT_FDCWD)
|
|
Packit |
6c4009 |
close (path_fd);
|
|
Packit |
6c4009 |
close (fd);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check OP/WRAPPER with various flags at a specific PATH and
|
|
Packit |
6c4009 |
MODE. */
|
|
Packit |
6c4009 |
static void
|
|
Packit |
6c4009 |
check_wrapper_mode (const char *op, wrapper_func wrapper,
|
|
Packit |
6c4009 |
const char *path, mode_t mode)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
check_wrapper_flags_mode (op, wrapper, path, O_WRONLY, mode);
|
|
Packit |
6c4009 |
check_wrapper_flags_mode (op, wrapper, path, O_WRONLY | O_EXCL, mode);
|
|
Packit |
6c4009 |
check_wrapper_flags_mode (op, wrapper, path, O_RDWR, mode);
|
|
Packit |
6c4009 |
check_wrapper_flags_mode (op, wrapper, path, O_RDWR | O_EXCL, mode);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check open/openat with varying permissions. */
|
|
Packit |
6c4009 |
static void
|
|
Packit |
6c4009 |
check_wrapper (const char *op, wrapper_func wrapper,
|
|
Packit |
6c4009 |
const char *path)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("info: testing %s at: %s\n", op, path);
|
|
Packit |
6c4009 |
check_wrapper_mode (op, wrapper, path, 0);
|
|
Packit |
6c4009 |
check_wrapper_mode (op, wrapper, path, 0640);
|
|
Packit |
6c4009 |
check_wrapper_mode (op, wrapper, path, 0600);
|
|
Packit |
6c4009 |
check_wrapper_mode (op, wrapper, path, 0755);
|
|
Packit |
6c4009 |
check_wrapper_mode (op, wrapper, path, 0750);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Verify that the directory at PATH supports O_TMPFILE. Exit with
|
|
Packit |
6c4009 |
status 77 (unsupported) if the kernel does not support O_TMPFILE.
|
|
Packit |
6c4009 |
Even with kernel support, not all file systems O_TMPFILE, so return
|
|
Packit |
6c4009 |
true if the directory supports O_TMPFILE, false if not. */
|
|
Packit |
6c4009 |
static bool
|
|
Packit |
6c4009 |
probe_path (const char *path)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
int fd = openat (AT_FDCWD, path, O_TMPFILE | O_RDWR, 0);
|
|
Packit |
6c4009 |
if (fd < 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (errno == EISDIR)
|
|
Packit |
6c4009 |
/* The system does not support O_TMPFILE. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("info: kernel does not support O_TMPFILE\n");
|
|
Packit |
6c4009 |
exit (77);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
if (errno == EOPNOTSUPP)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
printf ("info: path does not support O_TMPFILE: %s\n", path);
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
printf ("error: openat (\"%s\", O_TMPFILE | O_RDWR): %m\n", path);
|
|
Packit |
6c4009 |
exit (1);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
close (fd);
|
|
Packit |
6c4009 |
return true;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
static int
|
|
Packit |
6c4009 |
do_test (void)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
umask (0);
|
|
Packit |
6c4009 |
const char *paths[] = { ".", "/dev/shm", "/tmp",
|
|
Packit |
6c4009 |
getenv ("TEST_TMPFILE_PATH"),
|
|
Packit |
6c4009 |
NULL };
|
|
Packit |
6c4009 |
bool supported = false;
|
|
Packit |
6c4009 |
for (int i = 0; paths[i] != NULL; ++i)
|
|
Packit |
6c4009 |
if (probe_path (paths[i]))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
supported = true;
|
|
Packit |
6c4009 |
check_wrapper ("open", wrap_open, paths[i]);
|
|
Packit |
6c4009 |
check_wrapper ("openat", wrap_openat, paths[i]);
|
|
Packit |
6c4009 |
check_wrapper ("open64", wrap_open64, paths[i]);
|
|
Packit |
6c4009 |
check_wrapper ("openat64", wrap_openat64, paths[i]);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if (!supported)
|
|
Packit |
6c4009 |
return 77;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
return 0;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#else /* !O_TMPFILE */
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
static int
|
|
Packit |
6c4009 |
do_test (void)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
return 77;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#endif /* O_TMPFILE */
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#include <support/test-driver.c>
|