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