* Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com
*/
* This test verifies that ioctl() FICLONE/FICLONERANGE feature correctly raises
* exceptions when it's supposed to.
*/
#include "tst_test.h"
#include "lapi/ficlone.h"
#define MNTPOINT "mnt"
static struct file_clone_range *clone_range;
static int invalid_fd = -1;
static int rw_file = -1;
static int ro_file = -1;
static int wo_file = -1;
static int dir_fd = -1;
static int immut_fd = -1;
static int mnt_file = -1;
static struct tcase {
int *src_fd;
int *dst_fd;
int errno_exp;
char *msg;
} tcases[] = {
{&invalid_fd, &rw_file, EBADF, "invalid source"},
{&rw_file, &invalid_fd, EBADF, "invalid destination"},
{&rw_file, &ro_file, EBADF, "read-only destination"},
{&wo_file, &rw_file, EBADF, "write-only source"},
{&rw_file, &dir_fd, EISDIR, "source is a directory"},
{&dir_fd, &rw_file, EISDIR, "destination is a directory"},
{&mnt_file, &immut_fd, EPERM, "destination is immutable"},
{&rw_file, &mnt_file, EXDEV, "destination is on a different mount"},
{&mnt_file, &rw_file, EXDEV, "source is on a different mount"},
};
static void run(unsigned int n)
{
struct tcase *tc = &tcases[n];
TST_EXP_FAIL(ioctl(*tc->dst_fd, FICLONE, *tc->src_fd),
tc->errno_exp,
"%s", tc->msg);
clone_range->src_fd = *tc->src_fd;
TST_EXP_FAIL(ioctl(*tc->dst_fd, FICLONERANGE, clone_range),
tc->errno_exp,
"%s", tc->msg);
}
static void setup(void)
{
int attr;
struct stat sb;
rw_file = SAFE_OPEN("ok_only", O_CREAT | O_RDWR, 0640);
ro_file = SAFE_OPEN("rd_only", O_CREAT | O_RDONLY, 0640);
wo_file = SAFE_OPEN("rw_only", O_CREAT | O_WRONLY, 0640);
if (access("mydir", F_OK) == -1)
SAFE_MKDIR("mydir", 0640);
dir_fd = SAFE_OPEN("mydir", O_DIRECTORY, 0640);
attr = FS_IMMUTABLE_FL;
immut_fd = SAFE_OPEN(MNTPOINT"/immutable", O_CREAT | O_RDWR, 0640);
SAFE_IOCTL(immut_fd, FS_IOC_SETFLAGS, &attr);
mnt_file = SAFE_OPEN(MNTPOINT"/file", O_CREAT | O_RDWR, 0640);
SAFE_STAT(MNTPOINT, &sb);
clone_range->src_offset = 0;
clone_range->src_length = sb.st_blksize;
clone_range->dest_offset = 0;
}
static void cleanup(void)
{
int attr;
SAFE_IOCTL(immut_fd, FS_IOC_GETFLAGS, &attr);
attr &= ~FS_IMMUTABLE_FL;
SAFE_IOCTL(immut_fd, FS_IOC_SETFLAGS, &attr);
SAFE_CLOSE(immut_fd);
SAFE_CLOSE(rw_file);
SAFE_CLOSE(ro_file);
SAFE_CLOSE(wo_file);
SAFE_CLOSE(dir_fd);
SAFE_CLOSE(mnt_file);
}
static struct tst_test test = {
.timeout = 1,
.test = run,
.tcnt = ARRAY_SIZE(tcases),
.setup = setup,
.cleanup = cleanup,
.min_kver = "4.5",
.needs_root = 1,
.mount_device = 1,
.mntpoint = MNTPOINT,
.filesystems = (struct tst_fs []) {
{.type = "btrfs"},
{.type = "bcachefs"},
{
.type = "xfs",
.min_kver = "4.16",
.mkfs_ver = "mkfs.xfs >= 1.5.0",
.mkfs_opts = (const char *const []) {"-m", "reflink=1", NULL},
},
{}
},
.bufs = (struct tst_buffers []) {
{&clone_range, .size = sizeof(struct file_clone_range)},
{},
}
};