Improve the g_file_make_symbolic_link docs
[platform/upstream/glib.git] / gio / gfile.c
index 6ae4209..d104ab4 100644 (file)
  */
 
 #include "config.h"
+#ifdef HAVE_SPLICE
+#define _GNU_SOURCE
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
 #include <string.h>
 #include <sys/types.h>
 #ifdef HAVE_PWD_H
@@ -31,9 +38,9 @@
 #include "gfile.h"
 #include "gvfs.h"
 #include "gioscheduler.h"
-#include "glocalfile.h"
 #include "gsimpleasyncresult.h"
 #include "gfileattribute-priv.h"
+#include "gfiledescriptorbased.h"
 #include "gpollfilemonitor.h"
 #include "gappinfo.h"
 #include "gfileinputstream.h"
  * take a very long time to finish, and blocking may leave an application
  * unusable. Notable cases include:
  * g_file_mount_mountable() to mount a mountable file.
- * g_file_unmount_mountable() to unmount a mountable file.
- * g_file_eject_mountable() to eject a mountable file.
+ * g_file_unmount_mountable_with_operation() to unmount a mountable file.
+ * g_file_eject_mountable_with_operation() to eject a mountable file.
  * 
  * <para id="gfile-etag"><indexterm><primary>entity tag</primary></indexterm>
  * One notable feature of #GFile<!-- -->s are entity tags, or "etags" for 
  * </para>
  **/
 
-static void g_file_base_init (gpointer g_class);
-static void g_file_class_init (gpointer g_class,
-                              gpointer class_data);
-
 static void               g_file_real_query_info_async            (GFile                  *file,
                                                                   const char             *attributes,
                                                                   GFileQueryInfoFlags     flags,
@@ -192,6 +195,34 @@ static void               g_file_real_replace_async               (GFile
 static GFileOutputStream *g_file_real_replace_finish              (GFile                  *file,
                                                                   GAsyncResult           *res,
                                                                   GError                **error);
+static void               g_file_real_open_readwrite_async        (GFile                  *file,
+                                                                   int                  io_priority,
+                                                                   GCancellable           *cancellable,
+                                                                   GAsyncReadyCallback     callback,
+                                                                   gpointer                user_data);
+static GFileIOStream *    g_file_real_open_readwrite_finish       (GFile                  *file,
+                                                                   GAsyncResult           *res,
+                                                                   GError                **error);
+static void               g_file_real_create_readwrite_async      (GFile                  *file,
+                                                                   GFileCreateFlags        flags,
+                                                                   int                     io_priority,
+                                                                   GCancellable           *cancellable,
+                                                                   GAsyncReadyCallback     callback,
+                                                                   gpointer                user_data);
+static GFileIOStream *    g_file_real_create_readwrite_finish     (GFile                  *file,
+                                                                   GAsyncResult           *res,
+                                                                   GError                **error);
+static void               g_file_real_replace_readwrite_async     (GFile                  *file,
+                                                                   const char             *etag,
+                                                                   gboolean                make_backup,
+                                                                   GFileCreateFlags        flags,
+                                                                   int                     io_priority,
+                                                                   GCancellable           *cancellable,
+                                                                   GAsyncReadyCallback     callback,
+                                                                   gpointer                user_data);
+static GFileIOStream *    g_file_real_replace_readwrite_finish    (GFile                  *file,
+                                                                  GAsyncResult            *res,
+                                                                  GError                 **error);
 static gboolean           g_file_real_set_attributes_from_info    (GFile                  *file,
                                                                   GFileInfo              *info,
                                                                   GFileQueryInfoFlags     flags,
@@ -238,43 +269,12 @@ static gboolean           g_file_real_copy_finish                 (GFile
                                                                   GAsyncResult           *res,
                                                                   GError                **error);
 
-GType
-g_file_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      const GTypeInfo file_info =
-      {
-        sizeof (GFileIface), /* class_size */
-       g_file_base_init,   /* base_init */
-       NULL,           /* base_finalize */
-       g_file_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       0,
-       0,              /* n_preallocs */
-       NULL
-      };
-      GType g_define_type_id =
-       g_type_register_static (G_TYPE_INTERFACE, I_("GFile"),
-                               &file_info, 0);
-
-      g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
-
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
+typedef GFileIface GFileInterface;
+G_DEFINE_INTERFACE (GFile, g_file, G_TYPE_OBJECT)
 
 static void
-g_file_class_init (gpointer g_class,
-                  gpointer class_data)
+g_file_default_init (GFileIface *iface)
 {
-  GFileIface *iface = g_class;
-
   iface->enumerate_children_async = g_file_real_enumerate_children_async;
   iface->enumerate_children_finish = g_file_real_enumerate_children_finish;
   iface->set_display_name_async = g_file_real_set_display_name_async;
@@ -293,6 +293,12 @@ g_file_class_init (gpointer g_class,
   iface->create_finish = g_file_real_create_finish;
   iface->replace_async = g_file_real_replace_async;
   iface->replace_finish = g_file_real_replace_finish;
+  iface->open_readwrite_async = g_file_real_open_readwrite_async;
+  iface->open_readwrite_finish = g_file_real_open_readwrite_finish;
+  iface->create_readwrite_async = g_file_real_create_readwrite_async;
+  iface->create_readwrite_finish = g_file_real_create_readwrite_finish;
+  iface->replace_readwrite_async = g_file_real_replace_readwrite_async;
+  iface->replace_readwrite_finish = g_file_real_replace_readwrite_finish;
   iface->find_enclosing_mount_async = g_file_real_find_enclosing_mount_async;
   iface->find_enclosing_mount_finish = g_file_real_find_enclosing_mount_finish;
   iface->set_attributes_from_info = g_file_real_set_attributes_from_info;
@@ -300,11 +306,6 @@ g_file_class_init (gpointer g_class,
   iface->copy_finish = g_file_real_copy_finish;
 }
 
-static void
-g_file_base_init (gpointer g_class)
-{
-}
-
 
 /**
  * g_file_is_native:
@@ -620,6 +621,49 @@ g_file_get_parent (GFile *file)
 }
 
 /**
+ * g_file_has_parent:
+ * @file: input #GFile
+ * @parent: the parent to check for, or %NULL
+ *
+ * Checks if @file has a parent, and optionally, if it is @parent.
+ *
+ * If @parent is %NULL then this function returns %TRUE if @file has any
+ * parent at all.  If @parent is non-%NULL then %TRUE is only returned
+ * if @file is a child of @parent.
+ *
+ * Returns: %TRUE if @file is a child of @parent (or any parent in the
+ *          case that @parent is %NULL).
+ *
+ * Since: 2.24
+ **/
+gboolean
+g_file_has_parent (GFile *file,
+                   GFile *parent)
+{
+  GFile *actual_parent;
+  gboolean result;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (parent == NULL || G_IS_FILE (parent), FALSE);
+
+  actual_parent = g_file_get_parent (file);
+
+  if (actual_parent != NULL)
+    {
+      if (parent != NULL)
+        result = g_file_equal (parent, actual_parent);
+      else
+        result = TRUE;
+
+      g_object_unref (actual_parent);
+    }
+  else
+    result = FALSE;
+
+  return result;
+}
+
+/**
  * g_file_get_child:
  * @file: input #GFile.
  * @name: string containing the child's basename.
@@ -685,6 +729,8 @@ g_file_get_child_for_display_name (GFile      *file,
  * 
  * Checks whether @file has the prefix specified by @prefix. In other word, 
  * if the names of inital elements of @file<!-- -->s pathname match @prefix.
+ * Only full pathname elements are matched, so a path like /foo is not
+ * considered a prefix of /foobar, only of /foo/bar.
  * 
  * This call does no i/o, as it works purely on names. As such it can 
  * sometimes return %FALSE even if @file is inside a @prefix (from a 
@@ -1630,10 +1676,174 @@ g_file_replace (GFile             *file,
 }
 
 /**
+ * g_file_open_readwrite:
+ * @file: #GFile to open
+ * @cancellable: a #GCancellable
+ * @error: a #GError, or %NULL
+ *
+ * Opens an existing file for reading and writing. The result is
+ * a #GFileIOStream that can be used to read and write the contents of the file.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * If the file does not exist, the G_IO_ERROR_NOT_FOUND error will be returned.
+ * If the file is a directory, the G_IO_ERROR_IS_DIRECTORY error will be returned.
+ * Other errors are possible too, and depend on what kind of filesystem the file is on.
+ * Note that in many non-local file cases read and write streams are not supported,
+ * so make sure you really need to do read and write streaming, rather than
+ * just opening for reading or writing.
+ *
+ * Returns: #GFileIOStream or %NULL on error.
+ *     Free the returned object with g_object_unref().
+ *
+ * Since: 2.22
+ **/
+GFileIOStream *
+g_file_open_readwrite (GFile                      *file,
+                       GCancellable               *cancellable,
+                       GError                    **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->open_readwrite == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           _("Operation not supported"));
+      return NULL;
+    }
+
+  return (* iface->open_readwrite) (file, cancellable, error);
+}
+
+/**
+ * g_file_create_readwrite:
+ * @file: a #GFile
+ * @flags: a set of #GFileCreateFlags
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a new file and returns a stream for reading and writing to it.
+ * The file must not already exist.
+ *
+ * By default files created are generally readable by everyone,
+ * but if you pass #G_FILE_CREATE_PRIVATE in @flags the file
+ * will be made readable only to the current user, to the level that
+ * is supported on the target filesystem.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * If a file or directory with this name already exists the %G_IO_ERROR_EXISTS
+ * error will be returned. Some file systems don't allow all file names,
+ * and may return an %G_IO_ERROR_INVALID_FILENAME error, and if the name
+ * is too long, %G_IO_ERROR_FILENAME_TOO_LONG will be returned. Other errors
+ * are possible too, and depend on what kind of filesystem the file is on.
+ *
+ * Note that in many non-local file cases read and write streams are not
+ * supported, so make sure you really need to do read and write streaming,
+ * rather than just opening for reading or writing.
+ *
+ * Returns: a #GFileIOStream for the newly created file, or %NULL on error.
+ *     Free the returned object with g_object_unref().
+ *
+ * Since: 2.22
+ */
+GFileIOStream *
+g_file_create_readwrite (GFile             *file,
+                         GFileCreateFlags   flags,
+                         GCancellable      *cancellable,
+                         GError           **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->create_readwrite == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           _("Operation not supported"));
+      return NULL;
+    }
+
+  return (* iface->create_readwrite) (file, flags, cancellable, error);
+}
+
+/**
+ * g_file_replace_readwrite:
+ * @file: a #GFile
+ * @etag: an optional <link linkend="gfile-etag">entity tag</link> for the
+ *     current #GFile, or #NULL to ignore
+ * @make_backup: %TRUE if a backup should be created
+ * @flags: a set of #GFileCreateFlags
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @error: return location for a #GError, or %NULL
+ *
+ * Returns an output stream for overwriting the file in readwrite mode,
+ * possibly creating a backup copy of the file first. If the file doesn't
+ * exist, it will be created.
+ *
+ * For details about the behaviour, see g_file_replace() which does the same
+ * thing but returns an output stream only.
+ *
+ * Note that in many non-local file cases read and write streams are not
+ * supported, so make sure you really need to do read and write streaming,
+ * rather than just opening for reading or writing.
+ *
+ * Returns: a #GFileIOStream or %NULL on error.
+ *     Free the returned object with g_object_unref().
+ *
+ * Since: 2.22
+ */
+GFileIOStream *
+g_file_replace_readwrite (GFile             *file,
+                          const char        *etag,
+                          gboolean           make_backup,
+                          GFileCreateFlags   flags,
+                          GCancellable      *cancellable,
+                          GError           **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->replace_readwrite == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           _("Operation not supported"));
+      return NULL;
+    }
+
+  return (* iface->replace_readwrite) (file, etag, make_backup, flags, cancellable, error);
+}
+
+/**
  * g_file_read_async:
- * @file: input #GFile.
- * @io_priority: the <link linkend="io-priority">I/O priority</link> 
- *     of the request. 
+ * @file: input #GFile
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ *     of the request.
  * @cancellable: optional #GCancellable object, %NULL to ignore.
  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
  * @user_data: the data to pass to callback function
@@ -1918,93 +2128,329 @@ g_file_replace_finish (GFile         *file,
       if (g_simple_async_result_propagate_error (simple, error))
        return NULL;
     }
-  
+
   iface = G_FILE_GET_IFACE (file);
   return (* iface->replace_finish) (file, res, error);
 }
 
-static gboolean
-copy_symlink (GFile           *destination,
-             GFileCopyFlags   flags,
-             GCancellable    *cancellable,
-             const char      *target,
-             GError         **error)
+
+/**
+ * g_file_open_readwrite_async:
+ * @file: input #GFile.
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ *     of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously opens @file for reading and writing.
+ *
+ * For more details, see g_file_open_readwrite() which is
+ * the synchronous version of this call.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * g_file_open_readwrite_finish() to get the result of the operation.
+ *
+ * Since: 2.22
+ **/
+void
+g_file_open_readwrite_async (GFile                      *file,
+                             int                         io_priority,
+                             GCancellable               *cancellable,
+                             GAsyncReadyCallback         callback,
+                             gpointer                    user_data)
 {
-  GError *my_error;
-  gboolean tried_delete;
-  GFileInfo *info;
-  GFileType file_type;
+  GFileIface *iface;
 
-  tried_delete = FALSE;
+  g_return_if_fail (G_IS_FILE (file));
 
- retry:
-  my_error = NULL;
-  if (!g_file_make_symbolic_link (destination, target, cancellable, &my_error))
-    {
-      /* Maybe it already existed, and we want to overwrite? */
-      if (!tried_delete && (flags & G_FILE_COPY_OVERWRITE) && 
-         my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_EXISTS)
-       {
-         g_error_free (my_error);
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->open_readwrite_async) (file,
+                                   io_priority,
+                                   cancellable,
+                                   callback,
+                                   user_data);
+}
+
+/**
+ * g_file_open_readwrite_finish:
+ * @file: input #GFile.
+ * @res: a #GAsyncResult.
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an asynchronous file read operation started with
+ * g_file_open_readwrite_async().
+ *
+ * Returns: a #GFileIOStream or %NULL on error.
+ *     Free the returned object with g_object_unref().
+ *
+ * Since: 2.22
+ **/
+GFileIOStream *
+g_file_open_readwrite_finish (GFile                      *file,
+                              GAsyncResult               *res,
+                              GError                    **error)
+{
+  GFileIface *iface;
 
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
 
-         /* Don't overwrite if the destination is a directory */
-         info = g_file_query_info (destination, G_FILE_ATTRIBUTE_STANDARD_TYPE,
-                                   G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                   cancellable, &my_error);
-         if (info != NULL)
-           {
-             file_type = g_file_info_get_file_type (info);
-             g_object_unref (info);
-             
-             if (file_type == G_FILE_TYPE_DIRECTORY)
-               {
-                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
-                                       _("Can't copy over directory"));
-                 return FALSE;
-               }
-           }
-         
-         if (!g_file_delete (destination, cancellable, error))
-           return FALSE;
-         
-         tried_delete = TRUE;
-         goto retry;
-       }
-            /* Nah, fail */
-      g_propagate_error (error, my_error);
-      return FALSE;
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+        return NULL;
     }
 
-  return TRUE;
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->open_readwrite_finish) (file, res, error);
 }
 
-static GInputStream *
-open_source_for_copy (GFile           *source,
-                     GFile           *destination,
-                     GFileCopyFlags   flags,
-                     GCancellable    *cancellable,
-                     GError         **error)
-{
-  GError *my_error;
-  GInputStream *in;
-  GFileInfo *info;
-  GFileType file_type;
-  
-  my_error = NULL;
-  in = (GInputStream *)g_file_read (source, cancellable, &my_error);
-  if (in != NULL)
-    return in;
-
-  /* There was an error opening the source, try to set a good error for it: */
 
-  if (my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_IS_DIRECTORY)
-    {
-      /* The source is a directory, don't fail with WOULD_RECURSE immediately, 
-       * as that is less useful to the app. Better check for errors on the 
-       * target instead. 
-       */
-      g_error_free (my_error);
+/**
+ * g_file_create_readwrite_async:
+ * @file: input #GFile
+ * @flags: a set of #GFileCreateFlags
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ *     of the request
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously creates a new file and returns a stream for reading and
+ * writing to it. The file must not already exist.
+ *
+ * For more details, see g_file_create_readwrite() which is
+ * the synchronous version of this call.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call g_file_create_readwrite_finish() to get the result of the operation.
+ *
+ * Since: 2.22
+ */
+void
+g_file_create_readwrite_async (GFile               *file,
+                               GFileCreateFlags     flags,
+                               int                  io_priority,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->create_readwrite_async) (file,
+                                     flags,
+                                     io_priority,
+                                     cancellable,
+                                     callback,
+                                     user_data);
+}
+
+/**
+ * g_file_create_readwrite_finish:
+ * @file: input #GFile
+ * @res: a #GAsyncResult
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an asynchronous file create operation started with
+ * g_file_create_readwrite_async().
+ *
+ * Returns: a #GFileIOStream or %NULL on error.
+ *     Free the returned object with g_object_unref().
+ *
+ * Since: 2.22
+ **/
+GFileIOStream *
+g_file_create_readwrite_finish (GFile         *file,
+                                GAsyncResult  *res,
+                                GError       **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+        return NULL;
+    }
+
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->create_readwrite_finish) (file, res, error);
+}
+
+/**
+ * g_file_replace_readwrite_async:
+ * @file: input #GFile.
+ * @etag: an <link linkend="gfile-etag">entity tag</link> for the
+ *     current #GFile, or NULL to ignore.
+ * @make_backup: %TRUE if a backup should be created.
+ * @flags: a set of #GFileCreateFlags.
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ *     of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously overwrites the file in read-write mode, replacing the
+ * contents, possibly creating a backup copy of the file first.
+ *
+ * For more details, see g_file_replace_readwrite() which is
+ * the synchronous version of this call.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call g_file_replace_readwrite_finish() to get the result of the operation.
+ *
+ * Since: 2.22
+ */
+void
+g_file_replace_readwrite_async (GFile               *file,
+                                const char          *etag,
+                                gboolean             make_backup,
+                                GFileCreateFlags     flags,
+                                int                  io_priority,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->replace_readwrite_async) (file,
+                                      etag,
+                                      make_backup,
+                                      flags,
+                                      io_priority,
+                                      cancellable,
+                                      callback,
+                                      user_data);
+}
+
+/**
+ * g_file_replace_readwrite_finish:
+ * @file: input #GFile.
+ * @res: a #GAsyncResult.
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an asynchronous file replace operation started with
+ * g_file_replace_readwrite_async().
+ *
+ * Returns: a #GFileIOStream, or %NULL on error.
+ *     Free the returned object with g_object_unref().
+ *
+ * Since: 2.22
+ */
+GFileIOStream *
+g_file_replace_readwrite_finish (GFile         *file,
+                                 GAsyncResult  *res,
+                                 GError       **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+        return NULL;
+    }
+
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->replace_readwrite_finish) (file, res, error);
+}
+
+static gboolean
+copy_symlink (GFile           *destination,
+             GFileCopyFlags   flags,
+             GCancellable    *cancellable,
+             const char      *target,
+             GError         **error)
+{
+  GError *my_error;
+  gboolean tried_delete;
+  GFileInfo *info;
+  GFileType file_type;
+
+  tried_delete = FALSE;
+
+ retry:
+  my_error = NULL;
+  if (!g_file_make_symbolic_link (destination, target, cancellable, &my_error))
+    {
+      /* Maybe it already existed, and we want to overwrite? */
+      if (!tried_delete && (flags & G_FILE_COPY_OVERWRITE) && 
+         my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_EXISTS)
+       {
+         g_error_free (my_error);
+
+
+         /* Don't overwrite if the destination is a directory */
+         info = g_file_query_info (destination, G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                                   G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                   cancellable, &my_error);
+         if (info != NULL)
+           {
+             file_type = g_file_info_get_file_type (info);
+             g_object_unref (info);
+             
+             if (file_type == G_FILE_TYPE_DIRECTORY)
+               {
+                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                                       _("Can't copy over directory"));
+                 return FALSE;
+               }
+           }
+         
+         if (!g_file_delete (destination, cancellable, error))
+           return FALSE;
+         
+         tried_delete = TRUE;
+         goto retry;
+       }
+            /* Nah, fail */
+      g_propagate_error (error, my_error);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static GInputStream *
+open_source_for_copy (GFile           *source,
+                     GFile           *destination,
+                     GFileCopyFlags   flags,
+                     GCancellable    *cancellable,
+                     GError         **error)
+{
+  GError *my_error;
+  GInputStream *in;
+  GFileInfo *info;
+  GFileType file_type;
+  
+  my_error = NULL;
+  in = (GInputStream *)g_file_read (source, cancellable, &my_error);
+  if (in != NULL)
+    return in;
+
+  /* There was an error opening the source, try to set a good error for it: */
+
+  if (my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_IS_DIRECTORY)
+    {
+      /* The source is a directory, don't fail with WOULD_RECURSE immediately, 
+       * as that is less useful to the app. Better check for errors on the 
+       * target instead. 
+       */
+      g_error_free (my_error);
       my_error = NULL;
       
       info = g_file_query_info (destination, G_FILE_ATTRIBUTE_STANDARD_TYPE,
@@ -2108,7 +2554,7 @@ build_attribute_list_for_copy (GFileAttributeInfoList *attributes,
                g_string_append_c (s, ',');
                
              g_string_append (s, namespaces->infos[i].name);
-             g_string_append (s, ":*");
+             g_string_append (s, "::*");
            }
        }
     }
@@ -2190,7 +2636,6 @@ g_file_copy_attributes (GFile           *source,
   return res;
 }
 
-/* Closes the streams */
 static gboolean
 copy_stream_with_progress (GInputStream           *in,
                           GOutputStream          *out,
@@ -2208,28 +2653,32 @@ copy_stream_with_progress (GInputStream           *in,
   GFileInfo *info;
 
   total_size = -1;
-  info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (in),
-                                        G_FILE_ATTRIBUTE_STANDARD_SIZE,
-                                        cancellable, NULL);
-  if (info)
-    {
-      if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
-        total_size = g_file_info_get_size (info);
-      g_object_unref (info);
-    }
-
-  if (total_size == -1)
+  /* avoid performance impact of querying total size when it's not needed */
+  if (progress_callback)
     {
-      info = g_file_query_info (source, 
-                                G_FILE_ATTRIBUTE_STANDARD_SIZE,
-                                G_FILE_QUERY_INFO_NONE,
-                                cancellable, NULL);
+      info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (in),
+                                             G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                             cancellable, NULL);
       if (info)
         {
           if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
             total_size = g_file_info_get_size (info);
           g_object_unref (info);
         }
+
+      if (total_size == -1)
+        {
+          info = g_file_query_info (source, 
+                                    G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                    G_FILE_QUERY_INFO_NONE,
+                                    cancellable, NULL);
+          if (info)
+            {
+              if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
+                total_size = g_file_info_get_size (info);
+              g_object_unref (info);
+            }
+        }
     }
 
   if (total_size == -1)
@@ -2272,25 +2721,134 @@ copy_stream_with_progress (GInputStream           *in,
        progress_callback (current_size, total_size, progress_callback_data);
     }
 
-  if (!res)
-    error = NULL; /* Ignore further errors */
-
   /* Make sure we send full copied size */
   if (progress_callback)
     progress_callback (current_size, total_size, progress_callback_data);
-  
-  /* Don't care about errors in source here */
-  g_input_stream_close (in, cancellable, NULL);
 
-  /* But write errors on close are bad! */
-  if (!g_output_stream_close (out, cancellable, error))
-    res = FALSE;
+  return res;
+}
+
+#ifdef HAVE_SPLICE
+
+static gboolean
+do_splice (int     fd_in,
+          loff_t *off_in,
+           int     fd_out,
+          loff_t *off_out,
+           size_t  len,
+           long   *bytes_transferd,
+           GError **error)
+{
+  long result;
+
+retry:
+  result = splice (fd_in, off_in, fd_out, off_out, len, SPLICE_F_MORE);
+
+  if (result == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINTR)
+        goto retry;
+      else if (errsv == ENOSYS || errsv == EINVAL)
+        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                             _("Splice not supported"));
+      else
+        g_set_error (error, G_IO_ERROR,
+                     g_io_error_from_errno (errsv),
+                     _("Error splicing file: %s"),
+                     g_strerror (errsv));
+
+      return FALSE;
+    }
+
+  *bytes_transferd = result;
+  return TRUE;
+}
+
+static gboolean
+splice_stream_with_progress (GInputStream           *in,
+                             GOutputStream          *out,
+                             GCancellable           *cancellable,
+                             GFileProgressCallback   progress_callback,
+                             gpointer                progress_callback_data,
+                             GError                **error)
+{
+  int buffer[2];
+  gboolean res;
+  goffset total_size;
+  loff_t offset_in;
+  loff_t offset_out;
+  int fd_in, fd_out;
+
+  fd_in = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (in));
+  fd_out = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (out));
+
+  if (pipe (buffer) != 0)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                           "Pipe creation failed");
+      return FALSE;
+    }
+
+  total_size = -1;
+  /* avoid performance impact of querying total size when it's not needed */
+  if (progress_callback)
+    {
+      struct stat sbuf;
+
+      if (fstat (fd_in, &sbuf) == 0)
+        total_size = sbuf.st_size;
+    }
+
+  if (total_size == -1)
+    total_size = 0;
+
+  offset_in = offset_out = 0;
+  res = FALSE;
+  while (TRUE)
+    {
+      long n_read;
+      long n_written;
+
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        break;
+
+      if (!do_splice (fd_in, &offset_in, buffer[1], NULL, 1024*64, &n_read, error))
+        break;
+
+      if (n_read == 0)
+        {
+          res = TRUE;
+          break;
+        }
+
+      while (n_read > 0)
+        {
+          if (g_cancellable_set_error_if_cancelled (cancellable, error))
+            goto out;
+
+          if (!do_splice (buffer[0], NULL, fd_out, &offset_out, n_read, &n_written, error))
+            goto out;
+
+          n_read -= n_written;
+        }
+
+      if (progress_callback)
+        progress_callback (offset_in, total_size, progress_callback_data);
+    }
+
+  /* Make sure we send full copied size */
+  if (progress_callback)
+    progress_callback (offset_in, total_size, progress_callback_data);
+
+ out:
+  close (buffer[0]);
+  close (buffer[1]);
 
-  g_object_unref (in);
-  g_object_unref (out);
-      
   return res;
 }
+#endif
 
 static gboolean
 file_copy_fallback (GFile                  *source,
@@ -2305,20 +2863,27 @@ file_copy_fallback (GFile                  *source,
   GOutputStream *out;
   GFileInfo *info;
   const char *target;
+  gboolean result;
+#ifdef HAVE_SPLICE
+  gboolean fallback = TRUE;
+#endif
+
+  /* need to know the file type */
+  info = g_file_query_info (source,
+                           G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
+                           G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                           cancellable,
+                           error);
+
+  if (info == NULL)
+         return FALSE;
 
   /* Maybe copy the symlink? */
-  if (flags & G_FILE_COPY_NOFOLLOW_SYMLINKS)
+  if ((flags & G_FILE_COPY_NOFOLLOW_SYMLINKS) &&
+      g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK)
     {
-      info = g_file_query_info (source,
-                               G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
-                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                               cancellable,
-                               error);
-      if (info == NULL)
-       return FALSE;
-
-      if (g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK &&
-         (target = g_file_info_get_symlink_target (info)) != NULL)
+      target = g_file_info_get_symlink_target (info);
+      if (target)
        {
          if (!copy_symlink (destination, flags, cancellable, target, error))
            {
@@ -2329,10 +2894,23 @@ file_copy_fallback (GFile                  *source,
          g_object_unref (info);
          goto copied_file;
        }
-      
+        /* ... else fall back on a regular file copy */
+       g_object_unref (info);
+    }
+  /* Handle "special" files (pipes, device nodes, ...)? */
+  else if (g_file_info_get_file_type (info) == G_FILE_TYPE_SPECIAL)
+    {
+      /* FIXME: could try to recreate device nodes and others? */
+
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                           _("Can't copy special file"));
       g_object_unref (info);
+      return FALSE;
     }
-  
+  /* Everything else should just fall back on a regular copy. */
+  else
+    g_object_unref (info);
+
   in = open_source_for_copy (source, destination, flags, cancellable, error);
   if (in == NULL)
     return FALSE;
@@ -2342,7 +2920,7 @@ file_copy_fallback (GFile                  *source,
       out = (GOutputStream *)g_file_replace (destination,
                                             NULL,
                                             flags & G_FILE_COPY_BACKUP,
-                                             0,
+                                             G_FILE_CREATE_REPLACE_DESTINATION,
                                             cancellable, error);
     }
   else
@@ -2356,13 +2934,45 @@ file_copy_fallback (GFile                  *source,
       return FALSE;
     }
 
-  if (!copy_stream_with_progress (in, out, source, cancellable,
-                                 progress_callback, progress_callback_data,
-                                 error))
+#ifdef HAVE_SPLICE
+  if (G_IS_FILE_DESCRIPTOR_BASED (in) && G_IS_FILE_DESCRIPTOR_BASED (out))
+    {
+      GError *splice_err = NULL;
+
+      result = splice_stream_with_progress (in, out, cancellable,
+                                            progress_callback, progress_callback_data,
+                                            &splice_err);
+
+      if (result || !g_error_matches (splice_err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+        {
+          fallback = FALSE;
+          if (!result)
+            g_propagate_error (error, splice_err);
+        }
+      else
+        g_clear_error (&splice_err);
+    }
+
+  if (fallback)
+#endif
+    result = copy_stream_with_progress (in, out, source, cancellable,
+                                       progress_callback, progress_callback_data,
+                                       error);
+
+  /* Don't care about errors in source here */
+  g_input_stream_close (in, cancellable, NULL);
+
+  /* But write errors on close are bad! */
+  if (!g_output_stream_close (out, cancellable, result ? error : NULL))
+    result = FALSE;
+
+  g_object_unref (in);
+  g_object_unref (out);
+
+  if (result == FALSE)
     return FALSE;
 
  copied_file:
-
   /* Ignore errors here. Failure to copy metadata is not a hard error */
   g_file_copy_attributes (source, destination,
                          flags, cancellable, NULL);
@@ -2835,19 +3445,20 @@ g_file_make_directory_with_parents (GFile         *file,
 
 /**
  * g_file_make_symbolic_link:
- * @file: input #GFile.
- * @symlink_value: a string with the value of the new symlink.
+ * @file: a #GFile with the name of the symlink to create
+ * @symlink_value: a string with the path for the target of the new symlink
  * @cancellable: optional #GCancellable object, %NULL to ignore.
- * @error: a #GError. 
- * 
- * Creates a symbolic link.
+ * @error: a #GError.
+ *
+ * Creates a symbolic link named @file which contains the string
+ * @symlink_value.
  *
  * If @cancellable is not %NULL, then the operation can be cancelled by
  * triggering the cancellable object from another thread. If the operation
- * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. 
- * 
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
  * Returns: %TRUE on the creation of a new symlink, %FALSE otherwise.
- **/
+ */
 gboolean
 g_file_make_symbolic_link (GFile         *file,
                           const char    *symlink_value,
@@ -3032,7 +3643,7 @@ g_file_set_display_name (GFile         *file,
  * 
  * Asynchronously sets the display name for a given #GFile.
  * 
- * For more details, see g_set_display_name() which is
+ * For more details, see g_file_set_display_name() which is
  * the synchronous version of this call.
  *
  * When the operation is finished, @callback will be called. You can then call
@@ -3708,6 +4319,8 @@ g_file_mount_mountable_finish (GFile         *file,
  *
  * When the operation is finished, @callback will be called. You can then call
  * g_file_unmount_mountable_finish() to get the result of the operation.
+ *
+ * Deprecated: 2.22: Use g_file_unmount_mountable_with_operation() instead.
  **/
 void
 g_file_unmount_mountable (GFile               *file,
@@ -3753,6 +4366,8 @@ g_file_unmount_mountable (GFile               *file,
  *
  * Returns: %TRUE if the operation finished successfully. %FALSE
  * otherwise.
+ *
+ * Deprecated: 2.22: Use g_file_unmount_mountable_with_operation_finish() instead.
  **/
 gboolean
 g_file_unmount_mountable_finish (GFile         *file,
@@ -3772,40 +4387,229 @@ g_file_unmount_mountable_finish (GFile         *file,
     }
   
   iface = G_FILE_GET_IFACE (file);
-  return (* iface->unmount_mountable_finish) (file, result, error);
+  return (* iface->unmount_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_unmount_mountable_with_operation:
+ * @file: input #GFile.
+ * @flags: flags affecting the operation
+ * @mount_operation: a #GMountOperation, or %NULL to avoid user interaction.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied, or %NULL.
+ * @user_data: the data to pass to callback function
+ *
+ * Unmounts a file of type G_FILE_TYPE_MOUNTABLE.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * g_file_unmount_mountable_finish() to get the result of the operation.
+ *
+ * Since: 2.22
+ **/
+void
+g_file_unmount_mountable_with_operation (GFile               *file,
+                                         GMountUnmountFlags   flags,
+                                         GMountOperation     *mount_operation,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback  callback,
+                                         gpointer             user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->unmount_mountable == NULL && iface->unmount_mountable_with_operation == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+      return;
+    }
+
+  if (iface->unmount_mountable_with_operation != NULL)
+    (* iface->unmount_mountable_with_operation) (file,
+                                                 flags,
+                                                 mount_operation,
+                                                 cancellable,
+                                                 callback,
+                                                 user_data);
+  else
+    (* iface->unmount_mountable) (file,
+                                  flags,
+                                  cancellable,
+                                  callback,
+                                  user_data);
+}
+
+/**
+ * g_file_unmount_mountable_with_operation_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an unmount operation, see g_file_unmount_mountable_with_operation() for details.
+ *
+ * Finish an asynchronous unmount operation that was started
+ * with g_file_unmount_mountable_with_operation().
+ *
+ * Returns: %TRUE if the operation finished successfully. %FALSE
+ * otherwise.
+ *
+ * Since: 2.22
+ **/
+gboolean
+g_file_unmount_mountable_with_operation_finish (GFile         *file,
+                                                GAsyncResult  *result,
+                                                GError       **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+
+  iface = G_FILE_GET_IFACE (file);
+  if (iface->unmount_mountable_with_operation_finish != NULL)
+    return (* iface->unmount_mountable_with_operation_finish) (file, result, error);
+  else
+    return (* iface->unmount_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_eject_mountable:
+ * @file: input #GFile.
+ * @flags: flags affecting the operation
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied, or %NULL.
+ * @user_data: the data to pass to callback function
+ * 
+ * Starts an asynchronous eject on a mountable.  
+ * When this operation has completed, @callback will be called with
+ * @user_user data, and the operation can be finalized with 
+ * g_file_eject_mountable_finish().
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. 
+ *
+ * Deprecated: 2.22: Use g_file_eject_mountable_with_operation() instead.
+ **/
+void
+g_file_eject_mountable (GFile               *file,
+                       GMountUnmountFlags   flags,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  
+  if (iface->eject_mountable == NULL) 
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+      return;
+    }
+  
+  (* iface->eject_mountable) (file,
+                             flags,
+                             cancellable,
+                             callback,
+                             user_data);
+}
+
+/**
+ * g_file_eject_mountable_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, or %NULL
+ * 
+ * Finishes an asynchronous eject operation started by 
+ * g_file_eject_mountable().
+ * 
+ * Returns: %TRUE if the @file was ejected successfully. %FALSE 
+ * otherwise.
+ *
+ * Deprecated: 2.22: Use g_file_eject_mountable_with_operation_finish() instead.
+ **/
+gboolean
+g_file_eject_mountable_finish (GFile         *file,
+                              GAsyncResult  *result,
+                              GError       **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->eject_mountable_finish) (file, result, error);
 }
 
 /**
- * g_file_eject_mountable:
+ * g_file_eject_mountable_with_operation:
  * @file: input #GFile.
  * @flags: flags affecting the operation
+ * @mount_operation: a #GMountOperation, or %NULL to avoid user interaction.
  * @cancellable: optional #GCancellable object, %NULL to ignore.
  * @callback: a #GAsyncReadyCallback to call when the request is satisfied, or %NULL.
  * @user_data: the data to pass to callback function
- * 
- * Starts an asynchronous eject on a mountable.  
+ *
+ * Starts an asynchronous eject on a mountable.
  * When this operation has completed, @callback will be called with
- * @user_user data, and the operation can be finalized with 
- * g_file_eject_mountable_finish().
- * 
+ * @user_user data, and the operation can be finalized with
+ * g_file_eject_mountable_with_operation_finish().
+ *
  * If @cancellable is not %NULL, then the operation can be cancelled by
  * triggering the cancellable object from another thread. If the operation
- * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. 
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * Since: 2.22
  **/
 void
-g_file_eject_mountable (GFile               *file,
-                       GMountUnmountFlags   flags,
-                       GCancellable        *cancellable,
-                       GAsyncReadyCallback  callback,
-                       gpointer             user_data)
+g_file_eject_mountable_with_operation (GFile               *file,
+                                       GMountUnmountFlags   flags,
+                                       GMountOperation     *mount_operation,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
 {
   GFileIface *iface;
 
   g_return_if_fail (G_IS_FILE (file));
 
   iface = G_FILE_GET_IFACE (file);
-  
-  if (iface->eject_mountable == NULL
+
+  if (iface->eject_mountable == NULL && iface->eject_mountable_with_operation == NULL)
     {
       g_simple_async_report_error_in_idle (G_OBJECT (file),
                                           callback,
@@ -3815,33 +4619,43 @@ g_file_eject_mountable (GFile               *file,
                                           _("Operation not supported"));
       return;
     }
-  
-  (* iface->eject_mountable) (file,
-                             flags,
-                             cancellable,
-                             callback,
-                             user_data);
+
+  if (iface->eject_mountable_with_operation != NULL)
+    (* iface->eject_mountable_with_operation) (file,
+                                               flags,
+                                               mount_operation,
+                                               cancellable,
+                                               callback,
+                                               user_data);
+  else
+    (* iface->eject_mountable) (file,
+                                flags,
+                                cancellable,
+                                callback,
+                                user_data);
 }
 
 /**
- * g_file_eject_mountable_finish:
+ * g_file_eject_mountable_with_operation_finish:
  * @file: input #GFile.
  * @result: a #GAsyncResult.
  * @error: a #GError, or %NULL
- * 
- * Finishes an asynchronous eject operation started by 
- * g_file_eject_mountable().
- * 
- * Returns: %TRUE if the @file was ejected successfully. %FALSE 
+ *
+ * Finishes an asynchronous eject operation started by
+ * g_file_eject_mountable_with_operation().
+ *
+ * Returns: %TRUE if the @file was ejected successfully. %FALSE
  * otherwise.
+ *
+ * Since: 2.22
  **/
 gboolean
-g_file_eject_mountable_finish (GFile         *file,
-                              GAsyncResult  *result,
-                              GError       **error)
+g_file_eject_mountable_with_operation_finish (GFile         *file,
+                                              GAsyncResult  *result,
+                                              GError       **error)
 {
   GFileIface *iface;
-  
+
   g_return_val_if_fail (G_IS_FILE (file), FALSE);
   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
 
@@ -3851,9 +4665,12 @@ g_file_eject_mountable_finish (GFile         *file,
       if (g_simple_async_result_propagate_error (simple, error))
        return FALSE;
     }
-  
+
   iface = G_FILE_GET_IFACE (file);
-  return (* iface->eject_mountable_finish) (file, result, error);
+  if (iface->eject_mountable_with_operation_finish != NULL)
+    return (* iface->eject_mountable_with_operation_finish) (file, result, error);
+  else
+    return (* iface->eject_mountable_finish) (file, result, error);
 }
 
 /**
@@ -4390,24 +5207,253 @@ g_file_real_create_finish (GFile         *file,
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
   gpointer op;
 
-  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_create_async);
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_create_async);
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+  
+  return NULL;
+}
+
+typedef struct {
+  GFileOutputStream *stream;
+  char *etag;
+  gboolean make_backup;
+  GFileCreateFlags flags;
+} ReplaceAsyncData;
+
+static void
+replace_async_data_free (ReplaceAsyncData *data)
+{
+  if (data->stream)
+    g_object_unref (data->stream);
+  g_free (data->etag);
+  g_free (data);
+}
+
+static void
+replace_async_thread (GSimpleAsyncResult *res,
+                     GObject            *object,
+                     GCancellable       *cancellable)
+{
+  GFileIface *iface;
+  GFileOutputStream *stream;
+  GError *error = NULL;
+  ReplaceAsyncData *data;
+
+  iface = G_FILE_GET_IFACE (object);
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+
+  stream = iface->replace (G_FILE (object),
+                          data->etag,
+                          data->make_backup,
+                          data->flags,
+                          cancellable,
+                          &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->stream = stream;
+}
+
+static void
+g_file_real_replace_async (GFile               *file,
+                          const char          *etag,
+                          gboolean             make_backup,
+                          GFileCreateFlags     flags,
+                          int                  io_priority,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data)
+{
+  GSimpleAsyncResult *res;
+  ReplaceAsyncData *data;
+
+  data = g_new0 (ReplaceAsyncData, 1);
+  data->etag = g_strdup (etag);
+  data->make_backup = make_backup;
+  data->flags = flags;
+
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_replace_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)replace_async_data_free);
+
+  g_simple_async_result_run_in_thread (res, replace_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileOutputStream *
+g_file_real_replace_finish (GFile         *file,
+                           GAsyncResult  *res,
+                           GError       **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  ReplaceAsyncData *data;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_replace_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->stream)
+    return g_object_ref (data->stream);
+  
+  return NULL;
+}
+
+static void
+open_readwrite_async_thread (GSimpleAsyncResult *res,
+                            GObject            *object,
+                            GCancellable       *cancellable)
+{
+  GFileIface *iface;
+  GFileIOStream *stream;
+  GError *error = NULL;
+
+  iface = G_FILE_GET_IFACE (object);
+
+  if (iface->open_readwrite == NULL)
+    {
+      g_set_error_literal (&error, G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           _("Operation not supported"));
+
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+
+      return;
+    }
+
+  stream = iface->open_readwrite (G_FILE (object), cancellable, &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+}
+
+static void
+g_file_real_open_readwrite_async (GFile               *file,
+                                 int                  io_priority,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GSimpleAsyncResult *res;
+
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_open_readwrite_async);
+
+  g_simple_async_result_run_in_thread (res, open_readwrite_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileIOStream *
+g_file_real_open_readwrite_finish (GFile         *file,
+                                  GAsyncResult  *res,
+                                  GError       **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_open_readwrite_async);
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+
+  return NULL;
+}
+
+static void
+create_readwrite_async_thread (GSimpleAsyncResult *res,
+                              GObject            *object,
+                              GCancellable       *cancellable)
+{
+  GFileIface *iface;
+  GFileCreateFlags *data;
+  GFileIOStream *stream;
+  GError *error = NULL;
+
+  iface = G_FILE_GET_IFACE (object);
+
+  data = g_simple_async_result_get_op_res_gpointer (res);
+
+  if (iface->create_readwrite == NULL)
+    {
+      g_set_error_literal (&error, G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           _("Operation not supported"));
+
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+
+      return;
+    }
+
+  stream = iface->create_readwrite (G_FILE (object), *data, cancellable, &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+}
+
+static void
+g_file_real_create_readwrite_async (GFile               *file,
+                                   GFileCreateFlags     flags,
+                                   int                  io_priority,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
+{
+  GFileCreateFlags *data;
+  GSimpleAsyncResult *res;
+
+  data = g_new0 (GFileCreateFlags, 1);
+  *data = flags;
+
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_create_readwrite_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)g_free);
+
+  g_simple_async_result_run_in_thread (res, create_readwrite_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileIOStream *
+g_file_real_create_readwrite_finish (GFile         *file,
+                                     GAsyncResult  *res,
+                                     GError       **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_create_readwrite_async);
 
   op = g_simple_async_result_get_op_res_gpointer (simple);
   if (op)
     return g_object_ref (op);
-  
+
   return NULL;
 }
 
 typedef struct {
-  GFileOutputStream *stream;
+  GFileIOStream *stream;
   char *etag;
   gboolean make_backup;
   GFileCreateFlags flags;
-} ReplaceAsyncData;
+} ReplaceRWAsyncData;
 
 static void
-replace_async_data_free (ReplaceAsyncData *data)
+replace_rw_async_data_free (ReplaceRWAsyncData *data)
 {
   if (data->stream)
     g_object_unref (data->stream);
@@ -4416,25 +5462,25 @@ replace_async_data_free (ReplaceAsyncData *data)
 }
 
 static void
-replace_async_thread (GSimpleAsyncResult *res,
-                     GObject            *object,
-                     GCancellable       *cancellable)
+replace_readwrite_async_thread (GSimpleAsyncResult *res,
+                               GObject            *object,
+                               GCancellable       *cancellable)
 {
   GFileIface *iface;
-  GFileOutputStream *stream;
+  GFileIOStream *stream;
   GError *error = NULL;
-  ReplaceAsyncData *data;
+  ReplaceRWAsyncData *data;
 
   iface = G_FILE_GET_IFACE (object);
-  
+
   data = g_simple_async_result_get_op_res_gpointer (res);
 
-  stream = iface->replace (G_FILE (object),
-                          data->etag,
-                          data->make_backup,
-                          data->flags,
-                          cancellable,
-                          &error);
+  stream = iface->replace_readwrite (G_FILE (object),
+                                    data->etag,
+                                    data->make_backup,
+                                    data->flags,
+                                    cancellable,
+                                    &error);
 
   if (stream == NULL)
     {
@@ -4446,44 +5492,44 @@ replace_async_thread (GSimpleAsyncResult *res,
 }
 
 static void
-g_file_real_replace_async (GFile               *file,
-                          const char          *etag,
-                          gboolean             make_backup,
-                          GFileCreateFlags     flags,
-                          int                  io_priority,
-                          GCancellable        *cancellable,
-                          GAsyncReadyCallback  callback,
-                          gpointer             user_data)
+g_file_real_replace_readwrite_async (GFile               *file,
+                                    const char          *etag,
+                                    gboolean             make_backup,
+                                    GFileCreateFlags     flags,
+                                    int                  io_priority,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
 {
   GSimpleAsyncResult *res;
-  ReplaceAsyncData *data;
+  ReplaceRWAsyncData *data;
 
-  data = g_new0 (ReplaceAsyncData, 1);
+  data = g_new0 (ReplaceRWAsyncData, 1);
   data->etag = g_strdup (etag);
   data->make_backup = make_backup;
   data->flags = flags;
 
-  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_replace_async);
-  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)replace_async_data_free);
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_replace_readwrite_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)replace_rw_async_data_free);
 
-  g_simple_async_result_run_in_thread (res, replace_async_thread, io_priority, cancellable);
+  g_simple_async_result_run_in_thread (res, replace_readwrite_async_thread, io_priority, cancellable);
   g_object_unref (res);
 }
 
-static GFileOutputStream *
-g_file_real_replace_finish (GFile         *file,
-                           GAsyncResult  *res,
-                           GError       **error)
+static GFileIOStream *
+g_file_real_replace_readwrite_finish (GFile         *file,
+                                      GAsyncResult  *res,
+                                      GError       **error)
 {
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
-  ReplaceAsyncData *data;
+  ReplaceRWAsyncData *data;
 
-  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_replace_async);
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_replace_readwrite_async);
 
   data = g_simple_async_result_get_op_res_gpointer (simple);
   if (data->stream)
     return g_object_ref (data->stream);
-  
+
   return NULL;
 }
 
@@ -5065,11 +6111,14 @@ g_file_query_default_handler (GFile                  *file,
   char *path;
   
   uri_scheme = g_file_get_uri_scheme (file);
-  appinfo = g_app_info_get_default_for_uri_scheme (uri_scheme);
-  g_free (uri_scheme);
+  if (uri_scheme && uri_scheme[0] != '\0')
+    {
+      appinfo = g_app_info_get_default_for_uri_scheme (uri_scheme);
+      g_free (uri_scheme);
 
-  if (appinfo != NULL)
-    return appinfo;
+      if (appinfo != NULL)
+        return appinfo;
+    }
 
   info = g_file_query_info (file,
                            G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
@@ -5887,5 +6936,296 @@ g_file_replace_contents_finish (GFile         *file,
   return TRUE;
 }
 
+/**
+ * g_file_start_mountable:
+ * @file: input #GFile.
+ * @flags: flags affecting the operation
+ * @start_operation: a #GMountOperation, or %NULL to avoid user interaction.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied, or %NULL.
+ * @user_data: the data to pass to callback function
+ *
+ * Starts a file of type G_FILE_TYPE_MOUNTABLE.
+ * Using @start_operation, you can request callbacks when, for instance,
+ * passwords are needed during authentication.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * g_file_mount_mountable_finish() to get the result of the operation.
+ *
+ * Since: 2.22
+ */
+void
+g_file_start_mountable (GFile                      *file,
+                        GDriveStartFlags            flags,
+                        GMountOperation            *start_operation,
+                        GCancellable               *cancellable,
+                        GAsyncReadyCallback         callback,
+                        gpointer                    user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->start_mountable == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+      return;
+    }
+
+  (* iface->start_mountable) (file,
+                             flags,
+                             start_operation,
+                             cancellable,
+                             callback,
+                             user_data);
+}
+
+/**
+ * g_file_start_mountable_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, or %NULL
+ *
+ * Finishes a start operation. See g_file_start_mountable() for details.
+ *
+ * Finish an asynchronous start operation that was started
+ * with g_file_start_mountable().
+ *
+ * Returns: %TRUE if the operation finished successfully. %FALSE
+ * otherwise.
+ *
+ * Since: 2.22
+ */
+gboolean
+g_file_start_mountable_finish (GFile                      *file,
+                               GAsyncResult               *result,
+                               GError                    **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->start_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_stop_mountable:
+ * @file: input #GFile.
+ * @flags: flags affecting the operation
+ * @mount_operation: a #GMountOperation, or %NULL to avoid user interaction.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied, or %NULL.
+ * @user_data: the data to pass to callback function
+ *
+ * Stops a file of type G_FILE_TYPE_MOUNTABLE.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * g_file_stop_mountable_finish() to get the result of the operation.
+ *
+ * Since: 2.22
+ */
+void
+g_file_stop_mountable (GFile                      *file,
+                       GMountUnmountFlags          flags,
+                       GMountOperation            *mount_operation,
+                       GCancellable               *cancellable,
+                       GAsyncReadyCallback         callback,
+                       gpointer                    user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->stop_mountable == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+      return;
+    }
+
+  (* iface->stop_mountable) (file,
+                             flags,
+                             mount_operation,
+                             cancellable,
+                             callback,
+                             user_data);
+}
+
+/**
+ * g_file_stop_mountable_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an stop operation, see g_file_stop_mountable() for details.
+ *
+ * Finish an asynchronous stop operation that was started
+ * with g_file_stop_mountable().
+ *
+ * Returns: %TRUE if the operation finished successfully. %FALSE
+ * otherwise.
+ *
+ * Since: 2.22
+ */
+gboolean
+g_file_stop_mountable_finish (GFile                      *file,
+                              GAsyncResult               *result,
+                              GError                    **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->stop_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_poll_mountable:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied, or %NULL.
+ * @user_data: the data to pass to callback function
+ *
+ * Polls a file of type G_FILE_TYPE_MOUNTABLE.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * g_file_mount_mountable_finish() to get the result of the operation.
+ *
+ * Since: 2.22
+ */
+void
+g_file_poll_mountable (GFile                      *file,
+                       GCancellable               *cancellable,
+                       GAsyncReadyCallback         callback,
+                       gpointer                    user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->poll_mountable == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+      return;
+    }
+
+  (* iface->poll_mountable) (file,
+                             cancellable,
+                             callback,
+                             user_data);
+}
+
+/**
+ * g_file_poll_mountable_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, or %NULL
+ *
+ * Finishes a poll operation. See g_file_poll_mountable() for details.
+ *
+ * Finish an asynchronous poll operation that was polled
+ * with g_file_poll_mountable().
+ *
+ * Returns: %TRUE if the operation finished successfully. %FALSE
+ * otherwise.
+ *
+ * Since: 2.22
+ */
+gboolean
+g_file_poll_mountable_finish (GFile                      *file,
+                              GAsyncResult               *result,
+                              GError                    **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->poll_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_supports_thread_contexts:
+ * @file: a #GFile.
+ *
+ * Checks if @file supports <link
+ * linkend="g-main-context-push-thread-default-context">thread-default
+ * contexts</link>. If this returns %FALSE, you cannot perform
+ * asynchronous operations on @file in a thread that has a
+ * thread-default context.
+ *
+ * Returns: Whether or not @file supports thread-default contexts.
+ *
+ * Since: 2.22
+ */
+gboolean
+g_file_supports_thread_contexts (GFile *file)
+{
+ GFileIface *iface;
+
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ iface = G_FILE_GET_IFACE (file);
+ return iface->supports_thread_contexts;
+}
+
 #define __G_FILE_C__
 #include "gioaliasdef.c"