1 /* GIO - GLib Input, IO 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 <gfileiostream.h>
27 #include <gseekable.h>
28 #include "gasyncresult.h"
29 #include "gtask.h"
30 #include "gcancellable.h"
31 #include "gioerror.h"
32 #include "gfileoutputstream.h"
33 #include "glibintl.h"
34
35
36 /**
37 * GFileIOStream:
38 *
39 * `GFileIOStream` provides I/O streams that both read and write to the same
40 * file handle.
41 *
42 * `GFileIOStream` implements [iface@Gio.Seekable], which allows the I/O
43 * stream to jump to arbitrary positions in the file and to truncate
44 * the file, provided the filesystem of the file supports these
45 * operations.
46 *
47 * To find the position of a file I/O stream, use [method@Gio.Seekable.tell].
48 *
49 * To find out if a file I/O stream supports seeking, use
50 * [method@Gio.Seekable.can_seek]. To position a file I/O stream, use
51 * [method@Gio.Seekable.seek]. To find out if a file I/O stream supports
52 * truncating, use [method@Gio.Seekable.can_truncate]. To truncate a file I/O
53 * stream, use [method@Gio.Seekable.truncate].
54 *
55 * The default implementation of all the `GFileIOStream` operations
56 * and the implementation of [iface@Gio.Seekable] just call into the same
57 * operations on the output stream.
58 *
59 * Since: 2.22
60 **/
61
62 static void g_file_io_stream_seekable_iface_init (GSeekableIface *iface);
63 static goffset g_file_io_stream_seekable_tell (GSeekable *seekable);
64 static gboolean g_file_io_stream_seekable_can_seek (GSeekable *seekable);
65 static gboolean g_file_io_stream_seekable_seek (GSeekable *seekable,
66 goffset offset,
67 GSeekType type,
68 GCancellable *cancellable,
69 GError **error);
70 static gboolean g_file_io_stream_seekable_can_truncate (GSeekable *seekable);
71 static gboolean g_file_io_stream_seekable_truncate (GSeekable *seekable,
72 goffset offset,
73 GCancellable *cancellable,
74 GError **error);
75 static void g_file_io_stream_real_query_info_async (GFileIOStream *stream,
76 const char *attributes,
77 int io_priority,
78 GCancellable *cancellable,
79 GAsyncReadyCallback callback,
80 gpointer user_data);
81 static GFileInfo *g_file_io_stream_real_query_info_finish (GFileIOStream *stream,
82 GAsyncResult *result,
83 GError **error);
84
85 struct _GFileIOStreamPrivate {
86 GAsyncReadyCallback outstanding_callback;
87 };
88
89 G_DEFINE_TYPE_WITH_CODE (GFileIOStream, g_file_io_stream, G_TYPE_IO_STREAM,
90 G_ADD_PRIVATE (GFileIOStream)
91 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
92 g_file_io_stream_seekable_iface_init))
93
94 static void
95 g_file_io_stream_seekable_iface_init (GSeekableIface *iface)
96 {
97 iface->tell = g_file_io_stream_seekable_tell;
98 iface->can_seek = g_file_io_stream_seekable_can_seek;
99 iface->seek = g_file_io_stream_seekable_seek;
100 iface->can_truncate = g_file_io_stream_seekable_can_truncate;
101 iface->truncate_fn = g_file_io_stream_seekable_truncate;
102 }
103
104 static void
105 g_file_io_stream_init (GFileIOStream *stream)
106 {
107 stream->priv = g_file_io_stream_get_instance_private (stream);
108 }
109
110 /**
111 * g_file_io_stream_query_info:
112 * @stream: a #GFileIOStream.
113 * @attributes: a file attribute query string.
114 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
115 * @error: a #GError, %NULL to ignore.
116 *
117 * Queries a file io stream for the given @attributes.
118 * This function blocks while querying the stream. For the asynchronous
119 * version of this function, see g_file_io_stream_query_info_async().
120 * While the stream is blocked, the stream will set the pending flag
121 * internally, and any other operations on the stream will fail with
122 * %G_IO_ERROR_PENDING.
123 *
124 * Can fail if the stream was already closed (with @error being set to
125 * %G_IO_ERROR_CLOSED), the stream has pending operations (with @error being
126 * set to %G_IO_ERROR_PENDING), or if querying info is not supported for
127 * the stream's interface (with @error being set to %G_IO_ERROR_NOT_SUPPORTED). I
128 * all cases of failure, %NULL will be returned.
129 *
130 * If @cancellable is not %NULL, then the operation can be cancelled by
131 * triggering the cancellable object from another thread. If the operation
132 * was cancelled, the error %G_IO_ERROR_CANCELLED will be set, and %NULL will
133 * be returned.
134 *
135 * Returns: (transfer full): a #GFileInfo for the @stream, or %NULL on error.
136 *
137 * Since: 2.22
138 **/
139 GFileInfo *
140 g_file_io_stream_query_info (GFileIOStream *stream,
141 const char *attributes,
142 GCancellable *cancellable,
143 GError **error)
144 {
145 GFileIOStreamClass *class;
146 GIOStream *io_stream;
147 GFileInfo *info;
148
149 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), NULL);
150
151 io_stream = G_IO_STREAM (stream);
152
153 if (!g_io_stream_set_pending (io_stream, error))
154 return NULL;
155
156 info = NULL;
157
158 if (cancellable)
159 g_cancellable_push_current (cancellable);
160
161 class = G_FILE_IO_STREAM_GET_CLASS (stream);
162 if (class->query_info)
163 info = class->query_info (stream, attributes, cancellable, error);
164 else
165 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
166 _("Stream doesn’t support query_info"));
167
168 if (cancellable)
169 g_cancellable_pop_current (cancellable);
170
171 g_io_stream_clear_pending (io_stream);
172
173 return info;
174 }
175
176 static void
177 async_ready_callback_wrapper (GObject *source_object,
178 GAsyncResult *res,
179 gpointer user_data)
180 {
181 GFileIOStream *stream = G_FILE_IO_STREAM (source_object);
182
183 g_io_stream_clear_pending (G_IO_STREAM (stream));
184 if (stream->priv->outstanding_callback)
185 (*stream->priv->outstanding_callback) (source_object, res, user_data);
186 g_object_unref (stream);
187 }
188
189 /**
190 * g_file_io_stream_query_info_async:
191 * @stream: a #GFileIOStream.
192 * @attributes: a file attribute query string.
193 * @io_priority: the [I/O priority](iface.AsyncResult.html#io-priority) of the
194 * request
195 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
196 * @callback: (scope async): a #GAsyncReadyCallback
197 * to call when the request is satisfied
198 * @user_data: the data to pass to callback function
199 *
200 * Asynchronously queries the @stream for a #GFileInfo. When completed,
201 * @callback will be called with a #GAsyncResult which can be used to
202 * finish the operation with g_file_io_stream_query_info_finish().
203 *
204 * For the synchronous version of this function, see
205 * g_file_io_stream_query_info().
206 *
207 * Since: 2.22
208 **/
209 void
210 g_file_io_stream_query_info_async (GFileIOStream *stream,
211 const char *attributes,
212 int io_priority,
213 GCancellable *cancellable,
214 GAsyncReadyCallback callback,
215 gpointer user_data)
216 {
217 GFileIOStreamClass *klass;
218 GIOStream *io_stream;
219 GError *error = NULL;
220
221 g_return_if_fail (G_IS_FILE_IO_STREAM (stream));
222
223 io_stream = G_IO_STREAM (stream);
224
225 if (!g_io_stream_set_pending (io_stream, &error))
226 {
227 g_task_report_error (stream, callback, user_data,
228 g_file_io_stream_query_info_async,
229 error);
230 return;
231 }
232
233 klass = G_FILE_IO_STREAM_GET_CLASS (stream);
234
235 stream->priv->outstanding_callback = callback;
236 g_object_ref (stream);
237 klass->query_info_async (stream, attributes, io_priority, cancellable,
238 async_ready_callback_wrapper, user_data);
239 }
240
241 /**
242 * g_file_io_stream_query_info_finish:
243 * @stream: a #GFileIOStream.
244 * @result: a #GAsyncResult.
245 * @error: a #GError, %NULL to ignore.
246 *
247 * Finalizes the asynchronous query started
248 * by g_file_io_stream_query_info_async().
249 *
250 * Returns: (transfer full): A #GFileInfo for the finished query.
251 *
252 * Since: 2.22
253 **/
254 GFileInfo *
255 g_file_io_stream_query_info_finish (GFileIOStream *stream,
256 GAsyncResult *result,
257 GError **error)
258 {
259 GFileIOStreamClass *class;
260
261 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), NULL);
262 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
263
264 if (g_async_result_legacy_propagate_error (result, error))
265 return NULL;
266 else if (g_async_result_is_tagged (result, g_file_io_stream_query_info_async))
267 return g_task_propagate_pointer (G_TASK (result), error);
268
269 class = G_FILE_IO_STREAM_GET_CLASS (stream);
270 return class->query_info_finish (stream, result, error);
271 }
272
273 /**
274 * g_file_io_stream_get_etag:
275 * @stream: a #GFileIOStream.
276 *
277 * Gets the entity tag for the file when it has been written.
278 * This must be called after the stream has been written
279 * and closed, as the etag can change while writing.
280 *
281 * Returns: (nullable) (transfer full): the entity tag for the stream.
282 *
283 * Since: 2.22
284 **/
285 char *
286 g_file_io_stream_get_etag (GFileIOStream *stream)
287 {
288 GFileIOStreamClass *class;
289 GIOStream *io_stream;
290 char *etag;
291
292 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), NULL);
293
294 io_stream = G_IO_STREAM (stream);
295
296 if (!g_io_stream_is_closed (io_stream))
297 {
298 g_warning ("stream is not closed yet, can't get etag");
299 return NULL;
300 }
301
302 etag = NULL;
303
304 class = G_FILE_IO_STREAM_GET_CLASS (stream);
305 if (class->get_etag)
306 etag = class->get_etag (stream);
307
308 return etag;
309 }
310
311 static goffset
312 g_file_io_stream_tell (GFileIOStream *stream)
313 {
314 GFileIOStreamClass *class;
315 goffset offset;
316
317 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), 0);
318
319 class = G_FILE_IO_STREAM_GET_CLASS (stream);
320
321 offset = 0;
322 if (class->tell)
323 offset = class->tell (stream);
324
325 return offset;
326 }
327
328 static goffset
329 g_file_io_stream_seekable_tell (GSeekable *seekable)
330 {
331 return g_file_io_stream_tell (G_FILE_IO_STREAM (seekable));
332 }
333
334 static gboolean
335 g_file_io_stream_can_seek (GFileIOStream *stream)
336 {
337 GFileIOStreamClass *class;
338 gboolean can_seek;
339
340 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), FALSE);
341
342 class = G_FILE_IO_STREAM_GET_CLASS (stream);
343
344 can_seek = FALSE;
345 if (class->seek)
346 {
347 can_seek = TRUE;
348 if (class->can_seek)
349 can_seek = class->can_seek (stream);
350 }
351
352 return can_seek;
353 }
354
355 static gboolean
356 g_file_io_stream_seekable_can_seek (GSeekable *seekable)
357 {
358 return g_file_io_stream_can_seek (G_FILE_IO_STREAM (seekable));
359 }
360
361 static gboolean
362 g_file_io_stream_seek (GFileIOStream *stream,
363 goffset offset,
364 GSeekType type,
365 GCancellable *cancellable,
366 GError **error)
367 {
368 GFileIOStreamClass *class;
369 GIOStream *io_stream;
370 gboolean res;
371
372 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), FALSE);
373
374 io_stream = G_IO_STREAM (stream);
375 class = G_FILE_IO_STREAM_GET_CLASS (stream);
376
377 if (!class->seek)
378 {
379 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
380 _("Seek not supported on stream"));
381 return FALSE;
382 }
383
384 if (!g_io_stream_set_pending (io_stream, error))
385 return FALSE;
386
387 if (cancellable)
388 g_cancellable_push_current (cancellable);
389
390 res = class->seek (stream, offset, type, cancellable, error);
391
392 if (cancellable)
393 g_cancellable_pop_current (cancellable);
394
395 g_io_stream_clear_pending (io_stream);
396
397 return res;
398 }
399
400 static gboolean
401 g_file_io_stream_seekable_seek (GSeekable *seekable,
402 goffset offset,
403 GSeekType type,
404 GCancellable *cancellable,
405 GError **error)
406 {
407 return g_file_io_stream_seek (G_FILE_IO_STREAM (seekable),
408 offset, type, cancellable, error);
409 }
410
411 static gboolean
412 g_file_io_stream_can_truncate (GFileIOStream *stream)
413 {
414 GFileIOStreamClass *class;
415 gboolean can_truncate;
416
417 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), FALSE);
418
419 class = G_FILE_IO_STREAM_GET_CLASS (stream);
420
421 can_truncate = FALSE;
422 if (class->truncate_fn)
423 {
424 can_truncate = TRUE;
425 if (class->can_truncate)
426 can_truncate = class->can_truncate (stream);
427 }
428
429 return can_truncate;
430 }
431
432 static gboolean
433 g_file_io_stream_seekable_can_truncate (GSeekable *seekable)
434 {
435 return g_file_io_stream_can_truncate (G_FILE_IO_STREAM (seekable));
436 }
437
438 static gboolean
439 g_file_io_stream_truncate (GFileIOStream *stream,
440 goffset size,
441 GCancellable *cancellable,
442 GError **error)
443 {
444 GFileIOStreamClass *class;
445 GIOStream *io_stream;
446 gboolean res;
447
448 g_return_val_if_fail (G_IS_FILE_IO_STREAM (stream), FALSE);
449
450 io_stream = G_IO_STREAM (stream);
451 class = G_FILE_IO_STREAM_GET_CLASS (stream);
452
453 if (!class->truncate_fn)
454 {
455 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
456 _("Truncate not supported on stream"));
457 return FALSE;
458 }
459
460 if (!g_io_stream_set_pending (io_stream, error))
461 return FALSE;
462
463 if (cancellable)
464 g_cancellable_push_current (cancellable);
465
466 res = class->truncate_fn (stream, size, cancellable, error);
467
468 if (cancellable)
469 g_cancellable_pop_current (cancellable);
470
471 g_io_stream_clear_pending (io_stream);
472
473 return res;
474 }
475
476 static gboolean
477 g_file_io_stream_seekable_truncate (GSeekable *seekable,
478 goffset size,
479 GCancellable *cancellable,
480 GError **error)
481 {
482 return g_file_io_stream_truncate (G_FILE_IO_STREAM (seekable),
483 size, cancellable, error);
484 }
485 /*****************************************************
486 * Default implementations based on output stream *
487 *****************************************************/
488
489 static goffset
490 g_file_io_stream_real_tell (GFileIOStream *stream)
491 {
492 GOutputStream *out;
493 GSeekable *seekable;
494
495 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
496 seekable = G_SEEKABLE (out);
497
498 return g_seekable_tell (seekable);
499 }
500
501 static gboolean
502 g_file_io_stream_real_can_seek (GFileIOStream *stream)
503 {
504 GOutputStream *out;
505 GSeekable *seekable;
506
507 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
508 seekable = G_SEEKABLE (out);
509
510 return g_seekable_can_seek (seekable);
511 }
512
513 static gboolean
514 g_file_io_stream_real_seek (GFileIOStream *stream,
515 goffset offset,
516 GSeekType type,
517 GCancellable *cancellable,
518 GError **error)
519 {
520 GOutputStream *out;
521 GSeekable *seekable;
522
523 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
524 seekable = G_SEEKABLE (out);
525
526 return g_seekable_seek (seekable, offset, type, cancellable, error);
527 }
528
529 static gboolean
530 g_file_io_stream_real_can_truncate (GFileIOStream *stream)
531 {
532 GOutputStream *out;
533 GSeekable *seekable;
534
535 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
536 seekable = G_SEEKABLE (out);
537
538 return g_seekable_can_truncate (seekable);
539 }
540
541 static gboolean
542 g_file_io_stream_real_truncate_fn (GFileIOStream *stream,
543 goffset size,
544 GCancellable *cancellable,
545 GError **error)
546 {
547 GOutputStream *out;
548 GSeekable *seekable;
549
550 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
551 seekable = G_SEEKABLE (out);
552
553 return g_seekable_truncate (seekable, size, cancellable, error);
554 }
555
556 static char *
557 g_file_io_stream_real_get_etag (GFileIOStream *stream)
558 {
559 GOutputStream *out;
560 GFileOutputStream *file_out;
561
562 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
563 file_out = G_FILE_OUTPUT_STREAM (out);
564
565 return g_file_output_stream_get_etag (file_out);
566 }
567
568 static GFileInfo *
569 g_file_io_stream_real_query_info (GFileIOStream *stream,
570 const char *attributes,
571 GCancellable *cancellable,
572 GError **error)
573 {
574 GOutputStream *out;
575 GFileOutputStream *file_out;
576
577 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
578 file_out = G_FILE_OUTPUT_STREAM (out);
579
580 return g_file_output_stream_query_info (file_out,
581 attributes, cancellable, error);
582 }
583
584 typedef struct {
585 GObject *object;
586 GAsyncReadyCallback callback;
587 gpointer user_data;
588 } AsyncOpWrapper;
589
590 static AsyncOpWrapper *
591 async_op_wrapper_new (gpointer object,
592 GAsyncReadyCallback callback,
593 gpointer user_data)
594 {
595 AsyncOpWrapper *data;
596
597 data = g_new0 (AsyncOpWrapper, 1);
598 data->object = g_object_ref (object);
599 data->callback = callback;
600 data->user_data = user_data;
601
602 return data;
603 }
604
605 static void
606 async_op_wrapper_callback (GObject *source_object,
607 GAsyncResult *res,
608 gpointer user_data)
609 {
610 AsyncOpWrapper *data = user_data;
611 data->callback (data->object, res, data->user_data);
612 g_object_unref (data->object);
613 g_free (data);
614 }
615
616 static void
617 g_file_io_stream_real_query_info_async (GFileIOStream *stream,
618 const char *attributes,
619 int io_priority,
620 GCancellable *cancellable,
621 GAsyncReadyCallback callback,
622 gpointer user_data)
623 {
624 GOutputStream *out;
625 GFileOutputStream *file_out;
626 AsyncOpWrapper *data;
627
628 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
629 file_out = G_FILE_OUTPUT_STREAM (out);
630
631 data = async_op_wrapper_new (stream, callback, user_data);
632 g_file_output_stream_query_info_async (file_out,
633 attributes, io_priority,
634 cancellable, async_op_wrapper_callback, data);
635 }
636
637 static GFileInfo *
638 g_file_io_stream_real_query_info_finish (GFileIOStream *stream,
639 GAsyncResult *res,
640 GError **error)
641 {
642 GOutputStream *out;
643 GFileOutputStream *file_out;
644
645 out = g_io_stream_get_output_stream (G_IO_STREAM (stream));
646 file_out = G_FILE_OUTPUT_STREAM (out);
647
648 return g_file_output_stream_query_info_finish (file_out, res, error);
649 }
650
651 static void
652 g_file_io_stream_class_init (GFileIOStreamClass *klass)
653 {
654 klass->tell = g_file_io_stream_real_tell;
655 klass->can_seek = g_file_io_stream_real_can_seek;
656 klass->seek = g_file_io_stream_real_seek;
657 klass->can_truncate = g_file_io_stream_real_can_truncate;
658 klass->truncate_fn = g_file_io_stream_real_truncate_fn;
659 klass->query_info = g_file_io_stream_real_query_info;
660 klass->query_info_async = g_file_io_stream_real_query_info_async;
661 klass->query_info_finish = g_file_io_stream_real_query_info_finish;
662 klass->get_etag = g_file_io_stream_real_get_etag;
663 }