text: /*
    This file is part of libmicrohttpd
    Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
  
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
  
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
  
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
  
  /**
   * @file lib/response_from_fd.c
   * @brief implementation of MHD_response_from_fd()
   * @author Daniel Pittman
   * @author Christian Grothoff
   * @author Karlson2k (Evgeny Grin)
   */
  #include "internal.h"
  
  
  /**
   * Given a file descriptor, read data from the file
   * to generate the response.
   *
   * @param cls pointer to the response
   * @param pos offset in the file to access
   * @param buf where to write the data
   * @param max number of bytes to write at most
   * @return number of bytes written
   */
  static ssize_t
  file_reader (void *cls,
               uint64_t pos,
               char *buf,
               size_t max)
  {
    struct MHD_Response *response = cls;
  #if !defined(_WIN32) || defined(__CYGWIN__)
    ssize_t n;
  #else  /* _WIN32 && !__CYGWIN__ */
    const HANDLE fh = (HANDLE) _get_osfhandle (response->fd);
  #endif /* _WIN32 && !__CYGWIN__ */
    const int64_t offset64 = (int64_t)(pos + response->fd_off);
  
    if (offset64 < 0)
      return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */
  
  #if !defined(_WIN32) || defined(__CYGWIN__)
    if (max > SSIZE_MAX)
      max = SSIZE_MAX; /* Clamp to maximum return value. */
  
  #if defined(HAVE_PREAD64)
    n = pread64 (response->fd,
  	       buf,
  	       max,
  	       offset64);
  #elif defined(HAVE_PREAD)
    if ( (sizeof(off_t) < sizeof (uint64_t)) &&
         (offset64 > (uint64_t)INT32_MAX) )
      return MHD_CONTENT_READER_END_WITH_ERROR; /* Read at required position is not possible. */
  
    n = pread (response->fd,
  	     buf,
  	     max,
  	     (off_t) offset64);
  #else  /* ! HAVE_PREAD */
  #if defined(HAVE_LSEEK64)
    if (lseek64 (response->fd,
                 offset64,
                 SEEK_SET) != offset64)
      return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */
  #else  /* ! HAVE_LSEEK64 */
    if ( (sizeof(off_t) < sizeof (uint64_t)) &&
         (offset64 > (uint64_t)INT32_MAX) )
      return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */
  
    if (lseek (response->fd,
               (off_t) offset64,
               SEEK_SET) != (off_t) offset64)
      return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */
  #endif /* ! HAVE_LSEEK64 */
    n = read (response->fd,
              buf,
              max);
  
  #endif /* ! HAVE_PREAD */
    if (0 == n)
      return MHD_CONTENT_READER_END_OF_STREAM;
    if (n < 0)
      return MHD_CONTENT_READER_END_WITH_ERROR;
    return n;
  #else /* _WIN32 && !__CYGWIN__ */
    if (INVALID_HANDLE_VALUE == fh)
      return MHD_CONTENT_READER_END_WITH_ERROR; /* Value of 'response->fd' is not valid. */
    else
      {
        OVERLAPPED f_ol = {0, 0, {{0, 0}}, 0}; /* Initialize to zero. */
        ULARGE_INTEGER pos_uli;
        DWORD toRead = (max > INT32_MAX) ? INT32_MAX : (DWORD) max;
        DWORD resRead;
  
        pos_uli.QuadPart = (uint64_t) offset64; /* Simple transformation 64bit -> 2x32bit. */
        f_ol.Offset = pos_uli.LowPart;
        f_ol.OffsetHigh = pos_uli.HighPart;
        if (! ReadFile (fh,
  		      (void*)buf,
  		      toRead,
  		      &resRead,
  		      &f_ol))
          return MHD_CONTENT_READER_END_WITH_ERROR; /* Read error. */
        if (0 == resRead)
          return MHD_CONTENT_READER_END_OF_STREAM;
        return (ssize_t) resRead;
      }
  #endif /* _WIN32 && !__CYGWIN__ */
  }
  
  
  /**
   * Destroy file reader context.  Closes the file
   * descriptor.
   *
   * @param cls pointer to file descriptor
   */
  static void
  free_callback (void *cls)
  {
    struct MHD_Response *response = cls;
  
    (void) close (response->fd);
    response->fd = -1;
  }
  
  
  /**
   * Create a response object based on an @a fd from which
   * data is read.  The response object can be extended with
   * header information and then be used any number of times.
   *
   * @param sc status code to return
   * @param fd file descriptor referring to a file on disk with the
   *        data; will be closed when response is destroyed;
   *        fd should be in 'blocking' mode
   * @param offset offset to start reading from in the file;
   *        reading file beyond 2 GiB may be not supported by OS or
   *        MHD build; see ::MHD_FEATURE_LARGE_FILE
   * @param size size of the data portion of the response;
   *        sizes larger than 2 GiB may be not supported by OS or
   *        MHD build; see ::MHD_FEATURE_LARGE_FILE
   * @return NULL on error (i.e. invalid arguments, out of memory)
   * @ingroup response
   */
  struct MHD_Response *
  MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
  		      int fd,
  		      uint64_t offset,
  		      uint64_t size)
  {
    struct MHD_Response *response;
  
    mhd_assert (-1 != fd);
  #if !defined(HAVE___LSEEKI64) && !defined(HAVE_LSEEK64)
    if ( (sizeof (uint64_t) > sizeof (off_t)) &&
         ( (size > (uint64_t)INT32_MAX) ||
           (offset > (uint64_t)INT32_MAX) ||
           ((size + offset) >= (uint64_t)INT32_MAX) ) )
      return NULL;
  #endif
    if ( ((int64_t) size < 0) ||
         ((int64_t) offset < 0) ||
         ((int64_t) (size + offset) < 0) )
      return NULL;
  
    response = MHD_response_from_callback (sc,
  					 size,
  					 4 * 1024,
  					 &file_reader,
  					 NULL,
  					 &free_callback);
    if (NULL == response)
      return NULL;
    response->fd = fd;
    response->fd_off = offset;
    response->crc_cls = response;
    return response;
  }
  
  /* end of response_from_fd.c */
  
  