• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2024 Andrea Cervesato andrea.cervesato@suse.com
4  */
5 
6 /*\
7  * [Description]
8  *
9  * This test verifies that ioctl() FICLONE/FICLONERANGE feature correctly raises
10  * exceptions when it's supposed to.
11  */
12 
13 #include "tst_test.h"
14 #include "lapi/ficlone.h"
15 
16 #define MNTPOINT "mnt"
17 
18 static struct file_clone_range *clone_range;
19 
20 static int invalid_fd = -1;
21 static int rw_file = -1;
22 static int ro_file = -1;
23 static int wo_file = -1;
24 static int dir_fd = -1;
25 static int immut_fd = -1;
26 static int mnt_file = -1;
27 
28 static struct tcase {
29 	int *src_fd;
30 	int *dst_fd;
31 	int errno_exp;
32 	char *msg;
33 } tcases[] = {
34 	{&invalid_fd, &rw_file, EBADF, "invalid source"},
35 	{&rw_file, &invalid_fd, EBADF, "invalid destination"},
36 	{&rw_file, &ro_file, EBADF, "read-only destination"},
37 	{&wo_file, &rw_file, EBADF, "write-only source"},
38 	{&rw_file, &dir_fd, EISDIR, "source is a directory"},
39 	{&dir_fd, &rw_file, EISDIR, "destination is a directory"},
40 	{&mnt_file, &immut_fd, EPERM, "destination is immutable"},
41 	{&rw_file, &mnt_file, EXDEV, "destination is on a different mount"},
42 	{&mnt_file, &rw_file, EXDEV, "source is on a different mount"},
43 };
44 
run(unsigned int n)45 static void run(unsigned int n)
46 {
47 	struct tcase *tc = &tcases[n];
48 
49 	TST_EXP_FAIL(ioctl(*tc->dst_fd, FICLONE, *tc->src_fd),
50 		tc->errno_exp,
51 		"%s", tc->msg);
52 
53 	clone_range->src_fd = *tc->src_fd;
54 
55 	TST_EXP_FAIL(ioctl(*tc->dst_fd, FICLONERANGE, clone_range),
56 		tc->errno_exp,
57 		"%s", tc->msg);
58 }
59 
setup(void)60 static void setup(void)
61 {
62 	int attr;
63 	struct stat sb;
64 
65 	rw_file = SAFE_OPEN("ok_only", O_CREAT | O_RDWR, 0640);
66 	ro_file = SAFE_OPEN("rd_only", O_CREAT | O_RDONLY, 0640);
67 	wo_file = SAFE_OPEN("rw_only", O_CREAT | O_WRONLY, 0640);
68 
69 	if (access("mydir", F_OK) == -1)
70 		SAFE_MKDIR("mydir", 0640);
71 
72 	dir_fd = SAFE_OPEN("mydir", O_DIRECTORY, 0640);
73 
74 	attr = FS_IMMUTABLE_FL;
75 	immut_fd = SAFE_OPEN(MNTPOINT"/immutable", O_CREAT | O_RDWR, 0640);
76 	SAFE_IOCTL(immut_fd, FS_IOC_SETFLAGS, &attr);
77 
78 	mnt_file = SAFE_OPEN(MNTPOINT"/file", O_CREAT | O_RDWR, 0640);
79 
80 	SAFE_STAT(MNTPOINT, &sb);
81 
82 	clone_range->src_offset = 0;
83 	clone_range->src_length = sb.st_blksize;
84 	clone_range->dest_offset = 0;
85 }
86 
cleanup(void)87 static void cleanup(void)
88 {
89 	int attr;
90 
91 	SAFE_IOCTL(immut_fd, FS_IOC_GETFLAGS, &attr);
92 	attr &= ~FS_IMMUTABLE_FL;
93 	SAFE_IOCTL(immut_fd, FS_IOC_SETFLAGS, &attr);
94 	SAFE_CLOSE(immut_fd);
95 
96 	SAFE_CLOSE(rw_file);
97 	SAFE_CLOSE(ro_file);
98 	SAFE_CLOSE(wo_file);
99 	SAFE_CLOSE(dir_fd);
100 	SAFE_CLOSE(mnt_file);
101 }
102 
103 static struct tst_test test = {
104 	.timeout = 1,
105 	.test = run,
106 	.tcnt = ARRAY_SIZE(tcases),
107 	.setup = setup,
108 	.cleanup = cleanup,
109 	.min_kver = "4.5",
110 	.needs_root = 1,
111 	.mount_device = 1,
112 	.mntpoint = MNTPOINT,
113 	.filesystems = (struct tst_fs []) {
114 		{.type = "btrfs"},
115 		{.type = "bcachefs"},
116 		{
117 			.type = "xfs",
118 			.min_kver = "4.16",
119 			.mkfs_ver = "mkfs.xfs >= 1.5.0",
120 			.mkfs_opts = (const char *const []) {"-m", "reflink=1", NULL},
121 		},
122 		{}
123 	},
124 	.bufs = (struct tst_buffers []) {
125 		{&clone_range, .size = sizeof(struct file_clone_range)},
126 		{},
127 	}
128 };
129