summaryrefslogtreecommitdiff
path: root/gsystem-file-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'gsystem-file-utils.c')
-rw-r--r--gsystem-file-utils.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/gsystem-file-utils.c b/gsystem-file-utils.c
index 96b317a..639caca 100644
--- a/gsystem-file-utils.c
+++ b/gsystem-file-utils.c
@@ -37,6 +37,9 @@
#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)
@@ -1339,3 +1342,279 @@ gs_file_realpath (GFile *file)
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
+
+/**
+ * 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;
+ const char *path;
+ ssize_t bytes_read;
+ GVariant *ret_xattrs = NULL;
+ char *xattr_names = NULL;
+ char *xattr_names_canonical = NULL;
+ GVariantBuilder builder;
+ gboolean builder_initialized = FALSE;
+
+ path = gs_file_get_path_cached (f);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+ builder_initialized = TRUE;
+
+#ifdef GSYSTEM_CONFIG_XATTRS
+ 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;
+ }
+
+#endif
+
+ ret_xattrs = g_variant_builder_end (&builder);
+ 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);
+ g_clear_pointer (&xattr_names, g_free);
+ g_clear_pointer (&xattr_names_canonical, g_free);
+ 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
+}