1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2014
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc.
18 */
19/*
20 * NAME
21 *        sendfile09.c
22 *
23 * DESCRIPTION
24 *        Testcase copied from sendfile02.c to test the basic functionality of
25 *        the sendfile(2) system call on large file. There is a kernel bug which
26 *        introduced by commit 8f9c0119d7ba and fixed by commit 5d73320a96fcc.
27 *
28 * ALGORITHM
29 *        1. call sendfile(2) with offset at 0
30 *        2. call sendfile(2) with offset at 3GB
31 *
32 * USAGE:  <for command-line>
33 *  sendfile09 [-c n] [-i n] [-I x] [-P x] [-t]
34 *     where,
35 *             -i n : Execute test n times.
36 *             -I x : Execute test for x seconds.
37 *             -P x : Pause for x seconds between iterations.
38 *             -t   : Turn on syscall timing.
39 *
40 *
41 * RESTRICTIONS
42 *        Only supports 64bit systems and kernel 2.6.33 or above
43 */
44#include <stdio.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <sys/stat.h>
48#include <sys/sendfile.h>
49#include <sys/types.h>
50#include <unistd.h>
51#include <inttypes.h>
52#include "test.h"
53#include "safe_macros.h"
54
55#ifndef OFF_T
56#define OFF_T off_t
57#endif /* Not def: OFF_T */
58
59TCID_DEFINE(sendfile09);
60
61static char *in_file = "in";
62static char *out_file = "out";
63static int fd;
64static int in_fd;
65static int out_fd;
66
67static void cleanup(void);
68static void setup(void);
69
70#define ONE_GB (INT64_C(1) << 30)
71
72static struct test_case_t {
73	char *desc;
74	OFF_T offset;
75	int64_t count;
76	int64_t exp_retval;
77	int64_t exp_updated_offset;
78} testcases[] = {
79	{ "Test sendfile(2) with offset at 0",
80		0, ONE_GB, ONE_GB, ONE_GB},
81	{ "Test sendfile(2) with offset at 3GB",
82		3*ONE_GB, ONE_GB, ONE_GB, 4*ONE_GB}
83};
84
85static int TST_TOTAL = ARRAY_SIZE(testcases);
86
87void do_sendfile(struct test_case_t *t)
88{
89	off_t before_pos, after_pos;
90
91	out_fd = SAFE_OPEN(cleanup, out_file, O_WRONLY);
92	in_fd = SAFE_OPEN(cleanup, in_file, O_RDONLY);
93	before_pos = SAFE_LSEEK(cleanup, in_fd, 0, SEEK_CUR);
94
95	TEST(sendfile(out_fd, in_fd, &t->offset, t->count));
96	if (TEST_RETURN == -1)
97		tst_brkm(TBROK | TTERRNO, cleanup, "sendfile(2) failed");
98
99	after_pos = SAFE_LSEEK(cleanup, in_fd, 0, SEEK_CUR);
100
101	if (TEST_RETURN != t->exp_retval) {
102		tst_resm(TFAIL, "sendfile(2) failed to return "
103			"expected value, expected: %" PRId64 ", "
104			"got: %ld", t->exp_retval,
105			TEST_RETURN);
106	} else if (t->offset != t->exp_updated_offset) {
107		tst_resm(TFAIL, "sendfile(2) failed to update "
108			"OFFSET parameter to expected value, "
109			"expected: %" PRId64 ", got: %" PRId64,
110			t->exp_updated_offset,
111			(int64_t) t->offset);
112	} else if (before_pos != after_pos) {
113		tst_resm(TFAIL, "sendfile(2) updated the file position "
114			" of in_fd unexpectedly, expected file position: %"
115			PRId64 ", " " actual file position %" PRId64,
116			(int64_t) before_pos, (int64_t) after_pos);
117	} else {
118		tst_resm(TPASS, "%s", t->desc);
119	}
120
121	close(in_fd);
122	close(out_fd);
123}
124
125/*
126 * setup() - performs all ONE TIME setup for this test.
127 */
128void setup(void)
129{
130	int i;
131
132	tst_sig(FORK, DEF_HANDLER, cleanup);
133	TEST_PAUSE;
134
135	/* make a temporary directory and cd to it */
136	tst_tmpdir();
137
138	if (!tst_fs_has_free(NULL, ".", 5, TST_GB))
139		tst_brkm(TCONF, cleanup, "sendfile(2) on large file"
140			" needs 5G free space.");
141
142	/* create a 4G file */
143	fd = SAFE_CREAT(cleanup, in_file, 00700);
144	for (i = 1; i <= (4 * 1024); i++) {
145		SAFE_LSEEK(cleanup, fd, 1024 * 1024 - 1, SEEK_CUR);
146		SAFE_WRITE(cleanup, 1, fd, "C", 1);
147	}
148	close(fd);
149
150	fd = SAFE_CREAT(cleanup, out_file, 00700);
151	close(fd);
152}
153
154void cleanup(void)
155{
156	if (fd > 0)
157		close(fd);
158
159	if (in_fd > 0)
160		close(in_fd);
161
162	if (out_fd > 0)
163		close(out_fd);
164
165	tst_rmdir();
166}
167
168int main(int ac, char **av)
169{
170	int i;
171	int lc;
172
173#if __WORDSIZE == 32
174	tst_brkm(TCONF, NULL, "This test is only for 64bit");
175#endif
176
177	if (tst_kvercmp(2, 6, 33) < 0) {
178		tst_resm(TINFO, "sendfile(2) on large file "
179			"skipped for kernels < 2.6.33");
180		return 0;
181	}
182
183	tst_parse_opts(ac, av, NULL, NULL);
184
185	setup();
186
187	/*
188	 * The following loop checks looping state if -c option given
189	 */
190	for (lc = 0; TEST_LOOPING(lc); lc++) {
191		tst_count = 0;
192		for (i = 0; i < TST_TOTAL; ++i)
193			do_sendfile(&testcases[i]);
194	}
195
196	cleanup();
197	tst_exit();
198}
199