+static gboolean socket_disconnect (guint32 fd)
+{
+ struct _WapiHandle_socket *socket_handle;
+ gboolean ok;
+ gpointer handle = GUINT_TO_POINTER (fd);
+ int newsock, ret;
+
+ ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+ (gpointer *)&socket_handle);
+ if (ok == FALSE) {
+ g_warning ("%s: error looking up socket handle %p", __func__,
+ handle);
+ WSASetLastError (WSAENOTSOCK);
+ return(FALSE);
+ }
+
+ newsock = socket (socket_handle->domain, socket_handle->type,
+ socket_handle->protocol);
+ if (newsock == -1) {
+ gint errnum = errno;
+
+#ifdef DEBUG
+ g_message ("%s: socket error: %s", __func__, strerror (errno));
+#endif
+
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+
+ return(FALSE);
+ }
+
+ /* According to Stevens "Advanced Programming in the UNIX
+ * Environment: UNIX File I/O" dup2() is atomic so there
+ * should not be a race condition between the old fd being
+ * closed and the new socket fd being copied over
+ */
+ do {
+ ret = dup2 (newsock, fd);
+ } while (ret == -1 && errno == EAGAIN);
+
+ if (ret == -1) {
+ gint errnum = errno;
+
+#ifdef DEBUG
+ g_message ("%s: dup2 error: %s", __func__, strerror (errno));
+#endif
+
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+
+ return(FALSE);
+ }
+
+ close (newsock);
+
+ return(TRUE);
+}
+
+static gboolean wapi_disconnectex (guint32 fd, WapiOverlapped *overlapped,
+ guint32 flags, guint32 reserved)
+{
+#ifdef DEBUG
+ g_message ("%s: called on socket %d!", __func__, fd);
+#endif
+
+ if (reserved != 0) {
+ WSASetLastError (WSAEINVAL);
+ return(FALSE);
+ }
+
+ /* We could check the socket type here and fail unless its
+ * SOCK_STREAM, SOCK_SEQPACKET or SOCK_RDM (according to msdn)
+ * if we really wanted to
+ */
+
+ return(socket_disconnect (fd));
+}
+
+#define SF_BUFFER_SIZE 16384
+static gint
+wapi_sendfile (guint32 socket, gpointer fd, guint32 bytes_to_write, guint32 bytes_per_send, guint32 flags)
+{
+#if defined(HAVE_SENDFILE) && (defined(__linux__) || defined(DARWIN))
+ gint file = GPOINTER_TO_INT (fd);
+ gint n;
+ gint errnum;
+ gssize res;
+ struct stat statbuf;
+
+ n = fstat (file, &statbuf);
+ if (n == -1) {
+ errnum = errno;
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+ return SOCKET_ERROR;
+ }
+ do {
+#ifdef __linux__
+ res = sendfile (socket, file, NULL, statbuf.st_size);
+#elif defined(DARWIN)
+ /* TODO: header/tail could be sent in the 5th argument */
+ /* TODO: Might not send the entire file for non-blocking sockets */
+ res = sendfile (file, socket, 0, &statbuf.st_size, NULL, 0);
+#endif
+ } while (res != -1 && (errno == EINTR || errno == EAGAIN) && !_wapi_thread_cur_apc_pending ());
+ if (res == -1) {
+ errnum = errno;
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+ return SOCKET_ERROR;
+ }
+#else
+ /* Default implementation */
+ gint file = GPOINTER_TO_INT (fd);
+ gchar *buffer;
+ gint n;
+
+ buffer = g_malloc (SF_BUFFER_SIZE);
+ do {
+ do {
+ n = read (file, buffer, SF_BUFFER_SIZE);
+ } while (n == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
+ if (n == -1)
+ break;
+ if (n == 0) {
+ g_free (buffer);
+ return 0; /* We're done reading */
+ }
+ do {
+ n = send (socket, buffer, n, 0); /* short sends? enclose this in a loop? */
+ } while (n == -1 && errno == EINTR && !_wapi_thread_cur_apc_pending ());
+ } while (n != -1);
+
+ if (n == -1) {
+ gint errnum = errno;
+ errnum = errno_to_WSA (errnum, __func__);
+ WSASetLastError (errnum);
+ g_free (buffer);
+ return SOCKET_ERROR;
+ }
+ g_free (buffer);
+#endif
+ return 0;
+}
+
+gboolean
+TransmitFile (guint32 socket, gpointer file, guint32 bytes_to_write, guint32 bytes_per_send, WapiOverlapped *ol,
+ WapiTransmitFileBuffers *buffers, guint32 flags)
+{
+ gpointer sock = GUINT_TO_POINTER (socket);
+ gint ret;
+
+ if (startup_count == 0) {
+ WSASetLastError (WSANOTINITIALISED);
+ return FALSE;
+ }
+
+ if (_wapi_handle_type (sock) != WAPI_HANDLE_SOCKET) {
+ WSASetLastError (WSAENOTSOCK);
+ return FALSE;
+ }
+
+ /* Write the header */
+ if (buffers != NULL && buffers->Head != NULL && buffers->HeadLength > 0) {
+ ret = _wapi_send (socket, buffers->Head, buffers->HeadLength, 0);
+ if (ret == SOCKET_ERROR)
+ return FALSE;
+ }
+
+ ret = wapi_sendfile (socket, file, bytes_to_write, bytes_per_send, flags);
+ if (ret == SOCKET_ERROR)
+ return FALSE;
+
+ /* Write the tail */
+ if (buffers != NULL && buffers->Tail != NULL && buffers->TailLength > 0) {
+ ret = _wapi_send (socket, buffers->Tail, buffers->TailLength, 0);
+ if (ret == SOCKET_ERROR)
+ return FALSE;
+ }
+
+ if ((flags & TF_DISCONNECT) == TF_DISCONNECT)
+ closesocket (socket);
+
+ return TRUE;
+}
+
+static struct
+{
+ WapiGuid guid;
+ gpointer func;
+} extension_functions[] = {
+ {WSAID_DISCONNECTEX, wapi_disconnectex},
+ {WSAID_TRANSMITFILE, TransmitFile},
+ {{0}, NULL},
+};
+