root/source3/lib/sendfile.c

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

DEFINITIONS

This source file includes following definitions.
  1. sys_sendfile
  2. sys_sendfile
  3. sys_sendfile
  4. sys_sendfile
  5. sys_sendfile
  6. sys_sendfile
  7. sys_sendfile

   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 
  28 #if defined(LINUX_SENDFILE_API)
  29 
  30 #include <sys/sendfile.h>
  31 
  32 #ifndef MSG_MORE
  33 #define MSG_MORE 0x8000
  34 #endif
  35 
  36 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
  37 {
  38         size_t total=0;
  39         ssize_t ret;
  40         size_t hdr_len = 0;
  41 
  42         /*
  43          * Send the header first.
  44          * Use MSG_MORE to cork the TCP output until sendfile is called.
  45          */
  46 
  47         if (header) {
  48                 hdr_len = header->length;
  49                 while (total < hdr_len) {
  50                         ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
  51                         if (ret == -1)
  52                                 return -1;
  53                         total += ret;
  54                 }
  55         }
  56 
  57         total = count;
  58         while (total) {
  59                 ssize_t nwritten;
  60                 do {
  61 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
  62                         nwritten = sendfile64(tofd, fromfd, &offset, total);
  63 #else
  64                         nwritten = sendfile(tofd, fromfd, &offset, total);
  65 #endif
  66                 } while (nwritten == -1 && errno == EINTR);
  67                 if (nwritten == -1) {
  68                         if (errno == ENOSYS || errno == EINVAL) {
  69                                 /* Ok - we're in a world of pain here. We just sent
  70                                  * the header, but the sendfile failed. We have to
  71                                  * emulate the sendfile at an upper layer before we
  72                                  * disable it's use. So we do something really ugly.
  73                                  * We set the errno to a strange value so we can detect
  74                                  * this at the upper level and take care of it without
  75                                  * layer violation. JRA.
  76                                  */
  77                                 errno = EINTR; /* Normally we can never return this. */
  78                         }
  79                         return -1;
  80                 }
  81                 if (nwritten == 0) {
  82                         /*
  83                          * EOF, return a short read
  84                          */
  85                         return hdr_len + (count - total);
  86                 }
  87                 total -= nwritten;
  88         }
  89         return count + hdr_len;
  90 }
  91 
  92 #elif defined(LINUX_BROKEN_SENDFILE_API)
  93 
  94 /*
  95  * We must use explicit 32 bit types here. This code path means Linux
  96  * won't do proper 64-bit sendfile. JRA.
  97  */
  98 
  99 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
 100 
 101 
 102 #ifndef MSG_MORE
 103 #define MSG_MORE 0x8000
 104 #endif
 105 
 106 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
 107 {
 108         size_t total=0;
 109         ssize_t ret;
 110         ssize_t hdr_len = 0;
 111         uint32 small_total = 0;
 112         int32 small_offset;
 113 
 114         /* 
 115          * Fix for broken Linux 2.4 systems with no working sendfile64().
 116          * If the offset+count > 2 GB then pretend we don't have the
 117          * system call sendfile at all. The upper layer catches this
 118          * and uses a normal read. JRA.
 119          */
 120 
 121         if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
 122                 errno = ENOSYS;
 123                 return -1;
 124         }
 125 
 126         /*
 127          * Send the header first.
 128          * Use MSG_MORE to cork the TCP output until sendfile is called.
 129          */
 130 
 131         if (header) {
 132                 hdr_len = header->length;
 133                 while (total < hdr_len) {
 134                         ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
 135                         if (ret == -1)
 136                                 return -1;
 137                         total += ret;
 138                 }
 139         }
 140 
 141         small_total = (uint32)count;
 142         small_offset = (int32)offset;
 143 
 144         while (small_total) {
 145                 int32 nwritten;
 146                 do {
 147                         nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
 148                 } while (nwritten == -1 && errno == EINTR);
 149                 if (nwritten == -1) {
 150                         if (errno == ENOSYS || errno == EINVAL) {
 151                                 /* Ok - we're in a world of pain here. We just sent
 152                                  * the header, but the sendfile failed. We have to
 153                                  * emulate the sendfile at an upper layer before we
 154                                  * disable it's use. So we do something really ugly.
 155                                  * We set the errno to a strange value so we can detect
 156                                  * this at the upper level and take care of it without
 157                                  * layer violation. JRA.
 158                                  */
 159                                 errno = EINTR; /* Normally we can never return this. */
 160                         }
 161                         return -1;
 162                 }
 163                 if (nwritten == 0) {
 164                         /*
 165                          * EOF, return a short read
 166                          */
 167                         return hdr_len + (((uint32)count) - small_total);
 168                 }
 169                 small_total -= nwritten;
 170         }
 171         return count + hdr_len;
 172 }
 173 
 174 
 175 #elif defined(SOLARIS_SENDFILE_API)
 176 
 177 /*
 178  * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
 179  */
 180 
 181 #include <sys/sendfile.h>
 182 
 183 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
 184 {
 185         int sfvcnt;
 186         size_t total, xferred;
 187         struct sendfilevec vec[2];
 188         ssize_t hdr_len = 0;
 189 
 190         if (header) {
 191                 sfvcnt = 2;
 192 
 193                 vec[0].sfv_fd = SFV_FD_SELF;
 194                 vec[0].sfv_flag = 0;
 195                 vec[0].sfv_off = (off_t)header->data;
 196                 vec[0].sfv_len = hdr_len = header->length;
 197 
 198                 vec[1].sfv_fd = fromfd;
 199                 vec[1].sfv_flag = 0;
 200                 vec[1].sfv_off = offset;
 201                 vec[1].sfv_len = count;
 202 
 203         } else {
 204                 sfvcnt = 1;
 205 
 206                 vec[0].sfv_fd = fromfd;
 207                 vec[0].sfv_flag = 0;
 208                 vec[0].sfv_off = offset;
 209                 vec[0].sfv_len = count;
 210         }
 211 
 212         total = count + hdr_len;
 213 
 214         while (total) {
 215                 ssize_t nwritten;
 216 
 217                 /*
 218                  * Although not listed in the API error returns, this is almost certainly
 219                  * a slow system call and will be interrupted by a signal with EINTR. JRA.
 220                  */
 221 
 222                 xferred = 0;
 223 
 224 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
 225                         nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
 226 #else
 227                         nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
 228 #endif
 229                 if (nwritten == -1 && errno == EINTR) {
 230                         if (xferred == 0)
 231                                 continue; /* Nothing written yet. */
 232                         else
 233                                 nwritten = xferred;
 234                 }
 235 
 236                 if (nwritten == -1)
 237                         return -1;
 238                 if (nwritten == 0)
 239                         return -1; /* I think we're at EOF here... */
 240 
 241                 /*
 242                  * If this was a short (signal interrupted) write we may need
 243                  * to subtract it from the header data, or null out the header
 244                  * data altogether if we wrote more than vec[0].sfv_len bytes.
 245                  * We move vec[1].* to vec[0].* and set sfvcnt to 1
 246                  */
 247 
 248                 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
 249                         vec[1].sfv_off += nwritten - vec[0].sfv_len;
 250                         vec[1].sfv_len -= nwritten - vec[0].sfv_len;
 251 
 252                         /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
 253                         vec[0] = vec[1];
 254                         sfvcnt = 1;
 255                 } else {
 256                         vec[0].sfv_off += nwritten;
 257                         vec[0].sfv_len -= nwritten;
 258                 }
 259                 total -= nwritten;
 260         }
 261         return count + hdr_len;
 262 }
 263 
 264 #elif defined(HPUX_SENDFILE_API)
 265 
 266 #include <sys/socket.h>
 267 #include <sys/uio.h>
 268 
 269 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
 270 {
 271         size_t total=0;
 272         struct iovec hdtrl[2];
 273         size_t hdr_len = 0;
 274 
 275         if (header) {
 276                 /* Set up the header/trailer iovec. */
 277                 hdtrl[0].iov_base = header->data;
 278                 hdtrl[0].iov_len = hdr_len = header->length;
 279         } else {
 280                 hdtrl[0].iov_base = NULL;
 281                 hdtrl[0].iov_len = hdr_len = 0;
 282         }
 283         hdtrl[1].iov_base = NULL;
 284         hdtrl[1].iov_len = 0;
 285 
 286         total = count;
 287         while (total + hdtrl[0].iov_len) {
 288                 ssize_t nwritten;
 289 
 290                 /*
 291                  * HPUX guarantees that if any data was written before
 292                  * a signal interrupt then sendfile returns the number of
 293                  * bytes written (which may be less than requested) not -1.
 294                  * nwritten includes the header data sent.
 295                  */
 296 
 297                 do {
 298 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
 299                         nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
 300 #else
 301                         nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
 302 #endif
 303                 } while (nwritten == -1 && errno == EINTR);
 304                 if (nwritten == -1)
 305                         return -1;
 306                 if (nwritten == 0)
 307                         return -1; /* I think we're at EOF here... */
 308 
 309                 /*
 310                  * If this was a short (signal interrupted) write we may need
 311                  * to subtract it from the header data, or null out the header
 312                  * data altogether if we wrote more than hdtrl[0].iov_len bytes.
 313                  * We change nwritten to be the number of file bytes written.
 314                  */
 315 
 316                 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
 317                         if (nwritten >= hdtrl[0].iov_len) {
 318                                 nwritten -= hdtrl[0].iov_len;
 319                                 hdtrl[0].iov_base = NULL;
 320                                 hdtrl[0].iov_len = 0;
 321                         } else {
 322                                 /* iov_base is defined as a void *... */
 323                                 hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
 324                                 hdtrl[0].iov_len -= nwritten;
 325                                 nwritten = 0;
 326                         }
 327                 }
 328                 total -= nwritten;
 329                 offset += nwritten;
 330         }
 331         return count + hdr_len;
 332 }
 333 
 334 #elif defined(FREEBSD_SENDFILE_API)
 335 
 336 #include <sys/types.h>
 337 #include <sys/socket.h>
 338 #include <sys/uio.h>
 339 
 340 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
 341 {
 342         size_t total=0;
 343         struct sf_hdtr hdr;
 344         struct iovec hdtrl;
 345         size_t hdr_len = 0;
 346 
 347         hdr.headers = &hdtrl;
 348         hdr.hdr_cnt = 1;
 349         hdr.trailers = NULL;
 350         hdr.trl_cnt = 0;
 351 
 352         /* Set up the header iovec. */
 353         if (header) {
 354                 hdtrl.iov_base = header->data;
 355                 hdtrl.iov_len = hdr_len = header->length;
 356         } else {
 357                 hdtrl.iov_base = NULL;
 358                 hdtrl.iov_len = 0;
 359         }
 360 
 361         total = count;
 362         while (total + hdtrl.iov_len) {
 363                 SMB_OFF_T nwritten;
 364                 int ret;
 365 
 366                 /*
 367                  * FreeBSD sendfile returns 0 on success, -1 on error.
 368                  * Remember, the tofd and fromfd are reversed..... :-).
 369                  * nwritten includes the header data sent.
 370                  */
 371 
 372                 do {
 373                         ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
 374                 } while (ret == -1 && errno == EINTR);
 375                 if (ret == -1)
 376                         return -1;
 377 
 378                 if (nwritten == 0)
 379                         return -1; /* I think we're at EOF here... */
 380 
 381                 /*
 382                  * If this was a short (signal interrupted) write we may need
 383                  * to subtract it from the header data, or null out the header
 384                  * data altogether if we wrote more than hdtrl.iov_len bytes.
 385                  * We change nwritten to be the number of file bytes written.
 386                  */
 387 
 388                 if (hdtrl.iov_base && hdtrl.iov_len) {
 389                         if (nwritten >= hdtrl.iov_len) {
 390                                 nwritten -= hdtrl.iov_len;
 391                                 hdtrl.iov_base = NULL;
 392                                 hdtrl.iov_len = 0;
 393                         } else {
 394                                 hdtrl.iov_base =
 395                                     (caddr_t)hdtrl.iov_base + nwritten;
 396                                 hdtrl.iov_len -= nwritten;
 397                                 nwritten = 0;
 398                         }
 399                 }
 400                 total -= nwritten;
 401                 offset += nwritten;
 402         }
 403         return count + hdr_len;
 404 }
 405 
 406 #elif defined(AIX_SENDFILE_API)
 407 
 408 /* BEGIN AIX SEND_FILE */
 409 
 410 /* Contributed by William Jojo <jojowil@hvcc.edu> */
 411 #include <sys/socket.h>
 412 
 413 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
 414 {
 415         struct sf_parms hdtrl;
 416 
 417         /* Set up the header/trailer struct params. */
 418         if (header) {
 419                 hdtrl.header_data = header->data;
 420                 hdtrl.header_length = header->length;
 421         } else {
 422                 hdtrl.header_data = NULL;
 423                 hdtrl.header_length = 0;
 424         }
 425         hdtrl.trailer_data = NULL;
 426         hdtrl.trailer_length = 0;
 427 
 428         hdtrl.file_descriptor = fromfd;
 429         hdtrl.file_offset = offset;
 430         hdtrl.file_bytes = count;
 431 
 432         while ( hdtrl.file_bytes + hdtrl.header_length ) {
 433                 ssize_t ret;
 434 
 435                 /*
 436                  Return Value
 437 
 438                  There are three possible return values from send_file:
 439 
 440                  Value Description
 441 
 442                  -1 an error has occurred, errno contains the error code.
 443 
 444                  0 the command has completed successfully.
 445 
 446                  1 the command was completed partially, some data has been
 447                  transmitted but the command has to return for some reason,
 448                  for example, the command was interrupted by signals.
 449                 */
 450                 do {
 451                         ret = send_file(&tofd, &hdtrl, 0);
 452                 } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
 453                 if ( ret == -1 )
 454                         return -1;
 455         }
 456 
 457         return count + header->length;
 458 }
 459 /* END AIX SEND_FILE */
 460 
 461 #else /* No sendfile implementation. Return error. */
 462 
 463 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
     /* [<][>][^][v][top][bottom][index][help] */
 464 {
 465         /* No sendfile syscall. */
 466         errno = ENOSYS;
 467         return -1;
 468 }
 469 #endif

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