Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2006-2007 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 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <glib.h>
26 : : #include <gfileinputstream.h>
27 : : #include <gseekable.h>
28 : : #include "gcancellable.h"
29 : : #include "gasyncresult.h"
30 : : #include "gtask.h"
31 : : #include "gioerror.h"
32 : : #include "glibintl.h"
33 : :
34 : :
35 : : /**
36 : : * GFileInputStream:
37 : : *
38 : : * `GFileInputStream` provides input streams that take their
39 : : * content from a file.
40 : : *
41 : : * `GFileInputStream` implements [iface@Gio.Seekable], which allows the input
42 : : * stream to jump to arbitrary positions in the file, provided the
43 : : * filesystem of the file allows it. To find the position of a file
44 : : * input stream, use [method@Gio.Seekable.tell]. To find out if a file input
45 : : * stream supports seeking, use [vfunc@Gio.Seekable.can_seek].
46 : : * To position a file input stream, use [vfunc@Gio.Seekable.seek].
47 : : **/
48 : :
49 : : static void g_file_input_stream_seekable_iface_init (GSeekableIface *iface);
50 : : static goffset g_file_input_stream_seekable_tell (GSeekable *seekable);
51 : : static gboolean g_file_input_stream_seekable_can_seek (GSeekable *seekable);
52 : : static gboolean g_file_input_stream_seekable_seek (GSeekable *seekable,
53 : : goffset offset,
54 : : GSeekType type,
55 : : GCancellable *cancellable,
56 : : GError **error);
57 : : static gboolean g_file_input_stream_seekable_can_truncate (GSeekable *seekable);
58 : : static gboolean g_file_input_stream_seekable_truncate (GSeekable *seekable,
59 : : goffset offset,
60 : : GCancellable *cancellable,
61 : : GError **error);
62 : : static void g_file_input_stream_real_query_info_async (GFileInputStream *stream,
63 : : const char *attributes,
64 : : int io_priority,
65 : : GCancellable *cancellable,
66 : : GAsyncReadyCallback callback,
67 : : gpointer user_data);
68 : : static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream *stream,
69 : : GAsyncResult *result,
70 : : GError **error);
71 : :
72 : :
73 : : struct _GFileInputStreamPrivate {
74 : : GAsyncReadyCallback outstanding_callback;
75 : : };
76 : :
77 : 746 : G_DEFINE_TYPE_WITH_CODE (GFileInputStream, g_file_input_stream, G_TYPE_INPUT_STREAM,
78 : : G_ADD_PRIVATE (GFileInputStream)
79 : : G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
80 : : g_file_input_stream_seekable_iface_init))
81 : :
82 : : static void
83 : 16 : g_file_input_stream_class_init (GFileInputStreamClass *klass)
84 : : {
85 : 16 : klass->query_info_async = g_file_input_stream_real_query_info_async;
86 : 16 : klass->query_info_finish = g_file_input_stream_real_query_info_finish;
87 : 16 : }
88 : :
89 : : static void
90 : 16 : g_file_input_stream_seekable_iface_init (GSeekableIface *iface)
91 : : {
92 : 16 : iface->tell = g_file_input_stream_seekable_tell;
93 : 16 : iface->can_seek = g_file_input_stream_seekable_can_seek;
94 : 16 : iface->seek = g_file_input_stream_seekable_seek;
95 : 16 : iface->can_truncate = g_file_input_stream_seekable_can_truncate;
96 : 16 : iface->truncate_fn = g_file_input_stream_seekable_truncate;
97 : 16 : }
98 : :
99 : : static void
100 : 228 : g_file_input_stream_init (GFileInputStream *stream)
101 : : {
102 : 228 : stream->priv = g_file_input_stream_get_instance_private (stream);
103 : 228 : }
104 : :
105 : : /**
106 : : * g_file_input_stream_query_info:
107 : : * @stream: a #GFileInputStream.
108 : : * @attributes: a file attribute query string.
109 : : * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
110 : : * @error: a #GError location to store the error occurring, or %NULL to
111 : : * ignore.
112 : : *
113 : : * Queries a file input stream the given @attributes. This function blocks
114 : : * while querying the stream. For the asynchronous (non-blocking) version
115 : : * of this function, see g_file_input_stream_query_info_async(). While the
116 : : * stream is blocked, the stream will set the pending flag internally, and
117 : : * any other operations on the stream will fail with %G_IO_ERROR_PENDING.
118 : : *
119 : : * Returns: (transfer full): a #GFileInfo, or %NULL on error.
120 : : **/
121 : : GFileInfo *
122 : 65 : g_file_input_stream_query_info (GFileInputStream *stream,
123 : : const char *attributes,
124 : : GCancellable *cancellable,
125 : : GError **error)
126 : : {
127 : : GFileInputStreamClass *class;
128 : : GInputStream *input_stream;
129 : : GFileInfo *info;
130 : :
131 : 65 : g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
132 : :
133 : 65 : input_stream = G_INPUT_STREAM (stream);
134 : :
135 : 65 : if (!g_input_stream_set_pending (input_stream, error))
136 : 0 : return NULL;
137 : :
138 : 65 : info = NULL;
139 : :
140 : 65 : if (cancellable)
141 : 0 : g_cancellable_push_current (cancellable);
142 : :
143 : 65 : class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
144 : 65 : if (class->query_info)
145 : 65 : info = class->query_info (stream, attributes, cancellable, error);
146 : : else
147 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
148 : : _("Stream doesn’t support query_info"));
149 : :
150 : 65 : if (cancellable)
151 : 0 : g_cancellable_pop_current (cancellable);
152 : :
153 : 65 : g_input_stream_clear_pending (input_stream);
154 : :
155 : 65 : return info;
156 : : }
157 : :
158 : : static void
159 : 3 : async_ready_callback_wrapper (GObject *source_object,
160 : : GAsyncResult *res,
161 : : gpointer user_data)
162 : : {
163 : 3 : GFileInputStream *stream = G_FILE_INPUT_STREAM (source_object);
164 : :
165 : 3 : g_input_stream_clear_pending (G_INPUT_STREAM (stream));
166 : 3 : if (stream->priv->outstanding_callback)
167 : 3 : (*stream->priv->outstanding_callback) (source_object, res, user_data);
168 : 3 : g_object_unref (stream);
169 : 3 : }
170 : :
171 : : /**
172 : : * g_file_input_stream_query_info_async:
173 : : * @stream: a #GFileInputStream.
174 : : * @attributes: a file attribute query string.
175 : : * @io_priority: the [I/O priority](iface.AsyncResult.html#io-priority) of the request
176 : : * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
177 : : * @callback: (scope async): a #GAsyncReadyCallback
178 : : * to call when the request is satisfied
179 : : * @user_data: the data to pass to callback function
180 : : *
181 : : * Queries the stream information asynchronously.
182 : : * When the operation is finished @callback will be called.
183 : : * You can then call g_file_input_stream_query_info_finish()
184 : : * to get the result of the operation.
185 : : *
186 : : * For the synchronous version of this function,
187 : : * see g_file_input_stream_query_info().
188 : : *
189 : : * If @cancellable is not %NULL, then the operation can be cancelled by
190 : : * triggering the cancellable object from another thread. If the operation
191 : : * was cancelled, the error %G_IO_ERROR_CANCELLED will be set
192 : : *
193 : : **/
194 : : void
195 : 3 : g_file_input_stream_query_info_async (GFileInputStream *stream,
196 : : const char *attributes,
197 : : int io_priority,
198 : : GCancellable *cancellable,
199 : : GAsyncReadyCallback callback,
200 : : gpointer user_data)
201 : : {
202 : : GFileInputStreamClass *klass;
203 : : GInputStream *input_stream;
204 : 3 : GError *error = NULL;
205 : :
206 : 3 : g_return_if_fail (G_IS_FILE_INPUT_STREAM (stream));
207 : :
208 : 3 : input_stream = G_INPUT_STREAM (stream);
209 : :
210 : 3 : if (!g_input_stream_set_pending (input_stream, &error))
211 : : {
212 : 0 : g_task_report_error (stream, callback, user_data,
213 : : g_file_input_stream_query_info_async,
214 : : error);
215 : 0 : return;
216 : : }
217 : :
218 : 3 : klass = G_FILE_INPUT_STREAM_GET_CLASS (stream);
219 : :
220 : 3 : stream->priv->outstanding_callback = callback;
221 : 3 : g_object_ref (stream);
222 : 3 : klass->query_info_async (stream, attributes, io_priority, cancellable,
223 : : async_ready_callback_wrapper, user_data);
224 : : }
225 : :
226 : : /**
227 : : * g_file_input_stream_query_info_finish:
228 : : * @stream: a #GFileInputStream.
229 : : * @result: a #GAsyncResult.
230 : : * @error: a #GError location to store the error occurring,
231 : : * or %NULL to ignore.
232 : : *
233 : : * Finishes an asynchronous info query operation.
234 : : *
235 : : * Returns: (transfer full): #GFileInfo.
236 : : **/
237 : : GFileInfo *
238 : 3 : g_file_input_stream_query_info_finish (GFileInputStream *stream,
239 : : GAsyncResult *result,
240 : : GError **error)
241 : : {
242 : : GFileInputStreamClass *class;
243 : :
244 : 3 : g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
245 : 3 : g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
246 : :
247 : 3 : if (g_async_result_legacy_propagate_error (result, error))
248 : 0 : return NULL;
249 : 3 : else if (g_async_result_is_tagged (result, g_file_input_stream_query_info_async))
250 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
251 : :
252 : 3 : class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
253 : 3 : return class->query_info_finish (stream, result, error);
254 : : }
255 : :
256 : : static goffset
257 : 50 : g_file_input_stream_tell (GFileInputStream *stream)
258 : : {
259 : : GFileInputStreamClass *class;
260 : : goffset offset;
261 : :
262 : 50 : g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), 0);
263 : :
264 : 50 : class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
265 : :
266 : 50 : offset = 0;
267 : 50 : if (class->tell)
268 : 50 : offset = class->tell (stream);
269 : :
270 : 50 : return offset;
271 : : }
272 : :
273 : : static goffset
274 : 50 : g_file_input_stream_seekable_tell (GSeekable *seekable)
275 : : {
276 : 50 : return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable));
277 : : }
278 : :
279 : : static gboolean
280 : 9 : g_file_input_stream_can_seek (GFileInputStream *stream)
281 : : {
282 : : GFileInputStreamClass *class;
283 : : gboolean can_seek;
284 : :
285 : 9 : g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
286 : :
287 : 9 : class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
288 : :
289 : 9 : can_seek = FALSE;
290 : 9 : if (class->seek)
291 : : {
292 : 9 : can_seek = TRUE;
293 : 9 : if (class->can_seek)
294 : 9 : can_seek = class->can_seek (stream);
295 : : }
296 : :
297 : 9 : return can_seek;
298 : : }
299 : :
300 : : static gboolean
301 : 9 : g_file_input_stream_seekable_can_seek (GSeekable *seekable)
302 : : {
303 : 9 : return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable));
304 : : }
305 : :
306 : : static gboolean
307 : 14 : g_file_input_stream_seek (GFileInputStream *stream,
308 : : goffset offset,
309 : : GSeekType type,
310 : : GCancellable *cancellable,
311 : : GError **error)
312 : : {
313 : : GFileInputStreamClass *class;
314 : : GInputStream *input_stream;
315 : : gboolean res;
316 : :
317 : 14 : g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
318 : :
319 : 14 : input_stream = G_INPUT_STREAM (stream);
320 : 14 : class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
321 : :
322 : 14 : if (!class->seek)
323 : : {
324 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
325 : : _("Seek not supported on stream"));
326 : 0 : return FALSE;
327 : : }
328 : :
329 : 14 : if (!g_input_stream_set_pending (input_stream, error))
330 : 0 : return FALSE;
331 : :
332 : 14 : if (cancellable)
333 : 0 : g_cancellable_push_current (cancellable);
334 : :
335 : 14 : res = class->seek (stream, offset, type, cancellable, error);
336 : :
337 : 14 : if (cancellable)
338 : 0 : g_cancellable_pop_current (cancellable);
339 : :
340 : 14 : g_input_stream_clear_pending (input_stream);
341 : :
342 : 14 : return res;
343 : : }
344 : :
345 : : static gboolean
346 : 14 : g_file_input_stream_seekable_seek (GSeekable *seekable,
347 : : goffset offset,
348 : : GSeekType type,
349 : : GCancellable *cancellable,
350 : : GError **error)
351 : : {
352 : 14 : return g_file_input_stream_seek (G_FILE_INPUT_STREAM (seekable),
353 : : offset, type, cancellable, error);
354 : : }
355 : :
356 : : static gboolean
357 : 0 : g_file_input_stream_seekable_can_truncate (GSeekable *seekable)
358 : : {
359 : 0 : return FALSE;
360 : : }
361 : :
362 : : static gboolean
363 : 0 : g_file_input_stream_seekable_truncate (GSeekable *seekable,
364 : : goffset offset,
365 : : GCancellable *cancellable,
366 : : GError **error)
367 : : {
368 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
369 : : _("Truncate not allowed on input stream"));
370 : 0 : return FALSE;
371 : : }
372 : :
373 : : /********************************************
374 : : * Default implementation of async ops *
375 : : ********************************************/
376 : :
377 : : static void
378 : 3 : query_info_async_thread (GTask *task,
379 : : gpointer source_object,
380 : : gpointer task_data,
381 : : GCancellable *cancellable)
382 : : {
383 : 3 : GFileInputStream *stream = source_object;
384 : 3 : const char *attributes = task_data;
385 : : GFileInputStreamClass *class;
386 : 3 : GError *error = NULL;
387 : 3 : GFileInfo *info = NULL;
388 : :
389 : 3 : class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
390 : 3 : if (class->query_info)
391 : 3 : info = class->query_info (stream, attributes, cancellable, &error);
392 : : else
393 : 0 : g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
394 : : _("Stream doesn’t support query_info"));
395 : :
396 : 3 : if (info == NULL)
397 : 0 : g_task_return_error (task, error);
398 : : else
399 : 3 : g_task_return_pointer (task, info, g_object_unref);
400 : 3 : }
401 : :
402 : : static void
403 : 3 : g_file_input_stream_real_query_info_async (GFileInputStream *stream,
404 : : const char *attributes,
405 : : int io_priority,
406 : : GCancellable *cancellable,
407 : : GAsyncReadyCallback callback,
408 : : gpointer user_data)
409 : : {
410 : : GTask *task;
411 : :
412 : 3 : task = g_task_new (stream, cancellable, callback, user_data);
413 : 3 : g_task_set_source_tag (task, g_file_input_stream_real_query_info_async);
414 : 3 : g_task_set_task_data (task, g_strdup (attributes), g_free);
415 : 3 : g_task_set_priority (task, io_priority);
416 : :
417 : 3 : g_task_run_in_thread (task, query_info_async_thread);
418 : 3 : g_object_unref (task);
419 : 3 : }
420 : :
421 : : static GFileInfo *
422 : 3 : g_file_input_stream_real_query_info_finish (GFileInputStream *stream,
423 : : GAsyncResult *res,
424 : : GError **error)
425 : : {
426 : 3 : g_return_val_if_fail (g_task_is_valid (res, stream), NULL);
427 : :
428 : 3 : return g_task_propagate_pointer (G_TASK (res), error);
429 : : }
|