• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2019 SUSE LLC
4  * Author: Christian Amann <camann@suse.com>
5  */
6 
7 /*
8  * Tests basic error handling of the
9  * copy_file_range syscall
10  *
11  * 1) Try to copy contents to file open as readonly
12  *    -> EBADF
13  * 2) Try to copy contents to directory -> EISDIR
14  * 3) Try to copy contents to a file opened with the
15  *    O_APPEND flag -> EBADF
16  * 4) Try to copy contents to closed file descriptor
17  *    -> EBADF
18  * 5) Try to copy contents with invalid 'flags' value
19  *    -> EINVAL
20  * 6) Try to copy contents to a file chattred with +i
21  *    flag -> EPERM
22  * 7) Try to copy contents to a swapfile ->ETXTBSY
23  * 8) Try to copy contents to the same file with overlapping
24  *    ->EINVAL
25  * 9) Try to copy contents to a blkdev ->EINVAL
26  * 10) Try to copy contents to a chardev ->EINVAL
27  * 11) Try to copy contents to a FIFO ->EINVAL
28  * 12) Try to copy contenst to a PIPE ->EINVAL
29  * 13) Try to copy contents to a file with length beyond
30  *     16EiB wraps around 0 -> EOVERFLOW
31  * 14) Try to copy contents to a file with target file range
32  *     beyond maximum supported file size ->EFBIG
33  */
34 
35 #define _GNU_SOURCE
36 
37 #include "tst_test.h"
38 #include "copy_file_range.h"
39 
40 static int fd_src;
41 static int fd_dest;
42 static int fd_rdonly;
43 static int fd_dir;
44 static int fd_closed;
45 static int fd_append;
46 static int fd_immutable;
47 static int fd_swapfile;
48 static int fd_dup;
49 static int fd_blkdev;
50 static int fd_chrdev;
51 static int fd_fifo;
52 static int fd_pipe[2];
53 static int fd_copy;
54 static int need_unlink;
55 
56 static int chattr_i_nsup;
57 static int swap_nsup;
58 static int cross_sup;
59 static int loop_devn;
60 
61 static struct tcase {
62 	int	*copy_to_fd;
63 	int	flags;
64 	int	exp_err;
65 	loff_t     len;
66 	const char *tname;
67 	int     new_error;
68 } tcases[] = {
69 	{&fd_rdonly,	0,	EBADF,		CONTSIZE,	"readonly file",	0},
70 	{&fd_dir,	0,	EISDIR,		CONTSIZE,	"directory",	0},
71 	{&fd_append,	0,	EBADF,		CONTSIZE,	"append to file",	0},
72 	{&fd_closed,	0,	EBADF,		CONTSIZE,	"closed file descriptor",	0},
73 	{&fd_dest,	-1,	EINVAL,		CONTSIZE,	"invalid flags",	0},
74 	{&fd_immutable,	0,	EPERM,		CONTSIZE,	"immutable file",	1},
75 	{&fd_swapfile,	0,	ETXTBSY,	CONTSIZE,	"swap file",	1},
76 	{&fd_dup,	0,	EINVAL,		CONTSIZE/2,	"overlaping range",	1},
77 	{&fd_blkdev,	0,	EINVAL,		CONTSIZE,	"block device", 	0},
78 	{&fd_chrdev,	0,	EINVAL,		CONTSIZE,	"char device",	0},
79 	{&fd_fifo,	0,	EINVAL,		CONTSIZE,	"fifo", 	0},
80 	{&fd_pipe[0],	0,	EINVAL,		CONTSIZE,	"pipe", 	0},
81 #ifdef TST_ABI64
82 	{&fd_copy,	0,	EOVERFLOW,	ULLONG_MAX,	"max length", 	1},
83 #else
84 	{&fd_copy,	0,	EFBIG,		ULLONG_MAX,	"max length", 	1},
85 #endif
86 	{&fd_copy,	0,	EFBIG,		MIN_OFF,	"max file size", 	1},
87 };
88 
run_command(char * command,char * option,char * file)89 static int run_command(char *command, char *option, char *file)
90 {
91 	const char *const cmd[] = {command, option, file, NULL};
92 	int ret;
93 
94 	ret = tst_cmd(cmd, NULL, NULL, TST_CMD_PASS_RETVAL);
95 	switch (ret) {
96 	case 0:
97 	return 0;
98 	case 255:
99 		tst_res(TCONF, "%s binary not installed or failed", command);
100 	return 1;
101 	default:
102 		tst_res(TCONF, "%s exited with %i", command, ret);
103 	return 2;
104 	}
105 }
106 
verify_copy_file_range(unsigned int n)107 static void verify_copy_file_range(unsigned int n)
108 {
109 	struct tcase *tc = &tcases[n];
110 	loff_t dst = 0;
111 
112 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
113 
114 	if (tc->new_error && !cross_sup) {
115 		tst_res(TCONF,
116 			"copy_file_range() doesn't support cross-device, skip it");
117 		return;
118 	}
119 	if (tc->copy_to_fd == &fd_immutable && chattr_i_nsup) {
120 		tst_res(TCONF, "filesystem doesn't support chattr +i, skip it");
121 		return;
122 	}
123 	if (tc->copy_to_fd == &fd_swapfile && swap_nsup) {
124 		tst_res(TCONF, "filesystem doesn't support swapfile, skip it");
125 		return;
126 	}
127 	if (tc->copy_to_fd == &fd_blkdev && loop_devn == -1) {
128 		tst_res(TCONF, "filesystem doesn't have free loopdev, skip it");
129 		return;
130 	}
131 
132 	if (tc->copy_to_fd == &fd_copy)
133 		dst = tst_max_lfs_filesize();
134 
135 	TEST(sys_copy_file_range(fd_src, 0, *tc->copy_to_fd,
136 				&dst, tc->len, tc->flags));
137 
138 	if (TST_RET == -1) {
139 		if (tc->exp_err == TST_ERR) {
140 			tst_res(TPASS | TTERRNO,
141 					"copy_file_range failed as expected");
142 		} else {
143 			tst_res(TFAIL | TTERRNO,
144 				"copy_file_range failed unexpectedly; expected %s, but got",
145 				tst_strerrno(tc->exp_err));
146 			return;
147 		}
148 	} else {
149 		tst_res(TFAIL,
150 			"copy_file_range returned wrong value: %ld", TST_RET);
151 	}
152 }
153 
cleanup(void)154 static void cleanup(void)
155 {
156 	if (fd_append > 0)
157 		SAFE_CLOSE(fd_append);
158 	if (fd_dir > 0)
159 		SAFE_CLOSE(fd_dir);
160 	if (fd_rdonly > 0)
161 		SAFE_CLOSE(fd_rdonly);
162 	if (fd_dest > 0)
163 		SAFE_CLOSE(fd_dest);
164 	if (fd_src > 0)
165 		SAFE_CLOSE(fd_src);
166 	if (fd_immutable > 0) {
167 		run_command("chattr", "-i", FILE_IMMUTABLE_PATH);
168 		SAFE_CLOSE(fd_immutable);
169 	}
170 	if (fd_swapfile > 0) {
171 		run_command("swapoff", FILE_SWAP_PATH, NULL);
172 		SAFE_CLOSE(fd_swapfile);
173 	}
174 	if (fd_dup > 0)
175 		SAFE_CLOSE(fd_dup);
176 	if (fd_copy > 0)
177 		SAFE_CLOSE(fd_copy);
178 	if (need_unlink > 0)
179 		SAFE_UNLINK(FILE_FIFO);
180 
181 	if (fd_pipe[0] > 0) {
182 		SAFE_CLOSE(fd_pipe[0]);
183 		SAFE_CLOSE(fd_pipe[1]);
184 	}
185 }
186 
setup(void)187 static void setup(void)
188 {
189 	syscall_info();
190 	char dev_path[1024];
191 
192 	cross_sup = verify_cross_fs_copy_support(FILE_SRC_PATH, FILE_MNTED_PATH);
193 
194 	if (access(FILE_DIR_PATH, F_OK) == -1)
195 		SAFE_MKDIR(FILE_DIR_PATH, 0777);
196 	/*
197 	 * call tst_find_free_loopdev(), avoid overwriting its
198 	 * content on used loopdev.
199 	 */
200 	loop_devn = tst_find_free_loopdev(dev_path, sizeof(dev_path));
201 
202 	SAFE_MKNOD(FILE_FIFO, S_IFIFO | 0777, 0);
203 	need_unlink = 1;
204 
205 	fd_src    = SAFE_OPEN(FILE_SRC_PATH, O_RDWR | O_CREAT, 0664);
206 	fd_dest   = SAFE_OPEN(FILE_DEST_PATH, O_RDWR | O_CREAT, 0664);
207 	fd_rdonly = SAFE_OPEN(FILE_RDONL_PATH, O_RDONLY | O_CREAT, 0664);
208 	fd_dir    = SAFE_OPEN(FILE_DIR_PATH, O_DIRECTORY);
209 	fd_closed = -1;
210 	fd_append = SAFE_OPEN(FILE_DEST_PATH,
211 			O_RDWR | O_CREAT | O_APPEND, 0664);
212 	fd_immutable = SAFE_OPEN(FILE_IMMUTABLE_PATH, O_RDWR | O_CREAT, 0664);
213 	fd_swapfile = SAFE_OPEN(FILE_SWAP_PATH, O_RDWR | O_CREAT, 0600);
214 
215 	if (loop_devn != -1)
216 		fd_blkdev = SAFE_OPEN(dev_path, O_RDWR, 0600);
217 
218 	fd_chrdev = SAFE_OPEN(FILE_CHRDEV, O_RDWR, 0600);
219 	fd_fifo = SAFE_OPEN(FILE_FIFO, O_RDWR, 0600);
220 
221 	SAFE_PIPE(fd_pipe);
222 
223 	SAFE_WRITE(1, fd_src, CONTENT, CONTSIZE);
224 	close(fd_src);
225 	fd_src = SAFE_OPEN(FILE_SRC_PATH, O_RDONLY, 0664);
226 	fd_dup = SAFE_OPEN(FILE_SRC_PATH, O_WRONLY|O_CREAT, 0666);
227 
228 	fd_copy = SAFE_OPEN(FILE_COPY_PATH, O_RDWR | O_CREAT | O_TRUNC, 0664);
229 	chattr_i_nsup = run_command("chattr", "+i", FILE_IMMUTABLE_PATH);
230 
231 	if (!tst_fs_has_free(".", sysconf(_SC_PAGESIZE) * 10, TST_BYTES)) {
232 		tst_res(TCONF, "Insufficient disk space to create swap file");
233 		swap_nsup = 3;
234 		return;
235 	}
236 
237 	if (tst_fill_file(FILE_SWAP_PATH, 0, sysconf(_SC_PAGESIZE), 10) != 0) {
238 		tst_res(TCONF, "Failed to create swapfile");
239 		swap_nsup = 4;
240 		return;
241 	}
242 
243 	swap_nsup = run_command("mkswap", FILE_SWAP_PATH, NULL);
244 	swap_nsup = run_command("swapon", FILE_SWAP_PATH, NULL);
245 }
246 
247 static struct tst_test test = {
248 	.test = verify_copy_file_range,
249 	.tcnt = ARRAY_SIZE(tcases),
250 	.setup = setup,
251 	.cleanup = cleanup,
252 	.mount_device = 1,
253 	.mntpoint = MNTPOINT,
254 	.needs_root = 1,
255 	.test_variants = TEST_VARIANTS,
256 };
257