• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) Linux Test Project, 2017
3  *
4  * This program is free software;  you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  * the GNU General Public License for more details.
13  */
14 
15 #define _GNU_SOURCE
16 #include <stdio.h>
17 #include <errno.h>
18 #include "tst_test.h"
19 #include "tst_safe_stdio.h"
20 #include "lapi/syscalls.h"
21 
22 #define TEST_FILE_1 "copy_file_range_ltp01.txt"
23 #define TEST_FILE_2 "copy_file_range_ltp02.txt"
24 #define STR "abcdefghijklmnopqrstuvwxyz12345\n"
25 
26 #define verbose 0
27 
28 static size_t *len_arr;
29 static loff_t **off_arr;
30 static int len_sz, off_sz;
31 
setup(void)32 static void setup(void)
33 {
34 	int i, fd, page_size;
35 
36 	page_size = getpagesize();
37 
38 	fd = SAFE_OPEN(TEST_FILE_1, O_RDWR | O_CREAT, 0664);
39 	/* Writing page_size * 4 of data into test file */
40 	for (i = 0; i < (int)(page_size * 4); i++)
41 		SAFE_WRITE(1, fd, STR, strlen(STR));
42 	SAFE_CLOSE(fd);
43 
44 	len_sz = 4;
45 	len_arr = malloc(sizeof(size_t) * len_sz);
46 	len_arr[0] = 11;
47 	len_arr[1] = page_size - 1;
48 	len_arr[2] = page_size;
49 	len_arr[3] = page_size + 1;
50 
51 	off_sz = 6;
52 	off_arr = malloc(sizeof(loff_t *) * off_sz);
53 	for (i = 1; i < off_sz; i++)
54 		off_arr[i] = malloc(sizeof(loff_t));
55 
56 	off_arr[0] = NULL;
57 	*off_arr[1] = 0;
58 	*off_arr[2] = 17;
59 	*off_arr[3] = page_size - 1;
60 	*off_arr[4] = page_size;
61 	*off_arr[5] = page_size + 1;
62 }
63 
check_file_content(const char * fname1,const char * fname2,loff_t * off1,loff_t * off2,size_t len)64 static int check_file_content(const char *fname1, const char *fname2,
65 	loff_t *off1, loff_t *off2, size_t len)
66 {
67 	FILE *fp1, *fp2;
68 	int ch1, ch2;
69 	size_t count = 0;
70 
71 	fp1 = SAFE_FOPEN(fname1, "r");
72 	if (off1 && fseek(fp1, *off1, SEEK_SET))
73 		tst_brk(TBROK | TERRNO, "fseek() failed");
74 
75 	fp2 = SAFE_FOPEN(fname2, "r");
76 	if (off2 && fseek(fp2, *off2, SEEK_SET))
77 		tst_brk(TBROK | TERRNO, "fseek() failed");
78 
79 	do {
80 		ch1 = getc(fp1);
81 		ch2 = getc(fp2);
82 		count++;
83 	} while ((count < len) && (ch1 == ch2));
84 
85 	SAFE_FCLOSE(fp1);
86 	SAFE_FCLOSE(fp2);
87 
88 	return !(ch1 == ch2);
89 }
90 
check_file_offset(const char * m,int fd,loff_t len,loff_t * off_ori,loff_t * off_after)91 static int check_file_offset(const char *m, int fd, loff_t len,
92 	loff_t *off_ori, loff_t *off_after)
93 {
94 	int ret = 0;
95 
96 	if (off_ori) {
97 		/* FD should stay untouched, and off_in/out is updated */
98 		loff_t fd_off = SAFE_LSEEK(fd, 0, SEEK_CUR);
99 
100 		if (fd_off == 0) {
101 			if (verbose)
102 				tst_res(TPASS, "%s FD offset unchanged", m);
103 		} else {
104 			tst_res(TFAIL, "%s FD offset changed: %ld",
105 				m, (long)fd_off);
106 			ret = 1;
107 		}
108 
109 		if (!off_after) {
110 			tst_res(TFAIL, "%s offset is NULL", m);
111 			ret = 1;
112 		}
113 
114 		if ((off_after) && (*off_ori + len == *off_after)) {
115 			if (verbose) {
116 				tst_res(TPASS, "%s offset advanced as"
117 					" expected: %ld", m, (long)*off_after);
118 			}
119 		} else {
120 			tst_res(TFAIL, "%s offset unexpected value: %ld",
121 				m, (long)*off_after);
122 			ret = 1;
123 		}
124 	} else {
125 		/* FD offset is advanced by len */
126 		loff_t fd_off = SAFE_LSEEK(fd, 0, SEEK_CUR);
127 
128 		if (fd_off == len) {
129 			if (verbose) {
130 				tst_res(TPASS, "%s FD offset changed as"
131 					" expected: %ld", m, (long)fd_off);
132 			}
133 		} else {
134 			tst_res(TFAIL, "%s FD offset unexpected value: %ld",
135 				m, (long)fd_off);
136 			ret = 1;
137 		}
138 	}
139 
140 	return ret;
141 }
142 
test_one(size_t len,loff_t * off_in,loff_t * off_out)143 static void test_one(size_t len, loff_t *off_in, loff_t *off_out)
144 {
145 	size_t to_copy = len;
146 	int fd_in, fd_out, ret;
147 	loff_t *off_in_ori = off_in;
148 	loff_t *off_out_ori = off_out;
149 	loff_t off_in_copy;
150 	loff_t off_out_copy;
151 	char str_off_in[32], str_off_out[32];
152 
153 	if (off_in) {
154 		off_in_copy = *off_in;
155 		off_in = &off_in_copy;
156 		sprintf(str_off_in, "%ld", (long)*off_in);
157 	} else {
158 		strcpy(str_off_in, "NULL");
159 	}
160 
161 	if (off_out) {
162 		off_out_copy = *off_out;
163 		off_out = &off_out_copy;
164 		sprintf(str_off_out, "%ld", (long)*off_out);
165 	} else {
166 		strcpy(str_off_out, "NULL");
167 	}
168 
169 	fd_in = SAFE_OPEN(TEST_FILE_1, O_RDONLY);
170 	fd_out = SAFE_OPEN(TEST_FILE_2, O_CREAT | O_WRONLY | O_TRUNC, 0644);
171 
172 	/*
173 	 * copy_file_range() will return the number of bytes copied between
174 	 * files. This could be less than the length originally requested.
175 	 */
176 	do {
177 		TEST(tst_syscall(__NR_copy_file_range, fd_in, off_in, fd_out,
178 			off_out, to_copy, 0));
179 		if (TST_RET == -1) {
180 			tst_res(TFAIL | TTERRNO, "copy_file_range() failed");
181 			SAFE_CLOSE(fd_in);
182 			SAFE_CLOSE(fd_out);
183 			return;
184 		}
185 
186 		to_copy -= TST_RET;
187 	} while (to_copy > 0);
188 
189 	ret = check_file_content(TEST_FILE_1, TEST_FILE_2,
190 		off_in_ori, off_out_ori, len);
191 	if (ret)
192 		tst_res(TFAIL, "file contents do not match");
193 
194 	ret |= check_file_offset("(in)", fd_in, len, off_in_ori, off_in);
195 	ret |= check_file_offset("(out)", fd_out, len, off_out_ori, off_out);
196 
197 	tst_res(ret == 0 ? TPASS : TFAIL, "off_in: %s, off_out: %s, len: %ld",
198 			str_off_in, str_off_out, (long)len);
199 
200 	SAFE_CLOSE(fd_in);
201 	SAFE_CLOSE(fd_out);
202 }
203 
copy_file_range_verify(void)204 static void copy_file_range_verify(void)
205 {
206 	int i, j, k;
207 
208 	for (i = 0; i < len_sz; i++)
209 		for (j = 0; j < off_sz; j++)
210 			for (k = 0; k < off_sz; k++)
211 				test_one(len_arr[i], off_arr[j], off_arr[k]);
212 }
213 
214 static struct tst_test test = {
215 	.setup = setup,
216 	.needs_tmpdir = 1,
217 	.test_all = copy_file_range_verify,
218 };
219