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