1/*
2 Unix SMB/Netbios implementation.
3 Version 3.2.x
4 recvfile implementations.
5 Copyright (C) Jeremy Allison 2007.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
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 the
14 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, see <http://www.gnu.org/licenses/>.
18*/
19
20/*
21 * This file handles the OS dependent recvfile implementations.
22 * The API is such that it returns -1 on error, else returns the
23 * number of bytes written.
24 */
25
26#include "includes.h"
27
28/* Do this on our own in TRANSFER_BUF_SIZE chunks.
29 * It's safe to make direct syscalls to lseek/write here
30 * as we're below the Samba vfs layer.
31 *
32 * If tofd is -1 we just drain the incoming socket of count
33 * bytes without writing to the outgoing fd.
34 * If a write fails we do the same (to cope with disk full)
35 * errors.
36 *
37 * Returns -1 on short reads from fromfd (read error)
38 * and sets errno.
39 *
40 * Returns number of bytes written to 'tofd'
41 * or thrown away if 'tofd == -1'.
42 * return != count then sets errno.
43 * Returns count if complete success.
44 */
45
46#ifndef TRANSFER_BUF_SIZE
47#define TRANSFER_BUF_SIZE (128*1024)
48#endif
49
50static ssize_t default_sys_recvfile(int fromfd,
51			int tofd,
52			SMB_OFF_T offset,
53			size_t count)
54{
55	int saved_errno = 0;
56	size_t total = 0;
57	size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
58	size_t total_written = 0;
59	char *buffer = NULL;
60
61	DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
62		"offset=%.0f, count = %lu\n",
63		fromfd, tofd, (double)offset,
64		(unsigned long)count));
65
66	if (count == 0) {
67		return 0;
68	}
69
70	if (tofd != -1 && offset != (SMB_OFF_T)-1) {
71		if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
72			if (errno != ESPIPE) {
73				return -1;
74			}
75		}
76	}
77
78	buffer = SMB_MALLOC_ARRAY(char, bufsize);
79	if (buffer == NULL) {
80		return -1;
81	}
82
83	while (total < count) {
84		size_t num_written = 0;
85		ssize_t read_ret;
86		size_t toread = MIN(bufsize,count - total);
87
88		/* Read from socket - ignore EINTR. */
89		read_ret = sys_read(fromfd, buffer, toread);
90		if (read_ret <= 0) {
91			/* EOF or socket error. */
92			free(buffer);
93			return -1;
94		}
95
96		num_written = 0;
97
98		while (num_written < read_ret) {
99			ssize_t write_ret;
100
101			if (tofd == -1) {
102				write_ret = read_ret;
103			} else {
104				/* Write to file - ignore EINTR. */
105				write_ret = sys_write(tofd,
106						buffer + num_written,
107						read_ret - num_written);
108
109				if (write_ret <= 0) {
110					/* write error - stop writing. */
111					tofd = -1;
112					saved_errno = errno;
113					continue;
114				}
115			}
116
117			num_written += (size_t)write_ret;
118			total_written += (size_t)write_ret;
119		}
120
121		total += read_ret;
122	}
123
124	free(buffer);
125	if (saved_errno) {
126		/* Return the correct write error. */
127		errno = saved_errno;
128	}
129	return (ssize_t)total_written;
130}
131
132#if defined(HAVE_LINUX_SPLICE)
133
134/*
135 * Try and use the Linux system call to do this.
136 * Remember we only return -1 if the socket read
137 * failed. Else we return the number of bytes
138 * actually written. We always read count bytes
139 * from the network in the case of return != -1.
140 */
141
142
143ssize_t sys_recvfile(int fromfd,
144			int tofd,
145			SMB_OFF_T offset,
146			size_t count)
147{
148	static int pipefd[2] = { -1, -1 };
149	static bool try_splice_call = false;
150	size_t total_written = 0;
151	loff_t splice_offset = offset;
152
153	DEBUG(10,("sys_recvfile: from = %d, to = %d, "
154		"offset=%.0f, count = %lu\n",
155		fromfd, tofd, (double)offset,
156		(unsigned long)count));
157
158	if (count == 0) {
159		return 0;
160	}
161
162	/*
163	 * Older Linux kernels have splice for sendfile,
164	 * but it fails for recvfile. Ensure we only try
165	 * this once and always fall back to the userspace
166	 * implementation if recvfile splice fails. JRA.
167	 */
168
169	if (!try_splice_call) {
170		return default_sys_recvfile(fromfd,
171				tofd,
172				offset,
173				count);
174	}
175
176	if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
177		try_splice_call = false;
178		return default_sys_recvfile(fromfd, tofd, offset, count);
179	}
180
181	while (count > 0) {
182		int nread, to_write;
183
184		nread = splice(fromfd, NULL, pipefd[1], NULL,
185			       MIN(count, 16384), SPLICE_F_MOVE);
186		if (nread == -1) {
187			if (errno == EINTR) {
188				continue;
189			}
190			if (total_written == 0 &&
191			    (errno == EBADF || errno == EINVAL)) {
192				try_splice_call = false;
193				return default_sys_recvfile(fromfd, tofd,
194							    offset, count);
195			}
196			break;
197		}
198
199		to_write = nread;
200		while (to_write > 0) {
201			int thistime;
202			thistime = splice(pipefd[0], NULL, tofd,
203					  &splice_offset, to_write,
204					  SPLICE_F_MOVE);
205			if (thistime == -1) {
206				goto done;
207			}
208			to_write -= thistime;
209		}
210
211		total_written += nread;
212		count -= nread;
213	}
214
215 done:
216	if (total_written < count) {
217		int saved_errno = errno;
218		if (drain_socket(fromfd, count-total_written) !=
219				count-total_written) {
220			/* socket is dead. */
221			return -1;
222		}
223		errno = saved_errno;
224	}
225
226	return total_written;
227}
228#else
229
230/*****************************************************************
231 No recvfile system call - use the default 128 chunk implementation.
232*****************************************************************/
233
234ssize_t sys_recvfile(int fromfd,
235			int tofd,
236			SMB_OFF_T offset,
237			size_t count)
238{
239	return default_sys_recvfile(fromfd, tofd, offset, count);
240}
241#endif
242
243/*****************************************************************
244 Throw away "count" bytes from the client socket.
245*****************************************************************/
246
247ssize_t drain_socket(int sockfd, size_t count)
248{
249	return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count);
250}
251