[libgsystem] shutil: New submodule for high-level I/O operations



commit 82c4b585f374d094c9f5a5428585f879e7397b3d
Author: Colin Walters <walters verbum org>
Date:   Thu Nov 29 17:10:31 2012 -0500

    shutil: New submodule for high-level I/O operations
    
    Imported from ostree.

 Makefile-libgsystem.am |    2 +
 gsystem-shutil.c       |  239 ++++++++++++++++++++++++++++++++++++++++++++++++
 gsystem-shutil.h       |   47 ++++++++++
 libgsystem.h           |    1 +
 4 files changed, 289 insertions(+), 0 deletions(-)
---
diff --git a/Makefile-libgsystem.am b/Makefile-libgsystem.am
index 0f6e72f..6d368cc 100644
--- a/Makefile-libgsystem.am
+++ b/Makefile-libgsystem.am
@@ -24,6 +24,8 @@ libgsystem_la_SOURCES = \
 	$(libgsystem_srcpath)/gsystem-local-alloc.c \
 	$(libgsystem_srcpath)/gsystem-file-utils.h \
 	$(libgsystem_srcpath)/gsystem-file-utils.c \
+	$(libgsystem_srcpath)/gsystem-shutil.h \
+	$(libgsystem_srcpath)/gsystem-shutil.c \
 	$(libgsystem_srcpath)/libgsystem.h \
 	$(NULL)
 
diff --git a/gsystem-shutil.c b/gsystem-shutil.c
new file mode 100644
index 0000000..44904a7
--- /dev/null
+++ b/gsystem-shutil.c
@@ -0,0 +1,239 @@
+/* -*- 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 "libgsystem.h"
+#include <glib-unix.h>
+#include <sys/stat.h>
+
+static gboolean
+cp_internal (GFile         *src,
+             GFile         *dest,
+             gboolean       use_hardlinks,
+             GCancellable  *cancellable,
+             GError       **error)
+{
+  gboolean ret = FALSE;
+  gs_lobj GFileEnumerator *enumerator = NULL;
+  gs_lobj GFileInfo *file_info = NULL;
+  GError *temp_error = NULL;
+
+  enumerator = g_file_enumerate_children (src, "standard::type,standard::name",
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          cancellable, error);
+  if (!enumerator)
+    goto out;
+
+  if (!gs_file_ensure_directory (dest, FALSE, cancellable, error))
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, cancellable, &temp_error)) != NULL)
+    {
+      const char *name = g_file_info_get_name (file_info);
+      gs_lobj GFile *src_child = g_file_get_child (src, name);
+      gs_lobj GFile *dest_child = g_file_get_child (dest, name);
+
+      if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
+        {
+          if (!gs_file_ensure_directory (dest_child, FALSE, cancellable, error))
+            goto out;
+
+          /* Can't do this even though we'd like to; it fails with an error about
+           * setting standard::type not being supported =/
+           *
+           if (!g_file_set_attributes_from_info (dest_child, file_info, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+           cancellable, error))
+           goto out;
+          */
+          if (chmod (gs_file_get_path_cached (dest_child),
+                     g_file_info_get_attribute_uint32 (file_info, "unix::mode")) == -1)
+            {
+              int errsv = errno;
+              g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+                                   g_strerror (errsv));
+              goto out;
+            }
+
+          if (!cp_internal (src_child, dest_child, use_hardlinks, cancellable, error))
+            goto out;
+        }
+      else
+        {
+          gboolean did_link = FALSE;
+          (void) unlink (gs_file_get_path_cached (dest_child));
+          if (use_hardlinks)
+            {
+              if (link (gs_file_get_path_cached (src_child), gs_file_get_path_cached (dest_child)) == -1)
+                {
+                  if (!(errno == EMLINK || errno == EXDEV))
+                    {
+                      int errsv = errno;
+                      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv),
+                                           g_strerror (errsv));
+                      goto out;
+                    }
+                  use_hardlinks = FALSE;
+                }
+              else
+                did_link = TRUE;
+            }
+          if (!did_link)
+            {
+              if (!g_file_copy (src_child, dest_child,
+                                G_FILE_COPY_OVERWRITE | G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS,
+                                cancellable, NULL, NULL, error))
+                goto out;
+            }
+        }
+      g_clear_object (&file_info);
+    }
+  if (temp_error)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * gs_shutil_cp_al_or_fallback:
+ * @src: Source path
+ * @dest: Destination path
+ * @cancellable:
+ * @error:
+ *
+ * Recursively copy path @src (which must be a directory) to the
+ * target @dest.  If possible, hardlinks are used; if a hardlink is
+ * not possible, a regular copy is created.  Any existing files are
+ * overwritten.
+ *
+ * Returns: %TRUE on success
+ */
+gboolean
+gs_shutil_cp_al_or_fallback (GFile         *src,
+                             GFile         *dest,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  return cp_internal (src, dest, TRUE, cancellable, error);
+}
+
+/**
+ * gs_shutil_cp_a:
+ * @src: Source path
+ * @dest: Destination path
+ * @cancellable:
+ * @error:
+ *
+ * Recursively copy path @src (which must be a directory) to the
+ * target @dest.  Any existing files are overwritten.
+ *
+ * Returns: %TRUE on success
+ */
+gboolean
+gs_shutil_cp_a (GFile         *src,
+                GFile         *dest,
+                GCancellable  *cancellable,
+                GError       **error)
+{
+  return cp_internal (src, dest, FALSE, cancellable, error);
+}
+
+/**
+ * gs_shutil_rm_rf:
+ * @path: A file or directory
+ * @cancellable:
+ * @error:
+ *
+ * Recursively delete the filename referenced by @path; it may be a
+ * file or directory.  No error is thrown if @path does not exist.
+ */
+gboolean
+gs_shutil_rm_rf (GFile        *path,
+                 GCancellable *cancellable,
+                 GError      **error)
+{
+  gboolean ret = FALSE;
+  gs_lobj GFileEnumerator *dir_enum = NULL;
+  gs_lobj GFileInfo *file_info = NULL;
+  GError *temp_error = NULL;
+
+  dir_enum = g_file_enumerate_children (path, "standard::type,standard::name", 
+                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                        cancellable, &temp_error);
+  if (!dir_enum)
+    {
+      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+        {
+          g_clear_error (&temp_error);
+          ret = TRUE;
+        }
+      else
+        g_propagate_error (error, temp_error);
+
+      goto out;
+    }
+
+  while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
+    {
+      gs_lobj GFile *subpath = NULL;
+      GFileType type;
+      const char *name;
+
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+      
+      subpath = g_file_get_child (path, name);
+
+      if (type == G_FILE_TYPE_DIRECTORY)
+        {
+          if (!gs_shutil_rm_rf (subpath, cancellable, error))
+            goto out;
+        }
+      else
+        {
+          if (!gs_file_unlink (subpath, cancellable, error))
+            goto out;
+        }
+      g_clear_object (&file_info);
+    }
+  if (temp_error)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  if (!g_file_delete (path, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
diff --git a/gsystem-shutil.h b/gsystem-shutil.h
new file mode 100644
index 0000000..3cdea77
--- /dev/null
+++ b/gsystem-shutil.h
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * 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.
+ */
+
+#ifndef __GSYSTEM_SHUTIL_H__
+#define __GSYSTEM_SHUTIL_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+gboolean
+gs_shutil_cp_al_or_fallback (GFile         *src,
+                             GFile         *dest,
+                             GCancellable  *cancellable,
+                             GError       **error);
+
+gboolean
+gs_shutil_cp_a (GFile         *src,
+                GFile         *dest,
+                GCancellable  *cancellable,
+                GError       **error);
+
+gboolean
+gs_shutil_rm_rf (GFile        *path,
+                 GCancellable *cancellable,
+                 GError      **error);
+
+G_END_DECLS
+
+#endif
diff --git a/libgsystem.h b/libgsystem.h
index 7b6a8c8..cde7d61 100644
--- a/libgsystem.h
+++ b/libgsystem.h
@@ -27,6 +27,7 @@ G_BEGIN_DECLS
 
 #include <gsystem-local-alloc.h>
 #include <gsystem-file-utils.h>
+#include <gsystem-shutil.h>
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]