LCOV - code coverage report
Current view: top level - source3/lib - sendfile.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 0 53 0.0 %
Date: 2021-09-23 10:06:22 Functions: 0 1 0.0 %

          Line data    Source code
       1             : /*
       2             :  Unix SMB/Netbios implementation.
       3             :  Version 2.2.x / 3.0.x
       4             :  sendfile implementations.
       5             :  Copyright (C) Jeremy Allison 2002.
       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 sendfile 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             : 
      29             : #if defined(LINUX_SENDFILE_API)
      30             : 
      31             : #include <sys/sendfile.h>
      32             : 
      33             : #ifndef MSG_MORE
      34             : #define MSG_MORE 0x8000
      35             : #endif
      36             : 
      37           0 : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
      38             : {
      39           0 :         size_t total=0;
      40           0 :         ssize_t ret = -1;
      41           0 :         size_t hdr_len = 0;
      42           0 :         int old_flags = 0;
      43           0 :         bool socket_flags_changed = false;
      44             : 
      45             :         /*
      46             :          * Send the header first.
      47             :          * Use MSG_MORE to cork the TCP output until sendfile is called.
      48             :          */
      49             : 
      50           0 :         if (header) {
      51           0 :                 hdr_len = header->length;
      52           0 :                 while (total < hdr_len) {
      53           0 :                         ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
      54           0 :                         if (ret == -1) {
      55           0 :                                 if (errno == EAGAIN || errno == EWOULDBLOCK) {
      56             :                                         /*
      57             :                                          * send() must complete before we can
      58             :                                          * send any other outgoing data on the
      59             :                                          * socket. Ensure socket is in blocking
      60             :                                          * mode. For SMB2 by default the socket
      61             :                                          * is in non-blocking mode.
      62             :                                          */
      63           0 :                                         old_flags = fcntl(tofd, F_GETFL, 0);
      64           0 :                                         ret = set_blocking(tofd, true);
      65           0 :                                         if (ret == -1) {
      66           0 :                                                 goto out;
      67             :                                         }
      68           0 :                                         socket_flags_changed = true;
      69           0 :                                         continue;
      70             :                                 }
      71           0 :                                 goto out;
      72             :                         }
      73           0 :                         total += ret;
      74             :                 }
      75             :         }
      76             : 
      77           0 :         total = count;
      78           0 :         while (total) {
      79             :                 ssize_t nwritten;
      80             :                 do {
      81           0 :                         nwritten = sendfile(tofd, fromfd, &offset, total);
      82           0 :                 } while (nwritten == -1 && errno == EINTR);
      83           0 :                 if (nwritten == -1) {
      84           0 :                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
      85           0 :                                 if (socket_flags_changed) {
      86             :                                         /*
      87             :                                          * We're already in blocking
      88             :                                          * mode. This is an error.
      89             :                                          */
      90           0 :                                         ret = -1;
      91           0 :                                         goto out;
      92             :                                 }
      93             : 
      94             :                                 /*
      95             :                                  * Sendfile must complete before we can
      96             :                                  * send any other outgoing data on the socket.
      97             :                                  * Ensure socket is in blocking mode.
      98             :                                  * For SMB2 by default the socket is in
      99             :                                  * non-blocking mode.
     100             :                                  */
     101           0 :                                 old_flags = fcntl(tofd, F_GETFL, 0);
     102           0 :                                 ret = set_blocking(tofd, true);
     103           0 :                                 if (ret == -1) {
     104           0 :                                         goto out;
     105             :                                 }
     106           0 :                                 socket_flags_changed = true;
     107           0 :                                 continue;
     108             :                         }
     109             : 
     110           0 :                         if (errno == ENOSYS || errno == EINVAL) {
     111             :                                 /* Ok - we're in a world of pain here. We just sent
     112             :                                  * the header, but the sendfile failed. We have to
     113             :                                  * emulate the sendfile at an upper layer before we
     114             :                                  * disable it's use. So we do something really ugly.
     115             :                                  * We set the errno to a strange value so we can detect
     116             :                                  * this at the upper level and take care of it without
     117             :                                  * layer violation. JRA.
     118             :                                  */
     119           0 :                                 errno = EINTR; /* Normally we can never return this. */
     120             :                         }
     121           0 :                         ret = -1;
     122           0 :                         goto out;
     123             :                 }
     124           0 :                 if (nwritten == 0) {
     125             :                         /*
     126             :                          * EOF, return a short read
     127             :                          */
     128           0 :                         ret = hdr_len + (count - total);
     129           0 :                         goto out;
     130             :                 }
     131           0 :                 total -= nwritten;
     132             :         }
     133             : 
     134           0 :         ret = count + hdr_len;
     135             : 
     136           0 :   out:
     137             : 
     138           0 :         if (socket_flags_changed) {
     139           0 :                 int saved_errno = errno;
     140             :                 int err;
     141             : 
     142             :                 /* Restore the old state of the socket. */
     143           0 :                 err = fcntl(tofd, F_SETFL, old_flags);
     144           0 :                 if (err == -1) {
     145           0 :                         return -1;
     146             :                 }
     147           0 :                 if (ret == -1) {
     148           0 :                         errno = saved_errno;
     149             :                 }
     150             :         }
     151             : 
     152           0 :         return ret;
     153             : }
     154             : 
     155             : #elif defined(SOLARIS_SENDFILE_API)
     156             : 
     157             : /*
     158             :  * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
     159             :  */
     160             : 
     161             : #include <sys/sendfile.h>
     162             : 
     163             : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
     164             : {
     165             :         int sfvcnt;
     166             :         size_t total, xferred;
     167             :         struct sendfilevec vec[2];
     168             :         ssize_t hdr_len = 0;
     169             :         int old_flags = 0;
     170             :         ssize_t ret = -1;
     171             :         bool socket_flags_changed = false;
     172             : 
     173             :         if (header) {
     174             :                 sfvcnt = 2;
     175             : 
     176             :                 vec[0].sfv_fd = SFV_FD_SELF;
     177             :                 vec[0].sfv_flag = 0;
     178             :                 vec[0].sfv_off = (off_t)header->data;
     179             :                 vec[0].sfv_len = hdr_len = header->length;
     180             : 
     181             :                 vec[1].sfv_fd = fromfd;
     182             :                 vec[1].sfv_flag = 0;
     183             :                 vec[1].sfv_off = offset;
     184             :                 vec[1].sfv_len = count;
     185             : 
     186             :         } else {
     187             :                 sfvcnt = 1;
     188             : 
     189             :                 vec[0].sfv_fd = fromfd;
     190             :                 vec[0].sfv_flag = 0;
     191             :                 vec[0].sfv_off = offset;
     192             :                 vec[0].sfv_len = count;
     193             :         }
     194             : 
     195             :         total = count + hdr_len;
     196             : 
     197             :         while (total) {
     198             :                 ssize_t nwritten;
     199             : 
     200             :                 /*
     201             :                  * Although not listed in the API error returns, this is almost certainly
     202             :                  * a slow system call and will be interrupted by a signal with EINTR. JRA.
     203             :                  */
     204             : 
     205             :                 xferred = 0;
     206             : 
     207             :                         nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
     208             :                 if  (nwritten == -1 && errno == EINTR) {
     209             :                         if (xferred == 0)
     210             :                                 continue; /* Nothing written yet. */
     211             :                         else
     212             :                                 nwritten = xferred;
     213             :                 }
     214             : 
     215             :                 if (nwritten == -1) {
     216             :                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
     217             :                                 /*
     218             :                                  * Sendfile must complete before we can
     219             :                                  * send any other outgoing data on the socket.
     220             :                                  * Ensure socket is in blocking mode.
     221             :                                  * For SMB2 by default the socket is in
     222             :                                  * non-blocking mode.
     223             :                                  */
     224             :                                 old_flags = fcntl(tofd, F_GETFL, 0);
     225             :                                 ret = set_blocking(tofd, true);
     226             :                                 if (ret == -1) {
     227             :                                         goto out;
     228             :                                 }
     229             :                                 socket_flags_changed = true;
     230             :                                 continue;
     231             :                         }
     232             :                         ret = -1;
     233             :                         goto out;
     234             :                 }
     235             :                 if (nwritten == 0) {
     236             :                         ret = -1;
     237             :                         goto out; /* I think we're at EOF here... */
     238             :                 }
     239             : 
     240             :                 /*
     241             :                  * If this was a short (signal interrupted) write we may need
     242             :                  * to subtract it from the header data, or null out the header
     243             :                  * data altogether if we wrote more than vec[0].sfv_len bytes.
     244             :                  * We move vec[1].* to vec[0].* and set sfvcnt to 1
     245             :                  */
     246             : 
     247             :                 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
     248             :                         vec[1].sfv_off += nwritten - vec[0].sfv_len;
     249             :                         vec[1].sfv_len -= nwritten - vec[0].sfv_len;
     250             : 
     251             :                         /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
     252             :                         vec[0] = vec[1];
     253             :                         sfvcnt = 1;
     254             :                 } else {
     255             :                         vec[0].sfv_off += nwritten;
     256             :                         vec[0].sfv_len -= nwritten;
     257             :                 }
     258             :                 total -= nwritten;
     259             :         }
     260             :         ret = count + hdr_len;
     261             : 
     262             :   out:
     263             : 
     264             :         if (socket_flags_changed) {
     265             :                 int saved_errno;
     266             :                 int err;
     267             : 
     268             :                 if (ret == -1) {
     269             :                         saved_errno = errno;
     270             :                 }
     271             :                 /* Restore the old state of the socket. */
     272             :                 err = fcntl(tofd, F_SETFL, old_flags);
     273             :                 if (err == -1) {
     274             :                         return -1;
     275             :                 }
     276             :                 if (ret == -1) {
     277             :                         errno = saved_errno;
     278             :                 }
     279             :         }
     280             : 
     281             :         return ret;
     282             : }
     283             : 
     284             : #elif defined(HPUX_SENDFILE_API)
     285             : 
     286             : #include <sys/socket.h>
     287             : #include <sys/uio.h>
     288             : 
     289             : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
     290             : {
     291             :         size_t total=0;
     292             :         struct iovec hdtrl[2];
     293             :         size_t hdr_len = 0;
     294             :         int old_flags = 0;
     295             :         ssize_t ret = -1;
     296             :         bool socket_flags_changed = false;
     297             : 
     298             :         if (header) {
     299             :                 /* Set up the header/trailer iovec. */
     300             :                 hdtrl[0].iov_base = (void *)header->data;
     301             :                 hdtrl[0].iov_len = hdr_len = header->length;
     302             :         } else {
     303             :                 hdtrl[0].iov_base = NULL;
     304             :                 hdtrl[0].iov_len = hdr_len = 0;
     305             :         }
     306             :         hdtrl[1].iov_base = NULL;
     307             :         hdtrl[1].iov_len = 0;
     308             : 
     309             :         total = count;
     310             :         while (total + hdtrl[0].iov_len) {
     311             :                 ssize_t nwritten;
     312             : 
     313             :                 /*
     314             :                  * HPUX guarantees that if any data was written before
     315             :                  * a signal interrupt then sendfile returns the number of
     316             :                  * bytes written (which may be less than requested) not -1.
     317             :                  * nwritten includes the header data sent.
     318             :                  */
     319             : 
     320             :                 do {
     321             :                         nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
     322             :                 } while (nwritten == -1 && errno == EINTR);
     323             :                 if (nwritten == -1) {
     324             :                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
     325             :                                 /*
     326             :                                  * Sendfile must complete before we can
     327             :                                  * send any other outgoing data on the socket.
     328             :                                  * Ensure socket is in blocking mode.
     329             :                                  * For SMB2 by default the socket is in
     330             :                                  * non-blocking mode.
     331             :                                  */
     332             :                                 old_flags = fcntl(tofd, F_GETFL, 0);
     333             :                                 ret = set_blocking(tofd, true);
     334             :                                 if (ret == -1) {
     335             :                                         goto out;
     336             :                                 }
     337             :                                 socket_flags_changed = true;
     338             :                                 continue;
     339             :                         }
     340             :                         ret = -1;
     341             :                         goto out;
     342             :                 }
     343             :                 if (nwritten == 0) {
     344             :                         ret = -1; /* I think we're at EOF here... */
     345             :                         goto out;
     346             :                 }
     347             : 
     348             :                 /*
     349             :                  * If this was a short (signal interrupted) write we may need
     350             :                  * to subtract it from the header data, or null out the header
     351             :                  * data altogether if we wrote more than hdtrl[0].iov_len bytes.
     352             :                  * We change nwritten to be the number of file bytes written.
     353             :                  */
     354             : 
     355             :                 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
     356             :                         if (nwritten >= hdtrl[0].iov_len) {
     357             :                                 nwritten -= hdtrl[0].iov_len;
     358             :                                 hdtrl[0].iov_base = NULL;
     359             :                                 hdtrl[0].iov_len = 0;
     360             :                         } else {
     361             :                                 /* iov_base is defined as a void *... */
     362             :                                 hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten);
     363             :                                 hdtrl[0].iov_len -= nwritten;
     364             :                                 nwritten = 0;
     365             :                         }
     366             :                 }
     367             :                 total -= nwritten;
     368             :                 offset += nwritten;
     369             :         }
     370             :         ret = count + hdr_len;
     371             : 
     372             :   out:
     373             : 
     374             :         if (socket_flags_changed) {
     375             :                 int saved_errno;
     376             :                 int err;
     377             : 
     378             :                 if (ret == -1) {
     379             :                         saved_errno = errno;
     380             :                 }
     381             :                 /* Restore the old state of the socket. */
     382             :                 err = fcntl(tofd, F_SETFL, old_flags);
     383             :                 if (err == -1) {
     384             :                         return -1;
     385             :                 }
     386             :                 if (ret == -1) {
     387             :                         errno = saved_errno;
     388             :                 }
     389             :         }
     390             : 
     391             :         return ret;
     392             : }
     393             : 
     394             : #elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
     395             : 
     396             : #include <sys/types.h>
     397             : #include <sys/socket.h>
     398             : #include <sys/uio.h>
     399             : 
     400             : ssize_t sys_sendfile(int tofd, int fromfd,
     401             :             const DATA_BLOB *header, off_t offset, size_t count)
     402             : {
     403             :         struct sf_hdtr  sf_header = {0};
     404             :         struct iovec    io_header = {0};
     405             :         int old_flags = 0;
     406             : 
     407             :         off_t   nwritten;
     408             :         ssize_t ret = -1;
     409             :         bool socket_flags_changed = false;
     410             : 
     411             :         if (header) {
     412             :                 sf_header.headers = &io_header;
     413             :                 sf_header.hdr_cnt = 1;
     414             :                 io_header.iov_base = header->data;
     415             :                 io_header.iov_len = header->length;
     416             :                 sf_header.trailers = NULL;
     417             :                 sf_header.trl_cnt = 0;
     418             :         }
     419             : 
     420             :         while (count != 0) {
     421             : 
     422             :                 nwritten = count;
     423             : #if defined(DARWIN_SENDFILE_API)
     424             :                 /* Darwin recycles nwritten as a value-result parameter, apart from that this
     425             :                    sendfile implementation is quite the same as the FreeBSD one */
     426             :                 ret = sendfile(fromfd, tofd, offset, &nwritten, &sf_header, 0);
     427             : #else
     428             :                 ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0);
     429             : #endif
     430             :                 if (ret == -1 && errno != EINTR) {
     431             :                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
     432             :                                 /*
     433             :                                  * Sendfile must complete before we can
     434             :                                  * send any other outgoing data on the socket.
     435             :                                  * Ensure socket is in blocking mode.
     436             :                                  * For SMB2 by default the socket is in
     437             :                                  * non-blocking mode.
     438             :                                  */
     439             :                                 old_flags = fcntl(tofd, F_GETFL, 0);
     440             :                                 ret = set_blocking(tofd, true);
     441             :                                 if (ret == -1) {
     442             :                                         goto out;
     443             :                                 }
     444             :                                 socket_flags_changed = true;
     445             :                                 continue;
     446             :                         }
     447             :                         /* Send failed, we are toast. */
     448             :                         ret = -1;
     449             :                         goto out;
     450             :                 }
     451             : 
     452             :                 if (nwritten == 0) {
     453             :                         /* EOF of offset is after EOF. */
     454             :                         break;
     455             :                 }
     456             : 
     457             :                 if (sf_header.hdr_cnt) {
     458             :                         if (io_header.iov_len <= nwritten) {
     459             :                                 /* Entire header was sent. */
     460             :                                 sf_header.headers = NULL;
     461             :                                 sf_header.hdr_cnt = 0;
     462             :                                 nwritten -= io_header.iov_len;
     463             :                         } else {
     464             :                                 /* Partial header was sent. */
     465             :                                 io_header.iov_len -= nwritten;
     466             :                                 io_header.iov_base =
     467             :                                     ((uint8_t *)io_header.iov_base) + nwritten;
     468             :                                 nwritten = 0;
     469             :                         }
     470             :                 }
     471             : 
     472             :                 offset += nwritten;
     473             :                 count -= nwritten;
     474             :         }
     475             : 
     476             :         ret = nwritten;
     477             : 
     478             :   out:
     479             : 
     480             :         if (socket_flags_changed) {
     481             :                 int saved_errno;
     482             :                 int err;
     483             : 
     484             :                 if (ret == -1) {
     485             :                         saved_errno = errno;
     486             :                 }
     487             :                 /* Restore the old state of the socket. */
     488             :                 err = fcntl(tofd, F_SETFL, old_flags);
     489             :                 if (err == -1) {
     490             :                         return -1;
     491             :                 }
     492             :                 if (ret == -1) {
     493             :                         errno = saved_errno;
     494             :                 }
     495             :         }
     496             : 
     497             :         return ret;
     498             : }
     499             : 
     500             : #elif defined(AIX_SENDFILE_API)
     501             : 
     502             : /* BEGIN AIX SEND_FILE */
     503             : 
     504             : /* Contributed by William Jojo <jojowil@hvcc.edu> */
     505             : #include <sys/socket.h>
     506             : 
     507             : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
     508             : {
     509             :         struct sf_parms hdtrl;
     510             :         int old_flags = 0;
     511             :         ssize_t ret = -1;
     512             :         bool socket_flags_changed = false;
     513             : 
     514             :         /* Set up the header/trailer struct params. */
     515             :         if (header) {
     516             :                 hdtrl.header_data = header->data;
     517             :                 hdtrl.header_length = header->length;
     518             :         } else {
     519             :                 hdtrl.header_data = NULL;
     520             :                 hdtrl.header_length = 0;
     521             :         }
     522             :         hdtrl.trailer_data = NULL;
     523             :         hdtrl.trailer_length = 0;
     524             : 
     525             :         hdtrl.file_descriptor = fromfd;
     526             :         hdtrl.file_offset = offset;
     527             :         hdtrl.file_bytes = count;
     528             : 
     529             :         while ( hdtrl.file_bytes + hdtrl.header_length ) {
     530             :                 /*
     531             :                  Return Value
     532             : 
     533             :                  There are three possible return values from send_file:
     534             : 
     535             :                  Value Description
     536             : 
     537             :                  -1 an error has occurred, errno contains the error code.
     538             : 
     539             :                  0 the command has completed successfully.
     540             : 
     541             :                  1 the command was completed partially, some data has been
     542             :                  transmitted but the command has to return for some reason,
     543             :                  for example, the command was interrupted by signals.
     544             :                 */
     545             :                 do {
     546             :                         ret = send_file(&tofd, &hdtrl, 0);
     547             :                 } while ((ret == 1) || (ret == -1 && errno == EINTR));
     548             :                 if ( ret == -1 ) {
     549             :                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
     550             :                                 /*
     551             :                                  * Sendfile must complete before we can
     552             :                                  * send any other outgoing data on the socket.
     553             :                                  * Ensure socket is in blocking mode.
     554             :                                  * For SMB2 by default the socket is in
     555             :                                  * non-blocking mode.
     556             :                                  */
     557             :                                 old_flags = fcntl(tofd, F_GETFL, 0);
     558             :                                 ret = set_blocking(tofd, true);
     559             :                                 if (ret == -1) {
     560             :                                         goto out;
     561             :                                 }
     562             :                                 socket_flags_changed = true;
     563             :                                 continue;
     564             :                         }
     565             :                         goto out;
     566             :                 }
     567             :         }
     568             : 
     569             :         ret = count + header->length;
     570             : 
     571             :   out:
     572             : 
     573             :         if (socket_flags_changed) {
     574             :                 int saved_errno;
     575             :                 int err;
     576             : 
     577             :                 if (ret == -1) {
     578             :                         saved_errno = errno;
     579             :                 }
     580             :                 /* Restore the old state of the socket. */
     581             :                 err = fcntl(tofd, F_SETFL, old_flags);
     582             :                 if (err == -1) {
     583             :                         return -1;
     584             :                 }
     585             :                 if (ret == -1) {
     586             :                         errno = saved_errno;
     587             :                 }
     588             :         }
     589             : 
     590             :         return ret;
     591             : }
     592             : /* END AIX SEND_FILE */
     593             : 
     594             : #else /* No sendfile implementation. Return error. */
     595             : 
     596             : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
     597             : {
     598             :         /* No sendfile syscall. */
     599             :         errno = ENOSYS;
     600             :         return -1;
     601             : }
     602             : #endif

Generated by: LCOV version 1.13