1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *   Copyright (c) Red Hat Inc., 2007
5 *
6 *   This program is free software;  you can redistribute it and/or modify
7 *   it under the terms of the GNU General Public License as published by
8 *   the Free Software Foundation; either version 2 of the License, or
9 *   (at your option) any later version.
10 *
11 *   This program is distributed in the hope that it will be useful,
12 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
14 *   the GNU General Public License for more details.
15 *
16 *   You should have received a copy of the GNU General Public License
17 *   along with this program;  if not, write to the Free Software
18 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * NAME
23 *	sendfile05.c
24 *
25 * DESCRIPTION
26 *	Testcase to test that sendfile(2) system call returns EINVAL
27 *	when passing negative offset.
28 *
29 * USAGE:  <for command-line>
30 *  sendfile05 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
31 *     where,
32 *             -f   : Turn off functionality Testing.
33 *             -i n : Execute test n times.
34 *             -I x : Execute test for x seconds.
35 *             -P x : Pause for x seconds between iterations.
36 *             -t   : Turn on syscall timing.
37 *
38 * HISTORY
39 *	11/2007 Copyed from sendfile02.c by Masatake YAMATO
40 *
41 * RESTRICTIONS
42 *	NONE
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 <sys/socket.h>
51#include <sys/mman.h>
52#include <netinet/in.h>
53#include <arpa/inet.h>
54#include "test.h"
55
56#ifndef OFF_T
57#define OFF_T off_t
58#endif /* Not def: OFF_T */
59
60TCID_DEFINE(sendfile05);
61
62char in_file[100];
63char out_file[100];
64int out_fd;
65pid_t child_pid;
66static int sockfd, s;
67static struct sockaddr_in sin1;	/* shared between do_child and create_server */
68
69void cleanup(void);
70void do_child(void);
71void setup(void);
72int create_server(void);
73
74int TST_TOTAL = 1;
75
76#ifdef UCLINUX
77static char *argv0;
78#endif
79
80void do_sendfile(void)
81{
82	OFF_T offset;
83	int in_fd;
84	struct stat sb;
85
86	out_fd = create_server();
87
88	if ((in_fd = open(in_file, O_RDONLY)) < 0) {
89		tst_brkm(TBROK, cleanup, "open failed: %d", errno);
90	}
91	if (stat(in_file, &sb) < 0) {
92		tst_brkm(TBROK, cleanup, "stat failed: %d", errno);
93	}
94
95	offset = -1;
96	TEST(sendfile(out_fd, in_fd, &offset, sb.st_size));
97
98	if (TEST_RETURN != -1) {
99		tst_resm(TFAIL, "call succeeded unexpectedly");
100	} else {
101		if (TEST_ERRNO != EINVAL) {
102			tst_resm(TFAIL, "sendfile returned unexpected "
103				 "errno, expected: %d, got: %d",
104				 EINVAL, TEST_ERRNO);
105		} else {
106			tst_resm(TPASS, "sendfile() returned %d : %s",
107				 TEST_ERRNO, strerror(TEST_ERRNO));
108		}
109	}
110
111	shutdown(sockfd, SHUT_RDWR);
112	shutdown(s, SHUT_RDWR);
113	kill(child_pid, SIGKILL);
114	close(in_fd);
115}
116
117/*
118 * do_child
119 */
120void do_child(void)
121{
122	int lc;
123	socklen_t length;
124	char rbuf[4096];
125
126	for (lc = 0; TEST_LOOPING(lc); lc++) {
127		length = sizeof(sin1);
128		recvfrom(sockfd, rbuf, 4096, 0, (struct sockaddr *)&sin1,
129			 &length);
130	}
131	exit(0);
132}
133
134/*
135 * setup() - performs all ONE TIME setup for this test.
136 */
137void setup(void)
138{
139	int fd;
140	char buf[100];
141
142	tst_sig(FORK, DEF_HANDLER, cleanup);
143
144	TEST_PAUSE;
145
146	/* make a temporary directory and cd to it */
147	tst_tmpdir();
148	sprintf(in_file, "in.%d", getpid());
149	if ((fd = creat(in_file, 00700)) < 0) {
150		tst_brkm(TBROK, cleanup, "creat failed in setup, errno: %d",
151			 errno);
152	}
153	sprintf(buf, "abcdefghijklmnopqrstuvwxyz");
154	if (write(fd, buf, strlen(buf)) < 0) {
155		tst_brkm(TBROK, cleanup, "write failed, errno: %d", errno);
156	}
157	close(fd);
158	sprintf(out_file, "out.%d", getpid());
159}
160
161/*
162 * cleanup() - performs all ONE TIME cleanup for this test at
163 *	       completion or premature exit.
164 */
165void cleanup(void)
166{
167
168	close(out_fd);
169	/* delete the test directory created in setup() */
170	tst_rmdir();
171
172}
173
174int create_server(void)
175{
176	static int count = 0;
177	socklen_t slen = sizeof(sin1);
178
179	sockfd = socket(PF_INET, SOCK_DGRAM, 0);
180	if (sockfd < 0) {
181		tst_brkm(TBROK, cleanup, "call to socket() failed: %s",
182			 strerror(errno));
183		return -1;
184	}
185	sin1.sin_family = AF_INET;
186	sin1.sin_port = 0; /* pick random free port */
187	sin1.sin_addr.s_addr = INADDR_ANY;
188	count++;
189	if (bind(sockfd, (struct sockaddr *)&sin1, sizeof(sin1)) < 0) {
190		tst_brkm(TBROK, cleanup, "call to bind() failed: %s",
191			 strerror(errno));
192		return -1;
193	}
194	if (getsockname(sockfd, (struct sockaddr *)&sin1, &slen) == -1)
195		tst_brkm(TBROK | TERRNO, cleanup, "getsockname failed");
196
197	child_pid = FORK_OR_VFORK();
198	if (child_pid < 0) {
199		tst_brkm(TBROK, cleanup, "client/server fork failed: %s",
200			 strerror(errno));
201		return -1;
202	}
203	if (!child_pid) {	/* child */
204#ifdef UCLINUX
205		if (self_exec(argv0, "") < 0) {
206			tst_brkm(TBROK, cleanup, "self_exec failed");
207			return -1;
208
209		}
210#else
211		do_child();
212#endif
213	}
214
215	s = socket(PF_INET, SOCK_DGRAM, 0);
216	inet_aton("127.0.0.1", &sin1.sin_addr);
217	if (s < 0) {
218		tst_brkm(TBROK, cleanup, "call to socket() failed: %s",
219			 strerror(errno));
220		return -1;
221	}
222	if (connect(s, (struct sockaddr *)&sin1, sizeof(sin1)) < 0) {
223		tst_brkm(TBROK, cleanup, "call to connect() failed: %s",
224			 strerror(errno));
225	}
226	return s;
227
228}
229
230int main(int ac, char **av)
231{
232	int lc;
233
234	tst_parse_opts(ac, av, NULL, NULL);
235#ifdef UCLINUX
236	argv0 = av[0];
237	maybe_run_child(&do_child, "");
238#endif
239
240	setup();
241
242	/*
243	 * The following loop checks looping state if -c option given
244	 */
245	for (lc = 0; TEST_LOOPING(lc); lc++) {
246		tst_count = 0;
247
248		do_sendfile();
249	}
250	cleanup();
251
252	tst_exit();
253}
254