(root)/
glib-2.79.0/
gio/
gfileoutputstream.c
       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  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  g_file_output_stream_class_init (GFileOutputStreamClass *klass)
      88  {
      89    klass->query_info_async = g_file_output_stream_real_query_info_async;
      90    klass->query_info_finish = g_file_output_stream_real_query_info_finish;
      91  }
      92  
      93  static void
      94  g_file_output_stream_seekable_iface_init (GSeekableIface *iface)
      95  {
      96    iface->tell = g_file_output_stream_seekable_tell;
      97    iface->can_seek = g_file_output_stream_seekable_can_seek;
      98    iface->seek = g_file_output_stream_seekable_seek;
      99    iface->can_truncate = g_file_output_stream_seekable_can_truncate;
     100    iface->truncate_fn = g_file_output_stream_seekable_truncate;
     101  }
     102  
     103  static void
     104  g_file_output_stream_init (GFileOutputStream *stream)
     105  {
     106    stream->priv = g_file_output_stream_get_instance_private (stream);
     107  }
     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  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    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
     147    
     148    output_stream = G_OUTPUT_STREAM (stream);
     149    
     150    if (!g_output_stream_set_pending (output_stream, error))
     151      return NULL;
     152        
     153    info = NULL;
     154    
     155    if (cancellable)
     156      g_cancellable_push_current (cancellable);
     157    
     158    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     159    if (class->query_info)
     160      info = class->query_info (stream, attributes, cancellable, error);
     161    else
     162      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     163                           _("Stream doesn’t support query_info"));
     164    
     165    if (cancellable)
     166      g_cancellable_pop_current (cancellable);
     167    
     168    g_output_stream_clear_pending (output_stream);
     169    
     170    return info;
     171  }
     172  
     173  static void
     174  async_ready_callback_wrapper (GObject *source_object,
     175  			      GAsyncResult *res,
     176  			      gpointer      user_data)
     177  {
     178    GFileOutputStream *stream = G_FILE_OUTPUT_STREAM (source_object);
     179  
     180    g_output_stream_clear_pending (G_OUTPUT_STREAM (stream));
     181    if (stream->priv->outstanding_callback)
     182      (*stream->priv->outstanding_callback) (source_object, res, user_data);
     183    g_object_unref (stream);
     184  }
     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  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    GError *error = NULL;
     215  
     216    g_return_if_fail (G_IS_FILE_OUTPUT_STREAM (stream));
     217  
     218    output_stream = G_OUTPUT_STREAM (stream);
     219   
     220    if (!g_output_stream_set_pending (output_stream, &error))
     221      {
     222        g_task_report_error (stream, callback, user_data,
     223                             g_file_output_stream_query_info_async,
     224                             error);
     225        return;
     226      }
     227  
     228    klass = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     229  
     230    stream->priv->outstanding_callback = callback;
     231    g_object_ref (stream);
     232    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  g_file_output_stream_query_info_finish (GFileOutputStream     *stream,
     249  					   GAsyncResult         *result,
     250  					   GError              **error)
     251  {
     252    GFileOutputStreamClass *class;
     253  
     254    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
     255    g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
     256    
     257    if (g_async_result_legacy_propagate_error (result, error))
     258      return NULL;
     259    else if (g_async_result_is_tagged (result, g_file_output_stream_query_info_async))
     260      return g_task_propagate_pointer (G_TASK (result), error);
     261  
     262    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     263    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  g_file_output_stream_get_etag (GFileOutputStream  *stream)
     278  {
     279    GFileOutputStreamClass *class;
     280    GOutputStream *output_stream;
     281    char *etag;
     282    
     283    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
     284    
     285    output_stream = G_OUTPUT_STREAM (stream);
     286    
     287    if (!g_output_stream_is_closed (output_stream))
     288      {
     289        g_warning ("stream is not closed yet, can't get etag");
     290        return NULL;
     291      }
     292  
     293    etag = NULL;
     294    
     295    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     296    if (class->get_etag)
     297      etag = class->get_etag (stream);
     298    
     299    return etag;
     300  }
     301  
     302  static goffset
     303  g_file_output_stream_tell (GFileOutputStream  *stream)
     304  {
     305    GFileOutputStreamClass *class;
     306    goffset offset;
     307    
     308    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), 0);  
     309  
     310    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     311  
     312    offset = 0;
     313    if (class->tell)
     314      offset = class->tell (stream);
     315  
     316    return offset;
     317  }
     318  
     319  static goffset
     320  g_file_output_stream_seekable_tell (GSeekable *seekable)
     321  {
     322    return g_file_output_stream_tell (G_FILE_OUTPUT_STREAM (seekable));
     323  }
     324  
     325  static gboolean
     326  g_file_output_stream_can_seek (GFileOutputStream  *stream)
     327  {
     328    GFileOutputStreamClass *class;
     329    gboolean can_seek;
     330  
     331    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
     332  
     333    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     334  
     335    can_seek = FALSE;
     336    if (class->seek)
     337      {
     338        can_seek = TRUE;
     339        if (class->can_seek)
     340  	can_seek = class->can_seek (stream);
     341      }
     342    
     343    return can_seek;
     344  }
     345  
     346  static gboolean
     347  g_file_output_stream_seekable_can_seek (GSeekable *seekable)
     348  {
     349    return g_file_output_stream_can_seek (G_FILE_OUTPUT_STREAM (seekable));
     350  }
     351  
     352  static gboolean
     353  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    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
     364  
     365    output_stream = G_OUTPUT_STREAM (stream);
     366    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     367  
     368    if (!class->seek)
     369      {
     370        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     371                             _("Seek not supported on stream"));
     372        return FALSE;
     373      }
     374  
     375    if (!g_output_stream_set_pending (output_stream, error))
     376      return FALSE;
     377    
     378    if (cancellable)
     379      g_cancellable_push_current (cancellable);
     380    
     381    res = class->seek (stream, offset, type, cancellable, error);
     382    
     383    if (cancellable)
     384      g_cancellable_pop_current (cancellable);
     385  
     386    g_output_stream_clear_pending (output_stream);
     387    
     388    return res;
     389  }
     390  
     391  static gboolean
     392  g_file_output_stream_seekable_seek (GSeekable  *seekable,
     393  				    goffset     offset,
     394  				    GSeekType   type,
     395  				    GCancellable  *cancellable,
     396  				    GError    **error)
     397  {
     398    return g_file_output_stream_seek (G_FILE_OUTPUT_STREAM (seekable),
     399  				    offset, type, cancellable, error);
     400  }
     401  
     402  static gboolean
     403  g_file_output_stream_can_truncate (GFileOutputStream  *stream)
     404  {
     405    GFileOutputStreamClass *class;
     406    gboolean can_truncate;
     407  
     408    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
     409  
     410    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     411  
     412    can_truncate = FALSE;
     413    if (class->truncate_fn)
     414      {
     415        can_truncate = TRUE;
     416        if (class->can_truncate)
     417  	can_truncate = class->can_truncate (stream);
     418      }
     419    
     420    return can_truncate;
     421  }
     422  
     423  static gboolean
     424  g_file_output_stream_seekable_can_truncate (GSeekable  *seekable)
     425  {
     426    return g_file_output_stream_can_truncate (G_FILE_OUTPUT_STREAM (seekable));
     427  }
     428  
     429  static gboolean
     430  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    g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
     440  
     441    output_stream = G_OUTPUT_STREAM (stream);
     442    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     443  
     444    if (!class->truncate_fn)
     445      {
     446        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     447                             _("Truncate not supported on stream"));
     448        return FALSE;
     449      }
     450  
     451    if (!g_output_stream_set_pending (output_stream, error))
     452      return FALSE;
     453    
     454    if (cancellable)
     455      g_cancellable_push_current (cancellable);
     456    
     457    res = class->truncate_fn (stream, size, cancellable, error);
     458    
     459    if (cancellable)
     460      g_cancellable_pop_current (cancellable);
     461  
     462    g_output_stream_clear_pending (output_stream);
     463    
     464    return res;
     465  }
     466  
     467  static gboolean
     468  g_file_output_stream_seekable_truncate (GSeekable     *seekable,
     469  					goffset        size,
     470  					GCancellable  *cancellable,
     471  					GError       **error)
     472  {
     473    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  query_info_async_thread (GTask        *task,
     482                           gpointer      source_object,
     483                           gpointer      task_data,
     484                           GCancellable *cancellable)
     485  {
     486    GFileOutputStream *stream = source_object;
     487    const char *attributes = task_data;
     488    GFileOutputStreamClass *class;
     489    GError *error = NULL;
     490    GFileInfo *info = NULL;
     491  
     492    class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
     493    if (class->query_info)
     494      info = class->query_info (stream, attributes, cancellable, &error);
     495    else
     496      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     497                           _("Stream doesn’t support query_info"));
     498  
     499    if (info == NULL)
     500      g_task_return_error (task, error);
     501    else
     502      g_task_return_pointer (task, info, g_object_unref);
     503  }
     504  
     505  static void
     506  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    task = g_task_new (stream, cancellable, callback, user_data);
     516    g_task_set_source_tag (task, g_file_output_stream_real_query_info_async);
     517    g_task_set_task_data (task, g_strdup (attributes), g_free);
     518    g_task_set_priority (task, io_priority);
     519    
     520    g_task_run_in_thread (task, query_info_async_thread);
     521    g_object_unref (task);
     522  }
     523  
     524  static GFileInfo *
     525  g_file_output_stream_real_query_info_finish (GFileOutputStream     *stream,
     526  					     GAsyncResult         *res,
     527  					     GError              **error)
     528  {
     529    g_return_val_if_fail (g_task_is_valid (res, stream), NULL);
     530  
     531    return g_task_propagate_pointer (G_TASK (res), error);
     532  }