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 <gfileoutputstream.h>
27 : : #include <gseekable.h>
28 : : #include "gasyncresult.h"
29 : : #include "gtask.h"
30 : : #include "gcancellable.h"
31 : : #include "gioerror.h"
32 : : #include "glibintl.h"
33 : :
34 : :
35 : : /**
36 : : * GFileOutputStream:
37 : : *
38 : : * `GFileOutputStream` provides output streams that write their
39 : : * content to a file.
40 : : *
41 : : * `GFileOutputStream` implements [iface@Gio.Seekable], which allows the output
42 : : * stream to jump to arbitrary positions in the file and to truncate
43 : : * the file, provided the filesystem of the file supports these
44 : : * operations.
45 : : *
46 : : * To find the position of a file output stream, use [method@Gio.Seekable.tell].
47 : : * To find out if a file output stream supports seeking, use
48 : : * [method@Gio.Seekable.can_seek].To position a file output stream, use
49 : : * [method@Gio.Seekable.seek]. To find out if a file output stream supports
50 : : * truncating, use [method@Gio.Seekable.can_truncate]. To truncate a file output
51 : : * stream, use [method@Gio.Seekable.truncate].
52 : : **/
53 : :
54 : : static void g_file_output_stream_seekable_iface_init (GSeekableIface *iface);
55 : : static goffset g_file_output_stream_seekable_tell (GSeekable *seekable);
56 : : static gboolean g_file_output_stream_seekable_can_seek (GSeekable *seekable);
57 : : static gboolean g_file_output_stream_seekable_seek (GSeekable *seekable,
58 : : goffset offset,
59 : : GSeekType type,
60 : : GCancellable *cancellable,
61 : : GError **error);
62 : : static gboolean g_file_output_stream_seekable_can_truncate (GSeekable *seekable);
63 : : static gboolean g_file_output_stream_seekable_truncate (GSeekable *seekable,
64 : : goffset offset,
65 : : GCancellable *cancellable,
66 : : GError **error);
67 : : static void g_file_output_stream_real_query_info_async (GFileOutputStream *stream,
68 : : const char *attributes,
69 : : int io_priority,
70 : : GCancellable *cancellable,
71 : : GAsyncReadyCallback callback,
72 : : gpointer user_data);
73 : : static GFileInfo *g_file_output_stream_real_query_info_finish (GFileOutputStream *stream,
74 : : GAsyncResult *result,
75 : : GError **error);
76 : :
77 : : struct _GFileOutputStreamPrivate {
78 : : GAsyncReadyCallback outstanding_callback;
79 : : };
80 : :
81 : 806 : G_DEFINE_TYPE_WITH_CODE (GFileOutputStream, g_file_output_stream, G_TYPE_OUTPUT_STREAM,
82 : : G_ADD_PRIVATE (GFileOutputStream)
83 : : G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
84 : : g_file_output_stream_seekable_iface_init));
85 : :
86 : : static void
87 : 14 : g_file_output_stream_class_init (GFileOutputStreamClass *klass)
88 : : {
89 : 14 : klass->query_info_async = g_file_output_stream_real_query_info_async;
90 : 14 : klass->query_info_finish = g_file_output_stream_real_query_info_finish;
91 : 14 : }
92 : :
93 : : static void
94 : 14 : g_file_output_stream_seekable_iface_init (GSeekableIface *iface)
95 : : {
96 : 14 : iface->tell = g_file_output_stream_seekable_tell;
97 : 14 : iface->can_seek = g_file_output_stream_seekable_can_seek;
98 : 14 : iface->seek = g_file_output_stream_seekable_seek;
99 : 14 : iface->can_truncate = g_file_output_stream_seekable_can_truncate;
100 : 14 : iface->truncate_fn = g_file_output_stream_seekable_truncate;
101 : 14 : }
102 : :
103 : : static void
104 : 290 : g_file_output_stream_init (GFileOutputStream *stream)
105 : : {
106 : 290 : stream->priv = g_file_output_stream_get_instance_private (stream);
107 : 290 : }
108 : :
109 : : /**
110 : : * g_file_output_stream_query_info:
111 : : * @stream: a #GFileOutputStream.
112 : : * @attributes: a file attribute query string.
113 : : * @cancellable: optional #GCancellable object, %NULL to ignore.
114 : : * @error: a #GError, %NULL to ignore.
115 : : *
116 : : * Queries a file output stream for the given @attributes.
117 : : * This function blocks while querying the stream. For the asynchronous
118 : : * version of this function, see g_file_output_stream_query_info_async().
119 : : * While the stream is blocked, the stream will set the pending flag
120 : : * internally, and any other operations on the stream will fail with
121 : : * %G_IO_ERROR_PENDING.
122 : : *
123 : : * Can fail if the stream was already closed (with @error being set to
124 : : * %G_IO_ERROR_CLOSED), the stream has pending operations (with @error being
125 : : * set to %G_IO_ERROR_PENDING), or if querying info is not supported for
126 : : * the stream's interface (with @error being set to %G_IO_ERROR_NOT_SUPPORTED). In
127 : : * all cases of failure, %NULL will be returned.
128 : : *
129 : : * If @cancellable is not %NULL, then the operation can be cancelled by
130 : : * triggering the cancellable object from another thread. If the operation
131 : : * was cancelled, the error %G_IO_ERROR_CANCELLED will be set, and %NULL will
132 : : * be returned.
133 : : *
134 : : * Returns: (transfer full): a #GFileInfo for the @stream, or %NULL on error.
135 : : **/
136 : : GFileInfo *
137 : 1 : g_file_output_stream_query_info (GFileOutputStream *stream,
138 : : const char *attributes,
139 : : GCancellable *cancellable,
140 : : GError **error)
141 : : {
142 : : GFileOutputStreamClass *class;
143 : : GOutputStream *output_stream;
144 : : GFileInfo *info;
145 : :
146 : 1 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
147 : :
148 : 1 : output_stream = G_OUTPUT_STREAM (stream);
149 : :
150 : 1 : if (!g_output_stream_set_pending (output_stream, error))
151 : 0 : return NULL;
152 : :
153 : 1 : info = NULL;
154 : :
155 : 1 : if (cancellable)
156 : 0 : g_cancellable_push_current (cancellable);
157 : :
158 : 1 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
159 : 1 : if (class->query_info)
160 : 1 : info = class->query_info (stream, attributes, cancellable, error);
161 : : else
162 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
163 : : _("Stream doesn’t support query_info"));
164 : :
165 : 1 : if (cancellable)
166 : 0 : g_cancellable_pop_current (cancellable);
167 : :
168 : 1 : g_output_stream_clear_pending (output_stream);
169 : :
170 : 1 : return info;
171 : : }
172 : :
173 : : static void
174 : 0 : async_ready_callback_wrapper (GObject *source_object,
175 : : GAsyncResult *res,
176 : : gpointer user_data)
177 : : {
178 : 0 : GFileOutputStream *stream = G_FILE_OUTPUT_STREAM (source_object);
179 : :
180 : 0 : g_output_stream_clear_pending (G_OUTPUT_STREAM (stream));
181 : 0 : if (stream->priv->outstanding_callback)
182 : 0 : (*stream->priv->outstanding_callback) (source_object, res, user_data);
183 : 0 : g_object_unref (stream);
184 : 0 : }
185 : :
186 : : /**
187 : : * g_file_output_stream_query_info_async:
188 : : * @stream: a #GFileOutputStream.
189 : : * @attributes: a file attribute query string.
190 : : * @io_priority: the [I/O priority](iface.AsyncResult.html#io-priority) of the
191 : : * request
192 : : * @cancellable: optional #GCancellable object, %NULL to ignore.
193 : : * @callback: callback to call when the request is satisfied
194 : : * @user_data: the data to pass to callback function
195 : : *
196 : : * Asynchronously queries the @stream for a #GFileInfo. When completed,
197 : : * @callback will be called with a #GAsyncResult which can be used to
198 : : * finish the operation with g_file_output_stream_query_info_finish().
199 : : *
200 : : * For the synchronous version of this function, see
201 : : * g_file_output_stream_query_info().
202 : : *
203 : : **/
204 : : void
205 : 0 : g_file_output_stream_query_info_async (GFileOutputStream *stream,
206 : : const char *attributes,
207 : : int io_priority,
208 : : GCancellable *cancellable,
209 : : GAsyncReadyCallback callback,
210 : : gpointer user_data)
211 : : {
212 : : GFileOutputStreamClass *klass;
213 : : GOutputStream *output_stream;
214 : 0 : GError *error = NULL;
215 : :
216 : 0 : g_return_if_fail (G_IS_FILE_OUTPUT_STREAM (stream));
217 : :
218 : 0 : output_stream = G_OUTPUT_STREAM (stream);
219 : :
220 : 0 : if (!g_output_stream_set_pending (output_stream, &error))
221 : : {
222 : 0 : g_task_report_error (stream, callback, user_data,
223 : : g_file_output_stream_query_info_async,
224 : : error);
225 : 0 : return;
226 : : }
227 : :
228 : 0 : klass = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
229 : :
230 : 0 : stream->priv->outstanding_callback = callback;
231 : 0 : g_object_ref (stream);
232 : 0 : klass->query_info_async (stream, attributes, io_priority, cancellable,
233 : : async_ready_callback_wrapper, user_data);
234 : : }
235 : :
236 : : /**
237 : : * g_file_output_stream_query_info_finish:
238 : : * @stream: a #GFileOutputStream.
239 : : * @result: a #GAsyncResult.
240 : : * @error: a #GError, %NULL to ignore.
241 : : *
242 : : * Finalizes the asynchronous query started
243 : : * by g_file_output_stream_query_info_async().
244 : : *
245 : : * Returns: (transfer full): A #GFileInfo for the finished query.
246 : : **/
247 : : GFileInfo *
248 : 0 : g_file_output_stream_query_info_finish (GFileOutputStream *stream,
249 : : GAsyncResult *result,
250 : : GError **error)
251 : : {
252 : : GFileOutputStreamClass *class;
253 : :
254 : 0 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
255 : 0 : g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
256 : :
257 : 0 : if (g_async_result_legacy_propagate_error (result, error))
258 : 0 : return NULL;
259 : 0 : else if (g_async_result_is_tagged (result, g_file_output_stream_query_info_async))
260 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
261 : :
262 : 0 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
263 : 0 : return class->query_info_finish (stream, result, error);
264 : : }
265 : :
266 : : /**
267 : : * g_file_output_stream_get_etag:
268 : : * @stream: a #GFileOutputStream.
269 : : *
270 : : * Gets the entity tag for the file when it has been written.
271 : : * This must be called after the stream has been written
272 : : * and closed, as the etag can change while writing.
273 : : *
274 : : * Returns: (nullable) (transfer full): the entity tag for the stream.
275 : : **/
276 : : char *
277 : 3 : g_file_output_stream_get_etag (GFileOutputStream *stream)
278 : : {
279 : : GFileOutputStreamClass *class;
280 : : GOutputStream *output_stream;
281 : : char *etag;
282 : :
283 : 3 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
284 : :
285 : 3 : output_stream = G_OUTPUT_STREAM (stream);
286 : :
287 : 3 : if (!g_output_stream_is_closed (output_stream))
288 : : {
289 : 0 : g_warning ("stream is not closed yet, can't get etag");
290 : 0 : return NULL;
291 : : }
292 : :
293 : 3 : etag = NULL;
294 : :
295 : 3 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
296 : 3 : if (class->get_etag)
297 : 3 : etag = class->get_etag (stream);
298 : :
299 : 3 : return etag;
300 : : }
301 : :
302 : : static goffset
303 : 66 : g_file_output_stream_tell (GFileOutputStream *stream)
304 : : {
305 : : GFileOutputStreamClass *class;
306 : : goffset offset;
307 : :
308 : 66 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), 0);
309 : :
310 : 66 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
311 : :
312 : 66 : offset = 0;
313 : 66 : if (class->tell)
314 : 66 : offset = class->tell (stream);
315 : :
316 : 66 : return offset;
317 : : }
318 : :
319 : : static goffset
320 : 66 : g_file_output_stream_seekable_tell (GSeekable *seekable)
321 : : {
322 : 66 : return g_file_output_stream_tell (G_FILE_OUTPUT_STREAM (seekable));
323 : : }
324 : :
325 : : static gboolean
326 : 0 : g_file_output_stream_can_seek (GFileOutputStream *stream)
327 : : {
328 : : GFileOutputStreamClass *class;
329 : : gboolean can_seek;
330 : :
331 : 0 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
332 : :
333 : 0 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
334 : :
335 : 0 : can_seek = FALSE;
336 : 0 : if (class->seek)
337 : : {
338 : 0 : can_seek = TRUE;
339 : 0 : if (class->can_seek)
340 : 0 : can_seek = class->can_seek (stream);
341 : : }
342 : :
343 : 0 : return can_seek;
344 : : }
345 : :
346 : : static gboolean
347 : 0 : g_file_output_stream_seekable_can_seek (GSeekable *seekable)
348 : : {
349 : 0 : return g_file_output_stream_can_seek (G_FILE_OUTPUT_STREAM (seekable));
350 : : }
351 : :
352 : : static gboolean
353 : 17 : g_file_output_stream_seek (GFileOutputStream *stream,
354 : : goffset offset,
355 : : GSeekType type,
356 : : GCancellable *cancellable,
357 : : GError **error)
358 : : {
359 : : GFileOutputStreamClass *class;
360 : : GOutputStream *output_stream;
361 : : gboolean res;
362 : :
363 : 17 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
364 : :
365 : 17 : output_stream = G_OUTPUT_STREAM (stream);
366 : 17 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
367 : :
368 : 17 : if (!class->seek)
369 : : {
370 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
371 : : _("Seek not supported on stream"));
372 : 0 : return FALSE;
373 : : }
374 : :
375 : 17 : if (!g_output_stream_set_pending (output_stream, error))
376 : 0 : return FALSE;
377 : :
378 : 17 : if (cancellable)
379 : 0 : g_cancellable_push_current (cancellable);
380 : :
381 : 17 : res = class->seek (stream, offset, type, cancellable, error);
382 : :
383 : 17 : if (cancellable)
384 : 0 : g_cancellable_pop_current (cancellable);
385 : :
386 : 17 : g_output_stream_clear_pending (output_stream);
387 : :
388 : 17 : return res;
389 : : }
390 : :
391 : : static gboolean
392 : 17 : g_file_output_stream_seekable_seek (GSeekable *seekable,
393 : : goffset offset,
394 : : GSeekType type,
395 : : GCancellable *cancellable,
396 : : GError **error)
397 : : {
398 : 17 : return g_file_output_stream_seek (G_FILE_OUTPUT_STREAM (seekable),
399 : : offset, type, cancellable, error);
400 : : }
401 : :
402 : : static gboolean
403 : 0 : g_file_output_stream_can_truncate (GFileOutputStream *stream)
404 : : {
405 : : GFileOutputStreamClass *class;
406 : : gboolean can_truncate;
407 : :
408 : 0 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
409 : :
410 : 0 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
411 : :
412 : 0 : can_truncate = FALSE;
413 : 0 : if (class->truncate_fn)
414 : : {
415 : 0 : can_truncate = TRUE;
416 : 0 : if (class->can_truncate)
417 : 0 : can_truncate = class->can_truncate (stream);
418 : : }
419 : :
420 : 0 : return can_truncate;
421 : : }
422 : :
423 : : static gboolean
424 : 0 : g_file_output_stream_seekable_can_truncate (GSeekable *seekable)
425 : : {
426 : 0 : return g_file_output_stream_can_truncate (G_FILE_OUTPUT_STREAM (seekable));
427 : : }
428 : :
429 : : static gboolean
430 : 0 : g_file_output_stream_truncate (GFileOutputStream *stream,
431 : : goffset size,
432 : : GCancellable *cancellable,
433 : : GError **error)
434 : : {
435 : : GFileOutputStreamClass *class;
436 : : GOutputStream *output_stream;
437 : : gboolean res;
438 : :
439 : 0 : g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
440 : :
441 : 0 : output_stream = G_OUTPUT_STREAM (stream);
442 : 0 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
443 : :
444 : 0 : if (!class->truncate_fn)
445 : : {
446 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
447 : : _("Truncate not supported on stream"));
448 : 0 : return FALSE;
449 : : }
450 : :
451 : 0 : if (!g_output_stream_set_pending (output_stream, error))
452 : 0 : return FALSE;
453 : :
454 : 0 : if (cancellable)
455 : 0 : g_cancellable_push_current (cancellable);
456 : :
457 : 0 : res = class->truncate_fn (stream, size, cancellable, error);
458 : :
459 : 0 : if (cancellable)
460 : 0 : g_cancellable_pop_current (cancellable);
461 : :
462 : 0 : g_output_stream_clear_pending (output_stream);
463 : :
464 : 0 : return res;
465 : : }
466 : :
467 : : static gboolean
468 : 0 : g_file_output_stream_seekable_truncate (GSeekable *seekable,
469 : : goffset size,
470 : : GCancellable *cancellable,
471 : : GError **error)
472 : : {
473 : 0 : return g_file_output_stream_truncate (G_FILE_OUTPUT_STREAM (seekable),
474 : : size, cancellable, error);
475 : : }
476 : : /********************************************
477 : : * Default implementation of async ops *
478 : : ********************************************/
479 : :
480 : : static void
481 : 0 : query_info_async_thread (GTask *task,
482 : : gpointer source_object,
483 : : gpointer task_data,
484 : : GCancellable *cancellable)
485 : : {
486 : 0 : GFileOutputStream *stream = source_object;
487 : 0 : const char *attributes = task_data;
488 : : GFileOutputStreamClass *class;
489 : 0 : GError *error = NULL;
490 : 0 : GFileInfo *info = NULL;
491 : :
492 : 0 : class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
493 : 0 : if (class->query_info)
494 : 0 : info = class->query_info (stream, attributes, cancellable, &error);
495 : : else
496 : 0 : g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
497 : : _("Stream doesn’t support query_info"));
498 : :
499 : 0 : if (info == NULL)
500 : 0 : g_task_return_error (task, error);
501 : : else
502 : 0 : g_task_return_pointer (task, info, g_object_unref);
503 : 0 : }
504 : :
505 : : static void
506 : 0 : g_file_output_stream_real_query_info_async (GFileOutputStream *stream,
507 : : const char *attributes,
508 : : int io_priority,
509 : : GCancellable *cancellable,
510 : : GAsyncReadyCallback callback,
511 : : gpointer user_data)
512 : : {
513 : : GTask *task;
514 : :
515 : 0 : task = g_task_new (stream, cancellable, callback, user_data);
516 : 0 : g_task_set_source_tag (task, g_file_output_stream_real_query_info_async);
517 : 0 : g_task_set_task_data (task, g_strdup (attributes), g_free);
518 : 0 : g_task_set_priority (task, io_priority);
519 : :
520 : 0 : g_task_run_in_thread (task, query_info_async_thread);
521 : 0 : g_object_unref (task);
522 : 0 : }
523 : :
524 : : static GFileInfo *
525 : 0 : g_file_output_stream_real_query_info_finish (GFileOutputStream *stream,
526 : : GAsyncResult *res,
527 : : GError **error)
528 : : {
529 : 0 : g_return_val_if_fail (g_task_is_valid (res, stream), NULL);
530 : :
531 : 0 : return g_task_propagate_pointer (G_TASK (res), error);
532 : : }
|