1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2010 Red Hat, Inc.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 * Author: Tor Lillqvist <tml@iki.fi>
22 */
23
24 #include "config.h"
25
26 #include <windows.h>
27
28 #include <io.h>
29
30 #include <glib.h>
31 #include "gioerror.h"
32 #include "gwin32inputstream.h"
33 #include "giowin32-priv.h"
34 #include "gcancellable.h"
35 #include "gasynchelper.h"
36 #include "glibintl.h"
37
38 /**
39 * GWin32InputStream:
40 *
41 * `GWin32InputStream` implements [class@Gio.InputStream] for reading from a
42 * Windows file handle.
43 *
44 * Note that `<gio/gwin32inputstream.h>` belongs to the Windows-specific GIO
45 * interfaces, thus you have to use the `gio-windows-2.0.pc` pkg-config file
46 * when using it.
47 */
48
49 struct _GWin32InputStreamPrivate {
50 HANDLE handle;
51 gboolean close_handle;
52 gint fd;
53 };
54
55 enum {
56 PROP_0,
57 PROP_HANDLE,
58 PROP_CLOSE_HANDLE,
59 LAST_PROP
60 };
61
62 static GParamSpec *props[LAST_PROP];
63
64 G_DEFINE_TYPE_WITH_PRIVATE (GWin32InputStream, g_win32_input_stream, G_TYPE_INPUT_STREAM)
65
66 static void
67 g_win32_input_stream_set_property (GObject *object,
68 guint prop_id,
69 const GValue *value,
70 GParamSpec *pspec)
71 {
72 GWin32InputStream *win32_stream;
73
74 win32_stream = G_WIN32_INPUT_STREAM (object);
75
76 switch (prop_id)
77 {
78 case PROP_HANDLE:
79 win32_stream->priv->handle = g_value_get_pointer (value);
80 break;
81 case PROP_CLOSE_HANDLE:
82 win32_stream->priv->close_handle = g_value_get_boolean (value);
83 break;
84 default:
85 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
86 break;
87 }
88 }
89
90 static void
91 g_win32_input_stream_get_property (GObject *object,
92 guint prop_id,
93 GValue *value,
94 GParamSpec *pspec)
95 {
96 GWin32InputStream *win32_stream;
97
98 win32_stream = G_WIN32_INPUT_STREAM (object);
99
100 switch (prop_id)
101 {
102 case PROP_HANDLE:
103 g_value_set_pointer (value, win32_stream->priv->handle);
104 break;
105 case PROP_CLOSE_HANDLE:
106 g_value_set_boolean (value, win32_stream->priv->close_handle);
107 break;
108 default:
109 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
110 }
111 }
112
113 static gssize
114 g_win32_input_stream_read (GInputStream *stream,
115 void *buffer,
116 gsize count,
117 GCancellable *cancellable,
118 GError **error)
119 {
120 GWin32InputStream *win32_stream;
121 BOOL res;
122 DWORD nbytes, nread;
123 OVERLAPPED overlap = { 0, };
124 gssize retval = -1;
125
126 win32_stream = G_WIN32_INPUT_STREAM (stream);
127
128 if (g_cancellable_set_error_if_cancelled (cancellable, error))
129 return -1;
130
131 if (count > G_MAXINT)
132 nbytes = G_MAXINT;
133 else
134 nbytes = count;
135
136 overlap.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
137 g_return_val_if_fail (overlap.hEvent != NULL, -1);
138
139 res = ReadFile (win32_stream->priv->handle, buffer, nbytes, &nread, &overlap);
140 if (res)
141 retval = nread;
142 else
143 {
144 int errsv = GetLastError ();
145
146 if (errsv == ERROR_IO_PENDING &&
147 _g_win32_overlap_wait_result (win32_stream->priv->handle,
148 &overlap, &nread, cancellable))
149 {
150 retval = nread;
151 goto end;
152 }
153
154 if (g_cancellable_set_error_if_cancelled (cancellable, error))
155 goto end;
156
157 errsv = GetLastError ();
158 if (errsv == ERROR_MORE_DATA)
159 {
160 /* If a named pipe is being read in message mode and the
161 * next message is longer than the nNumberOfBytesToRead
162 * parameter specifies, ReadFile returns FALSE and
163 * GetLastError returns ERROR_MORE_DATA */
164 retval = nread;
165 goto end;
166 }
167 else if (errsv == ERROR_HANDLE_EOF ||
168 errsv == ERROR_BROKEN_PIPE)
169 {
170 /* TODO: the other end of a pipe may call the WriteFile
171 * function with nNumberOfBytesToWrite set to zero. In this
172 * case, it's not possible for the caller to know if it's
173 * broken pipe or a read of 0. Perhaps we should add a
174 * is_broken flag for this win32 case.. */
175 retval = 0;
176 }
177 else
178 {
179 gchar *emsg;
180
181 emsg = g_win32_error_message (errsv);
182 g_set_error (error, G_IO_ERROR,
183 g_io_error_from_win32_error (errsv),
184 _("Error reading from handle: %s"),
185 emsg);
186 g_free (emsg);
187 }
188 }
189
190 end:
191 CloseHandle (overlap.hEvent);
192 return retval;
193 }
194
195 static gboolean
196 g_win32_input_stream_close (GInputStream *stream,
197 GCancellable *cancellable,
198 GError **error)
199 {
200 GWin32InputStream *win32_stream;
201 BOOL res;
202
203 win32_stream = G_WIN32_INPUT_STREAM (stream);
204
205 if (!win32_stream->priv->close_handle)
206 return TRUE;
207
208 if (win32_stream->priv->fd != -1)
209 {
210 if (close (win32_stream->priv->fd) < 0)
211 {
212 int errsv = errno;
213
214 g_set_error (error, G_IO_ERROR,
215 g_io_error_from_errno (errsv),
216 _("Error closing file descriptor: %s"),
217 g_strerror (errsv));
218 return FALSE;
219 }
220 }
221 else
222 {
223 res = CloseHandle (win32_stream->priv->handle);
224 if (!res)
225 {
226 int errsv = GetLastError ();
227 gchar *emsg = g_win32_error_message (errsv);
228
229 g_set_error (error, G_IO_ERROR,
230 g_io_error_from_win32_error (errsv),
231 _("Error closing handle: %s"),
232 emsg);
233 g_free (emsg);
234 return FALSE;
235 }
236 }
237
238 return TRUE;
239 }
240
241 static void
242 g_win32_input_stream_class_init (GWin32InputStreamClass *klass)
243 {
244 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
245 GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
246
247 gobject_class->get_property = g_win32_input_stream_get_property;
248 gobject_class->set_property = g_win32_input_stream_set_property;
249
250 stream_class->read_fn = g_win32_input_stream_read;
251 stream_class->close_fn = g_win32_input_stream_close;
252
253 /**
254 * GWin32InputStream:handle:
255 *
256 * The handle that the stream reads from.
257 *
258 * Since: 2.26
259 */
260 props[PROP_HANDLE] =
261 g_param_spec_pointer ("handle", NULL, NULL,
262 G_PARAM_READABLE |
263 G_PARAM_WRITABLE |
264 G_PARAM_CONSTRUCT_ONLY |
265 G_PARAM_STATIC_STRINGS);
266
267 /**
268 * GWin32InputStream:close-handle:
269 *
270 * Whether to close the file handle when the stream is closed.
271 *
272 * Since: 2.26
273 */
274 props[PROP_CLOSE_HANDLE] =
275 g_param_spec_boolean ("close-handle", NULL, NULL,
276 TRUE,
277 G_PARAM_READABLE |
278 G_PARAM_WRITABLE |
279 G_PARAM_STATIC_STRINGS);
280
281 g_object_class_install_properties (gobject_class, LAST_PROP, props);
282 }
283
284 static void
285 g_win32_input_stream_init (GWin32InputStream *win32_stream)
286 {
287 win32_stream->priv = g_win32_input_stream_get_instance_private (win32_stream);
288 win32_stream->priv->handle = NULL;
289 win32_stream->priv->close_handle = TRUE;
290 win32_stream->priv->fd = -1;
291 }
292
293 /**
294 * g_win32_input_stream_new:
295 * @handle: a Win32 file handle
296 * @close_handle: %TRUE to close the handle when done
297 *
298 * Creates a new #GWin32InputStream for the given @handle.
299 *
300 * If @close_handle is %TRUE, the handle will be closed
301 * when the stream is closed.
302 *
303 * Note that "handle" here means a Win32 HANDLE, not a "file descriptor"
304 * as used in the Windows C libraries.
305 *
306 * Returns: a new #GWin32InputStream
307 **/
308 GInputStream *
309 g_win32_input_stream_new (void *handle,
310 gboolean close_handle)
311 {
312 GWin32InputStream *stream;
313
314 g_return_val_if_fail (handle != NULL, NULL);
315
316 stream = g_object_new (G_TYPE_WIN32_INPUT_STREAM,
317 "handle", handle,
318 "close-handle", close_handle,
319 NULL);
320
321 return G_INPUT_STREAM (stream);
322 }
323
324 /**
325 * g_win32_input_stream_set_close_handle:
326 * @stream: a #GWin32InputStream
327 * @close_handle: %TRUE to close the handle when done
328 *
329 * Sets whether the handle of @stream shall be closed
330 * when the stream is closed.
331 *
332 * Since: 2.26
333 */
334 void
335 g_win32_input_stream_set_close_handle (GWin32InputStream *stream,
336 gboolean close_handle)
337 {
338 g_return_if_fail (G_IS_WIN32_INPUT_STREAM (stream));
339
340 close_handle = close_handle != FALSE;
341 if (stream->priv->close_handle != close_handle)
342 {
343 stream->priv->close_handle = close_handle;
344 g_object_notify (G_OBJECT (stream), "close-handle");
345 }
346 }
347
348 /**
349 * g_win32_input_stream_get_close_handle:
350 * @stream: a #GWin32InputStream
351 *
352 * Returns whether the handle of @stream will be
353 * closed when the stream is closed.
354 *
355 * Returns: %TRUE if the handle is closed when done
356 *
357 * Since: 2.26
358 */
359 gboolean
360 g_win32_input_stream_get_close_handle (GWin32InputStream *stream)
361 {
362 g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), FALSE);
363
364 return stream->priv->close_handle;
365 }
366
367 /**
368 * g_win32_input_stream_get_handle:
369 * @stream: a #GWin32InputStream
370 *
371 * Return the Windows file handle that the stream reads from.
372 *
373 * Returns: The file handle of @stream
374 *
375 * Since: 2.26
376 */
377 void *
378 g_win32_input_stream_get_handle (GWin32InputStream *stream)
379 {
380 g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), NULL);
381
382 return stream->priv->handle;
383 }
384
385 GInputStream *
386 g_win32_input_stream_new_from_fd (gint fd,
387 gboolean close_fd)
388 {
389 GWin32InputStream *win32_stream;
390
391 win32_stream = G_WIN32_INPUT_STREAM (g_win32_input_stream_new ((HANDLE) _get_osfhandle (fd), close_fd));
392 win32_stream->priv->fd = fd;
393
394 return (GInputStream*)win32_stream;
395 }