| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2007-2019 Daniel Pittman, Christian Grothoff and |
| Karlson2k (Evgeny Grin) |
| |
| 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" |
| |
| |
| /** |
| * Size of single file read operation for |
| * file-backed responses. |
| */ |
| #ifndef MHD_FILE_READ_BLOCK_SIZE |
| #ifdef _WIN32 |
| #define MHD_FILE_READ_BLOCK_SIZE 16384 /* 16k */ |
| #else /* _WIN32 */ |
| #define MHD_FILE_READ_BLOCK_SIZE 4096 /* 4k */ |
| #endif /* _WIN32 */ |
| #endif /* !MHD_FD_BLOCK_SIZE */ |
| |
| /** |
| * 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, |
| MHD_FILE_READ_BLOCK_SIZE, |
| &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 */ |