Coverage Report

Created: 2025-07-01 07:09

/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
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * Author: Alexander Larsson <alexl@redhat.com>
19
 */
20
21
#include "config.h"
22
23
#include <glib.h>
24
#include <gfileinputstream.h>
25
#include <gseekable.h>
26
#include "gcancellable.h"
27
#include "gasyncresult.h"
28
#include "gtask.h"
29
#include "gioerror.h"
30
#include "glibintl.h"
31
32
33
/**
34
 * SECTION:gfileinputstream
35
 * @short_description: File input streaming operations
36
 * @include: gio/gio.h
37
 * @see_also: #GInputStream, #GDataInputStream, #GSeekable
38
 *
39
 * GFileInputStream provides input streams that take their
40
 * content from a file.
41
 *
42
 * GFileInputStream implements #GSeekable, which allows the input 
43
 * stream to jump to arbitrary positions in the file, provided the 
44
 * filesystem of the file allows it. To find the position of a file
45
 * input stream, use g_seekable_tell(). To find out if a file input
46
 * stream supports seeking, use g_seekable_can_seek().
47
 * To position a file input stream, use g_seekable_seek().
48
 **/
49
50
static void       g_file_input_stream_seekable_iface_init    (GSeekableIface       *iface);
51
static goffset    g_file_input_stream_seekable_tell          (GSeekable            *seekable);
52
static gboolean   g_file_input_stream_seekable_can_seek      (GSeekable            *seekable);
53
static gboolean   g_file_input_stream_seekable_seek          (GSeekable            *seekable,
54
                    goffset               offset,
55
                    GSeekType             type,
56
                    GCancellable         *cancellable,
57
                    GError              **error);
58
static gboolean   g_file_input_stream_seekable_can_truncate  (GSeekable            *seekable);
59
static gboolean   g_file_input_stream_seekable_truncate      (GSeekable            *seekable,
60
                    goffset               offset,
61
                    GCancellable         *cancellable,
62
                    GError              **error);
63
static void       g_file_input_stream_real_query_info_async  (GFileInputStream     *stream,
64
                    const char           *attributes,
65
                    int                   io_priority,
66
                    GCancellable         *cancellable,
67
                    GAsyncReadyCallback   callback,
68
                    gpointer              user_data);
69
static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream     *stream,
70
                    GAsyncResult         *result,
71
                    GError              **error);
72
73
74
struct _GFileInputStreamPrivate {
75
  GAsyncReadyCallback outstanding_callback;
76
};
77
78
G_DEFINE_TYPE_WITH_CODE (GFileInputStream, g_file_input_stream, G_TYPE_INPUT_STREAM,
79
                         G_ADD_PRIVATE (GFileInputStream)
80
       G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
81
            g_file_input_stream_seekable_iface_init))
82
83
static void
84
g_file_input_stream_class_init (GFileInputStreamClass *klass)
85
0
{
86
0
  klass->query_info_async = g_file_input_stream_real_query_info_async;
87
0
  klass->query_info_finish = g_file_input_stream_real_query_info_finish;
88
0
}
89
90
static void
91
g_file_input_stream_seekable_iface_init (GSeekableIface *iface)
92
0
{
93
0
  iface->tell = g_file_input_stream_seekable_tell;
94
0
  iface->can_seek = g_file_input_stream_seekable_can_seek;
95
0
  iface->seek = g_file_input_stream_seekable_seek;
96
0
  iface->can_truncate = g_file_input_stream_seekable_can_truncate;
97
0
  iface->truncate_fn = g_file_input_stream_seekable_truncate;
98
0
}
99
100
static void
101
g_file_input_stream_init (GFileInputStream *stream)
102
0
{
103
0
  stream->priv = g_file_input_stream_get_instance_private (stream);
104
0
}
105
106
/**
107
 * g_file_input_stream_query_info:
108
 * @stream: a #GFileInputStream.
109
 * @attributes: a file attribute query string.
110
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. 
111
 * @error: a #GError location to store the error occurring, or %NULL to 
112
 * ignore.
113
 *
114
 * Queries a file input stream the given @attributes. This function blocks 
115
 * while querying the stream. For the asynchronous (non-blocking) version 
116
 * of this function, see g_file_input_stream_query_info_async(). While the 
117
 * stream is blocked, the stream will set the pending flag internally, and 
118
 * any other operations on the stream will fail with %G_IO_ERROR_PENDING.
119
 *
120
 * Returns: (transfer full): a #GFileInfo, or %NULL on error.
121
 **/
122
GFileInfo *
123
g_file_input_stream_query_info (GFileInputStream  *stream,
124
                                const char        *attributes,
125
                                GCancellable      *cancellable,
126
                                GError           **error)
127
0
{
128
0
  GFileInputStreamClass *class;
129
0
  GInputStream *input_stream;
130
0
  GFileInfo *info;
131
  
132
0
  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
133
  
134
0
  input_stream = G_INPUT_STREAM (stream);
135
  
136
0
  if (!g_input_stream_set_pending (input_stream, error))
137
0
    return NULL;
138
      
139
0
  info = NULL;
140
  
141
0
  if (cancellable)
142
0
    g_cancellable_push_current (cancellable);
143
  
144
0
  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
145
0
  if (class->query_info)
146
0
    info = class->query_info (stream, attributes, cancellable, error);
147
0
  else
148
0
    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
149
0
                         _("Stream doesn’t support query_info"));
150
151
0
  if (cancellable)
152
0
    g_cancellable_pop_current (cancellable);
153
  
154
0
  g_input_stream_clear_pending (input_stream);
155
  
156
0
  return info;
157
0
}
158
159
static void
160
async_ready_callback_wrapper (GObject      *source_object,
161
                              GAsyncResult *res,
162
                              gpointer      user_data)
163
0
{
164
0
  GFileInputStream *stream = G_FILE_INPUT_STREAM (source_object);
165
166
0
  g_input_stream_clear_pending (G_INPUT_STREAM (stream));
167
0
  if (stream->priv->outstanding_callback)
168
0
    (*stream->priv->outstanding_callback) (source_object, res, user_data);
169
0
  g_object_unref (stream);
170
0
}
171
172
/**
173
 * g_file_input_stream_query_info_async:
174
 * @stream: a #GFileInputStream.
175
 * @attributes: a file attribute query string.
176
 * @io_priority: the [I/O priority][io-priority] of the request
177
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. 
178
 * @callback: (scope async): callback to call when the request is satisfied
179
 * @user_data: (closure): 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
}