LCOV - code coverage report
Current view: top level - source3/lib - recvfile.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 0 110 0.0 %
Date: 2024-04-21 15:09:00 Functions: 0 3 0.0 %

          Line data    Source code
       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             : #include "system/filesys.h"
      28             : #include "lib/util/sys_rw.h"
      29             : 
      30             : /* Do this on our own in TRANSFER_BUF_SIZE chunks.
      31             :  * It's safe to make direct syscalls to lseek/write here
      32             :  * as we're below the Samba vfs layer.
      33             :  *
      34             :  * Returns -1 on short reads from fromfd (read error)
      35             :  * and sets errno.
      36             :  *
      37             :  * Returns number of bytes written to 'tofd'
      38             :  * return != count then sets errno.
      39             :  * Returns count if complete success.
      40             :  */
      41             : 
      42             : #ifndef TRANSFER_BUF_SIZE
      43             : #define TRANSFER_BUF_SIZE (128*1024)
      44             : #endif
      45             : 
      46           0 : static ssize_t default_sys_recvfile(int fromfd,
      47             :                         int tofd,
      48             :                         off_t offset,
      49             :                         size_t count)
      50           0 : {
      51           0 :         int saved_errno = 0;
      52           0 :         size_t total = 0;
      53           0 :         size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
      54           0 :         size_t total_written = 0;
      55           0 :         char buffer[bufsize];
      56             : 
      57           0 :         DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
      58             :                 "offset=%.0f, count = %lu\n",
      59             :                 fromfd, tofd, (double)offset,
      60             :                 (unsigned long)count));
      61             : 
      62           0 :         if (count == 0) {
      63           0 :                 return 0;
      64             :         }
      65             : 
      66           0 :         if (tofd != -1 && offset != (off_t)-1) {
      67           0 :                 if (lseek(tofd, offset, SEEK_SET) == -1) {
      68           0 :                         if (errno != ESPIPE) {
      69           0 :                                 return -1;
      70             :                         }
      71             :                 }
      72             :         }
      73             : 
      74           0 :         while (total < count) {
      75           0 :                 size_t num_written = 0;
      76           0 :                 ssize_t read_ret;
      77           0 :                 size_t toread = MIN(bufsize,count - total);
      78             : 
      79             :                 /*
      80             :                  * Read from socket - ignore EINTR.
      81             :                  * Can't use sys_read() as that also
      82             :                  * ignores EAGAIN and EWOULDBLOCK.
      83             :                  */
      84           0 :                 do {
      85           0 :                         read_ret = read(fromfd, buffer, toread);
      86           0 :                 } while (read_ret == -1 && errno == EINTR);
      87             : 
      88           0 :                 if (read_ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
      89             :                         /*
      90             :                          * fromfd socket is in non-blocking mode.
      91             :                          * If we already read some and wrote
      92             :                          * it successfully, return that.
      93             :                          * Only return -1 if this is the first read
      94             :                          * attempt. Caller will handle both cases.
      95             :                          */
      96           0 :                         if (total_written != 0) {
      97           0 :                                 return total_written;
      98             :                         }
      99           0 :                         return -1;
     100             :                 }
     101             : 
     102           0 :                 if (read_ret <= 0) {
     103             :                         /* EOF or socket error. */
     104           0 :                         return -1;
     105             :                 }
     106             : 
     107           0 :                 num_written = 0;
     108             : 
     109             :                 /* Don't write any more after a write error. */
     110           0 :                 while (tofd != -1 && (num_written < read_ret)) {
     111           0 :                         ssize_t write_ret;
     112             : 
     113             :                         /* Write to file - ignore EINTR. */
     114           0 :                         write_ret = sys_write(tofd,
     115             :                                         buffer + num_written,
     116             :                                         read_ret - num_written);
     117             : 
     118           0 :                         if (write_ret <= 0) {
     119             :                                 /* write error - stop writing. */
     120           0 :                                 tofd = -1;
     121           0 :                                 if (total_written == 0) {
     122             :                                         /* Ensure we return
     123             :                                            -1 if the first
     124             :                                            write failed. */
     125           0 :                                         total_written = -1;
     126             :                                 }
     127           0 :                                 saved_errno = errno;
     128           0 :                                 break;
     129             :                         }
     130             : 
     131           0 :                         num_written += (size_t)write_ret;
     132           0 :                         total_written += (size_t)write_ret;
     133             :                 }
     134             : 
     135           0 :                 total += read_ret;
     136             :         }
     137             : 
     138           0 :         if (saved_errno) {
     139             :                 /* Return the correct write error. */
     140           0 :                 errno = saved_errno;
     141             :         }
     142           0 :         return (ssize_t)total_written;
     143             : }
     144             : 
     145             : #if defined(HAVE_LINUX_SPLICE)
     146             : 
     147             : /*
     148             :  * Try and use the Linux system call to do this.
     149             :  * Remember we only return -1 if the socket read
     150             :  * failed. Else we return the number of bytes
     151             :  * actually written. We always read count bytes
     152             :  * from the network in the case of return != -1.
     153             :  */
     154             : 
     155             : 
     156           0 : ssize_t sys_recvfile(int fromfd,
     157             :                         int tofd,
     158             :                         off_t offset,
     159             :                         size_t count)
     160             : {
     161           0 :         static int pipefd[2] = { -1, -1 };
     162           0 :         static bool try_splice_call = false;
     163           0 :         size_t total_written = 0;
     164           0 :         loff_t splice_offset = offset;
     165             : 
     166           0 :         DEBUG(10,("sys_recvfile: from = %d, to = %d, "
     167             :                 "offset=%.0f, count = %lu\n",
     168             :                 fromfd, tofd, (double)offset,
     169             :                 (unsigned long)count));
     170             : 
     171           0 :         if (count == 0) {
     172           0 :                 return 0;
     173             :         }
     174             : 
     175             :         /*
     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.
     180             :          */
     181             : 
     182           0 :         if (!try_splice_call) {
     183           0 :                 return default_sys_recvfile(fromfd,
     184             :                                 tofd,
     185             :                                 offset,
     186             :                                 count);
     187             :         }
     188             : 
     189           0 :         if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
     190           0 :                 try_splice_call = false;
     191           0 :                 return default_sys_recvfile(fromfd, tofd, offset, count);
     192             :         }
     193             : 
     194           0 :         while (count > 0) {
     195           0 :                 int nread, to_write;
     196             : 
     197           0 :                 nread = splice(fromfd, NULL, pipefd[1], NULL,
     198             :                                MIN(count, 16384), SPLICE_F_MOVE);
     199           0 :                 if (nread == -1) {
     200           0 :                         if (errno == EINTR) {
     201           0 :                                 continue;
     202             :                         }
     203           0 :                         if (total_written == 0 &&
     204           0 :                             (errno == EBADF || errno == EINVAL)) {
     205           0 :                                 try_splice_call = false;
     206           0 :                                 return default_sys_recvfile(fromfd, tofd,
     207             :                                                             offset, count);
     208             :                         }
     209           0 :                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
     210             :                                 /*
     211             :                                  * fromfd socket is in non-blocking mode.
     212             :                                  * If we already read some and wrote
     213             :                                  * it successfully, return that.
     214             :                                  * Only return -1 if this is the first read
     215             :                                  * attempt. Caller will handle both cases.
     216             :                                  */
     217           0 :                                 if (total_written != 0) {
     218           0 :                                         return total_written;
     219             :                                 }
     220           0 :                                 return -1;
     221             :                         }
     222           0 :                         break;
     223             :                 }
     224             : 
     225           0 :                 to_write = nread;
     226           0 :                 while (to_write > 0) {
     227           0 :                         int thistime;
     228           0 :                         thistime = splice(pipefd[0], NULL, tofd,
     229             :                                           &splice_offset, to_write,
     230             :                                           SPLICE_F_MOVE);
     231           0 :                         if (thistime == -1) {
     232           0 :                                 goto done;
     233             :                         }
     234           0 :                         to_write -= thistime;
     235             :                 }
     236             : 
     237           0 :                 total_written += nread;
     238           0 :                 count -= nread;
     239             :         }
     240             : 
     241           0 :  done:
     242           0 :         if (count) {
     243           0 :                 int saved_errno = errno;
     244           0 :                 if (drain_socket(fromfd, count) != count) {
     245             :                         /* socket is dead. */
     246           0 :                         return -1;
     247             :                 }
     248           0 :                 errno = saved_errno;
     249             :         }
     250             : 
     251           0 :         return total_written;
     252             : }
     253             : #else
     254             : 
     255             : /*****************************************************************
     256             :  No recvfile system call - use the default 128 chunk implementation.
     257             : *****************************************************************/
     258             : 
     259             : ssize_t sys_recvfile(int fromfd,
     260             :                         int tofd,
     261             :                         off_t offset,
     262             :                         size_t count)
     263             : {
     264             :         return default_sys_recvfile(fromfd, tofd, offset, count);
     265             : }
     266             : #endif
     267             : 
     268             : /*****************************************************************
     269             :  Throw away "count" bytes from the client socket.
     270             :  Returns count or -1 on error.
     271             :  Must only operate on a blocking socket.
     272             : *****************************************************************/
     273             : 
     274           0 : ssize_t drain_socket(int sockfd, size_t count)
     275           0 : {
     276           0 :         size_t total = 0;
     277           0 :         size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
     278           0 :         char buffer[bufsize];
     279           0 :         int old_flags = 0;
     280             : 
     281           0 :         if (count == 0) {
     282           0 :                 return 0;
     283             :         }
     284             : 
     285           0 :         old_flags = fcntl(sockfd, F_GETFL, 0);
     286           0 :         if (set_blocking(sockfd, true) == -1) {
     287           0 :                 return -1;
     288             :         }
     289             : 
     290           0 :         while (total < count) {
     291           0 :                 ssize_t read_ret;
     292           0 :                 size_t toread = MIN(bufsize,count - total);
     293             : 
     294             :                 /* Read from socket - ignore EINTR. */
     295           0 :                 read_ret = sys_read(sockfd, buffer, toread);
     296           0 :                 if (read_ret <= 0) {
     297             :                         /* EOF or socket error. */
     298           0 :                         count = (size_t)-1;
     299           0 :                         goto out;
     300             :                 }
     301           0 :                 total += read_ret;
     302             :         }
     303             : 
     304           0 :   out:
     305             : 
     306           0 :         if (fcntl(sockfd, F_SETFL, old_flags) == -1) {
     307           0 :                 return -1;
     308             :         }
     309           0 :         return count;
     310             : }

Generated by: LCOV version 1.14