From f5399c8348b04f9cf5bd4d9d3903e5843c1feeb5 Mon Sep 17 00:00:00 2001 From: Colin Walters <walters@verbum.org> Date: Sun, 15 Feb 2015 16:09:58 -0500 Subject: [PATCH] Import xattr reading code from libgsystem --- Makefile-libglnx.am | 6 +- glnx-xattrs.c | 283 ++++++++++++++++++++++++++++++++++++++++++++ glnx-xattrs.h | 45 +++++++ libglnx.h | 1 + 4 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 glnx-xattrs.c create mode 100644 glnx-xattrs.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 462000133..a04f19e76 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -26,11 +26,13 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-errors.c \ $(libglnx_srcpath)/glnx-dirfd.h \ $(libglnx_srcpath)/glnx-dirfd.c \ + $(libglnx_srcpath)/glnx-xattrs.h \ + $(libglnx_srcpath)/glnx-xattrs.c \ $(libglnx_srcpath)/glnx-shutil.h \ $(libglnx_srcpath)/glnx-shutil.c \ $(libglnx_srcpath)/libglnx.h \ $(NULL) libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) -libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic -libglnx_la_LIBADD = $(libglnx_libs) +libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic +libglnx_la_LIBADD = $(libglnx_libs) -lattr diff --git a/glnx-xattrs.c b/glnx-xattrs.c new file mode 100644 index 000000000..249b2115b --- /dev/null +++ b/glnx-xattrs.c @@ -0,0 +1,283 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 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" + +#include <string.h> +#include <stdio.h> + +#include <glnx-xattrs.h> +#include <glnx-errors.h> +#include <glnx-local-alloc.h> + +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 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 gboolean +read_xattr_name_array (const char *path, + int fd, + const char *xattrs, + size_t len, + GVariantBuilder *builder, + GError **error) +{ + gboolean ret = FALSE; + const char *p; + int r; + const char *funcstr; + + g_assert (path != NULL || fd != -1); + + funcstr = fd != -1 ? "fgetxattr" : "lgetxattr"; + + p = xattrs; + while (p < xattrs+len) + { + ssize_t bytes_read; + char *buf; + GBytes *bytes = NULL; + + if (fd != -1) + bytes_read = fgetxattr (fd, p, NULL, 0); + else + bytes_read = lgetxattr (path, p, NULL, 0); + if (bytes_read < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", funcstr); + goto out; + } + if (bytes_read == 0) + continue; + + buf = g_malloc (bytes_read); + bytes = g_bytes_new_take (buf, bytes_read); + if (fd != -1) + r = fgetxattr (fd, p, buf, bytes_read); + else + r = lgetxattr (path, p, buf, bytes_read); + if (r < 0) + { + g_bytes_unref (bytes); + glnx_set_prefix_error_from_errno (error, "%s", funcstr); + 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; +} + +static gboolean +get_xattrs_impl (const char *path, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ssize_t bytes_read; + glnx_free char *xattr_names = NULL; + glnx_free char *xattr_names_canonical = NULL; + GVariantBuilder builder; + gboolean builder_initialized = FALSE; + g_autoptr(GVariant) ret_xattrs = NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); + builder_initialized = TRUE; + + bytes_read = llistxattr (path, NULL, 0); + + if (bytes_read < 0) + { + if (errno != ENOTSUP) + { + glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); + goto out; + } + } + else if (bytes_read > 0) + { + xattr_names = g_malloc (bytes_read); + if (llistxattr (path, xattr_names, bytes_read) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); + goto out; + } + xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); + + if (!read_xattr_name_array (path, -1, xattr_names_canonical, bytes_read, &builder, error)) + goto out; + } + + ret_xattrs = g_variant_builder_end (&builder); + builder_initialized = FALSE; + g_variant_ref_sink (ret_xattrs); + + ret = TRUE; + if (out_xattrs) + *out_xattrs = g_steal_pointer (&ret_xattrs); + out: + if (!builder_initialized) + g_variant_builder_clear (&builder); + return ret; +} + +/** + * glnx_fd_get_all_xattrs: + * @fd: a file descriptor + * @out_xattrs: (out): A new #GVariant containing the extended attributes + * @cancellable: Cancellable + * @error: Error + * + * Read all extended attributes from @fd 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 +glnx_fd_get_all_xattrs (int fd, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ssize_t bytes_read; + glnx_free char *xattr_names = NULL; + glnx_free char *xattr_names_canonical = NULL; + GVariantBuilder builder; + gboolean builder_initialized = FALSE; + g_autoptr(GVariant) ret_xattrs = NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); + builder_initialized = TRUE; + + bytes_read = flistxattr (fd, NULL, 0); + + if (bytes_read < 0) + { + if (errno != ENOTSUP) + { + glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); + goto out; + } + } + else if (bytes_read > 0) + { + xattr_names = g_malloc (bytes_read); + if (flistxattr (fd, xattr_names, bytes_read) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); + goto out; + } + xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); + + if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, bytes_read, &builder, error)) + goto out; + } + + ret_xattrs = g_variant_builder_end (&builder); + builder_initialized = FALSE; + g_variant_ref_sink (ret_xattrs); + + ret = TRUE; + if (out_xattrs) + *out_xattrs = g_steal_pointer (&ret_xattrs); + out: + if (!builder_initialized) + g_variant_builder_clear (&builder); + return ret; +} + +/** + * glnx_dfd_name_get_all_xattrs: + * @dfd: Parent directory file descriptor + * @name: File name + * @out_xattrs: (out): Extended attribute set + * @cancellable: Cancellable + * @error: Error + * + * Load all extended attributes for the file named @name residing in + * directory @dfd. + */ +gboolean +glnx_dfd_name_get_all_xattrs (int dfd, + const char *name, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + if (dfd == AT_FDCWD || dfd == -1) + { + return get_xattrs_impl (name, out_xattrs, cancellable, error); + } + else + { + char buf[PATH_MAX]; + /* A workaround for the lack of lgetxattrat(), thanks to Florian Weimer: + * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html + */ + snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name); + return get_xattrs_impl (buf, out_xattrs, cancellable, error); + } +} diff --git a/glnx-xattrs.h b/glnx-xattrs.h new file mode 100644 index 000000000..360092d43 --- /dev/null +++ b/glnx-xattrs.h @@ -0,0 +1,45 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 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. + */ + +#pragma once + +#include <glnx-backport-autoptr.h> +#include <limits.h> +#include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <attr/xattr.h> + +G_BEGIN_DECLS + +gboolean +glnx_dfd_name_get_all_xattrs (int dfd, + const char *name, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_fd_get_all_xattrs (int fd, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error); + +G_END_DECLS diff --git a/libglnx.h b/libglnx.h index 294b90447..e74eedabf 100644 --- a/libglnx.h +++ b/libglnx.h @@ -30,5 +30,6 @@ G_BEGIN_DECLS #include <glnx-errors.h> #include <glnx-dirfd.h> #include <glnx-shutil.h> +#include <glnx-xattrs.h> G_END_DECLS -- GitLab