summary refs log tree commit diff
path: root/libgsystem/gsystem-file-utils.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
committerMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
commit2c032d8f1c6292c1338a615e6ec40252889ba85c (patch)
tree1f77182220b2b0264288ba4a476ab47e5bc48716 /libgsystem/gsystem-file-utils.c
parent33491bc4279481db8ae47213e34a6d695a0e8830 (diff)
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'libgsystem/gsystem-file-utils.c')
-rw-r--r--libgsystem/gsystem-file-utils.c1644
1 files changed, 0 insertions, 1644 deletions
diff --git a/libgsystem/gsystem-file-utils.c b/libgsystem/gsystem-file-utils.c
deleted file mode 100644
index 02606035..00000000
--- a/libgsystem/gsystem-file-utils.c
+++ /dev/null
@@ -1,1644 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2012 William Jon McCann <mccann@redhat.com>
- * Copyright (C) 2012 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <string.h>
-
-#define _GSYSTEM_NO_LOCAL_ALLOC
-#include "libgsystem.h"
-#include "gsystem-glib-compat.h"
-#include <glib/gstdio.h>
-#include <gio/gunixinputstream.h>
-#include <gio/gfiledescriptorbased.h>
-#include <gio/gunixoutputstream.h>
-#include <glib-unix.h>
-#include <limits.h>
-#include <dirent.h>
-#ifdef GSYSTEM_CONFIG_XATTRS
-#include <attr/xattr.h>
-#endif
-
-static int
-close_nointr (int fd)
-{
-  int res;
-  /* Note this is NOT actually a retry loop.
-   * See: https://bugzilla.gnome.org/show_bug.cgi?id=682819
-   */
-  res = close (fd);
-  /* Just ignore EINTR...on Linux, retrying is wrong. */
-  if (res == EINTR)
-    res = 0;
-  return res;
-}
-
-static void
-close_nointr_noerror (int fd)
-{
-  (void) close_nointr (fd);
-}
-
-static int
-open_nointr (const char *path, int flags, mode_t mode)
-{
-  int res;
-  do
-    res = open (path, flags, mode);
-  while (G_UNLIKELY (res == -1 && errno == EINTR));
-  return res;
-}
-
-static inline void
-_set_error_from_errno (GError **error)
-{
-  int errsv = errno;
-  g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv),
-                       g_strerror (errsv));
-}
-
-/**
- * gs_file_openat_noatime:
- * @dfd: File descriptor for directory
- * @name: Pathname, relative to @dfd
- * @ret_fd: (out): Returned file descriptor
- * @cancellable: Cancellable
- * @error: Error
- *
- * Wrapper for openat() using %O_RDONLY with %O_NOATIME if available.
- */
-gboolean
-gs_file_openat_noatime (int            dfd,
-                        const char    *name,
-                        int           *ret_fd,
-                        GCancellable  *cancellable,
-                        GError       **error)
-{
-  int fd;
-
-#ifdef O_NOATIME
-  do
-    fd = openat (dfd, name, O_RDONLY | O_NOATIME, 0);
-  while (G_UNLIKELY (fd == -1 && errno == EINTR));
-  /* Only the owner or superuser may use O_NOATIME; so we may get
-   * EPERM.  EINVAL may happen if the kernel is really old...
-   */
-  if (fd == -1 && (errno == EPERM || errno == EINVAL))
-#endif
-    do
-      fd = openat (dfd, name, O_RDONLY, 0);
-    while (G_UNLIKELY (fd == -1 && errno == EINTR));
-  
-  if (fd == -1)
-    {
-      _set_error_from_errno (error);
-      return FALSE;
-    }
-  else
-    {
-      *ret_fd = fd;
-      return TRUE;
-    }
-}
-
-/**
- * gs_file_read_noatime:
- * @file: a #GFile
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Like g_file_read(), but try to avoid updating the file's
- * access time.  This should be used by background scanning
- * components such as search indexers, antivirus programs, etc.
- *
- * Returns: (transfer full): A new input stream, or %NULL on error
- */
-GInputStream *
-gs_file_read_noatime (GFile         *file,
-                      GCancellable  *cancellable,
-                      GError       **error)
-{
-  const char *path = NULL;
-  int fd;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return NULL;
-
-  path = gs_file_get_path_cached (file);
-  if (path == NULL)
-    {
-      char *uri;
-      uri = g_file_get_uri (file);
-      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
-                   "%s has no associated path", uri);
-      g_free (uri);
-      return NULL;
-    }
-
-  if (!gs_file_openat_noatime (AT_FDCWD, path, &fd, cancellable, error))
-    return NULL;
-
-  return g_unix_input_stream_new (fd, TRUE);
-}
-
-/**
- * gs_stream_fstat:
- * @stream: A stream containing a Unix file descriptor
- * @stbuf: Memory location to write stat buffer
- * @cancellable:
- * @error:
- *
- * Some streams created via libgsystem are #GUnixInputStream; these do
- * not support e.g. g_file_input_stream_query_info().  This function
- * allows dropping to the raw unix fstat() call for these types of
- * streams, while still conveniently wrapped with the normal GLib
- * handling of @cancellable and @error.
- */
-gboolean
-gs_stream_fstat (GFileDescriptorBased *stream,
-                 struct stat          *stbuf,
-                 GCancellable         *cancellable,
-                 GError              **error)
-{
-  gboolean ret = FALSE;
-  int fd;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    goto out;
-
-  fd = g_file_descriptor_based_get_fd (stream);
-
-  if (fstat (fd, stbuf) == -1)
-    {
-      _set_error_from_errno (error);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-/**
- * gs_file_map_noatime: (skip)
- * @file: a #GFile
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Like g_mapped_file_new(), but try to avoid updating the file's
- * access time.  This should be used by background scanning
- * components such as search indexers, antivirus programs, etc.
- *
- * Returns: (transfer full): A new mapped file, or %NULL on error
- */
-GMappedFile *
-gs_file_map_noatime (GFile         *file,
-                     GCancellable  *cancellable,
-                     GError       **error)
-{
-  const char *path;
-  int fd;
-  GMappedFile *ret;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return NULL;
-
-  path = gs_file_get_path_cached (file);
-  if (path == NULL)
-    return NULL;
-
-  if (!gs_file_openat_noatime (AT_FDCWD, path, &fd, cancellable, error))
-    return NULL;
-  
-  ret = g_mapped_file_new_from_fd (fd, FALSE, error);
-  close_nointr_noerror (fd); /* Ignore errors - we always want to close */
-
-  return ret;
-}
-
-#if GLIB_CHECK_VERSION(2,34,0)
-/**
- * gs_file_map_readonly:
- * @file: a #GFile
- * @cancellable:
- * @error:
- *
- * Return a #GBytes which references a readonly view of the contents of
- * @file.  This function uses #GMappedFile internally.
- *
- * Returns: (transfer full): a newly referenced #GBytes
- */
-GBytes *
-gs_file_map_readonly (GFile         *file,
-                      GCancellable  *cancellable,
-                      GError       **error)
-{
-  GMappedFile *mfile;
-  GBytes *ret;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return NULL;
-
-  mfile = g_mapped_file_new (gs_file_get_path_cached (file), FALSE, error);
-  if (!mfile)
-    return NULL;
-
-  ret = g_mapped_file_get_bytes (mfile);
-  g_mapped_file_unref (mfile);
-  return ret;
-}
-#endif
-
-/**
- * gs_file_sync_data:
- * @file: a #GFile
- * @cancellable:
- * @error:
- *
- * Wraps the UNIX fsync() function (or fdatasync(), if available), which
- * ensures that the data in @file is on non-volatile storage.
- */
-gboolean
-gs_file_sync_data (GFile          *file,
-                   GCancellable   *cancellable,
-                   GError        **error)
-{
-  gboolean ret = FALSE;
-  int res;
-  int fd = -1; 
-
-  if (!gs_file_openat_noatime (AT_FDCWD, gs_file_get_path_cached (file), &fd,
-                               cancellable, error))
-    goto out;
-
-  do
-    {
-#ifdef __linux
-      res = fdatasync (fd);
-#else
-      res = fsync (fd);
-#endif
-    }
-  while (G_UNLIKELY (res != 0 && errno == EINTR));
-  if (res != 0)
-    {
-      _set_error_from_errno (error);
-      goto out;
-    }
-
-  res = close_nointr (fd);
-  if (res != 0)
-    {
-      _set_error_from_errno (error);
-      goto out;
-    }
-  fd = -1;
-  
-  ret = TRUE;
- out:
-  if (fd != -1)
-    close_nointr_noerror (fd);
-  return ret;
-}
-
-/**
- * gs_file_create:
- * @file: Path to non-existent file
- * @mode: Unix access permissions
- * @out_stream: (out) (transfer full) (allow-none): Newly created output, or %NULL
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Like g_file_create(), except this function allows specifying the
- * access mode.  This allows atomically creating private files.
- */
-gboolean
-gs_file_create (GFile          *file,
-                int             mode,
-                GOutputStream **out_stream,
-                GCancellable   *cancellable,
-                GError        **error)
-{
-  gboolean ret = FALSE;
-  int fd;
-  GOutputStream *ret_stream = NULL;
-
-  fd = open_nointr (gs_file_get_path_cached (file), O_WRONLY | O_CREAT | O_EXCL, mode);
-  if (fd < 0)
-    {
-      _set_error_from_errno (error);
-      goto out;
-    }
-
-  if (fchmod (fd, mode) < 0)
-    {
-      close (fd);
-      _set_error_from_errno (error);
-      goto out;
-    }
-  
-  ret_stream = g_unix_output_stream_new (fd, TRUE);
-  
-  ret = TRUE;
-  gs_transfer_out_value (out_stream, &ret_stream);
- out:
-  g_clear_object (&ret_stream);
-  return ret;
-}
-
-static const char *
-get_default_tmp_prefix (void)
-{
-  static char *tmpprefix = NULL;
-
-  if (g_once_init_enter (&tmpprefix))
-    {
-      const char *prgname = g_get_prgname ();
-      const char *p;
-      char *prefix;
-      char *iter;
-
-      if (prgname)
-        {
-          p = strrchr (prgname, '/');
-          if (p)
-            prgname = p + 1;
-        }
-      else
-        prgname = "";
-          
-      prefix = g_strdup_printf ("tmp-%s%u-", prgname, getuid ());
-      for (iter = prefix; *iter; iter++)
-        {
-          char c = *iter;
-          if (c == ' ')
-            *iter = '_';
-        }
-      
-      g_once_init_leave (&tmpprefix, prefix);
-    }
-
-  return tmpprefix;
-}
-
-/**
- * gs_fileutil_gen_tmp_name:
- * @prefix: (allow-none): String prepended to the result
- * @suffix: (allow-none): String suffixed to the result
- *
- * Generate a name suitable for use as a temporary file.  This
- * function does no I/O; it is not guaranteed that a file with that
- * name does not exist.
- */
-char *
-gs_fileutil_gen_tmp_name (const char *prefix,
-                          const char *suffix)
-{
-  static const char table[] = "ABCEDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789";
-  GString *str = g_string_new ("");
-  guint i;
-
-  if (!prefix)
-    prefix = get_default_tmp_prefix ();
-  if (!suffix)
-    suffix = "tmp";
-
-  g_string_append (str, prefix);
-  for (i = 0; i < 8; i++)
-    {
-      int offset = g_random_int_range (0, sizeof (table) - 1);
-      g_string_append_c (str, (guint8)table[offset]);
-    }
-  g_string_append_c (str, '.');
-  g_string_append (str, suffix);
-
-  return g_string_free (str, FALSE);
-}
-
-/**
- * gs_file_open_dir_fd:
- * @path: Directory name
- * @out_fd: (out): File descriptor for directory
- * @cancellable: Cancellable
- * @error: Error
- *
- * On success, sets @out_fd to a file descriptor for the directory
- * that can be used with UNIX functions such as openat().
- */
-gboolean
-gs_file_open_dir_fd (GFile         *path,
-                     int           *out_fd,
-                     GCancellable  *cancellable,
-                     GError       **error)
-{
-  /* Linux specific probably */
-  *out_fd = open (gs_file_get_path_cached (path), O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC);
-  if (*out_fd == -1)
-    {
-      _set_error_from_errno (error);
-      return FALSE;
-    }
-  return TRUE;
-}
-
-/**
- * gs_file_open_in_tmpdir_at:
- * @tmpdir_fd: Directory to place temporary file
- * @mode: Default mode (will be affected by umask)
- * @out_name: (out) (transfer full): Newly created file name
- * @out_stream: (out) (transfer full) (allow-none): Newly created output stream
- * @cancellable:
- * @error:
- *
- * Like g_file_open_tmp(), except the file will be created in the
- * provided @tmpdir, and allows specification of the Unix @mode, which
- * means private files may be created.  Return values will be stored
- * in @out_name, and optionally @out_stream.
- */
-gboolean
-gs_file_open_in_tmpdir_at (int                tmpdir_fd,
-                           int                mode,
-                           char             **out_name,
-                           GOutputStream    **out_stream,
-                           GCancellable      *cancellable,
-                           GError           **error)
-{
-  gboolean ret = FALSE;
-  const int max_attempts = 128;
-  int i;
-  char *tmp_name = NULL;
-  int fd;
-
-  /* 128 attempts seems reasonable... */
-  for (i = 0; i < max_attempts; i++)
-    {
-      g_free (tmp_name);
-      tmp_name = gs_fileutil_gen_tmp_name (NULL, NULL);
-
-      do
-        fd = openat (tmpdir_fd, tmp_name, O_WRONLY | O_CREAT | O_EXCL, mode);
-      while (fd == -1 && errno == EINTR);
-      if (fd < 0 && errno != EEXIST)
-        {
-          _set_error_from_errno (error);
-          goto out;
-        }
-      else if (fd != -1)
-        break;
-    }
-  if (i == max_attempts)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Exhausted attempts to open temporary file");
-      goto out;
-    }
-
-  ret = TRUE;
-  gs_transfer_out_value (out_name, &tmp_name);
-  if (out_stream)
-    *out_stream = g_unix_output_stream_new (fd, TRUE);
-  else
-    (void) close (fd);
- out:
-  g_free (tmp_name);
-  return ret;
-}
-
-/**
- * gs_file_open_in_tmpdir:
- * @tmpdir: Directory to place temporary file
- * @mode: Default mode (will be affected by umask)
- * @out_file: (out) (transfer full): Newly created file path
- * @out_stream: (out) (transfer full) (allow-none): Newly created output stream
- * @cancellable:
- * @error:
- *
- * Like g_file_open_tmp(), except the file will be created in the
- * provided @tmpdir, and allows specification of the Unix @mode, which
- * means private files may be created.  Return values will be stored
- * in @out_file, and optionally @out_stream.
- */
-gboolean
-gs_file_open_in_tmpdir (GFile             *tmpdir,
-                        int                mode,
-                        GFile            **out_file,
-                        GOutputStream    **out_stream,
-                        GCancellable      *cancellable,
-                        GError           **error)
-{
-  gboolean ret = FALSE;
-  DIR *d = NULL;
-  int dfd = -1;
-  char *tmp_name = NULL;
-  GOutputStream *ret_stream = NULL;
-
-  d = opendir (gs_file_get_path_cached (tmpdir));
-  if (!d)
-    {
-      _set_error_from_errno (error);
-      goto out;
-    }
-  dfd = dirfd (d);
-
-  if (!gs_file_open_in_tmpdir_at (dfd, mode, &tmp_name,
-                                  out_stream ? &ret_stream : NULL,
-                                  cancellable, error))
-    goto out;
- 
-  ret = TRUE;
-  *out_file = g_file_get_child (tmpdir, tmp_name);
-  gs_transfer_out_value (out_stream, &ret_stream);
- out:
-  if (d) (void) closedir (d);
-  g_clear_object (&ret_stream);
-  g_free (tmp_name);
-  return ret;
-}
-
-static gboolean
-linkcopy_internal_attempt (GFile          *src,
-                          GFile          *dest,
-                          GFile          *dest_parent,
-                          GFileCopyFlags  flags,
-                          gboolean        sync_data,
-                          gboolean        enable_guestfs_fuse_workaround,
-                          gboolean       *out_try_again,
-                          GCancellable   *cancellable,
-                          GError        **error)
-{
-  gboolean ret = FALSE;
-  int res;
-  char *tmp_name = NULL;
-  GFile *tmp_dest = NULL;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    goto out;
-
-  tmp_name = gs_fileutil_gen_tmp_name (NULL, NULL);
-  tmp_dest = g_file_get_child (dest_parent, tmp_name);
-
-  res = link (gs_file_get_path_cached (src), gs_file_get_path_cached (tmp_dest));
-  if (res == -1)
-    {
-      if (errno == EEXIST)
-        {
-          /* Nothing, fall through */
-          *out_try_again = TRUE;
-          ret = TRUE;
-          goto out;
-        }
-      else if (errno == EXDEV || errno == EMLINK || errno == EPERM
-               || (enable_guestfs_fuse_workaround && errno == ENOENT))
-        {
-          if (!g_file_copy (src, tmp_dest, flags,
-                            cancellable, NULL, NULL, error))
-            goto out;
-        }
-      else
-        {
-          _set_error_from_errno (error);
-          goto out;
-        }
-    }
-      
-  if (sync_data)
-    {
-      /* Now, we need to fsync */
-      if (!gs_file_sync_data (tmp_dest, cancellable, error))
-        goto out;
-    }
-
-  if (!gs_file_rename (tmp_dest, dest, cancellable, error))
-    goto out;
-
-  ret = TRUE;
-  *out_try_again = FALSE;
- out:
-  g_clear_pointer (&tmp_name, g_free);
-  g_clear_object (&tmp_dest);
-  return ret;
-}
-
-static gboolean
-linkcopy_internal (GFile          *src,
-                   GFile          *dest,
-                   GFileCopyFlags  flags,
-                   gboolean        sync_data,
-                   GCancellable   *cancellable,
-                   GError        **error)
-{
-  gboolean ret = FALSE;
-  gboolean dest_exists;
-  int i;
-  gboolean enable_guestfs_fuse_workaround;
-  struct stat src_stat;
-  struct stat dest_stat;
-  GFile *dest_parent = NULL;
-
-  flags |= G_FILE_COPY_NOFOLLOW_SYMLINKS;
-
-  g_return_val_if_fail ((flags & (G_FILE_COPY_BACKUP | G_FILE_COPY_TARGET_DEFAULT_PERMS)) == 0, FALSE);
-
-  dest_parent = g_file_get_parent (dest);
-
-  if (lstat (gs_file_get_path_cached (src), &src_stat) == -1)
-    {
-      int errsv = errno;
-      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
-                           g_strerror (errsv));
-      goto out;
-    }
-
-  if (lstat (gs_file_get_path_cached (dest), &dest_stat) == -1)
-    dest_exists = FALSE;
-  else
-    dest_exists = TRUE;
-  
-  if (((flags & G_FILE_COPY_OVERWRITE) == 0) && dest_exists)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
-                           "File exists");
-      goto out;
-    }
-
-  /* Work around the behavior of link() where it's a no-op if src and
-   * dest are the same.
-   */
-  if (dest_exists &&
-      src_stat.st_dev == dest_stat.st_dev &&
-      src_stat.st_ino == dest_stat.st_ino)
-    {
-      ret = TRUE;
-      goto out;
-    }
-
-  enable_guestfs_fuse_workaround = getenv ("LIBGSYSTEM_ENABLE_GUESTFS_FUSE_WORKAROUND") != NULL;
-
-  /* 128 attempts seems reasonable... */
-  for (i = 0; i < 128; i++)
-    {
-      gboolean tryagain = FALSE;
-
-      if (!linkcopy_internal_attempt (src, dest, dest_parent,
-                                      flags, sync_data,
-                                      enable_guestfs_fuse_workaround,
-                                      &tryagain,
-                                      cancellable, error))
-        goto out;
-
-      if (!tryagain)
-        break;
-    }
-
-  ret = TRUE;
- out:
-  g_clear_object (&dest_parent);
-  return ret;
-
-}
-
-/**
- * gs_file_linkcopy:
- * @src: Source file
- * @dest: Destination file
- * @flags: flags
- * @cancellable:
- * @error:
- *
- * First tries to use the UNIX link() call, but if the files are on
- * separate devices, fall back to copying via g_file_copy().
- *
- * The given @flags have different semantics than those documented
- * when hardlinking is used.  Specifically, both
- * #G_FILE_COPY_TARGET_DEFAULT_PERMS and #G_FILE_COPY_BACKUP are not
- * supported.  #G_FILE_COPY_NOFOLLOW_SYMLINKS treated as if it was
- * always given - if you want to follow symbolic links, you will need
- * to resolve them manually.
- *
- * Beware - do not use this function if @src may be modified, and it's
- * undesirable for the changes to also be reflected in @dest.  The
- * best use of this function is in the case where @src and @dest are
- * read-only, or where @src is a temporary file, and you want to put
- * it in the final place.
- */
-gboolean
-gs_file_linkcopy (GFile          *src,
-                  GFile          *dest,
-                  GFileCopyFlags  flags,
-                  GCancellable   *cancellable,
-                  GError        **error)
-{
-  return linkcopy_internal (src, dest, flags, FALSE, cancellable, error);
-}
-
-/**
- * gs_file_linkcopy_sync_data:
- * @src: Source file
- * @dest: Destination file
- * @flags: flags
- * @cancellable:
- * @error:
- *
- * This function is similar to gs_file_linkcopy(), except it also uses
- * gs_file_sync_data() to ensure that @dest is in stable storage
- * before it is moved into place.
- */
-gboolean
-gs_file_linkcopy_sync_data (GFile          *src,
-                            GFile          *dest,
-                            GFileCopyFlags  flags,
-                            GCancellable   *cancellable,
-                            GError        **error)
-{
-  return linkcopy_internal (src, dest, flags, TRUE, cancellable, error);
-}
-
-static char *
-gs_file_get_target_path (GFile *file)
-{
-  GFileInfo *info;
-  const char *target;
-  char *path;
-
-  info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NONE, NULL, NULL);
-  if (info == NULL)
-    return NULL;
-  target = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
-  path = g_filename_from_uri (target, NULL, NULL);
-  g_object_unref (info);
-
-  return path;
-}
-
-G_LOCK_DEFINE_STATIC (pathname_cache);
-
-/**
- * gs_file_get_path_cached:
- *
- * Like g_file_get_path(), but returns a constant copy so callers
- * don't need to free the result.
- */
-const char *
-gs_file_get_path_cached (GFile *file)
-{
-  const char *path;
-  static GQuark _file_path_quark = 0;
-
-  if (G_UNLIKELY (_file_path_quark) == 0)
-    _file_path_quark = g_quark_from_static_string ("gsystem-file-path");
-
-  G_LOCK (pathname_cache);
-
-  path = g_object_get_qdata ((GObject*)file, _file_path_quark);
-  if (!path)
-    {
-      if (g_file_has_uri_scheme (file, "trash") ||
-          g_file_has_uri_scheme (file, "recent"))
-        path = gs_file_get_target_path (file);
-      else
-        path = g_file_get_path (file);
-      if (path == NULL)
-        {
-          G_UNLOCK (pathname_cache);
-          return NULL;
-        }
-      g_object_set_qdata_full ((GObject*)file, _file_path_quark, (char*)path, (GDestroyNotify)g_free);
-    }
-
-  G_UNLOCK (pathname_cache);
-
-  return path;
-}
-
-/**
- * gs_file_get_basename_cached:
- *
- * Like g_file_get_basename(), but returns a constant copy so callers
- * don't need to free the result.
- */
-const char *
-gs_file_get_basename_cached (GFile *file)
-{
-  const char *name;
-  static GQuark _file_name_quark = 0;
-
-  if (G_UNLIKELY (_file_name_quark) == 0)
-    _file_name_quark = g_quark_from_static_string ("gsystem-file-name");
-
-  G_LOCK (pathname_cache);
-
-  name = g_object_get_qdata ((GObject*)file, _file_name_quark);
-  if (!name)
-    {
-      name = g_file_get_basename (file);
-      g_object_set_qdata_full ((GObject*)file, _file_name_quark, (char*)name, (GDestroyNotify)g_free);
-    }
-
-  G_UNLOCK (pathname_cache);
-
-  return name;
-}
-
-/**
- * gs_file_enumerator_iterate:
- * @direnum: an open #GFileEnumerator
- * @out_info: (out) (transfer none) (allow-none): Output location for the next #GFileInfo
- * @out_child: (out) (transfer none) (allow-none): Output location for the next #GFile, or %NULL
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * This is a version of g_file_enumerator_next_file() that's easier to
- * use correctly from C programs.  With g_file_enumerator_next_file(),
- * the gboolean return value signifies "end of iteration or error", which
- * requires allocation of a temporary #GError.
- *
- * In contrast, with this function, a %FALSE return from
- * gs_file_enumerator_iterate() <emphasis>always</emphasis> means
- * "error".  End of iteration is signaled by @out_info being %NULL.
- *
- * Another crucial difference is that the references for @out_info and
- * @out_child are owned by @direnum (they are cached as hidden
- * properties).  You must not unref them in your own code.  This makes
- * memory management significantly easier for C code in combination
- * with loops.
- *
- * Finally, this function optionally allows retrieving a #GFile as
- * well.
- *
- * The code pattern for correctly using gs_file_enumerator_iterate() from C
- * is:
- *
- * |[
- * direnum = g_file_enumerate_children (file, ...);
- * while (TRUE)
- *   {
- *     GFileInfo *info;
- *     if (!gs_file_enumerator_iterate (direnum, &info, NULL, cancellable, error))
- *       goto out;
- *     if (!info)
- *       break;
- *     ... do stuff with "info"; do not unref it! ...
- *   }
- * 
- * out:
- *   g_object_unref (direnum); // Note: frees the last @info
- * ]|
- */
-gboolean
-gs_file_enumerator_iterate (GFileEnumerator  *direnum,
-                            GFileInfo       **out_info,
-                            GFile           **out_child,
-                            GCancellable     *cancellable,
-                            GError          **error)
-{
-  gboolean ret = FALSE;
-  GError *temp_error = NULL;
-
-  static GQuark cached_info_quark;
-  static GQuark cached_child_quark;
-  static gsize quarks_initialized;
-
-  g_return_val_if_fail (direnum != NULL, FALSE);
-  g_return_val_if_fail (out_info != NULL, FALSE);
-
-  if (g_once_init_enter (&quarks_initialized))
-    {
-      cached_info_quark = g_quark_from_static_string ("gsystem-cached-info");
-      cached_child_quark = g_quark_from_static_string ("gsystem-cached-child");
-      g_once_init_leave (&quarks_initialized, 1);
-    }
-
-  
-  *out_info = g_file_enumerator_next_file (direnum, cancellable, &temp_error);
-  if (out_child)
-    *out_child = NULL;
-  if (temp_error != NULL)
-    {
-      g_propagate_error (error, temp_error);
-      goto out;
-    }
-  else if (*out_info != NULL)
-    {
-      g_object_set_qdata_full ((GObject*)direnum, cached_info_quark, *out_info, (GDestroyNotify)g_object_unref);
-      if (out_child != NULL)
-        {
-          const char *name = g_file_info_get_name (*out_info);
-          *out_child = g_file_get_child (g_file_enumerator_get_container (direnum), name);
-          g_object_set_qdata_full ((GObject*)direnum, cached_child_quark, *out_child, (GDestroyNotify)g_object_unref);
-        }
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-/**
- * gs_file_rename:
- * @from: Current path
- * @to: New path
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * This function wraps the raw Unix function rename().
- *
- * Returns: %TRUE on success, %FALSE on error
- */
-gboolean
-gs_file_rename (GFile          *from,
-                GFile          *to,
-                GCancellable   *cancellable,
-                GError        **error)
-{
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  if (rename (gs_file_get_path_cached (from),
-              gs_file_get_path_cached (to)) < 0)
-    {
-      _set_error_from_errno (error);
-      return FALSE;
-    }
-  return TRUE;
-}
-
-/**
- * gs_file_unlink:
- * @path: Path to file
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Like g_file_delete(), except this function does not follow Unix
- * symbolic links, and will delete a symbolic link even if it's
- * pointing to a nonexistent file.  In other words, this function
- * merely wraps the raw Unix function unlink().
- *
- * Returns: %TRUE on success, %FALSE on error
- */
-gboolean
-gs_file_unlink (GFile          *path,
-                GCancellable   *cancellable,
-                GError        **error)
-{
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  if (unlink (gs_file_get_path_cached (path)) < 0)
-    {
-      _set_error_from_errno (error);
-      return FALSE;
-    }
-  return TRUE;
-}
-
-static gboolean
-chown_internal (GFile          *path,
-                gboolean        dereference_links,
-                guint32         owner,
-                guint32         group,
-                GCancellable   *cancellable,
-                GError        **error)
-{
-  gboolean ret = FALSE;
-  int res;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  do
-    if (dereference_links)
-      res = chown (gs_file_get_path_cached (path), owner, group);
-    else
-      res = lchown (gs_file_get_path_cached (path), owner, group);
-  while (G_UNLIKELY (res != 0 && errno == EINTR));
-
-  if (res < 0)
-    {
-      _set_error_from_errno (error);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-/**
- * gs_file_chown:
- * @path: Path to file
- * @owner: UNIX owner
- * @group: UNIX group
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Merely wraps UNIX chown().
- *
- * Returns: %TRUE on success, %FALSE on error
- */
-gboolean
-gs_file_chown (GFile          *path,
-               guint32         owner,
-               guint32         group,
-               GCancellable   *cancellable,
-               GError        **error)
-{
-  return chown_internal (path, TRUE, owner, group, cancellable, error);
-}
-
-/**
- * gs_file_lchown:
- * @path: Path to file
- * @owner: UNIX owner
- * @group: UNIX group
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Merely wraps UNIX lchown().
- *
- * Returns: %TRUE on success, %FALSE on error
- */
-gboolean
-gs_file_lchown (GFile          *path,
-                guint32         owner,
-                guint32         group,
-                GCancellable   *cancellable,
-                GError        **error)
-{
-  return chown_internal (path, FALSE, owner, group, cancellable, error);
-}
-
-/**
- * gs_file_chmod:
- * @path: Path to file
- * @mode: UNIX mode
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Merely wraps UNIX chmod().
- *
- * Returns: %TRUE on success, %FALSE on error
- */
-gboolean
-gs_file_chmod (GFile          *path,
-               guint           mode,
-               GCancellable   *cancellable,
-               GError        **error)
-{
-  gboolean ret = FALSE;
-  int res;
-
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  do
-    res = chmod (gs_file_get_path_cached (path), mode);
-  while (G_UNLIKELY (res != 0 && errno == EINTR));
-
-  if (res < 0)
-    {
-      _set_error_from_errno (error);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-/**
- * gs_file_ensure_directory:
- * @dir: Path to create as directory
- * @with_parents: Also create parent directories
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Like g_file_make_directory(), except does not throw an error if the
- * directory already exists.
- */
-gboolean
-gs_file_ensure_directory (GFile         *dir,
-                          gboolean       with_parents, 
-                          GCancellable  *cancellable,
-                          GError       **error)
-{
-  gboolean ret = FALSE;
-  GError *temp_error = NULL;
-  GFile *parent = NULL;
-
-  if (!g_file_make_directory (dir, cancellable, &temp_error))
-    {
-      if (with_parents &&
-          g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-        {
-          g_clear_error (&temp_error);
-
-          parent = g_file_get_parent (dir);
-          if (parent)
-            {
-              if (!gs_file_ensure_directory (parent, TRUE, cancellable, error))
-                goto out;
-            }
-          if (!gs_file_ensure_directory (dir, FALSE, cancellable, error))
-            goto out;
-        }
-      else if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-      else
-        g_clear_error (&temp_error);
-    }
-
-  ret = TRUE;
- out:
-  g_clear_object (&parent);
-  return ret;
-}
-
-/**
- * gs_file_ensure_directory_mode:
- * @dir: Path to create as directory
- * @mode: Create directory with these permissions
- * @cancellable: a #GCancellable
- * @error: a #GError
- *
- * Wraps UNIX mkdir() function with support for @cancellable, and
- * uses @error instead of errno.
- */
-gboolean
-gs_file_ensure_directory_mode (GFile         *dir,
-                               guint          mode,
-                               GCancellable  *cancellable,
-                               GError       **error)
-{
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  if (mkdir (gs_file_get_path_cached (dir), mode) == -1 && errno != EEXIST)
-    {
-      _set_error_from_errno (error);
-      return FALSE;
-    }
-  return TRUE;
-}
-
-/**
- * gs_file_load_contents_utf8:
- * @file: Path to file whose contents must be UTF-8
- * @cancellable:
- * @error:
- *
- * Like g_file_load_contents(), except validates the contents are
- * UTF-8.
- */
-gchar *
-gs_file_load_contents_utf8 (GFile         *file,
-                            GCancellable  *cancellable,
-                            GError       **error)
-{
-  gboolean ret = FALSE;
-  gsize len;
-  char *ret_contents = NULL;
-
-  if (!g_file_load_contents (file, cancellable, &ret_contents, &len,
-                             NULL, error))
-    goto out;
-  if (!g_utf8_validate (ret_contents, len, NULL))
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVALID_DATA,
-                   "Invalid UTF-8");
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  if (!ret)
-    {
-      g_free (ret_contents);
-      return NULL;
-    }
-  return ret_contents;
-}
-
-static int
-path_common_directory (char *one,
-                       char *two)
-{
-  int dir_index = 0;
-  int i = 0;
-
-  while (*one && *two)
-    {
-      if (*one != *two)
-        break;
-      if (*one == '/')
-        dir_index = i + 1;
-
-      one++;
-      two++;
-      i++;
-    }
-
-  return dir_index;
-}
-
-/**
- * gs_file_get_relpath:
- * @one: The first #GFile
- * @two: The second #GFile
- *
- * Like gs_file_get_relative_path(), but does not mandate that
- * the two files have any parent in common. This function will
- * instead insert "../" where appropriate.
- *
- * Returns: (transfer full): The relative path between the two.
- */
-gchar *
-gs_file_get_relpath (GFile *one,
-                     GFile *two)
-{
-  gchar *simple_path;
-  gchar *one_path, *one_suffix;
-  gchar *two_path, *two_suffix;
-  GString *path;
-  int i;
-
-  simple_path = g_file_get_relative_path (one, two);
-  if (simple_path)
-    return simple_path;
-
-  one_path = g_file_get_path (one);
-  two_path = g_file_get_path (two);
-
-  i = path_common_directory (one_path, two_path);
-  one_suffix = one_path + i;
-  two_suffix = two_path + i;
-
-  path = g_string_new ("");
-
-  /* For every leftover path segment one has, append "../" so
-   * that we reach the same directory. */
-  while (*one_suffix)
-    {
-      g_string_append (path, "../");
-      one_suffix = strchr (one_suffix, '/');
-      if (one_suffix == NULL)
-        break;
-      one_suffix++;
-    }
-
-  /* And now append the leftover stuff on two's side. */
-  g_string_append (path, two_suffix);
-
-  g_free (one_path);
-  g_free (two_path);
-
-  return g_string_free (path, FALSE);
-}
-
-/**
- * gs_file_realpath:
- * @file: A #GFile
- *
- * Return a #GFile that contains the same path with symlinks
- * followed. That is, it's a #GFile whose path is the result
- * of calling realpath() on @file.
- *
- * Returns: (allow-none) (transfer full): A new #GFile or %NULL if @file is invalid
- */
-GFile *
-gs_file_realpath (GFile *file)
-{
-  gchar *path;
-  gchar path_real[PATH_MAX];
-
-  path = g_file_get_path (file);
-
-  if (realpath ((const char *) path, path_real) == NULL)
-    {
-      g_free (path);
-      return NULL;
-    }
-
-  g_free (path);
-  return g_file_new_for_path (path_real);
-}
-
-#ifdef GSYSTEM_CONFIG_XATTRS
-static char *
-canonicalize_xattrs (char    *xattr_string,
-                     size_t   len)
-{
-  char *p;
-  GSList *xattrs = NULL;
-  GSList *iter;
-  GString *result;
-
-  result = g_string_new (0);
-
-  p = xattr_string;
-  while (p < xattr_string+len)
-    {
-      xattrs = g_slist_prepend (xattrs, p);
-      p += strlen (p) + 1;
-    }
-
-  xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
-  for (iter = xattrs; iter; iter = iter->next) {
-    g_string_append (result, iter->data);
-    g_string_append_c (result, '\0');
-  }
-
-  g_slist_free (xattrs);
-  return g_string_free (result, FALSE);
-}
-
-static GVariant *
-variant_new_ay_bytes (GBytes *bytes)
-{
-  gsize size;
-  gconstpointer data;
-  data = g_bytes_get_data (bytes, &size);
-  g_bytes_ref (bytes);
-  return g_variant_new_from_data (G_VARIANT_TYPE ("ay"), data, size,
-                                  TRUE, (GDestroyNotify)g_bytes_unref, bytes);
-}
-
-static gboolean
-read_xattr_name_array (const char *path,
-                       const char *xattrs,
-                       size_t      len,
-                       GVariantBuilder *builder,
-                       GError  **error)
-{
-  gboolean ret = FALSE;
-  const char *p;
-
-  p = xattrs;
-  while (p < xattrs+len)
-    {
-      ssize_t bytes_read;
-      char *buf;
-      GBytes *bytes = NULL;
-
-      bytes_read = lgetxattr (path, p, NULL, 0);
-      if (bytes_read < 0)
-        {
-          _set_error_from_errno (error);
-          g_prefix_error (error, "lgetxattr (%s, %s) failed: ", path, p);
-          goto out;
-        }
-      if (bytes_read == 0)
-        continue;
-
-      buf = g_malloc (bytes_read);
-      bytes = g_bytes_new_take (buf, bytes_read);
-      if (lgetxattr (path, p, buf, bytes_read) < 0)
-        {
-          g_bytes_unref (bytes);
-          _set_error_from_errno (error);
-          g_prefix_error (error, "lgetxattr (%s, %s) failed: ", path, p);
-          goto out;
-        }
-      
-      g_variant_builder_add (builder, "(@ay@ay)",
-                             g_variant_new_bytestring (p),
-                             variant_new_ay_bytes (bytes));
-
-      p = p + strlen (p) + 1;
-      g_bytes_unref (bytes);
-    }
-  
-  ret = TRUE;
- out:
-  return ret;
-}
-#endif
-
-static gboolean
-get_xattrs_impl (GFile          *f,
-                 GVariantBuilder *builder,
-                 GCancellable   *cancellable,
-                 GError        **error)
-{
-#ifdef GSYSTEM_CONFIG_XATTRS
-  gboolean ret = FALSE;
-  const char *path;
-  ssize_t bytes_read;
-  char *xattr_names = NULL;
-  char *xattr_names_canonical = NULL;
-
-  path = gs_file_get_path_cached (f);
-
-  bytes_read = llistxattr (path, NULL, 0);
-
-  if (bytes_read < 0)
-    {
-      if (errno != ENOTSUP)
-        {
-          _set_error_from_errno (error);
-          g_prefix_error (error, "llistxattr (%s) failed: ", path);
-          goto out;
-        }
-    }
-  else if (bytes_read > 0)
-    {
-      xattr_names = g_malloc (bytes_read);
-      if (llistxattr (path, xattr_names, bytes_read) < 0)
-        {
-          _set_error_from_errno (error);
-          g_prefix_error (error, "llistxattr (%s) failed: ", path);
-          goto out;
-        }
-      xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
-      
-      if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, builder, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  g_clear_pointer (&xattr_names, g_free);
-  g_clear_pointer (&xattr_names_canonical, g_free);
-  return ret;
-#else
-  return TRUE;
-#endif
-}
-
-/**
- * gs_file_get_all_xattrs:
- * @f: a #GFile
- * @out_xattrs: (out): A new #GVariant containing the extended attributes
- * @cancellable: Cancellable
- * @error: Error
- *
- * Read all extended attributes of @f in a canonical sorted order, and
- * set @out_xattrs with the result.
- *
- * If the filesystem does not support extended attributes, @out_xattrs
- * will have 0 elements, and this function will return successfully.
- */
-gboolean
-gs_file_get_all_xattrs (GFile         *f,
-                        GVariant     **out_xattrs,
-                        GCancellable  *cancellable,
-                        GError       **error)
-{
-  gboolean ret = FALSE;
-  GVariantBuilder builder;
-  gboolean builder_initialized = FALSE;
-  GVariant *ret_xattrs = NULL;
-
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
-  builder_initialized = TRUE;
-
-  if (!get_xattrs_impl (f, &builder,
-                        cancellable, error))
-    goto out;
-
-  ret_xattrs = g_variant_builder_end (&builder);
-  builder_initialized = FALSE;
-  g_variant_ref_sink (ret_xattrs);
-  
-  ret = TRUE;
-  gs_transfer_out_value (out_xattrs, &ret_xattrs);
- out:
-  g_clear_pointer (&ret_xattrs, g_variant_unref);
-  if (!builder_initialized)
-    g_variant_builder_clear (&builder);
-  return ret;
-}
-
-/**
- * gs_fd_set_all_xattrs:
- * @fd: File descriptor
- * @xattrs: Extended attributes
- * @cancellable: Cancellable
- * @error: Error
- *
- * For each attribute in @xattrs, set its value on the file or
- * directory referred to by @fd.  This function does not remove any
- * attributes not in @xattrs.
- */
-gboolean
-gs_fd_set_all_xattrs (int            fd,
-                      GVariant      *xattrs,
-                      GCancellable  *cancellable,
-                      GError       **error)
-{
-#ifdef GSYSTEM_CONFIG_XATTRS
-  gboolean ret = FALSE;
-  int i, n;
-
-  n = g_variant_n_children (xattrs);
-  for (i = 0; i < n; i++)
-    {
-      const guint8* name;
-      const guint8* value_data;
-      GVariant *value = NULL;
-      gsize value_len;
-      int res;
-
-      g_variant_get_child (xattrs, i, "(^&ay@ay)",
-                           &name, &value);
-      value_data = g_variant_get_fixed_array (value, &value_len, 1);
-      
-      do
-        res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0);
-      while (G_UNLIKELY (res == -1 && errno == EINTR));
-      g_variant_unref (value);
-      if (G_UNLIKELY (res == -1))
-        {
-          _set_error_from_errno (error);
-          goto out;
-        }
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-#else
-  return TRUE;
-#endif
-}
-
-/**
- * gs_file_set_all_xattrs:
- * @file: File descriptor
- * @xattrs: Extended attributes
- * @cancellable: Cancellable
- * @error: Error
- *
- * For each attribute in @xattrs, set its value on the file or
- * directory referred to by @file.  This function does not remove any
- * attributes not in @xattrs.
- */
-gboolean
-gs_file_set_all_xattrs (GFile         *file,
-                        GVariant      *xattrs,
-                        GCancellable  *cancellable,
-                        GError       **error)
-{
-#ifdef GSYSTEM_CONFIG_XATTRS
-  gboolean ret = FALSE;
-  const char *path;
-  int i, n;
-
-  path = gs_file_get_path_cached (file);
-
-  n = g_variant_n_children (xattrs);
-  for (i = 0; i < n; i++)
-    {
-      const guint8* name;
-      GVariant *value;
-      const guint8* value_data;
-      gsize value_len;
-      gboolean loop_err;
-
-      g_variant_get_child (xattrs, i, "(^&ay@ay)",
-                           &name, &value);
-      value_data = g_variant_get_fixed_array (value, &value_len, 1);
-      
-      loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0;
-      g_clear_pointer (&value, (GDestroyNotify) g_variant_unref);
-      if (loop_err)
-        {
-          _set_error_from_errno (error);
-          g_prefix_error (error, "lsetxattr (%s, %s) failed: ", path, name);
-          goto out;
-        }
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-#else
-  return TRUE;
-#endif
-}