* Copyright (c) 2015 Fujitsu Ltd.
* Author: Guangwen Feng <fenggw-fnst@cn.fujitsu.com>
* Copyright (c) 2025 SUSE LLC <mdoucha@suse.cz>
*/
* Basic test for O_PATH flag of :man2:`open`:
*
* Obtain a file descriptor that can be used to perform operations
* that act purely at the file descriptor level, the file itself is
* not opened, the operations :man2:`read`, :man2:`write`, :man2:`fchmod`,
* :man2:`fchown` and :man2:`fgetxattr` fail with the error EBADF.
*
* The operations include but are not limited to the syscalls above.
*/
#include "config.h"
#include <sys/ioctl.h>
#include <linux/fs.h>
#ifdef HAVE_SYS_XATTR_H
#include <sys/types.h>
#include <sys/xattr.h>
#endif
#include "tst_test.h"
#include "tst_safe_macros.h"
#include "lapi/fcntl.h"
#define TESTFILE "testfile"
static int path_fd = -1, dup_fd = -1;
static int verify_read(int fd);
static int verify_write(int fd);
static int verify_fchmod(int fd);
static int verify_fchown(int fd);
static int verify_ioctl(int fd);
static int verify_mmap(int fd);
#ifdef HAVE_SYS_XATTR_H
static int verify_fgetxattr(int fd);
#endif
static const struct {
int (*func)(int fd);
const char *name;
} testcases[] = {
{verify_read, "read"},
{verify_write, "write"},
{verify_fchmod, "fchmod"},
{verify_fchown, "fchown"},
{verify_ioctl, "ioctl"},
{verify_mmap, "mmap"},
#ifdef HAVE_SYS_XATTR_H
{verify_fgetxattr, "fgetxattr"},
#endif
{}
};
static void setup(void)
{
path_fd = SAFE_OPEN(TESTFILE, O_RDWR | O_CREAT, 0644);
SAFE_CLOSE(path_fd);
path_fd = SAFE_OPEN(TESTFILE, O_PATH);
dup_fd = SAFE_DUP(path_fd);
}
static void run(void)
{
int i;
for (i = 0; testcases[i].func; i++) {
TST_EXP_FAIL(testcases[i].func(path_fd), EBADF,
"%s() on original FD", testcases[i].name);
TST_EXP_FAIL(testcases[i].func(dup_fd), EBADF,
"%s() on duplicated FD", testcases[i].name);
}
}
static int verify_read(int fd)
{
char buf[255];
return read(fd, buf, sizeof(buf));
}
static int verify_write(int fd)
{
return write(fd, "w", 1);
}
static int verify_fchmod(int fd)
{
return fchmod(fd, 0666);
}
static int verify_fchown(int fd)
{
return fchown(fd, 1000, 1000);
}
static int verify_ioctl(int fd)
{
int arg;
return ioctl(fd, FIGETBSZ, &arg);
}
static int verify_mmap(int fd)
{
void *ptr;
ptr = mmap(NULL, 1, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED)
return -1;
SAFE_MUNMAP(ptr, 1);
return 0;
}
#ifdef HAVE_SYS_XATTR_H
static int verify_fgetxattr(int fd)
{
return fgetxattr(fd, "tkey", NULL, 0);
}
#endif
static void cleanup(void)
{
if (path_fd >= 0)
SAFE_CLOSE(path_fd);
if (dup_fd >= 0)
SAFE_CLOSE(dup_fd);
}
static struct tst_test test = {
.setup = setup,
.test_all = run,
.cleanup = cleanup,
.needs_tmpdir = 1
};