2 * Copyright (C) Jeremy Allison 2007
3 * Copyright (c) 2013 Ralph Boehme <sloowfranklin@gmail.com>
4 * All rights reserved. See COPYRIGHT.
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 * 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 the
13 * GNU General Public License for more details.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #endif /* HAVE_CONFIG_H */
27 #include <sys/socket.h>
31 #include <sys/select.h>
33 #include <atalk/adouble.h>
34 #include <atalk/logger.h>
35 #include <atalk/util.h>
37 static int ad_recvfile_init(const struct adouble *ad, int eid, off_t *off)
41 if (eid == ADEID_DFORK) {
42 fd = ad_data_fileno(ad);
44 *off += ad_getentryoff(ad, eid);
45 fd = ad_reso_fileno(ad);
52 * If tofd is -1, drain the incoming socket of count bytes without writing to the outgoing fd,
53 * if a write fails we do the same.
55 * Returns -1 on short reads from fromfd (read error) and sets errno.
57 * Returns number of bytes written to 'tofd' or thrown away if 'tofd == -1'.
58 * return != count then sets errno.
59 * Returns count if complete success.
62 #define TRANSFER_BUF_SIZE (128*1024)
64 static ssize_t default_sys_recvfile(int fromfd,
71 size_t bufsize = MIN(TRANSFER_BUF_SIZE, count);
72 size_t total_written = 0;
79 LOG(log_maxdebug, logtype_dsi, "default_recvfile: from = %d, to = %d, offset = %.0f, count = %lu\n",
80 fromfd, tofd, (double)offset, (unsigned long)count);
82 if ((buffer = malloc(bufsize)) == NULL)
85 while (total < count) {
86 size_t num_written = 0;
88 size_t toread = MIN(bufsize,count - total);
90 /* Read from socket - ignore EINTR. */
91 read_ret = read(fromfd, buffer, toread);
93 /* EOF or socket error. */
100 while (num_written < read_ret) {
104 write_ret = read_ret;
106 /* Write to file - ignore EINTR. */
107 write_ret = pwrite(tofd, buffer + num_written, read_ret - num_written, offset);
108 if (write_ret <= 0) {
109 /* write error - stop writing. */
115 num_written += (size_t)write_ret;
116 total_written += (size_t)write_ret;
123 /* Return the correct write error. */
126 return (ssize_t)total_written;
130 static int waitfordata(int socket)
133 int maxfd = socket + 1;
140 FD_SET(socket, &readfds);
141 if ((ret = select(maxfd, &readfds, NULL, NULL, NULL)) <= 0) {
142 if (ret == -1 && errno == EINTR)
144 LOG(log_error, logtype_dsi, "waitfordata: unexpected select return: %d %s",
145 ret, ret < 0 ? strerror(errno) : "");
148 if (FD_ISSET(socket, &readfds))
156 * Try and use the Linux system call to do this.
157 * Remember we only return -1 if the socket read
158 * failed. Else we return the number of bytes
159 * actually written. We always read count bytes
160 * from the network in the case of return != -1.
162 static ssize_t sys_recvfile(int fromfd, int tofd, off_t offset, size_t count, int splice_size)
164 static int pipefd[2] = { -1, -1 };
165 static bool try_splice_call = true;
166 size_t total_written = 0;
167 loff_t splice_offset = offset;
169 LOG(log_debug, logtype_dsi, "sys_recvfile: from = %d, to = %d, offset = %.0f, count = %lu",
170 fromfd, tofd, (double)offset, (unsigned long)count);
176 * Older Linux kernels have splice for sendfile,
177 * but it fails for recvfile. Ensure we only try
178 * this once and always fall back to the userspace
179 * implementation if recvfile splice fails. JRA.
182 if (!try_splice_call) {
187 if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
188 try_splice_call = false;
196 nread = splice(fromfd, NULL, pipefd[1], NULL, MIN(count, splice_size), SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
201 if (errno == EAGAIN) {
202 if (waitfordata(fromfd) != -1)
206 if (total_written == 0 && (errno == EBADF || errno == EINVAL)) {
207 LOG(log_warning, logtype_dsi, "splice() doesn't work for recvfile");
208 try_splice_call = false;
216 while (to_write > 0) {
218 thistime = splice(pipefd[0], NULL, tofd, &splice_offset, to_write, SPLICE_F_MOVE);
221 to_write -= thistime;
224 total_written += nread;
229 LOG(log_maxdebug, logtype_dsi, "sys_recvfile: total_written: %zu", total_written);
231 return total_written;
235 /*****************************************************************
236 No recvfile system call - use the default 128 chunk implementation.
237 *****************************************************************/
239 ssize_t sys_recvfile(int fromfd, int tofd, off_t offset, size_t count)
241 return default_sys_recvfile(fromfd, tofd, offset, count);
245 /* read from a socket and write to an adouble file */
246 ssize_t ad_recvfile(struct adouble *ad, int eid, int sock, off_t off, size_t len, int splice_size)
250 off_t off_fork = off;
252 fd = ad_recvfile_init(ad, eid, &off_fork);
253 if ((cc = sys_recvfile(sock, fd, off_fork, len, splice_size)) != len)
256 if ((eid != ADEID_DFORK) && (off > ad_getentrylen(ad, eid)))
257 ad_setentrylen(ad, eid, off);