Coverage Report

Created: 2025-06-13 06:55

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