lib/sendfile.c

説明を見る。
00001 /*
00002  Unix SMB/Netbios implementation.
00003  Version 2.2.x / 3.0.x
00004  sendfile implementations.
00005  Copyright (C) Jeremy Allison 2002.
00006 
00007  This program is free software; you can redistribute it and/or modify
00008  it under the terms of the GNU General Public License as published by
00009  the Free Software Foundation; either version 2 of the License, or
00010  (at your option) any later version.
00011  This program is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with this program; if not, write to the Free Software
00018  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019 */
00020 
00021 /*
00022  * This file handles the OS dependent sendfile implementations.
00023  * The API is such that it returns -1 on error, else returns the
00024  * number of bytes written.
00025  */
00026 
00027 #include "includes.h"
00028 
00029 #if defined(LINUX_SENDFILE_API)
00030 
00031 #include <sys/sendfile.h>
00032 
00033 #ifndef MSG_MORE
00034 #define MSG_MORE 0x8000
00035 #endif
00036 
00037 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
00038 {
00039         size_t total=0;
00040         ssize_t ret;
00041         size_t hdr_len = 0;
00042 
00043         /*
00044          * Send the header first.
00045          * Use MSG_MORE to cork the TCP output until sendfile is called.
00046          */
00047 
00048         if (header) {
00049                 hdr_len = header->length;
00050                 while (total < hdr_len) {
00051                         ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
00052                         if (ret == -1)
00053                                 return -1;
00054                         total += ret;
00055                 }
00056         }
00057 
00058         total = count;
00059         while (total) {
00060                 ssize_t nwritten;
00061                 do {
00062 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
00063                         nwritten = sendfile64(tofd, fromfd, &offset, total);
00064 #else
00065                         nwritten = sendfile(tofd, fromfd, &offset, total);
00066 #endif
00067                 } while (nwritten == -1 && errno == EINTR);
00068                 if (nwritten == -1) {
00069                         if (errno == ENOSYS) {
00070                                 /* Ok - we're in a world of pain here. We just sent
00071                                  * the header, but the sendfile failed. We have to
00072                                  * emulate the sendfile at an upper layer before we
00073                                  * disable it's use. So we do something really ugly.
00074                                  * We set the errno to a strange value so we can detect
00075                                  * this at the upper level and take care of it without
00076                                  * layer violation. JRA.
00077                                  */
00078                                 errno = EINTR; /* Normally we can never return this. */
00079                         }
00080                         return -1;
00081                 }
00082                 if (nwritten == 0)
00083                         return -1; /* I think we're at EOF here... */
00084                 total -= nwritten;
00085         }
00086         return count + hdr_len;
00087 }
00088 
00089 #elif defined(LINUX_BROKEN_SENDFILE_API)
00090 
00091 /*
00092  * We must use explicit 32 bit types here. This code path means Linux
00093  * won't do proper 64-bit sendfile. JRA.
00094  */
00095 
00096 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
00097 
00098 
00099 #ifndef MSG_MORE
00100 #define MSG_MORE 0x8000
00101 #endif
00102 
00103 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
00104 {
00105         size_t total=0;
00106         ssize_t ret;
00107         ssize_t hdr_len = 0;
00108         uint32 small_total = 0;
00109         int32 small_offset;
00110 
00111         /* 
00112          * Fix for broken Linux 2.4 systems with no working sendfile64().
00113          * If the offset+count > 2 GB then pretend we don't have the
00114          * system call sendfile at all. The upper layer catches this
00115          * and uses a normal read. JRA.
00116          */
00117 
00118         if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
00119                 errno = ENOSYS;
00120                 return -1;
00121         }
00122 
00123         /*
00124          * Send the header first.
00125          * Use MSG_MORE to cork the TCP output until sendfile is called.
00126          */
00127 
00128         if (header) {
00129                 hdr_len = header->length;
00130                 while (total < hdr_len) {
00131                         ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
00132                         if (ret == -1)
00133                                 return -1;
00134                         total += ret;
00135                 }
00136         }
00137 
00138         small_total = (uint32)count;
00139         small_offset = (int32)offset;
00140 
00141         while (small_total) {
00142                 int32 nwritten;
00143                 do {
00144                         nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
00145                 } while (nwritten == -1 && errno == EINTR);
00146                 if (nwritten == -1) {
00147                         if (errno == ENOSYS) {
00148                                 /* Ok - we're in a world of pain here. We just sent
00149                                  * the header, but the sendfile failed. We have to
00150                                  * emulate the sendfile at an upper layer before we
00151                                  * disable it's use. So we do something really ugly.
00152                                  * We set the errno to a strange value so we can detect
00153                                  * this at the upper level and take care of it without
00154                                  * layer violation. JRA.
00155                                  */
00156                                 errno = EINTR; /* Normally we can never return this. */
00157                         }
00158                         return -1;
00159                 }
00160                 if (nwritten == 0)
00161                         return -1; /* I think we're at EOF here... */
00162                 small_total -= nwritten;
00163         }
00164         return count + hdr_len;
00165 }
00166 
00167 
00168 #elif defined(SOLARIS_SENDFILE_API)
00169 
00170 /*
00171  * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
00172  */
00173 
00174 #include <sys/sendfile.h>
00175 
00176 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
00177 {
00178         int sfvcnt;
00179         size_t total, xferred;
00180         struct sendfilevec vec[2];
00181         ssize_t hdr_len = 0;
00182 
00183         if (header) {
00184                 sfvcnt = 2;
00185 
00186                 vec[0].sfv_fd = SFV_FD_SELF;
00187                 vec[0].sfv_flag = 0;
00188                 vec[0].sfv_off = (off_t)header->data;
00189                 vec[0].sfv_len = hdr_len = header->length;
00190 
00191                 vec[1].sfv_fd = fromfd;
00192                 vec[1].sfv_flag = 0;
00193                 vec[1].sfv_off = offset;
00194                 vec[1].sfv_len = count;
00195 
00196         } else {
00197                 sfvcnt = 1;
00198 
00199                 vec[0].sfv_fd = fromfd;
00200                 vec[0].sfv_flag = 0;
00201                 vec[0].sfv_off = offset;
00202                 vec[0].sfv_len = count;
00203         }
00204 
00205         total = count + hdr_len;
00206 
00207         while (total) {
00208                 ssize_t nwritten;
00209 
00210                 /*
00211                  * Although not listed in the API error returns, this is almost certainly
00212                  * a slow system call and will be interrupted by a signal with EINTR. JRA.
00213                  */
00214 
00215                 xferred = 0;
00216 
00217 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
00218                         nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
00219 #else
00220                         nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
00221 #endif
00222                 if (nwritten == -1 && errno == EINTR) {
00223                         if (xferred == 0)
00224                                 continue; /* Nothing written yet. */
00225                         else
00226                                 nwritten = xferred;
00227                 }
00228 
00229                 if (nwritten == -1)
00230                         return -1;
00231                 if (nwritten == 0)
00232                         return -1; /* I think we're at EOF here... */
00233 
00234                 /*
00235                  * If this was a short (signal interrupted) write we may need
00236                  * to subtract it from the header data, or null out the header
00237                  * data altogether if we wrote more than vec[0].sfv_len bytes.
00238                  * We move vec[1].* to vec[0].* and set sfvcnt to 1
00239                  */
00240 
00241                 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
00242                         vec[1].sfv_off += nwritten - vec[0].sfv_len;
00243                         vec[1].sfv_len -= nwritten - vec[0].sfv_len;
00244 
00245                         /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
00246                         vec[0] = vec[1];
00247                         sfvcnt = 1;
00248                 } else {
00249                         vec[0].sfv_off += nwritten;
00250                         vec[0].sfv_len -= nwritten;
00251                 }
00252                 total -= nwritten;
00253         }
00254         return count + hdr_len;
00255 }
00256 
00257 #elif defined(HPUX_SENDFILE_API)
00258 
00259 #include <sys/socket.h>
00260 #include <sys/uio.h>
00261 
00262 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
00263 {
00264         size_t total=0;
00265         struct iovec hdtrl[2];
00266         size_t hdr_len = 0;
00267 
00268         if (header) {
00269                 /* Set up the header/trailer iovec. */
00270                 hdtrl[0].iov_base = header->data;
00271                 hdtrl[0].iov_len = hdr_len = header->length;
00272         } else {
00273                 hdtrl[0].iov_base = NULL;
00274                 hdtrl[0].iov_len = hdr_len = 0;
00275         }
00276         hdtrl[1].iov_base = NULL;
00277         hdtrl[1].iov_len = 0;
00278 
00279         total = count;
00280         while (total + hdtrl[0].iov_len) {
00281                 ssize_t nwritten;
00282 
00283                 /*
00284                  * HPUX guarantees that if any data was written before
00285                  * a signal interrupt then sendfile returns the number of
00286                  * bytes written (which may be less than requested) not -1.
00287                  * nwritten includes the header data sent.
00288                  */
00289 
00290                 do {
00291 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
00292                         nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
00293 #else
00294                         nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
00295 #endif
00296                 } while (nwritten == -1 && errno == EINTR);
00297                 if (nwritten == -1)
00298                         return -1;
00299                 if (nwritten == 0)
00300                         return -1; /* I think we're at EOF here... */
00301 
00302                 /*
00303                  * If this was a short (signal interrupted) write we may need
00304                  * to subtract it from the header data, or null out the header
00305                  * data altogether if we wrote more than hdtrl[0].iov_len bytes.
00306                  * We change nwritten to be the number of file bytes written.
00307                  */
00308 
00309                 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
00310                         if (nwritten >= hdtrl[0].iov_len) {
00311                                 nwritten -= hdtrl[0].iov_len;
00312                                 hdtrl[0].iov_base = NULL;
00313                                 hdtrl[0].iov_len = 0;
00314                         } else {
00315                                 /* iov_base is defined as a void *... */
00316                                 hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
00317                                 hdtrl[0].iov_len -= nwritten;
00318                                 nwritten = 0;
00319                         }
00320                 }
00321                 total -= nwritten;
00322                 offset += nwritten;
00323         }
00324         return count + hdr_len;
00325 }
00326 
00327 #elif defined(FREEBSD_SENDFILE_API)
00328 
00329 #include <sys/types.h>
00330 #include <sys/socket.h>
00331 #include <sys/uio.h>
00332 
00333 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
00334 {
00335         size_t total=0;
00336         struct sf_hdtr hdr;
00337         struct iovec hdtrl;
00338         size_t hdr_len = 0;
00339 
00340         hdr.headers = &hdtrl;
00341         hdr.hdr_cnt = 1;
00342         hdr.trailers = NULL;
00343         hdr.trl_cnt = 0;
00344 
00345         /* Set up the header iovec. */
00346         if (header) {
00347                 hdtrl.iov_base = header->data;
00348                 hdtrl.iov_len = hdr_len = header->length;
00349         } else {
00350                 hdtrl.iov_base = NULL;
00351                 hdtrl.iov_len = 0;
00352         }
00353 
00354         total = count;
00355         while (total + hdtrl.iov_len) {
00356                 SMB_OFF_T nwritten;
00357                 int ret;
00358 
00359                 /*
00360                  * FreeBSD sendfile returns 0 on success, -1 on error.
00361                  * Remember, the tofd and fromfd are reversed..... :-).
00362                  * nwritten includes the header data sent.
00363                  */
00364 
00365                 do {
00366                         ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
00367                 } while (ret == -1 && errno == EINTR);
00368                 if (ret == -1)
00369                         return -1;
00370 
00371                 if (nwritten == 0)
00372                         return -1; /* I think we're at EOF here... */
00373 
00374                 /*
00375                  * If this was a short (signal interrupted) write we may need
00376                  * to subtract it from the header data, or null out the header
00377                  * data altogether if we wrote more than hdtrl.iov_len bytes.
00378                  * We change nwritten to be the number of file bytes written.
00379                  */
00380 
00381                 if (hdtrl.iov_base && hdtrl.iov_len) {
00382                         if (nwritten >= hdtrl.iov_len) {
00383                                 nwritten -= hdtrl.iov_len;
00384                                 hdtrl.iov_base = NULL;
00385                                 hdtrl.iov_len = 0;
00386                         } else {
00387                                 hdtrl.iov_base += nwritten;
00388                                 hdtrl.iov_len -= nwritten;
00389                                 nwritten = 0;
00390                         }
00391                 }
00392                 total -= nwritten;
00393                 offset += nwritten;
00394         }
00395         return count + hdr_len;
00396 }
00397 
00398 #elif defined(AIX_SENDFILE_API)
00399 
00400 /* BEGIN AIX SEND_FILE */
00401 
00402 /* Contributed by William Jojo <jojowil@hvcc.edu> */
00403 #include <sys/socket.h>
00404 
00405 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
00406 {
00407         struct sf_parms hdtrl;
00408 
00409         /* Set up the header/trailer struct params. */
00410         if (header) {
00411                 hdtrl.header_data = header->data;
00412                 hdtrl.header_length = header->length;
00413         } else {
00414                 hdtrl.header_data = NULL;
00415                 hdtrl.header_length = 0;
00416         }
00417         hdtrl.trailer_data = NULL;
00418         hdtrl.trailer_length = 0;
00419 
00420         hdtrl.file_descriptor = fromfd;
00421         hdtrl.file_offset = offset;
00422         hdtrl.file_bytes = count;
00423 
00424         while ( hdtrl.file_bytes + hdtrl.header_length ) {
00425                 ssize_t ret;
00426 
00427                 /*
00428                  Return Value
00429 
00430                  There are three possible return values from send_file:
00431 
00432                  Value Description
00433 
00434                  -1 an error has occurred, errno contains the error code.
00435 
00436                  0 the command has completed successfully.
00437 
00438                  1 the command was completed partially, some data has been
00439                  transmitted but the command has to return for some reason,
00440                  for example, the command was interrupted by signals.
00441                 */
00442                 do {
00443                         ret = send_file(&tofd, &hdtrl, 0);
00444                 } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
00445                 if ( ret == -1 )
00446                         return -1;
00447         }
00448 
00449         return count + header->length;
00450 }
00451 /* END AIX SEND_FILE */
00452 
00453 #else /* No sendfile implementation. Return error. */
00454 
00455 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
00456 {
00457         /* No sendfile syscall. */
00458         errno = ENOSYS;
00459         return -1;
00460 }
00461 #endif

Sambaに対してSat Aug 29 21:22:59 2009に生成されました。  doxygen 1.4.7