Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/regression/sockets/sendfile/sendfile.c
103855 views
1
/*-
2
* Copyright (c) 2006 Robert N. M. Watson
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/types.h>
28
#include <sys/socket.h>
29
#include <sys/stat.h>
30
#include <sys/wait.h>
31
32
#include <netinet/in.h>
33
34
#include <err.h>
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <limits.h>
38
#include <md5.h>
39
#include <signal.h>
40
#include <stdint.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <unistd.h>
45
46
/*
47
* Simple regression test for sendfile. Creates a file sized at four pages
48
* and then proceeds to send it over a series of sockets, exercising a number
49
* of cases and performing limited validation.
50
*/
51
52
#define FAIL(msg) {printf("# %s\n", msg); \
53
return (-1);}
54
55
#define FAIL_ERR(msg) {printf("# %s: %s\n", msg, strerror(errno)); \
56
return (-1);}
57
58
#define TEST_PORT 5678
59
#define TEST_MAGIC 0x4440f7bb
60
#define TEST_PAGES 4
61
#define TEST_SECONDS 30
62
63
struct test_header {
64
uint32_t th_magic;
65
uint32_t th_header_length;
66
uint32_t th_offset;
67
uint32_t th_length;
68
char th_md5[33];
69
};
70
71
struct sendfile_test {
72
uint32_t hdr_length;
73
uint32_t offset;
74
uint32_t length;
75
uint32_t file_size;
76
};
77
78
static int file_fd;
79
static char path[PATH_MAX];
80
static int listen_socket;
81
static int accept_socket;
82
83
static int test_th(struct test_header *th, uint32_t *header_length,
84
uint32_t *offset, uint32_t *length);
85
static void signal_alarm(int signum);
86
static void setup_alarm(int seconds);
87
static void cancel_alarm(void);
88
static int receive_test(void);
89
static void run_child(void);
90
static int new_test_socket(int *connect_socket);
91
static void init_th(struct test_header *th, uint32_t header_length,
92
uint32_t offset, uint32_t length);
93
static int send_test(int connect_socket, struct sendfile_test);
94
static int write_test_file(size_t file_size);
95
static void run_parent(void);
96
static void cleanup(void);
97
98
99
static int
100
test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset,
101
uint32_t *length)
102
{
103
104
if (th->th_magic != htonl(TEST_MAGIC))
105
FAIL("magic number not found in header")
106
*header_length = ntohl(th->th_header_length);
107
*offset = ntohl(th->th_offset);
108
*length = ntohl(th->th_length);
109
return (0);
110
}
111
112
static void
113
signal_alarm(int signum)
114
{
115
(void)signum;
116
117
printf("# test timeout\n");
118
119
if (accept_socket > 0)
120
close(accept_socket);
121
if (listen_socket > 0)
122
close(listen_socket);
123
124
_exit(-1);
125
}
126
127
static void
128
setup_alarm(int seconds)
129
{
130
struct itimerval itv;
131
bzero(&itv, sizeof(itv));
132
(void)seconds;
133
itv.it_value.tv_sec = seconds;
134
135
signal(SIGALRM, signal_alarm);
136
setitimer(ITIMER_REAL, &itv, NULL);
137
}
138
139
static void
140
cancel_alarm(void)
141
{
142
struct itimerval itv;
143
bzero(&itv, sizeof(itv));
144
setitimer(ITIMER_REAL, &itv, NULL);
145
}
146
147
static int
148
receive_test(void)
149
{
150
uint32_t header_length, offset, length, counter;
151
struct test_header th;
152
ssize_t len;
153
char buf[10240];
154
MD5_CTX md5ctx;
155
char *rxmd5;
156
157
len = read(accept_socket, &th, sizeof(th));
158
if (len < 0 || (size_t)len < sizeof(th))
159
FAIL_ERR("read")
160
161
if (test_th(&th, &header_length, &offset, &length) != 0)
162
return (-1);
163
164
MD5Init(&md5ctx);
165
166
counter = 0;
167
while (1) {
168
len = read(accept_socket, buf, sizeof(buf));
169
if (len < 0 || len == 0)
170
break;
171
counter += len;
172
MD5Update(&md5ctx, buf, len);
173
}
174
175
rxmd5 = MD5End(&md5ctx, NULL);
176
177
if ((counter != header_length+length) ||
178
memcmp(th.th_md5, rxmd5, 33) != 0)
179
FAIL("receive length mismatch")
180
181
free(rxmd5);
182
return (0);
183
}
184
185
static void
186
run_child(void)
187
{
188
struct sockaddr_in sin;
189
int rc = 0;
190
191
listen_socket = socket(PF_INET, SOCK_STREAM, 0);
192
if (listen_socket < 0) {
193
printf("# socket: %s\n", strerror(errno));
194
rc = -1;
195
}
196
197
if (!rc) {
198
bzero(&sin, sizeof(sin));
199
sin.sin_len = sizeof(sin);
200
sin.sin_family = AF_INET;
201
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
202
sin.sin_port = htons(TEST_PORT);
203
204
if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
205
printf("# bind: %s\n", strerror(errno));
206
rc = -1;
207
}
208
}
209
210
if (!rc && listen(listen_socket, -1) < 0) {
211
printf("# listen: %s\n", strerror(errno));
212
rc = -1;
213
}
214
215
if (!rc) {
216
accept_socket = accept(listen_socket, NULL, NULL);
217
setup_alarm(TEST_SECONDS);
218
if (receive_test() != 0)
219
rc = -1;
220
}
221
222
cancel_alarm();
223
if (accept_socket > 0)
224
close(accept_socket);
225
if (listen_socket > 0)
226
close(listen_socket);
227
228
_exit(rc);
229
}
230
231
static int
232
new_test_socket(int *connect_socket)
233
{
234
struct sockaddr_in sin;
235
int rc = 0;
236
237
*connect_socket = socket(PF_INET, SOCK_STREAM, 0);
238
if (*connect_socket < 0)
239
FAIL_ERR("socket")
240
241
bzero(&sin, sizeof(sin));
242
sin.sin_len = sizeof(sin);
243
sin.sin_family = AF_INET;
244
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
245
sin.sin_port = htons(TEST_PORT);
246
247
if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0)
248
FAIL_ERR("connect")
249
250
return (rc);
251
}
252
253
static void
254
init_th(struct test_header *th, uint32_t header_length, uint32_t offset,
255
uint32_t length)
256
{
257
bzero(th, sizeof(*th));
258
th->th_magic = htonl(TEST_MAGIC);
259
th->th_header_length = htonl(header_length);
260
th->th_offset = htonl(offset);
261
th->th_length = htonl(length);
262
263
MD5FileChunk(path, th->th_md5, offset, length);
264
}
265
266
static int
267
send_test(int connect_socket, struct sendfile_test test)
268
{
269
struct test_header th;
270
struct sf_hdtr hdtr, *hdtrp;
271
struct iovec headers;
272
char *header;
273
ssize_t len;
274
int length;
275
off_t off;
276
277
len = lseek(file_fd, 0, SEEK_SET);
278
if (len != 0)
279
FAIL_ERR("lseek")
280
281
struct stat st;
282
if (fstat(file_fd, &st) < 0)
283
FAIL_ERR("fstat")
284
length = st.st_size - test.offset;
285
if (test.length > 0 && test.length < (uint32_t)length)
286
length = test.length;
287
288
init_th(&th, test.hdr_length, test.offset, length);
289
290
len = write(connect_socket, &th, sizeof(th));
291
if (len != sizeof(th))
292
return (-1);
293
294
if (test.hdr_length != 0) {
295
header = malloc(test.hdr_length);
296
if (header == NULL)
297
FAIL_ERR("malloc")
298
299
hdtrp = &hdtr;
300
bzero(&headers, sizeof(headers));
301
headers.iov_base = header;
302
headers.iov_len = test.hdr_length;
303
bzero(&hdtr, sizeof(hdtr));
304
hdtr.headers = &headers;
305
hdtr.hdr_cnt = 1;
306
hdtr.trailers = NULL;
307
hdtr.trl_cnt = 0;
308
} else {
309
hdtrp = NULL;
310
header = NULL;
311
}
312
313
if (sendfile(file_fd, connect_socket, test.offset, test.length,
314
hdtrp, &off, 0) < 0) {
315
if (header != NULL)
316
free(header);
317
FAIL_ERR("sendfile")
318
}
319
320
if (length == 0) {
321
struct stat sb;
322
323
if (fstat(file_fd, &sb) == 0)
324
length = sb.st_size - test.offset;
325
}
326
327
if (header != NULL)
328
free(header);
329
330
if (off != length)
331
FAIL("offset != length")
332
333
return (0);
334
}
335
336
static int
337
write_test_file(size_t file_size)
338
{
339
char *page_buffer;
340
ssize_t len;
341
static size_t current_file_size = 0;
342
343
if (file_size == current_file_size)
344
return (0);
345
else if (file_size < current_file_size) {
346
if (ftruncate(file_fd, file_size) != 0)
347
FAIL_ERR("ftruncate");
348
current_file_size = file_size;
349
return (0);
350
}
351
352
page_buffer = malloc(file_size);
353
if (page_buffer == NULL)
354
FAIL_ERR("malloc")
355
bzero(page_buffer, file_size);
356
357
len = write(file_fd, page_buffer, file_size);
358
if (len < 0)
359
FAIL_ERR("write")
360
361
len = lseek(file_fd, 0, SEEK_SET);
362
if (len < 0)
363
FAIL_ERR("lseek")
364
if (len != 0)
365
FAIL("len != 0")
366
367
free(page_buffer);
368
current_file_size = file_size;
369
return (0);
370
}
371
372
static void
373
run_parent(void)
374
{
375
int connect_socket;
376
int status;
377
int test_num;
378
int test_count;
379
int pid;
380
size_t desired_file_size = 0;
381
382
const int pagesize = getpagesize();
383
384
struct sendfile_test tests[] = {
385
{ .hdr_length = 0, .offset = 0, .length = 1 },
386
{ .hdr_length = 0, .offset = 0, .length = pagesize },
387
{ .hdr_length = 0, .offset = 1, .length = 1 },
388
{ .hdr_length = 0, .offset = 1, .length = pagesize },
389
{ .hdr_length = 0, .offset = pagesize, .length = pagesize },
390
{ .hdr_length = 0, .offset = 0, .length = 2*pagesize },
391
{ .hdr_length = 0, .offset = 0, .length = 0 },
392
{ .hdr_length = 0, .offset = pagesize, .length = 0 },
393
{ .hdr_length = 0, .offset = 2*pagesize, .length = 0 },
394
{ .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 },
395
{ .hdr_length = 0, .offset = 0, .length = pagesize,
396
.file_size = 1 }
397
};
398
399
test_count = sizeof(tests) / sizeof(tests[0]);
400
printf("1..%d\n", test_count);
401
402
for (test_num = 1; test_num <= test_count; test_num++) {
403
404
desired_file_size = tests[test_num - 1].file_size;
405
if (desired_file_size == 0)
406
desired_file_size = TEST_PAGES * pagesize;
407
if (write_test_file(desired_file_size) != 0) {
408
printf("not ok %d\n", test_num);
409
continue;
410
}
411
412
pid = fork();
413
if (pid == -1) {
414
printf("not ok %d\n", test_num);
415
continue;
416
}
417
418
if (pid == 0)
419
run_child();
420
421
usleep(250000);
422
423
if (new_test_socket(&connect_socket) != 0) {
424
printf("not ok %d\n", test_num);
425
kill(pid, SIGALRM);
426
close(connect_socket);
427
continue;
428
}
429
430
if (send_test(connect_socket, tests[test_num-1]) != 0) {
431
printf("not ok %d\n", test_num);
432
kill(pid, SIGALRM);
433
close(connect_socket);
434
continue;
435
}
436
437
close(connect_socket);
438
if (waitpid(pid, &status, 0) == pid) {
439
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
440
printf("%s %d\n", "ok", test_num);
441
else
442
printf("%s %d\n", "not ok", test_num);
443
}
444
else {
445
printf("not ok %d\n", test_num);
446
}
447
}
448
}
449
450
static void
451
cleanup(void)
452
{
453
454
unlink(path);
455
}
456
457
int
458
main(int argc, char *argv[])
459
{
460
461
path[0] = '\0';
462
463
if (argc == 1) {
464
snprintf(path, sizeof(path), "sendfile.XXXXXXXXXXXX");
465
file_fd = mkstemp(path);
466
if (file_fd == -1)
467
FAIL_ERR("mkstemp");
468
} else if (argc == 2) {
469
(void)strlcpy(path, argv[1], sizeof(path));
470
file_fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);
471
if (file_fd == -1)
472
FAIL_ERR("open");
473
} else {
474
FAIL("usage: sendfile [path]");
475
}
476
477
atexit(cleanup);
478
479
run_parent();
480
return (0);
481
}
482
483