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