From ca162dece24c3b3ff849f15bfcab1e38d59adb3b Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 20 May 2016 14:14:47 -0400 Subject: [PATCH] unpacker: major rework - Delete unpack_to_dfd path - Get rid of copynpaste stuff and use the newly reworked ostree libarchive API which now supports the callbacks we need Closes: #289 Approved by: cgwalters --- Makefile-libpriv.am | 4 - src/app/rpmostree-internals-builtin-unpack.c | 75 +- src/libpriv/ostree-libarchive-input-stream.c | 185 ---- src/libpriv/ostree-libarchive-input-stream.h | 65 -- .../rpmostree-ostree-libarchive-copynpaste.c | 180 ---- .../rpmostree-ostree-libarchive-copynpaste.h | 44 - src/libpriv/rpmostree-unpacker.c | 914 ++++++------------ src/libpriv/rpmostree-unpacker.h | 40 +- 8 files changed, 349 insertions(+), 1158 deletions(-) delete mode 100644 src/libpriv/ostree-libarchive-input-stream.c delete mode 100644 src/libpriv/ostree-libarchive-input-stream.h delete mode 100644 src/libpriv/rpmostree-ostree-libarchive-copynpaste.c delete mode 100644 src/libpriv/rpmostree-ostree-libarchive-copynpaste.h diff --git a/Makefile-libpriv.am b/Makefile-libpriv.am index 1537998f..b2c10803 100644 --- a/Makefile-libpriv.am +++ b/Makefile-libpriv.am @@ -37,10 +37,6 @@ librpmostreepriv_la_SOURCES = \ src/libpriv/rpmostree-rpm-util.h \ src/libpriv/rpmostree-unpacker.c \ src/libpriv/rpmostree-unpacker.h \ - src/libpriv/ostree-libarchive-input-stream.c \ - src/libpriv/ostree-libarchive-input-stream.h \ - src/libpriv/rpmostree-ostree-libarchive-copynpaste.c \ - src/libpriv/rpmostree-ostree-libarchive-copynpaste.h \ src/libpriv/rpmostree-output.c \ src/libpriv/rpmostree-output.h \ $(NULL) diff --git a/src/app/rpmostree-internals-builtin-unpack.c b/src/app/rpmostree-internals-builtin-unpack.c index 750e3cb9..d3a5d421 100644 --- a/src/app/rpmostree-internals-builtin-unpack.c +++ b/src/app/rpmostree-internals-builtin-unpack.c @@ -36,17 +36,18 @@ #include "rpmostree-libbuiltin.h" #include "rpmostree-rpm-util.h" #include "rpmostree-unpacker.h" +#include "rpmostree-postprocess.h" #include "libgsystem.h" -static gboolean opt_suid_fcaps = FALSE; -static gboolean opt_owner = FALSE; -static gboolean opt_to_ostree_repo = FALSE; +static gboolean opt_selinux = FALSE; +static gboolean opt_ostree_convention = FALSE; static GOptionEntry option_entries[] = { - { "suid-fcaps", 0, 0, G_OPTION_ARG_NONE, &opt_suid_fcaps, "Enable setting suid/sgid and capabilities", NULL }, - { "owner", 0, 0, G_OPTION_ARG_NONE, &opt_owner, "Enable chown", NULL }, - { "to-ostree-repo", 0, 0, G_OPTION_ARG_NONE, &opt_to_ostree_repo, "Interpret TARGET as an OSTree repo", "REPO" }, + { "selinux", 0, 0, G_OPTION_ARG_NONE, &opt_selinux, + "Enable setting SELinux labels", NULL }, + { "ostree-convention", 0, 0, G_OPTION_ARG_NONE, &opt_ostree_convention, + "Change file paths following ostree conventions", NULL }, { NULL } }; @@ -57,14 +58,15 @@ rpmostree_internals_builtin_unpack (int argc, GError **error) { int exit_status = EXIT_FAILURE; - GOptionContext *context = g_option_context_new ("ROOT RPM"); + GOptionContext *context = g_option_context_new ("REPO RPM"); RpmOstreeUnpackerFlags flags = 0; glnx_unref_object RpmOstreeUnpacker *unpacker = NULL; const char *target; const char *rpmpath; glnx_fd_close int rootfs_fd = -1; glnx_unref_object OstreeRepo *ostree_repo = NULL; - + glnx_unref_object OstreeSePolicy *sepolicy = NULL; + if (!rpmostree_option_context_parse (context, option_entries, &argc, &argv, @@ -76,55 +78,44 @@ rpmostree_internals_builtin_unpack (int argc, if (argc < 3) { - rpmostree_usage_error (context, "TARGET and RPM must be specified", error); + rpmostree_usage_error (context, "REPO and RPM must be specified", error); goto out; } target = argv[1]; rpmpath = argv[2]; - if (opt_to_ostree_repo) - { - g_autoptr(GFile) to_ostree_repo_file = g_file_new_for_path (target); + { + g_autoptr(GFile) ostree_repo_file = g_file_new_for_path (target); - ostree_repo = ostree_repo_new (to_ostree_repo_file); - if (!ostree_repo_open (ostree_repo, cancellable, error)) - goto out; - } - else - { - if (!glnx_opendirat (AT_FDCWD, argv[1], TRUE, &rootfs_fd, error)) - goto out; - } + ostree_repo = ostree_repo_new (ostree_repo_file); + if (!ostree_repo_open (ostree_repo, cancellable, error)) + goto out; + } - /* suid implies owner too...anything else is dangerous, as we might write - * a setuid binary for the caller. - */ - if (opt_owner || opt_suid_fcaps) - flags |= RPMOSTREE_UNPACKER_FLAGS_OWNER; - if (opt_suid_fcaps) - flags |= RPMOSTREE_UNPACKER_FLAGS_SUID_FSCAPS; + if (opt_ostree_convention) + flags |= RPMOSTREE_UNPACKER_FLAGS_OSTREE_CONVENTION; unpacker = rpmostree_unpacker_new_at (AT_FDCWD, rpmpath, flags, error); if (!unpacker) goto out; - if (opt_to_ostree_repo) - { - const char *branch = rpmostree_unpacker_get_ostree_branch (unpacker); - g_autofree char *checksum = NULL; + /* just use current policy */ + if (opt_selinux) + if (!rpmostree_prepare_rootfs_get_sepolicy (AT_FDCWD, "/", &sepolicy, + cancellable, error)) + goto out; - if (!rpmostree_unpacker_unpack_to_ostree (unpacker, ostree_repo, NULL, - &checksum, cancellable, error)) - goto out; + { + const char *branch = rpmostree_unpacker_get_ostree_branch (unpacker); + g_autofree char *checksum = NULL; - g_print ("Imported %s to %s -> %s\n", rpmpath, branch, checksum); - } - else - { - if (!rpmostree_unpacker_unpack_to_dfd (unpacker, rootfs_fd, cancellable, error)) - goto out; - } + if (!rpmostree_unpacker_unpack_to_ostree (unpacker, ostree_repo, sepolicy, + &checksum, cancellable, error)) + goto out; + + g_print ("Imported %s to %s -> %s\n", rpmpath, branch, checksum); + } exit_status = EXIT_SUCCESS; out: diff --git a/src/libpriv/ostree-libarchive-input-stream.c b/src/libpriv/ostree-libarchive-input-stream.c deleted file mode 100644 index a987e403..00000000 --- a/src/libpriv/ostree-libarchive-input-stream.c +++ /dev/null @@ -1,185 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2011 Colin Walters - * - * 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 -#include -#include "ostree-libarchive-input-stream.h" - -enum { - PROP_0, - PROP_ARCHIVE -}; - -G_DEFINE_TYPE (OstreeLibarchiveInputStream, _ostree_libarchive_input_stream, G_TYPE_INPUT_STREAM) - -struct _OstreeLibarchiveInputStreamPrivate { - struct archive *archive; -}; - -static void ostree_libarchive_input_stream_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void ostree_libarchive_input_stream_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static gssize ostree_libarchive_input_stream_read (GInputStream *stream, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error); -static gboolean ostree_libarchive_input_stream_close (GInputStream *stream, - GCancellable *cancellable, - GError **error); - -static void -ostree_libarchive_input_stream_finalize (GObject *object) -{ - G_OBJECT_CLASS (_ostree_libarchive_input_stream_parent_class)->finalize (object); -} - -static void -_ostree_libarchive_input_stream_class_init (OstreeLibarchiveInputStreamClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - - g_type_class_add_private (klass, sizeof (OstreeLibarchiveInputStreamPrivate)); - - gobject_class->get_property = ostree_libarchive_input_stream_get_property; - gobject_class->set_property = ostree_libarchive_input_stream_set_property; - gobject_class->finalize = ostree_libarchive_input_stream_finalize; - - stream_class->read_fn = ostree_libarchive_input_stream_read; - stream_class->close_fn = ostree_libarchive_input_stream_close; - - /** - * OstreeLibarchiveInputStream:archive: - * - * The archive that the stream reads from. - */ - g_object_class_install_property (gobject_class, - PROP_ARCHIVE, - g_param_spec_pointer ("archive", - "", "", - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - -} - -static void -ostree_libarchive_input_stream_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - OstreeLibarchiveInputStream *self; - - self = OSTREE_LIBARCHIVE_INPUT_STREAM (object); - - switch (prop_id) - { - case PROP_ARCHIVE: - self->priv->archive = g_value_get_pointer (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -ostree_libarchive_input_stream_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - OstreeLibarchiveInputStream *self; - - self = OSTREE_LIBARCHIVE_INPUT_STREAM (object); - - switch (prop_id) - { - case PROP_ARCHIVE: - g_value_set_pointer (value, self->priv->archive); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -_ostree_libarchive_input_stream_init (OstreeLibarchiveInputStream *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM, - OstreeLibarchiveInputStreamPrivate); - -} - -GInputStream * -_ostree_libarchive_input_stream_new (struct archive *a) -{ - OstreeLibarchiveInputStream *stream; - - stream = g_object_new (OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM, - "archive", a, - NULL); - - return G_INPUT_STREAM (stream); -} - -static gssize -ostree_libarchive_input_stream_read (GInputStream *stream, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error) -{ - OstreeLibarchiveInputStream *self; - gssize res = -1; - - self = OSTREE_LIBARCHIVE_INPUT_STREAM (stream); - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return -1; - - res = archive_read_data (self->priv->archive, buffer, count); - if (res < 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "%s", archive_error_string (self->priv->archive)); - } - - return res; -} - -static gboolean -ostree_libarchive_input_stream_close (GInputStream *stream, - GCancellable *cancellable, - GError **error) -{ - return TRUE; -} diff --git a/src/libpriv/ostree-libarchive-input-stream.h b/src/libpriv/ostree-libarchive-input-stream.h deleted file mode 100644 index bcfc7bd2..00000000 --- a/src/libpriv/ostree-libarchive-input-stream.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2011 Colin Walters - * - * 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. - * - * Author: Alexander Larsson - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -#define OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM (_ostree_libarchive_input_stream_get_type ()) -#define OSTREE_LIBARCHIVE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM, OstreeLibarchiveInputStream)) -#define OSTREE_LIBARCHIVE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM, OstreeLibarchiveInputStreamClass)) -#define OSTREE_IS_LIBARCHIVE_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM)) -#define OSTREE_IS_LIBARCHIVE_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM)) -#define OSTREE_LIBARCHIVE_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_LIBARCHIVE_INPUT_STREAM, OstreeLibarchiveInputStreamClass)) - -typedef struct _OstreeLibarchiveInputStream OstreeLibarchiveInputStream; -typedef struct _OstreeLibarchiveInputStreamClass OstreeLibarchiveInputStreamClass; -typedef struct _OstreeLibarchiveInputStreamPrivate OstreeLibarchiveInputStreamPrivate; - -struct _OstreeLibarchiveInputStream -{ - GInputStream parent_instance; - - /*< private >*/ - OstreeLibarchiveInputStreamPrivate *priv; -}; - -struct _OstreeLibarchiveInputStreamClass -{ - GInputStreamClass parent_class; - - /*< private >*/ - /* Padding for future expansion */ - void (*_g_reserved1) (void); - void (*_g_reserved2) (void); - void (*_g_reserved3) (void); - void (*_g_reserved4) (void); - void (*_g_reserved5) (void); -}; - -GType _ostree_libarchive_input_stream_get_type (void) G_GNUC_CONST; - -GInputStream * _ostree_libarchive_input_stream_new (struct archive *a); - -G_END_DECLS diff --git a/src/libpriv/rpmostree-ostree-libarchive-copynpaste.c b/src/libpriv/rpmostree-ostree-libarchive-copynpaste.c deleted file mode 100644 index c079fb17..00000000 --- a/src/libpriv/rpmostree-ostree-libarchive-copynpaste.c +++ /dev/null @@ -1,180 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2015 Red Hat, Inc. - * - * Licensed under the GNU Lesser General Public License Version 2.1 - * - * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * Implements unpacking an RPM. The design here is to reuse - * libarchive's RPM support for most of it. We do however need to - * look at file capabilities, which are part of the header. - * - * Hence we end up with two file descriptors open. - */ - -#include "config.h" - -#include "rpmostree-ostree-libarchive-copynpaste.h" -#include "ostree-libarchive-input-stream.h" - -#include - -gboolean -rpmostree_split_path_ptrarray_validate (const char *path, - GPtrArray **out_components, - GError **error) -{ - gboolean ret = FALSE; - g_autoptr(GPtrArray) ret_components = NULL; - - if (strlen (path) > PATH_MAX) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Path '%s' is too long", path); - goto out; - } - - ret_components = g_ptr_array_new_with_free_func (g_free); - - do - { - const char *p = strchr (path, '/'); - g_autofree char *component = NULL; - - if (!p) - { - component = g_strdup (path); - path = NULL; - } - else - { - component = g_strndup (path, p - path); - path = p + 1; - } - - if (!component[0]) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid empty component in path '%s'", path); - goto out; - } - if (g_str_equal (component, ".") || - g_str_equal (component, "..")) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid special element '.' or '..' in path %s", path); - goto out; - } - - g_ptr_array_add (ret_components, (char*)g_steal_pointer (&component)); - } while (path && *path); - - ret = TRUE; - *out_components = g_steal_pointer (&ret_components); - out: - return ret; -} - -static GFileType -gfile_type_for_mode (guint32 mode) -{ - if (S_ISDIR (mode)) - return G_FILE_TYPE_DIRECTORY; - else if (S_ISREG (mode)) - return G_FILE_TYPE_REGULAR; - else if (S_ISLNK (mode)) - return G_FILE_TYPE_SYMBOLIC_LINK; - else if (S_ISBLK (mode) || S_ISCHR(mode) || S_ISFIFO(mode)) - return G_FILE_TYPE_SPECIAL; - else - return G_FILE_TYPE_UNKNOWN; -} - -static GFileInfo * -_ostree_header_gfile_info_new (mode_t mode, uid_t uid, gid_t gid) -{ - GFileInfo *ret = g_file_info_new (); - g_file_info_set_attribute_uint32 (ret, "standard::type", gfile_type_for_mode (mode)); - g_file_info_set_attribute_boolean (ret, "standard::is-symlink", S_ISLNK (mode)); - g_file_info_set_attribute_uint32 (ret, "unix::uid", uid); - g_file_info_set_attribute_uint32 (ret, "unix::gid", gid); - g_file_info_set_attribute_uint32 (ret, "unix::mode", mode); - return ret; -} - -GFileInfo * -_rpmostree_libarchive_to_file_info (struct archive_entry *entry) -{ - g_autoptr(GFileInfo) ret = NULL; - struct stat st; - - st = *archive_entry_stat (entry); - - if (S_ISDIR (st.st_mode)) - { - /* Always ensure we can write and execute directories...since - * this content should ultimately be read-only entirely, we're - * just breaking things by dropping write permissions during - * builds. - */ - st.st_mode |= 0700; - } - - ret = _ostree_header_gfile_info_new (st.st_mode, st.st_uid, st.st_gid); - - if (S_ISREG (st.st_mode)) - g_file_info_set_attribute_uint64 (ret, "standard::size", st.st_size); - if (S_ISLNK (st.st_mode)) - g_file_info_set_attribute_byte_string (ret, "standard::symlink-target", archive_entry_symlink (entry)); - - return g_steal_pointer (&ret); -} - -gboolean -_rpmostree_import_libarchive_entry_file (OstreeRepo *repo, - struct archive *a, - struct archive_entry *entry, - GFileInfo *file_info, - GVariant *xattrs, - guchar **out_csum, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - g_autoptr(GInputStream) file_object_input = NULL; - g_autoptr(GInputStream) archive_stream = NULL; - guint64 length; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) - archive_stream = _ostree_libarchive_input_stream_new (a); - - if (!ostree_raw_file_to_content_stream (archive_stream, file_info, xattrs, - &file_object_input, &length, cancellable, error)) - goto out; - - if (!ostree_repo_write_content (repo, NULL, file_object_input, length, out_csum, - cancellable, error)) - goto out; - - ret = TRUE; - out: - return ret; -} diff --git a/src/libpriv/rpmostree-ostree-libarchive-copynpaste.h b/src/libpriv/rpmostree-ostree-libarchive-copynpaste.h deleted file mode 100644 index 91687d42..00000000 --- a/src/libpriv/rpmostree-ostree-libarchive-copynpaste.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2015 Colin Walters - * - * This program 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 licence 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 -#include -#include - -#include "libglnx.h" - -gboolean -rpmostree_split_path_ptrarray_validate (const char *path, - GPtrArray **out_components, - GError **error); - -GFileInfo *_rpmostree_libarchive_to_file_info (struct archive_entry *entry); - -gboolean -_rpmostree_import_libarchive_entry_file (OstreeRepo *repo, - struct archive *a, - struct archive_entry *entry, - GFileInfo *file_info, - GVariant *xattrs, - guchar **out_csum, - GCancellable *cancellable, - GError **error); diff --git a/src/libpriv/rpmostree-unpacker.c b/src/libpriv/rpmostree-unpacker.c index 5487a6df..c73ec792 100644 --- a/src/libpriv/rpmostree-unpacker.c +++ b/src/libpriv/rpmostree-unpacker.c @@ -36,7 +36,6 @@ #include "rpmostree-unpacker.h" #include "rpmostree-core.h" #include "rpmostree-rpm-util.h" -#include "rpmostree-ostree-libarchive-copynpaste.h" #include #include #include @@ -47,6 +46,7 @@ #include #include +#include typedef GObjectClass RpmOstreeUnpackerClass; @@ -59,7 +59,7 @@ struct RpmOstreeUnpacker Header hdr; rpmfi fi; off_t cpio_offset; - GHashTable *fscaps; + GHashTable *rpmfi_overrides; RpmOstreeUnpackerFlags flags; char *ostree_branch; @@ -86,7 +86,9 @@ rpmostree_unpacker_finalize (GObject *object) if (self->owns_fd && self->fd != -1) (void) close (self->fd); g_free (self->ostree_branch); - + + g_hash_table_unref (self->rpmfi_overrides); + G_OBJECT_CLASS (rpmostree_unpacker_parent_class)->finalize (object); } @@ -99,8 +101,10 @@ rpmostree_unpacker_class_init (RpmOstreeUnpackerClass *klass) } static void -rpmostree_unpacker_init (RpmOstreeUnpacker *p) +rpmostree_unpacker_init (RpmOstreeUnpacker *self) { + self->rpmfi_overrides = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); } typedef int(*archive_setup_func)(struct archive *); @@ -233,6 +237,33 @@ rpmostree_unpacker_read_metainfo (int fd, return ret; } +static void +build_rpmfi_overrides (RpmOstreeUnpacker *self) +{ + int i; + + /* Right now as I understand it, we need the owner user/group and + * possibly filesystem capabilities from the header. + * + * Otherwise we can just use the CPIO data. + */ + while ((i = rpmfiNext (self->fi)) >= 0) + { + const char *user = rpmfiFUser (self->fi); + const char *group = rpmfiFGroup (self->fi); + const char *fcaps = rpmfiFCaps (self->fi); + const char *fn = rpmfiFN (self->fi); + + if (g_str_equal (user, "root") && + g_str_equal (group, "root") && + (fcaps == NULL || fcaps[0] == '\0')) + continue; + + g_hash_table_insert (self->rpmfi_overrides, g_strdup (fn), + GINT_TO_POINTER (i)); + } +} + RpmOstreeUnpacker * rpmostree_unpacker_new_fd (int fd, RpmOstreeUnpackerFlags flags, GError **error) { @@ -242,7 +273,7 @@ rpmostree_unpacker_new_fd (int fd, RpmOstreeUnpackerFlags flags, GError **error) struct archive *archive; gsize cpio_offset; - archive = rpm2cpio (fd, error); + archive = rpm2cpio (fd, error); if (archive == NULL) goto out; @@ -257,6 +288,8 @@ rpmostree_unpacker_new_fd (int fd, RpmOstreeUnpackerFlags flags, GError **error) ret->hdr = g_steal_pointer (&hdr); ret->cpio_offset = cpio_offset; + build_rpmfi_overrides (ret); + out: if (archive) archive_read_free (archive); @@ -292,287 +325,32 @@ rpmostree_unpacker_new_at (int dfd, const char *path, RpmOstreeUnpackerFlags fla return ret; } -static inline const char * -path_relative (const char *src) -{ - if (src[0] == '.' && src[1] == '/') - src += 2; - while (src[0] == '/') - src++; - return src; -} - -static GHashTable * -build_rpmfi_overrides (RpmOstreeUnpacker *self) -{ - g_autoptr(GHashTable) rpmfi_overrides = NULL; - int i; - - /* Right now as I understand it, we need the owner user/group and - * possibly filesystem capabilities from the header. - * - * Otherwise we can just use the CPIO data. - */ - rpmfi_overrides = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - for (i = 0; rpmfiNext (self->fi) >= 0; i++) - { - const char *user = rpmfiFUser (self->fi); - const char *group = rpmfiFGroup (self->fi); - const char *fcaps = rpmfiFCaps (self->fi); - - if (g_str_equal (user, "root") && g_str_equal (group, "root") - && !(fcaps && fcaps[0])) - continue; - - g_hash_table_insert (rpmfi_overrides, - g_strdup (path_relative (rpmfiFN (self->fi))), - GINT_TO_POINTER (i)); - } - - return g_steal_pointer (&rpmfi_overrides); -} - static gboolean -next_archive_entry (struct archive *archive, - struct archive_entry **out_entry, - GError **error) +get_rpmfi_override (RpmOstreeUnpacker *self, + const char *path, + const char **out_user, + const char **out_group, + const char **out_fcaps) { - int r; + gpointer v; - r = archive_read_next_header (archive, out_entry); - if (r == ARCHIVE_EOF) - { - *out_entry = NULL; - return TRUE; - } - else if (r != ARCHIVE_OK) - { - propagate_libarchive_error (error, archive); - return FALSE; - } + /* Note: we use extended here because the value might be index 0 */ + if (!g_hash_table_lookup_extended (self->rpmfi_overrides, path, NULL, &v)) + return FALSE; + + rpmfiInit (self->fi, GPOINTER_TO_INT (v)); + g_assert (rpmfiNext (self->fi) >= 0); + + if (out_user) + *out_user = rpmfiFUser (self->fi); + if (out_group) + *out_group = rpmfiFGroup (self->fi); + if (out_fcaps) + *out_fcaps = rpmfiFCaps (self->fi); return TRUE; } -gboolean -rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *self, - int rootfs_fd, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - g_autoptr(GHashTable) rpmfi_overrides = NULL; - g_autoptr(GHashTable) hardlinks = - g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - rpmfi_overrides = build_rpmfi_overrides (self); - - while (TRUE) - { - int r; - const char *fn; - struct archive_entry *entry; - glnx_fd_close int destfd = -1; - mode_t fmode; - uid_t owner_uid = 0; - gid_t owner_gid = 0; - const struct stat *archive_st; - const char *hardlink; - rpmfi fi = NULL; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - if (!next_archive_entry (self->archive, &entry, error)) - goto out; - if (entry == NULL) - break; - - fn = path_relative (archive_entry_pathname (entry)); - - archive_st = archive_entry_stat (entry); - - hardlink = archive_entry_hardlink (entry); - if (hardlink) - { - g_hash_table_insert (hardlinks, g_strdup (hardlink), g_strdup (fn)); - continue; - } - - /* Don't try to mkdir parents of "" (originally /) */ - if (fn[0]) - { - char *fn_copy = strdupa (fn); /* alloca */ - const char *dname = dirname (fn_copy); - - /* Ensure parent directories exist */ - if (!glnx_shutil_mkdir_p_at (rootfs_fd, dname, 0755, cancellable, error)) - goto out; - } - - { gpointer v; - if (g_hash_table_lookup_extended (rpmfi_overrides, fn, NULL, &v)) - { - int override_fi_idx = GPOINTER_TO_INT (v); - fi = self->fi; - rpmfiInit (self->fi, override_fi_idx); - } - } - fmode = archive_st->st_mode; - - if (S_ISDIR (fmode)) - { - /* Always ensure we can write and execute directories...since - * this content should ultimately be read-only entirely, we're - * just breaking things by dropping write permissions during - * builds. - */ - fmode |= 0700; - /* Don't try to mkdir "" (originally /) */ - if (fn[0]) - { - g_assert (fn[0] != '/'); - if (!glnx_shutil_mkdir_p_at (rootfs_fd, fn, fmode, cancellable, error)) - goto out; - } - } - else if (S_ISLNK (fmode)) - { - g_assert (fn[0] != '/'); - if (symlinkat (archive_entry_symlink (entry), rootfs_fd, fn) < 0) - { - glnx_set_error_from_errno (error); - g_prefix_error (error, "Creating %s: ", fn); - goto out; - } - } - else if (S_ISREG (fmode)) - { - size_t remain = archive_st->st_size; - - g_assert (fn[0] != '/'); - destfd = openat (rootfs_fd, fn, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW, 0600); - if (destfd < 0) - { - glnx_set_error_from_errno (error); - g_prefix_error (error, "Creating %s: ", fn); - goto out; - } - - while (remain) - { - const void *buf; - size_t size; - gint64 off; - - r = archive_read_data_block (self->archive, &buf, &size, &off); - if (r == ARCHIVE_EOF) - break; - if (r != ARCHIVE_OK) - { - propagate_libarchive_error (error, self->archive); - goto out; - } - - if (glnx_loop_write (destfd, buf, size) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } - remain -= size; - } - } - else - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "RPM contains non-regular/non-symlink file %s", - fn); - goto out; - } - - if (fi != NULL && (self->flags & RPMOSTREE_UNPACKER_FLAGS_OWNER) > 0) - { - struct passwd *pwent; - struct group *grent; - - pwent = getpwnam (rpmfiFUser (fi)); - if (pwent == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unknown user '%s'", rpmfiFUser (fi)); - goto out; - } - owner_uid = pwent->pw_uid; - - grent = getgrnam (rpmfiFGroup (fi)); - if (grent == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unknown group '%s'", rpmfiFGroup (fi)); - goto out; - } - owner_gid = grent->gr_gid; - - if (fchownat (rootfs_fd, fn, owner_uid, owner_gid, AT_SYMLINK_NOFOLLOW) < 0) - { - glnx_set_error_from_errno (error); - g_prefix_error (error, "fchownat: "); - goto out; - } - } - - if (S_ISREG (fmode)) - { - if ((self->flags & RPMOSTREE_UNPACKER_FLAGS_SUID_FSCAPS) == 0) - fmode &= 0777; - else if (fi != NULL) - { - const char *fcaps = rpmfiFCaps (fi); - if (fcaps != NULL && fcaps[0]) - { - cap_t caps = cap_from_text (fcaps); - if (cap_set_fd (destfd, caps) != 0) - { - glnx_set_error_from_errno (error); - g_prefix_error (error, "Setting capabilities: "); - goto out; - } - } - } - - if (fchmod (destfd, fmode) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } - } - } - - { GHashTableIter hashiter; - gpointer k,v; - - g_hash_table_iter_init (&hashiter, hardlinks); - - while (g_hash_table_iter_next (&hashiter, &k, &v)) - { - const char *src = path_relative (k); - const char *dest = path_relative (v); - - if (linkat (rootfs_fd, src, rootfs_fd, dest, 0) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } - } - } - - ret = TRUE; - out: - return ret; -} - const char * rpmostree_unpacker_get_ostree_branch (RpmOstreeUnpacker *self) { @@ -582,281 +360,6 @@ rpmostree_unpacker_get_ostree_branch (RpmOstreeUnpacker *self) return self->ostree_branch; } -static gboolean -write_directory_meta (OstreeRepo *repo, - GFileInfo *file_info, - GVariant *xattrs, - char **out_checksum, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - g_autoptr(GVariant) dirmeta = NULL; - g_autofree guchar *csum = NULL; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - dirmeta = ostree_create_directory_metadata (file_info, xattrs); - - if (!ostree_repo_write_metadata (repo, OSTREE_OBJECT_TYPE_DIR_META, NULL, - dirmeta, &csum, cancellable, error)) - goto out; - - ret = TRUE; - *out_checksum = ostree_checksum_from_bytes (csum); - out: - return ret; -} - -struct _cap_struct { - struct __user_cap_header_struct head; - union { - struct __user_cap_data_struct set; - __u32 flat[3]; - } u[_LINUX_CAPABILITY_U32S_2]; -}; -/* Rewritten version of _fcaps_save from libcap, since it's not - * exposed, and we need to generate the raw value. - */ -static void -cap_t_to_vfs (cap_t cap_d, struct vfs_cap_data *rawvfscap, int *out_size) -{ - guint32 eff_not_zero, magic; - guint tocopy, i; - - /* Hardcoded to 2. There is apparently a version 3 but it just maps - * to 2. I doubt another version would ever be implemented, and - * even if it was we'd need to be backcompatible forever. Anyways, - * setuid/fcaps binaries should go away entirely. - */ - magic = VFS_CAP_REVISION_2; - tocopy = VFS_CAP_U32_2; - *out_size = XATTR_CAPS_SZ_2; - - for (eff_not_zero = 0, i = 0; i < tocopy; i++) - eff_not_zero |= cap_d->u[i].flat[CAP_EFFECTIVE]; - - /* Here we're also not validating that the kernel understands - * the capabilities. - */ - - for (i = 0; i < tocopy; i++) - { - rawvfscap->data[i].permitted - = GUINT32_TO_LE(cap_d->u[i].flat[CAP_PERMITTED]); - rawvfscap->data[i].inheritable - = GUINT32_TO_LE(cap_d->u[i].flat[CAP_INHERITABLE]); - } - - if (eff_not_zero == 0) - rawvfscap->magic_etc = GUINT32_TO_LE(magic); - else - rawvfscap->magic_etc = GUINT32_TO_LE(magic|VFS_CAP_FLAGS_EFFECTIVE); -} - -static char * -tweak_path_for_ostree (const char *path) -{ - path = path_relative (path); - if (g_str_has_prefix (path, "etc/")) - return g_strconcat ("usr/", path, NULL); - else if (strcmp (path, "etc") == 0) - return g_strdup ("usr"); - return g_strdup (path); -} - -static gboolean -import_one_libarchive_entry_to_ostree (RpmOstreeUnpacker *self, - GHashTable *rpmfi_overrides, - OstreeRepo *repo, - OstreeSePolicy *sepolicy, - struct archive_entry *entry, - OstreeMutableTree *root, - const char *default_dir_checksum, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - g_autoptr(GPtrArray) pathname_parts = NULL; - g_autofree char *pathname = NULL; - glnx_unref_object OstreeMutableTree *parent = NULL; - const char *basename; - const struct stat *st; - g_auto(GVariantBuilder) xattr_builder; - rpmfi fi = NULL; - - g_variant_builder_init (&xattr_builder, (GVariantType*)"a(ayay)"); - - pathname = tweak_path_for_ostree (archive_entry_pathname (entry)); - st = archive_entry_stat (entry); - { gpointer v; - if (g_hash_table_lookup_extended (rpmfi_overrides, pathname, NULL, &v)) - { - int override_fi_idx = GPOINTER_TO_INT (v); - fi = self->fi; - rpmfiInit (self->fi, override_fi_idx); - } - } - - if (fi != NULL) - { - const char *fcaps = rpmfiFCaps (fi); - if (fcaps != NULL && fcaps[0]) - { - cap_t caps = cap_from_text (fcaps); - struct vfs_cap_data vfscap = { 0, }; - int vfscap_size; - g_autoptr(GBytes) vfsbytes = NULL; - - cap_t_to_vfs (caps, &vfscap, &vfscap_size); - vfsbytes = g_bytes_new (&vfscap, vfscap_size); - - g_variant_builder_add (&xattr_builder, "(@ay@ay)", - g_variant_new_bytestring ("security.capability"), - g_variant_new_from_bytes ((GVariantType*)"ay", - vfsbytes, - FALSE)); - } - } - - if (!pathname[0]) - { - parent = NULL; - basename = NULL; - } - else - { - if (!rpmostree_split_path_ptrarray_validate (pathname, &pathname_parts, error)) - goto out; - - if (default_dir_checksum) - { - if (!ostree_mutable_tree_ensure_parent_dirs (root, pathname_parts, - default_dir_checksum, - &parent, - error)) - goto out; - } - else - { - if (!ostree_mutable_tree_walk (root, pathname_parts, 0, &parent, error)) - goto out; - } - basename = (const char*)pathname_parts->pdata[pathname_parts->len-1]; - } - - if (archive_entry_hardlink (entry)) - { - g_autofree char *hardlink = tweak_path_for_ostree (archive_entry_hardlink (entry)); - const char *hardlink_basename; - g_autoptr(GPtrArray) hardlink_split_path = NULL; - glnx_unref_object OstreeMutableTree *hardlink_source_parent = NULL; - glnx_unref_object OstreeMutableTree *hardlink_source_subdir = NULL; - g_autofree char *hardlink_source_checksum = NULL; - - g_assert (parent != NULL); - - if (!rpmostree_split_path_ptrarray_validate (hardlink, &hardlink_split_path, error)) - goto out; - if (hardlink_split_path->len == 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid hardlink path %s", hardlink); - goto out; - } - - hardlink_basename = hardlink_split_path->pdata[hardlink_split_path->len - 1]; - - if (!ostree_mutable_tree_walk (root, hardlink_split_path, 0, &hardlink_source_parent, error)) - goto out; - - if (!ostree_mutable_tree_lookup (hardlink_source_parent, hardlink_basename, - &hardlink_source_checksum, - &hardlink_source_subdir, - error)) - { - g_prefix_error (error, "While resolving hardlink target: "); - goto out; - } - - if (hardlink_source_subdir) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Hardlink %s refers to directory %s", - pathname, hardlink); - goto out; - } - g_assert (hardlink_source_checksum); - - if (!ostree_mutable_tree_replace_file (parent, - basename, - hardlink_source_checksum, - error)) - goto out; - } - else - { - g_autofree char *object_checksum = NULL; - g_autoptr(GFileInfo) file_info = NULL; - glnx_unref_object OstreeMutableTree *subdir = NULL; - - file_info = _rpmostree_libarchive_to_file_info (entry); - - if (S_ISDIR (st->st_mode)) - { - if (!write_directory_meta (repo, file_info, NULL, &object_checksum, - cancellable, error)) - goto out; - - if (parent == NULL) - { - subdir = g_object_ref (root); - } - else - { - if (!ostree_mutable_tree_ensure_dir (parent, basename, &subdir, error)) - goto out; - } - - ostree_mutable_tree_set_metadata_checksum (subdir, object_checksum); - } - else if (S_ISREG (st->st_mode) || S_ISLNK (st->st_mode)) - { - g_autofree guchar *object_csum = NULL; - - if (parent == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't import file as root directory"); - goto out; - } - - if (!_rpmostree_import_libarchive_entry_file (repo, self->archive, entry, file_info, - g_variant_builder_end (&xattr_builder), - &object_csum, - cancellable, error)) - goto out; - - object_checksum = ostree_checksum_from_bytes (object_csum); - if (!ostree_mutable_tree_replace_file (parent, basename, - object_checksum, - error)) - goto out; - } - else - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unsupported file type for path '%s'", pathname); - goto out; - } - } - - ret = TRUE; - out: - return ret; -} - static gboolean get_lead_sig_header_as_bytes (RpmOstreeUnpacker *self, GBytes **out_metadata, @@ -905,94 +408,263 @@ get_lead_sig_header_as_bytes (RpmOstreeUnpacker *self, return ret; } -gboolean -rpmostree_unpacker_unpack_to_ostree (RpmOstreeUnpacker *self, - OstreeRepo *repo, - OstreeSePolicy *sepolicy, - char **out_commit, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - g_autoptr(GHashTable) rpmfi_overrides = NULL; - g_autoptr(GHashTable) hardlinks = - g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - g_autofree char *default_dir_checksum = NULL; - g_autoptr(GFile) root = NULL; - glnx_unref_object OstreeMutableTree *mtree = NULL; - g_autoptr(GBytes) header_bytes = NULL; - g_autofree char *commit_checksum = NULL; - g_auto(GVariantBuilder) metadata_builder; +struct _cap_struct { + struct __user_cap_header_struct head; + union { + struct __user_cap_data_struct set; + __u32 flat[3]; + } u[_LINUX_CAPABILITY_U32S_2]; +}; +/* Rewritten version of _fcaps_save from libcap, since it's not + * exposed, and we need to generate the raw value. + */ +static void +cap_t_to_vfs (cap_t cap_d, struct vfs_cap_data *rawvfscap, int *out_size) +{ + guint32 eff_not_zero, magic; + guint tocopy, i; + + /* Hardcoded to 2. There is apparently a version 3 but it just maps + * to 2. I doubt another version would ever be implemented, and + * even if it was we'd need to be backcompatible forever. Anyways, + * setuid/fcaps binaries should go away entirely. + */ + magic = VFS_CAP_REVISION_2; + tocopy = VFS_CAP_U32_2; + *out_size = XATTR_CAPS_SZ_2; + + for (eff_not_zero = 0, i = 0; i < tocopy; i++) + eff_not_zero |= cap_d->u[i].flat[CAP_EFFECTIVE]; + + /* Here we're also not validating that the kernel understands + * the capabilities. + */ + + for (i = 0; i < tocopy; i++) + { + rawvfscap->data[i].permitted + = GUINT32_TO_LE(cap_d->u[i].flat[CAP_PERMITTED]); + rawvfscap->data[i].inheritable + = GUINT32_TO_LE(cap_d->u[i].flat[CAP_INHERITABLE]); + } + + if (eff_not_zero == 0) + rawvfscap->magic_etc = GUINT32_TO_LE(magic); + else + rawvfscap->magic_etc = GUINT32_TO_LE(magic|VFS_CAP_FLAGS_EFFECTIVE); +} + +static gboolean +build_metadata_variant (RpmOstreeUnpacker *self, + OstreeSePolicy *sepolicy, + GVariant **out_variant, + GCancellable *cancellable, + GError **error) +{ + g_auto(GVariantBuilder) metadata_builder; g_variant_builder_init (&metadata_builder, (GVariantType*)"a{sv}"); - rpmfi_overrides = build_rpmfi_overrides (self); + /* NB: We store the full header of the RPM in the commit for two reasons: + * first, it holds the file security capabilities, and secondly, we'll need to + * provide it to librpm when it updates the rpmdb (see + * rpmostree_context_assemble_commit()). */ + { + g_autoptr(GBytes) metadata = NULL; - g_assert (sepolicy == NULL); - - /* Default directories are 0/0/0755, and right now we're ignoring - * SELinux. (This might be a problem for /etc, but in practice - * anything with nontrivial perms should be in the packages) - */ - { glnx_unref_object GFileInfo *default_dir_perms = g_file_info_new (); - g_file_info_set_attribute_uint32 (default_dir_perms, "unix::uid", 0); - g_file_info_set_attribute_uint32 (default_dir_perms, "unix::gid", 0); - g_file_info_set_attribute_uint32 (default_dir_perms, "unix::mode", 0755 | S_IFDIR); - - if (!write_directory_meta (repo, default_dir_perms, NULL, - &default_dir_checksum, cancellable, error)) - goto out; - } - - if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) - goto out; - - mtree = ostree_mutable_tree_new (); - ostree_mutable_tree_set_metadata_checksum (mtree, default_dir_checksum); - - { g_autoptr(GBytes) metadata = NULL; - if (!get_lead_sig_header_as_bytes (self, &metadata, cancellable, error)) - goto out; + return FALSE; g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.metadata", - g_variant_new_from_bytes ((GVariantType*)"ay", metadata, TRUE)); + g_variant_new_from_bytes ((GVariantType*)"ay", + metadata, TRUE)); } - - while (TRUE) - { - struct archive_entry *entry; - - if (!next_archive_entry (self->archive, &entry, error)) - goto out; - if (entry == NULL) - break; - if (!import_one_libarchive_entry_to_ostree (self, rpmfi_overrides, repo, - sepolicy, entry, mtree, - default_dir_checksum, - cancellable, error)) - goto out; + /* The current sepolicy that was used to label the unpacked files is important + * to record. It will help us during future overlays to determine whether the + * files should be relabeled. */ + if (sepolicy) + g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.sepolicy", + g_variant_new_string + (ostree_sepolicy_get_csum (sepolicy))); + + /* let's be nice to our future selves just in case */ + g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.unpack_version", + g_variant_new_uint32 (1)); + + *out_variant = g_variant_builder_end (&metadata_builder); + return TRUE; +} + +typedef struct +{ + RpmOstreeUnpacker *self; + GError **error; +} cb_data; + +static OstreeRepoCommitFilterResult +filter_cb (OstreeRepo *repo, + const char *path, + GFileInfo *file_info, + gpointer user_data) +{ + RpmOstreeUnpacker *self = ((cb_data*)user_data)->self; + GError **error = ((cb_data*)user_data)->error; + + /* For now we fail if an RPM requires a file to be owned by non-root. The + * problem is that RPM provides strings, but ostree records uids/gids. Any + * mapping we choose would be specific to a certain userdb and thus not + * portable. To properly support this will probably require switching over to + * systemd-sysusers: https://github.com/projectatomic/rpm-ostree/issues/49 */ + + const char *user = NULL; + const char *group = NULL; + + guint32 uid = g_file_info_get_attribute_uint32 (file_info, "unix::uid"); + guint32 gid = g_file_info_get_attribute_uint32 (file_info, "unix::gid"); + + gboolean was_null = (*error == NULL); + + if (*error == NULL && + get_rpmfi_override (self, path, &user, &group, NULL) && + (user != NULL || group != NULL)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "path \"%s\" marked as %s%s%s)", + path, user, group ? ":" : "", group ?: ""); + } + + /* This should normally never happen since by design RPM doesn't use ids at + * all, but might as well check since it's right there. */ + if (*error == NULL && (uid != 0 || gid != 0)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "path \"%s\" marked as %u:%u)", path, uid, gid); + } + + if (was_null && *error != NULL) + g_prefix_error (error, "Non-root ownership currently unsupported"); + + return OSTREE_REPO_COMMIT_FILTER_ALLOW; +} + +static GVariant* +xattr_cb (OstreeRepo *repo, + const char *path, + GFileInfo *file_info, + gpointer user_data) +{ + RpmOstreeUnpacker *self = user_data; + const char *fcaps = NULL; + + if (get_rpmfi_override (self, path, NULL, NULL, &fcaps) && fcaps != NULL) + { + g_auto(GVariantBuilder) builder; + cap_t caps = cap_from_text (fcaps); + struct vfs_cap_data vfscap = { 0, }; + g_autoptr(GBytes) vfsbytes = NULL; + int vfscap_size; + + cap_t_to_vfs (caps, &vfscap, &vfscap_size); + vfsbytes = g_bytes_new (&vfscap, vfscap_size); + + g_variant_builder_init (&builder, (GVariantType*)"a(ayay)"); + g_variant_builder_add (&builder, "(@ay@ay)", + g_variant_new_bytestring ("security.capability"), + g_variant_new_from_bytes ((GVariantType*)"ay", + vfsbytes, FALSE)); + return g_variant_ref_sink (g_variant_builder_end (&builder)); + } + + return NULL; +} + +static gboolean +import_rpm_to_repo (RpmOstreeUnpacker *self, + OstreeRepo *repo, + OstreeSePolicy *sepolicy, + char **out_csum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GVariant *metadata = NULL; /* floating */ + g_autoptr(GFile) root = NULL; + OstreeRepoCommitModifier *modifier = NULL; + OstreeRepoImportArchiveOptions opts = { 0 }; + glnx_unref_object OstreeMutableTree *mtree = NULL; + + GError *cb_error = NULL; + cb_data fdata = { self, &cb_error }; + + modifier = ostree_repo_commit_modifier_new (0, filter_cb, &fdata, NULL); + ostree_repo_commit_modifier_set_xattr_callback (modifier, xattr_cb, + NULL, self); + ostree_repo_commit_modifier_set_sepolicy (modifier, sepolicy); + + opts.ignore_unsupported_content = TRUE; + opts.autocreate_parents = TRUE; + opts.use_ostree_convention = + (self->flags & RPMOSTREE_UNPACKER_FLAGS_OSTREE_CONVENTION); + + mtree = ostree_mutable_tree_new (); + + if (!ostree_repo_import_archive_to_mtree (repo, &opts, self->archive, mtree, + modifier, cancellable, error)) + goto out; + + /* check if any of the cbs set an error */ + if (cb_error != NULL) + { + *error = cb_error; + goto out; } if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error)) goto out; - if (!ostree_repo_write_commit (repo, NULL, "", "", - g_variant_builder_end (&metadata_builder), - OSTREE_REPO_FILE (root), - &commit_checksum, cancellable, error)) + if (!build_metadata_variant (self, sepolicy, &metadata, cancellable, error)) goto out; - ostree_repo_transaction_set_ref (repo, NULL, - rpmostree_unpacker_get_ostree_branch (self), - commit_checksum); + if (!ostree_repo_write_commit (repo, NULL, "", "", metadata, + OSTREE_REPO_FILE (root), out_csum, + cancellable, error)) + goto out; + + ret = TRUE; +out: + if (modifier) + ostree_repo_commit_modifier_unref (modifier); + return ret; +} + +gboolean +rpmostree_unpacker_unpack_to_ostree (RpmOstreeUnpacker *self, + OstreeRepo *repo, + OstreeSePolicy *sepolicy, + char **out_csum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + g_autofree char *csum = NULL; + const char *branch = NULL; + + if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) + goto out; + + if (!import_rpm_to_repo (self, repo, sepolicy, &csum, cancellable, error)) + goto out; + + branch = rpmostree_unpacker_get_ostree_branch (self); + ostree_repo_transaction_set_ref (repo, NULL, branch, csum); if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; + *out_csum = g_steal_pointer (&csum); + ret = TRUE; - *out_commit = g_steal_pointer (&commit_checksum); out: ostree_repo_abort_transaction (repo, cancellable, NULL); return ret; diff --git a/src/libpriv/rpmostree-unpacker.h b/src/libpriv/rpmostree-unpacker.h index 28d7566f..656e2953 100644 --- a/src/libpriv/rpmostree-unpacker.h +++ b/src/libpriv/rpmostree-unpacker.h @@ -33,20 +33,24 @@ typedef struct RpmOstreeUnpacker RpmOstreeUnpacker; GType rpmostree_unpacker_get_type (void); +/** + * RpmOstreeUnpackerFlags: + * @RPMOSTREE_UNPACKER_FLAGS_OSTREE_CONVENTION: Move files to follow ostree convention + */ typedef enum { - RPMOSTREE_UNPACKER_FLAGS_SUID_FSCAPS = (1 << 0), - RPMOSTREE_UNPACKER_FLAGS_OWNER = (1 << 1) + RPMOSTREE_UNPACKER_FLAGS_OSTREE_CONVENTION = (1 << 0) } RpmOstreeUnpackerFlags; -#define RPMOSTREE_UNPACKER_FLAGS_ALL (RPMOSTREE_UNPACKER_FLAGS_SUID_FSCAPS | RPMOSTREE_UNPACKER_FLAGS_OWNER) -RpmOstreeUnpacker *rpmostree_unpacker_new_fd (int fd, RpmOstreeUnpackerFlags flags, GError **error); +RpmOstreeUnpacker* +rpmostree_unpacker_new_fd (int fd, + RpmOstreeUnpackerFlags flags, + GError **error); -RpmOstreeUnpacker *rpmostree_unpacker_new_at (int dfd, const char *path, RpmOstreeUnpackerFlags flags, GError **error); - -gboolean rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *unpacker, - int dfd, - GCancellable *cancellable, - GError **error); +RpmOstreeUnpacker* +rpmostree_unpacker_new_at (int dfd, + const char *path, + RpmOstreeUnpackerFlags flags, + GError **error); gboolean rpmostree_unpacker_read_metainfo (int fd, @@ -55,11 +59,13 @@ rpmostree_unpacker_read_metainfo (int fd, rpmfi *out_fi, GError **error); -const char *rpmostree_unpacker_get_ostree_branch (RpmOstreeUnpacker *unpacker); +const char* +rpmostree_unpacker_get_ostree_branch (RpmOstreeUnpacker *unpacker); -gboolean rpmostree_unpacker_unpack_to_ostree (RpmOstreeUnpacker *unpacker, - OstreeRepo *repo, - OstreeSePolicy *sepolicy, - char **out_commit, - GCancellable *cancellable, - GError **error); +gboolean +rpmostree_unpacker_unpack_to_ostree (RpmOstreeUnpacker *unpacker, + OstreeRepo *repo, + OstreeSePolicy *sepolicy, + char **out_commit, + GCancellable *cancellable, + GError **error);