/* 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 */