root/source3/lib/recvfile.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. default_sys_recvfile
  2. sys_recvfile
  3. sys_recvfile
  4. drain_socket

   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 
  50 static ssize_t default_sys_recvfile(int fromfd,
     /* [<][>][^][v][top][bottom][index][help] */
  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 
 143 ssize_t sys_recvfile(int fromfd,
     /* [<][>][^][v][top][bottom][index][help] */
 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 
 234 ssize_t sys_recvfile(int fromfd,
     /* [<][>][^][v][top][bottom][index][help] */
 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 
 247 ssize_t drain_socket(int sockfd, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
 248 {
 249         return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count);
 250 }

/* [<][>][^][v][top][bottom][index][help] */