Coverage Report

Created: 2025-06-13 06:20

/src/glib/gio/gfileinputstream.c
Line
Count
Source (jump to first uncovered line)
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
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
g_file_input_stream_class_init (GFileInputStreamClass *klass)
84
0
{
85
0
  klass->query_info_async = g_file_input_stream_real_query_info_async;
86
0
  klass->query_info_finish = g_file_input_stream_real_query_info_finish;
87
0
}
88
89
static void
90
g_file_input_stream_seekable_iface_init (GSeekableIface *iface)
91
0
{
92
0
  iface->tell = g_file_input_stream_seekable_tell;
93
0
  iface->can_seek = g_file_input_stream_seekable_can_seek;
94
0
  iface->seek = g_file_input_stream_seekable_seek;
95
0
  iface->can_truncate = g_file_input_stream_seekable_can_truncate;
96
0
  iface->truncate_fn = g_file_input_stream_seekable_truncate;
97
0
}
98
99
static void
100
g_file_input_stream_init (GFileInputStream *stream)
101
0
{
102
0
  stream->priv = g_file_input_stream_get_instance_private (stream);
103
0
}
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
g_file_input_stream_query_info (GFileInputStream  *stream,
123
                                const char        *attributes,
124
                                GCancellable      *cancellable,
125
                                GError           **error)
126
0
{
127
0
  GFileInputStreamClass *class;
128
0
  GInputStream *input_stream;
129
0
  GFileInfo *info;
130
  
131
0
  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
132
  
133
0
  input_stream = G_INPUT_STREAM (stream);
134
  
135
0
  if (!g_input_stream_set_pending (input_stream, error))
136
0
    return NULL;
137
      
138
0
  info = NULL;
139
  
140
0
  if (cancellable)
141
0
    g_cancellable_push_current (cancellable);
142
  
143
0
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
144
0
  if (class->query_info)
145
0
    info = class->query_info (stream, attributes, cancellable, error);
146
0
  else
147
0
    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
148
0
                         _("Stream doesn’t support query_info"));
149
150
0
  if (cancellable)
151
0
    g_cancellable_pop_current (cancellable);
152
  
153
0
  g_input_stream_clear_pending (input_stream);
154
  
155
0
  return info;
156
0
}
157
158
static void
159
async_ready_callback_wrapper (GObject      *source_object,
160
                              GAsyncResult *res,
161
                              gpointer      user_data)
162
0
{
163
0
  GFileInputStream *stream = G_FILE_INPUT_STREAM (source_object);
164
165
0
  g_input_stream_clear_pending (G_INPUT_STREAM (stream));
166
0
  if (stream->priv->outstanding_callback)
167
0
    (*stream->priv->outstanding_callback) (source_object, res, user_data);
168
0
  g_object_unref (stream);
169
0
}
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
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
0
{
202
0
  GFileInputStreamClass *klass;
203
0
  GInputStream *input_stream;
204
0
  GError *error = NULL;
205
206
0
  g_return_if_fail (G_IS_FILE_INPUT_STREAM (stream));
207
208
0
  input_stream = G_INPUT_STREAM (stream);
209
  
210
0
  if (!g_input_stream_set_pending (input_stream, &error))
211
0
    {
212
0
      g_task_report_error (stream, callback, user_data,
213
0
                           g_file_input_stream_query_info_async,
214
0
                           error);
215
0
      return;
216
0
    }
217
218
0
  klass = G_FILE_INPUT_STREAM_GET_CLASS (stream);
219
220
0
  stream->priv->outstanding_callback = callback;
221
0
  g_object_ref (stream);
222
0
  klass->query_info_async (stream, attributes, io_priority, cancellable,
223
0
                           async_ready_callback_wrapper, user_data);
224
0
}
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
g_file_input_stream_query_info_finish (GFileInputStream  *stream,
239
                                       GAsyncResult      *result,
240
                                       GError           **error)
241
0
{
242
0
  GFileInputStreamClass *class;
243
244
0
  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
245
0
  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
246
247
0
  if (g_async_result_legacy_propagate_error (result, error))
248
0
    return NULL;
249
0
  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
0
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
253
0
  return class->query_info_finish (stream, result, error);
254
0
}
255
256
static goffset
257
g_file_input_stream_tell (GFileInputStream *stream)
258
0
{
259
0
  GFileInputStreamClass *class;
260
0
  goffset offset;
261
262
0
  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), 0);
263
264
0
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
265
266
0
  offset = 0;
267
0
  if (class->tell)
268
0
    offset = class->tell (stream);
269
270
0
  return offset;
271
0
}
272
273
static goffset
274
g_file_input_stream_seekable_tell (GSeekable *seekable)
275
0
{
276
0
  return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable));
277
0
}
278
279
static gboolean
280
g_file_input_stream_can_seek (GFileInputStream *stream)
281
0
{
282
0
  GFileInputStreamClass *class;
283
0
  gboolean can_seek;
284
285
0
  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
286
287
0
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
288
289
0
  can_seek = FALSE;
290
0
  if (class->seek)
291
0
    {
292
0
      can_seek = TRUE;
293
0
      if (class->can_seek)
294
0
  can_seek = class->can_seek (stream);
295
0
    }
296
  
297
0
  return can_seek;
298
0
}
299
300
static gboolean
301
g_file_input_stream_seekable_can_seek (GSeekable *seekable)
302
0
{
303
0
  return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable));
304
0
}
305
306
static gboolean
307
g_file_input_stream_seek (GFileInputStream  *stream,
308
        goffset            offset,
309
        GSeekType          type,
310
        GCancellable      *cancellable,
311
        GError           **error)
312
0
{
313
0
  GFileInputStreamClass *class;
314
0
  GInputStream *input_stream;
315
0
  gboolean res;
316
317
0
  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
318
319
0
  input_stream = G_INPUT_STREAM (stream);
320
0
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
321
322
0
  if (!class->seek)
323
0
    {
324
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
325
0
                           _("Seek not supported on stream"));
326
0
      return FALSE;
327
0
    }
328
329
0
  if (!g_input_stream_set_pending (input_stream, error))
330
0
    return FALSE;
331
  
332
0
  if (cancellable)
333
0
    g_cancellable_push_current (cancellable);
334
  
335
0
  res = class->seek (stream, offset, type, cancellable, error);
336
  
337
0
  if (cancellable)
338
0
    g_cancellable_pop_current (cancellable);
339
340
0
  g_input_stream_clear_pending (input_stream);
341
  
342
0
  return res;
343
0
}
344
345
static gboolean
346
g_file_input_stream_seekable_seek (GSeekable     *seekable,
347
           goffset        offset,
348
           GSeekType      type,
349
           GCancellable  *cancellable,
350
           GError       **error)
351
0
{
352
0
  return g_file_input_stream_seek (G_FILE_INPUT_STREAM (seekable),
353
0
           offset, type, cancellable, error);
354
0
}
355
356
static gboolean
357
g_file_input_stream_seekable_can_truncate (GSeekable *seekable)
358
0
{
359
0
  return FALSE;
360
0
}
361
362
static gboolean
363
g_file_input_stream_seekable_truncate (GSeekable     *seekable,
364
               goffset        offset,
365
               GCancellable  *cancellable,
366
               GError       **error)
367
0
{
368
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
369
0
                       _("Truncate not allowed on input stream"));
370
0
  return FALSE;
371
0
}
372
373
/********************************************
374
 *   Default implementation of async ops    *
375
 ********************************************/
376
377
static void
378
query_info_async_thread (GTask        *task,
379
                         gpointer      source_object,
380
                         gpointer      task_data,
381
                         GCancellable *cancellable)
382
0
{
383
0
  GFileInputStream *stream = source_object;
384
0
  const char *attributes = task_data;
385
0
  GFileInputStreamClass *class;
386
0
  GError *error = NULL;
387
0
  GFileInfo *info = NULL;
388
389
0
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
390
0
  if (class->query_info)
391
0
    info = class->query_info (stream, attributes, cancellable, &error);
392
0
  else
393
0
    g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
394
0
                         _("Stream doesn’t support query_info"));
395
396
0
  if (info == NULL)
397
0
    g_task_return_error (task, error);
398
0
  else
399
0
    g_task_return_pointer (task, info, g_object_unref);
400
0
}
401
402
static void
403
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
0
{
410
0
  GTask *task;
411
412
0
  task = g_task_new (stream, cancellable, callback, user_data);
413
0
  g_task_set_source_tag (task, g_file_input_stream_real_query_info_async);
414
0
  g_task_set_task_data (task, g_strdup (attributes), g_free);
415
0
  g_task_set_priority (task, io_priority);
416
  
417
0
  g_task_run_in_thread (task, query_info_async_thread);
418
0
  g_object_unref (task);
419
0
}
420
421
static GFileInfo *
422
g_file_input_stream_real_query_info_finish (GFileInputStream  *stream,
423
                                            GAsyncResult      *res,
424
                                            GError           **error)
425
0
{
426
0
  g_return_val_if_fail (g_task_is_valid (res, stream), NULL);
427
428
0
  return g_task_propagate_pointer (G_TASK (res), error);
429
0
}