| /* GIO - GLib Input, Output and Streaming Library | |
| * | |
| * Copyright (C) 2006-2010 Red Hat, Inc. | |
| * | |
| * SPDX-License-Identifier: LGPL-2.1-or-later | |
| * | |
| * 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, see <http://www.gnu.org/licenses/>. | |
| * | |
| * Author: Alexander Larsson <alexl@redhat.com> | |
| * Author: Tor Lillqvist <tml@iki.fi> | |
| */ | |
| #include "config.h" | |
| #include <windows.h> | |
| #include <io.h> | |
| #include <glib.h> | |
| #include "gioerror.h" | |
| #include "gwin32inputstream.h" | |
| #include "giowin32-priv.h" | |
| #include "gcancellable.h" | |
| #include "gasynchelper.h" | |
| #include "glibintl.h" | |
| /** | |
| * GWin32InputStream: | |
| * | |
| * `GWin32InputStream` implements [class@Gio.InputStream] for reading from a | |
| * Windows file handle. | |
| * | |
| * Note that `<gio/gwin32inputstream.h>` belongs to the Windows-specific GIO | |
| * interfaces, thus you have to use the `gio-windows-2.0.pc` pkg-config file | |
| * when using it. | |
| */ | |
| struct _GWin32InputStreamPrivate { | |
| HANDLE handle; | |
| gboolean close_handle; | |
| gint fd; | |
| }; | |
| enum { | |
| PROP_0, | |
| PROP_HANDLE, | |
| PROP_CLOSE_HANDLE, | |
| LAST_PROP | |
| }; | |
| static GParamSpec *props[LAST_PROP]; | |
| G_DEFINE_TYPE_WITH_PRIVATE (GWin32InputStream, g_win32_input_stream, G_TYPE_INPUT_STREAM) | |
| static void | |
| g_win32_input_stream_set_property (GObject *object, | |
| guint prop_id, | |
| const GValue *value, | |
| GParamSpec *pspec) | |
| { | |
| GWin32InputStream *win32_stream; | |
| win32_stream = G_WIN32_INPUT_STREAM (object); | |
| switch (prop_id) | |
| { | |
| case PROP_HANDLE: | |
| win32_stream->priv->handle = g_value_get_pointer (value); | |
| break; | |
| case PROP_CLOSE_HANDLE: | |
| win32_stream->priv->close_handle = g_value_get_boolean (value); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| break; | |
| } | |
| } | |
| static void | |
| g_win32_input_stream_get_property (GObject *object, | |
| guint prop_id, | |
| GValue *value, | |
| GParamSpec *pspec) | |
| { | |
| GWin32InputStream *win32_stream; | |
| win32_stream = G_WIN32_INPUT_STREAM (object); | |
| switch (prop_id) | |
| { | |
| case PROP_HANDLE: | |
| g_value_set_pointer (value, win32_stream->priv->handle); | |
| break; | |
| case PROP_CLOSE_HANDLE: | |
| g_value_set_boolean (value, win32_stream->priv->close_handle); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| } | |
| } | |
| static gssize | |
| g_win32_input_stream_read (GInputStream *stream, | |
| void *buffer, | |
| gsize count, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GWin32InputStream *win32_stream; | |
| BOOL res; | |
| DWORD nbytes, nread; | |
| OVERLAPPED overlap = { 0, }; | |
| gssize retval = -1; | |
| win32_stream = G_WIN32_INPUT_STREAM (stream); | |
| if (g_cancellable_set_error_if_cancelled (cancellable, error)) | |
| return -1; | |
| if (count > G_MAXINT) | |
| nbytes = G_MAXINT; | |
| else | |
| nbytes = count; | |
| overlap.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); | |
| g_return_val_if_fail (overlap.hEvent != NULL, -1); | |
| res = ReadFile (win32_stream->priv->handle, buffer, nbytes, &nread, &overlap); | |
| if (res) | |
| retval = nread; | |
| else | |
| { | |
| int errsv = GetLastError (); | |
| if (errsv == ERROR_IO_PENDING && | |
| _g_win32_overlap_wait_result (win32_stream->priv->handle, | |
| &overlap, &nread, cancellable)) | |
| { | |
| retval = nread; | |
| goto end; | |
| } | |
| if (g_cancellable_set_error_if_cancelled (cancellable, error)) | |
| goto end; | |
| errsv = GetLastError (); | |
| if (errsv == ERROR_MORE_DATA) | |
| { | |
| /* If a named pipe is being read in message mode and the | |
| * next message is longer than the nNumberOfBytesToRead | |
| * parameter specifies, ReadFile returns FALSE and | |
| * GetLastError returns ERROR_MORE_DATA */ | |
| retval = nread; | |
| goto end; | |
| } | |
| else if (errsv == ERROR_HANDLE_EOF || | |
| errsv == ERROR_BROKEN_PIPE) | |
| { | |
| /* TODO: the other end of a pipe may call the WriteFile | |
| * function with nNumberOfBytesToWrite set to zero. In this | |
| * case, it's not possible for the caller to know if it's | |
| * broken pipe or a read of 0. Perhaps we should add a | |
| * is_broken flag for this win32 case.. */ | |
| retval = 0; | |
| } | |
| else | |
| { | |
| gchar *emsg; | |
| emsg = g_win32_error_message (errsv); | |
| g_set_error (error, G_IO_ERROR, | |
| g_io_error_from_win32_error (errsv), | |
| _("Error reading from handle: %s"), | |
| emsg); | |
| g_free (emsg); | |
| } | |
| } | |
| end: | |
| CloseHandle (overlap.hEvent); | |
| return retval; | |
| } | |
| static gboolean | |
| g_win32_input_stream_close (GInputStream *stream, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GWin32InputStream *win32_stream; | |
| BOOL res; | |
| win32_stream = G_WIN32_INPUT_STREAM (stream); | |
| if (!win32_stream->priv->close_handle) | |
| return TRUE; | |
| if (win32_stream->priv->fd != -1) | |
| { | |
| if (close (win32_stream->priv->fd) < 0) | |
| { | |
| int errsv = errno; | |
| g_set_error (error, G_IO_ERROR, | |
| g_io_error_from_errno (errsv), | |
| _("Error closing file descriptor: %s"), | |
| g_strerror (errsv)); | |
| return FALSE; | |
| } | |
| } | |
| else | |
| { | |
| res = CloseHandle (win32_stream->priv->handle); | |
| if (!res) | |
| { | |
| int errsv = GetLastError (); | |
| gchar *emsg = g_win32_error_message (errsv); | |
| g_set_error (error, G_IO_ERROR, | |
| g_io_error_from_win32_error (errsv), | |
| _("Error closing handle: %s"), | |
| emsg); | |
| g_free (emsg); | |
| return FALSE; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| static void | |
| g_win32_input_stream_class_init (GWin32InputStreamClass *klass) | |
| { | |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); | |
| GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); | |
| gobject_class->get_property = g_win32_input_stream_get_property; | |
| gobject_class->set_property = g_win32_input_stream_set_property; | |
| stream_class->read_fn = g_win32_input_stream_read; | |
| stream_class->close_fn = g_win32_input_stream_close; | |
| /** | |
| * GWin32InputStream:handle: | |
| * | |
| * The handle that the stream reads from. | |
| * | |
| * Since: 2.26 | |
| */ | |
| props[PROP_HANDLE] = | |
| g_param_spec_pointer ("handle", NULL, NULL, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_CONSTRUCT_ONLY | | |
| G_PARAM_STATIC_STRINGS); | |
| /** | |
| * GWin32InputStream:close-handle: | |
| * | |
| * Whether to close the file handle when the stream is closed. | |
| * | |
| * Since: 2.26 | |
| */ | |
| props[PROP_CLOSE_HANDLE] = | |
| g_param_spec_boolean ("close-handle", NULL, NULL, | |
| TRUE, | |
| G_PARAM_READABLE | | |
| G_PARAM_WRITABLE | | |
| G_PARAM_STATIC_STRINGS); | |
| g_object_class_install_properties (gobject_class, LAST_PROP, props); | |
| } | |
| static void | |
| g_win32_input_stream_init (GWin32InputStream *win32_stream) | |
| { | |
| win32_stream->priv = g_win32_input_stream_get_instance_private (win32_stream); | |
| win32_stream->priv->handle = NULL; | |
| win32_stream->priv->close_handle = TRUE; | |
| win32_stream->priv->fd = -1; | |
| } | |
| /** | |
| * g_win32_input_stream_new: | |
| * @handle: a Win32 file handle | |
| * @close_handle: %TRUE to close the handle when done | |
| * | |
| * Creates a new #GWin32InputStream for the given @handle. | |
| * | |
| * If @close_handle is %TRUE, the handle will be closed | |
| * when the stream is closed. | |
| * | |
| * Note that "handle" here means a Win32 HANDLE, not a "file descriptor" | |
| * as used in the Windows C libraries. | |
| * | |
| * Returns: a new #GWin32InputStream | |
| **/ | |
| GInputStream * | |
| g_win32_input_stream_new (void *handle, | |
| gboolean close_handle) | |
| { | |
| GWin32InputStream *stream; | |
| g_return_val_if_fail (handle != NULL, NULL); | |
| stream = g_object_new (G_TYPE_WIN32_INPUT_STREAM, | |
| "handle", handle, | |
| "close-handle", close_handle, | |
| NULL); | |
| return G_INPUT_STREAM (stream); | |
| } | |
| /** | |
| * g_win32_input_stream_set_close_handle: | |
| * @stream: a #GWin32InputStream | |
| * @close_handle: %TRUE to close the handle when done | |
| * | |
| * Sets whether the handle of @stream shall be closed | |
| * when the stream is closed. | |
| * | |
| * Since: 2.26 | |
| */ | |
| void | |
| g_win32_input_stream_set_close_handle (GWin32InputStream *stream, | |
| gboolean close_handle) | |
| { | |
| g_return_if_fail (G_IS_WIN32_INPUT_STREAM (stream)); | |
| close_handle = close_handle != FALSE; | |
| if (stream->priv->close_handle != close_handle) | |
| { | |
| stream->priv->close_handle = close_handle; | |
| g_object_notify (G_OBJECT (stream), "close-handle"); | |
| } | |
| } | |
| /** | |
| * g_win32_input_stream_get_close_handle: | |
| * @stream: a #GWin32InputStream | |
| * | |
| * Returns whether the handle of @stream will be | |
| * closed when the stream is closed. | |
| * | |
| * Returns: %TRUE if the handle is closed when done | |
| * | |
| * Since: 2.26 | |
| */ | |
| gboolean | |
| g_win32_input_stream_get_close_handle (GWin32InputStream *stream) | |
| { | |
| g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), FALSE); | |
| return stream->priv->close_handle; | |
| } | |
| /** | |
| * g_win32_input_stream_get_handle: | |
| * @stream: a #GWin32InputStream | |
| * | |
| * Return the Windows file handle that the stream reads from. | |
| * | |
| * Returns: The file handle of @stream | |
| * | |
| * Since: 2.26 | |
| */ | |
| void * | |
| g_win32_input_stream_get_handle (GWin32InputStream *stream) | |
| { | |
| g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), NULL); | |
| return stream->priv->handle; | |
| } | |
| GInputStream * | |
| g_win32_input_stream_new_from_fd (gint fd, | |
| gboolean close_fd) | |
| { | |
| GWin32InputStream *win32_stream; | |
| win32_stream = G_WIN32_INPUT_STREAM (g_win32_input_stream_new ((HANDLE) _get_osfhandle (fd), close_fd)); | |
| win32_stream->priv->fd = fd; | |
| return (GInputStream*)win32_stream; | |
| } |