Major revamp/extension of libhif/unpacker code
This is in preparation for `rpm-ostree container`, which handles unpacking RPMs as non-root. At the moment, I'm copying code in from both ostree's libarchive bits (fixable...may need to export some utility functions) and some functions from libhif (harder, see: http://lists.rpm.org/pipermail/rpm-ecosystem/2016-January/000297.html ) There's lots more cleanup to do here, but I don't want to block on the resolution of the libhif changes.
This commit is contained in:
parent
a09ba2c9aa
commit
b716959252
@ -37,6 +37,10 @@ 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 \
|
||||
$(NULL)
|
||||
librpmostreepriv_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libpriv -I$(libglnx_srcpath) -DPKGLIBDIR=\"$(pkglibdir)\" $(PKGDEP_RPMOSTREE_CFLAGS)
|
||||
librpmostreepriv_la_LIBADD = $(AM_LDFLAGS) $(PKGDEP_RPMOSTREE_LIBS) libglnx.la $(CAP_LIBS)
|
||||
|
@ -63,7 +63,7 @@ AC_SEARCH_LIBS([rpmsqSetInterruptSafety], [rpmio],
|
||||
PKG_CHECK_MODULES(PKGDEP_GIO_UNIX, [gio-unix-2.0])
|
||||
PKG_CHECK_MODULES(PKGDEP_RPMOSTREE, [gio-unix-2.0 >= 2.40.0 json-glib-1.0
|
||||
ostree-1 >= 2015.1 libgsystem >= 2015.1
|
||||
rpm hawkey libhif >= 0.2.0
|
||||
rpm hawkey libhif >= 0.2.0 librepo
|
||||
libarchive])
|
||||
# Hawkey ABI change in 0.5.3
|
||||
AC_MSG_CHECKING([For hawkey 0.5.3 ABI break])
|
||||
|
2
libglnx
2
libglnx
@ -1 +1 @@
|
||||
Subproject commit 3c470803d08477dab9c7faada29a5f4c59dd519e
|
||||
Subproject commit 769522753c25537e520adc322fa62e5390272add
|
@ -14,6 +14,9 @@ BuildRequires: gtk-doc
|
||||
BuildRequires: gnome-common
|
||||
BuildRequires: pkgconfig(ostree-1)
|
||||
BuildRequires: pkgconfig(libgsystem)
|
||||
BuildRequires: pkgconfig(libarchive)
|
||||
BuildRequires: pkgconfig(libhif)
|
||||
BuildRequires: pkgconfig(librepo)
|
||||
BuildRequires: pkgconfig(json-glib-1.0)
|
||||
BuildRequires: pkgconfig(rpm)
|
||||
BuildRequires: pkgconfig(hawkey)
|
||||
|
@ -95,15 +95,6 @@ typedef struct {
|
||||
GBytes *serialized_treefile;
|
||||
} RpmOstreeTreeComposeContext;
|
||||
|
||||
static int
|
||||
ptrarray_sort_compare_strings (gconstpointer ap,
|
||||
gconstpointer bp)
|
||||
{
|
||||
char **asp = (gpointer)ap;
|
||||
char **bsp = (gpointer)bp;
|
||||
return strcmp (*asp, *bsp);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compute_checksum_from_treefile_and_goal (RpmOstreeTreeComposeContext *self,
|
||||
HyGoal goal,
|
||||
@ -127,26 +118,7 @@ compute_checksum_from_treefile_and_goal (RpmOstreeTreeComposeContext *self,
|
||||
/* FIXME; we should also hash the post script */
|
||||
|
||||
/* Hash in each package */
|
||||
{ _cleanup_hypackagelist_ HyPackageList pkglist = NULL;
|
||||
HyPackage pkg;
|
||||
guint i;
|
||||
gs_unref_ptrarray GPtrArray *nevras = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
pkglist = hy_goal_list_installs (goal);
|
||||
|
||||
FOR_PACKAGELIST(pkg, pkglist, i)
|
||||
{
|
||||
g_ptr_array_add (nevras, hy_package_get_nevra (pkg));
|
||||
}
|
||||
|
||||
g_ptr_array_sort (nevras, ptrarray_sort_compare_strings);
|
||||
|
||||
for (i = 0; i < nevras->len; i++)
|
||||
{
|
||||
const char *nevra = nevras->pdata[i];
|
||||
g_checksum_update (checksum, (guint8*)nevra, strlen (nevra));
|
||||
}
|
||||
}
|
||||
_rpmostree_hif_add_checksum_goal (checksum, goal);
|
||||
|
||||
ret_checksum = g_strdup (g_checksum_get_string (checksum));
|
||||
|
||||
@ -178,8 +150,8 @@ install_packages_in_root (RpmOstreeTreeComposeContext *self,
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint progress_sigid;
|
||||
char **strviter;
|
||||
GFile *contextdir = self->treefile_context_dirs->pdata[0];
|
||||
g_auto(RpmOstreeHifInstall) hifinstall = { 0, };
|
||||
gs_unref_object HifContext *hifctx = NULL;
|
||||
gs_free char *cachedir = g_build_filename (gs_file_get_path_cached (self->workdir),
|
||||
"cache",
|
||||
@ -274,45 +246,14 @@ install_packages_in_root (RpmOstreeTreeComposeContext *self,
|
||||
}
|
||||
|
||||
/* --- Downloading metadata --- */
|
||||
{ g_auto(GLnxConsoleRef) console = { 0, };
|
||||
gs_unref_object HifState *hifstate = hif_state_new ();
|
||||
if (!_rpmostree_libhif_console_download_metadata (hifctx, cancellable, error))
|
||||
goto out;
|
||||
|
||||
progress_sigid = g_signal_connect (hifstate, "percentage-changed",
|
||||
G_CALLBACK (on_hifstate_percentage_changed),
|
||||
"Downloading metadata:");
|
||||
|
||||
glnx_console_lock (&console);
|
||||
|
||||
if (!hif_context_setup_sack (hifctx, hifstate, error))
|
||||
goto out;
|
||||
|
||||
g_signal_handler_disconnect (hifstate, progress_sigid);
|
||||
}
|
||||
|
||||
for (strviter = packages; strviter && *strviter; strviter++)
|
||||
{
|
||||
if (!hif_context_install (hifctx, *strviter, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* --- Resolving dependencies --- */
|
||||
{ g_auto(GLnxConsoleRef) console = { 0, };
|
||||
gs_unref_object HifState *hifstate = hif_state_new ();
|
||||
|
||||
progress_sigid = g_signal_connect (hifstate, "percentage-changed",
|
||||
G_CALLBACK (on_hifstate_percentage_changed),
|
||||
"Resolving dependencies:");
|
||||
|
||||
glnx_console_lock (&console);
|
||||
|
||||
if (!hif_transaction_depsolve (hif_context_get_transaction (hifctx),
|
||||
hif_context_get_goal (hifctx),
|
||||
hifstate, error))
|
||||
goto out;
|
||||
|
||||
g_signal_handler_disconnect (hifstate, progress_sigid);
|
||||
}
|
||||
if (!_rpmostree_libhif_console_prepare_install (hifctx, NULL, (const char *const*)packages,
|
||||
&hifinstall, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* FIXME - just do a depsolve here before we compute download requirements */
|
||||
if (!compute_checksum_from_treefile_and_goal (self, hif_context_get_goal (hifctx),
|
||||
&ret_new_inputhash, error))
|
||||
goto out;
|
||||
@ -343,24 +284,10 @@ install_packages_in_root (RpmOstreeTreeComposeContext *self,
|
||||
g_print ("Previous commit found, but without rpmostree.inputhash metadata key\n");
|
||||
}
|
||||
|
||||
rpmostree_print_transaction (hifctx);
|
||||
|
||||
/* --- Downloading packages --- */
|
||||
{ g_auto(GLnxConsoleRef) console = { 0, };
|
||||
gs_unref_object HifState *hifstate = hif_state_new ();
|
||||
|
||||
progress_sigid = g_signal_connect (hifstate, "percentage-changed",
|
||||
G_CALLBACK (on_hifstate_percentage_changed),
|
||||
"Downloading packages:");
|
||||
|
||||
glnx_console_lock (&console);
|
||||
|
||||
if (!hif_transaction_download (hif_context_get_transaction (hifctx), hifstate, error))
|
||||
goto out;
|
||||
|
||||
g_signal_handler_disconnect (hifstate, progress_sigid);
|
||||
}
|
||||
|
||||
if (!_rpmostree_libhif_console_download_rpms (hifctx, -1, &hifinstall, cancellable, error))
|
||||
goto out;
|
||||
|
||||
{ g_auto(GLnxConsoleRef) console = { 0, };
|
||||
gs_unref_object HifState *hifstate = hif_state_new ();
|
||||
|
||||
@ -773,8 +700,7 @@ rpmostree_compose_builtin_tree (int argc,
|
||||
goto out;
|
||||
g_ptr_array_add (packages, NULL);
|
||||
|
||||
{
|
||||
gs_unref_object JsonGenerator *generator = json_generator_new ();
|
||||
{ gs_unref_object JsonGenerator *generator = json_generator_new ();
|
||||
char *treefile_buf = NULL;
|
||||
gsize len;
|
||||
|
||||
|
185
src/libpriv/ostree-libarchive-input-stream.c
Normal file
185
src/libpriv/ostree-libarchive-input-stream.c
Normal file
@ -0,0 +1,185 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2011 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 <archive.h>
|
||||
#include <gio/gio.h>
|
||||
#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;
|
||||
}
|
65
src/libpriv/ostree-libarchive-input-stream.h
Normal file
65
src/libpriv/ostree-libarchive-input-stream.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2011 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.
|
||||
*
|
||||
* Author: Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -23,15 +23,100 @@
|
||||
#include <gio/gio.h>
|
||||
#include <libhif.h>
|
||||
#include <libhif/hif-utils.h>
|
||||
#include <libhif/hif-package.h>
|
||||
#include <ostree.h>
|
||||
|
||||
#include "libglnx.h"
|
||||
|
||||
struct RpmOstreeHifInstall {
|
||||
GPtrArray *packages_requested;
|
||||
/* Target state */
|
||||
GPtrArray *packages_to_download;
|
||||
guint64 n_bytes_to_fetch;
|
||||
|
||||
/* Current state */
|
||||
guint n_packages_fetched;
|
||||
guint64 n_bytes_fetched;
|
||||
};
|
||||
|
||||
typedef struct RpmOstreeHifInstall RpmOstreeHifInstall;
|
||||
|
||||
struct RpmOstreePackageDownloadMetrics {
|
||||
guint64 bytes;
|
||||
};
|
||||
|
||||
typedef struct RpmOstreePackageDownloadMetrics RpmOstreePackageDownloadMetrics;
|
||||
|
||||
HifContext *_rpmostree_libhif_new_default (void);
|
||||
|
||||
HifContext *_rpmostree_libhif_new (int rpmmd_cache_dfd,
|
||||
const char *installroot,
|
||||
const char *repos_dir,
|
||||
const char *const *enabled_repos,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void _rpmostree_reset_rpm_sighandlers (void);
|
||||
|
||||
void _rpmostree_libhif_set_cache_dfd (HifContext *hifctx, int dfd);
|
||||
|
||||
rpmts _rpmostree_libhif_ts_new (HifContext *hifctx);
|
||||
|
||||
gboolean _rpmostree_libhif_setup (HifContext *context,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void _rpmostree_libhif_repos_disable_all (HifContext *context);
|
||||
|
||||
void _rpmostree_libhif_set_ostree_repo (HifContext *context);
|
||||
|
||||
void _rpmostree_hif_add_checksum_goal (GChecksum *checksum, HyGoal goal);
|
||||
char * _rpmostree_hif_checksum_goal (GChecksumType type, HyGoal goal);
|
||||
|
||||
char *_rpmostree_get_cache_branch_header (Header hdr);
|
||||
char *_rpmostree_get_cache_branch_pkg (HyPackage pkg);
|
||||
|
||||
gboolean _rpmostree_libhif_repos_enable_by_name (HifContext *context,
|
||||
const char *name,
|
||||
GError **error);
|
||||
|
||||
gboolean _rpmostree_libhif_console_download_metadata (HifContext *context,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* This API allocates an install context, use with one of the later ones */
|
||||
gboolean _rpmostree_libhif_console_prepare_install (HifContext *context,
|
||||
OstreeRepo *repo,
|
||||
const char *const *packages,
|
||||
struct RpmOstreeHifInstall *out_install,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean _rpmostree_libhif_console_download_rpms (HifContext *context,
|
||||
int target_dfd,
|
||||
struct RpmOstreeHifInstall *install,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean _rpmostree_libhif_console_download_import (HifContext *context,
|
||||
OstreeRepo *repo,
|
||||
struct RpmOstreeHifInstall *install,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean _rpmostree_libhif_console_assemble_commit (HifContext *context,
|
||||
int tmpdir_dfd,
|
||||
OstreeRepo *ostreerepo,
|
||||
const char *name,
|
||||
struct RpmOstreeHifInstall *install,
|
||||
char **out_commit,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
static inline void
|
||||
_rpmostree_hif_install_cleanup (struct RpmOstreeHifInstall *hifinst)
|
||||
{
|
||||
g_clear_pointer (&hifinst->packages_requested, g_ptr_array_unref);
|
||||
g_clear_pointer (&hifinst->packages_to_download, g_ptr_array_unref);
|
||||
}
|
||||
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(RpmOstreeHifInstall, _rpmostree_hif_install_cleanup)
|
||||
|
180
src/libpriv/rpmostree-ostree-libarchive-copynpaste.c
Normal file
180
src/libpriv/rpmostree-ostree-libarchive-copynpaste.c
Normal file
@ -0,0 +1,180 @@
|
||||
/* -*- 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 <string.h>
|
||||
|
||||
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;
|
||||
}
|
44
src/libpriv/rpmostree-ostree-libarchive-copynpaste.h
Normal file
44
src/libpriv/rpmostree-ostree-libarchive-copynpaste.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2015 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* 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 <ostree.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
#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);
|
@ -32,7 +32,10 @@
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/capability.h>
|
||||
#include <gio/gunixinputstream.h>
|
||||
#include "rpmostree-unpacker.h"
|
||||
#include "rpmostree-hif.h"
|
||||
#include "rpmostree-ostree-libarchive-copynpaste.h"
|
||||
#include <rpm/rpmlib.h>
|
||||
#include <rpm/rpmlog.h>
|
||||
#include <rpm/rpmfi.h>
|
||||
@ -54,8 +57,11 @@ struct RpmOstreeUnpacker
|
||||
gboolean owns_fd;
|
||||
Header hdr;
|
||||
rpmfi fi;
|
||||
off_t cpio_offset;
|
||||
GHashTable *fscaps;
|
||||
RpmOstreeUnpackerFlags flags;
|
||||
|
||||
char *ostree_branch;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(RpmOstreeUnpacker, rpmostree_unpacker, G_TYPE_OBJECT)
|
||||
@ -76,8 +82,9 @@ rpmostree_unpacker_finalize (GObject *object)
|
||||
archive_read_free (self->archive);
|
||||
if (self->fi)
|
||||
(void) rpmfiFree (self->fi);
|
||||
if (self->owns_fd)
|
||||
if (self->owns_fd && self->fd != -1)
|
||||
(void) close (self->fd);
|
||||
g_free (self->ostree_branch);
|
||||
|
||||
G_OBJECT_CLASS (rpmostree_unpacker_parent_class)->finalize (object);
|
||||
}
|
||||
@ -153,19 +160,24 @@ rpm2cpio (int fd, GError **error)
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rpm_parse_hdr_fi (int fd, rpmfi *out_fi, Header *out_header,
|
||||
GError **error)
|
||||
gboolean
|
||||
rpmostree_unpacker_read_metainfo (int fd,
|
||||
Header *out_header,
|
||||
gsize *out_cpio_offset,
|
||||
rpmfi *out_fi,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autofree char *abspath = g_strdup_printf ("/proc/self/fd/%d", fd);
|
||||
FD_t rpmfd;
|
||||
rpmfi fi = NULL;
|
||||
Header hdr = NULL;
|
||||
rpmts ts = NULL;
|
||||
FD_t rpmfd;
|
||||
int r;
|
||||
Header ret_header = NULL;
|
||||
rpmfi ret_fi = NULL;
|
||||
gsize ret_cpio_offset;
|
||||
g_autofree char *abspath = g_strdup_printf ("/proc/self/fd/%d", fd);
|
||||
|
||||
ts = rpmtsCreate ();
|
||||
_rpmostree_reset_rpm_sighandlers ();
|
||||
rpmtsSetVSFlags (ts, _RPMVSF_NOSIGNATURES);
|
||||
|
||||
/* librpm needs Fopenfd */
|
||||
@ -187,7 +199,7 @@ rpm_parse_hdr_fi (int fd, rpmfi *out_fi, Header *out_header,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = rpmReadPackageFile (ts, rpmfd, abspath, &hdr)) != RPMRC_OK)
|
||||
if ((r = rpmReadPackageFile (ts, rpmfd, abspath, &ret_header)) != RPMRC_OK)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
@ -196,17 +208,27 @@ rpm_parse_hdr_fi (int fd, rpmfi *out_fi, Header *out_header,
|
||||
abspath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret_cpio_offset = Ftell (rpmfd);
|
||||
|
||||
fi = rpmfiNew (ts, hdr, RPMTAG_BASENAMES, (RPMFI_NOHEADER | RPMFI_FLAGS_INSTALL));
|
||||
fi = rpmfiInit (fi, 0);
|
||||
*out_fi = g_steal_pointer (&fi);
|
||||
*out_header = g_steal_pointer (&hdr);
|
||||
if (out_fi)
|
||||
{
|
||||
ret_fi = rpmfiNew (ts, ret_header, RPMTAG_BASENAMES, (RPMFI_NOHEADER | RPMFI_FLAGS_INSTALL));
|
||||
ret_fi = rpmfiInit (ret_fi, 0);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
if (out_header)
|
||||
*out_header = g_steal_pointer (&ret_header);
|
||||
if (out_fi)
|
||||
*out_fi = g_steal_pointer (&ret_fi);
|
||||
if (out_cpio_offset)
|
||||
*out_cpio_offset = ret_cpio_offset;
|
||||
out:
|
||||
if (fi != NULL)
|
||||
rpmfiFree (fi);
|
||||
if (hdr != NULL)
|
||||
headerFree (hdr);
|
||||
if (ret_header)
|
||||
headerFree (ret_header);
|
||||
if (rpmfd)
|
||||
(void) Fclose (rpmfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -217,13 +239,13 @@ rpmostree_unpacker_new_fd (int fd, RpmOstreeUnpackerFlags flags, GError **error)
|
||||
Header hdr = NULL;
|
||||
rpmfi fi = NULL;
|
||||
struct archive *archive;
|
||||
gsize cpio_offset;
|
||||
|
||||
archive = rpm2cpio (fd, error);
|
||||
if (archive == NULL)
|
||||
goto out;
|
||||
|
||||
rpm_parse_hdr_fi (fd, &fi, &hdr, error);
|
||||
if (fi == NULL)
|
||||
if (!rpmostree_unpacker_read_metainfo (fd, &hdr, &cpio_offset, &fi, error))
|
||||
goto out;
|
||||
|
||||
ret = g_object_new (RPMOSTREE_TYPE_UNPACKER, NULL);
|
||||
@ -231,6 +253,8 @@ rpmostree_unpacker_new_fd (int fd, RpmOstreeUnpackerFlags flags, GError **error)
|
||||
ret->fi = g_steal_pointer (&fi);
|
||||
ret->archive = g_steal_pointer (&archive);
|
||||
ret->flags = flags;
|
||||
ret->hdr = g_steal_pointer (&hdr);
|
||||
ret->cpio_offset = cpio_offset;
|
||||
|
||||
out:
|
||||
if (archive)
|
||||
@ -267,6 +291,68 @@ 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)
|
||||
{
|
||||
int r;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *self,
|
||||
int rootfs_fd,
|
||||
@ -274,120 +360,101 @@ rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *self,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
while (rpmfiNext (self->fi) >= 0)
|
||||
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;
|
||||
rpmfileAttrs fflags = rpmfiFFlags (self->fi);
|
||||
rpm_mode_t fmode = rpmfiFMode (self->fi);
|
||||
rpm_loff_t fsize = rpmfiFSize (self->fi);
|
||||
const char *hardlink;
|
||||
const char *fn = rpmfiFN (self->fi);
|
||||
const char *fuser = rpmfiFUser (self->fi);
|
||||
const char *fgroup = rpmfiFGroup (self->fi);
|
||||
const char *fcaps = rpmfiFCaps (self->fi);
|
||||
const struct stat *archive_st;
|
||||
g_autofree char *dname = NULL;
|
||||
glnx_fd_close int destfd = -1;
|
||||
mode_t fmode;
|
||||
uid_t owner_uid = 0;
|
||||
gid_t owner_gid = 0;
|
||||
glnx_fd_close int destfd = -1;
|
||||
const struct stat *archive_st;
|
||||
const char *hardlink;
|
||||
rpmfi fi = NULL;
|
||||
|
||||
r = archive_read_next_header (self->archive, &entry);
|
||||
if (r == ARCHIVE_EOF)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Unexpected end of RPM cpio stream");
|
||||
goto out;
|
||||
}
|
||||
else if (r != ARCHIVE_OK)
|
||||
{
|
||||
propagate_libarchive_error (error, self->archive);
|
||||
goto out;
|
||||
}
|
||||
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);
|
||||
|
||||
g_print ("%s %s:%s mode=%u size=%" G_GUINT64_FORMAT " attrs=%u",
|
||||
fn, fuser, fgroup, fmode, (guint64) fsize, fflags);
|
||||
if (fcaps)
|
||||
g_print (" fcaps=\"%s\"", fcaps);
|
||||
g_print ("\n");
|
||||
|
||||
if (fn[0] == '/')
|
||||
fn += 1;
|
||||
dname = dirname (g_strdup (fn));
|
||||
|
||||
/* Ensure parent directories exist */
|
||||
if (!glnx_shutil_mkdir_p_at (rootfs_fd, dname, 0755, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (archive_st->st_mode != fmode)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Corrupted RPM (mode: fi=%u, archive=%u", fmode, archive_st->st_mode);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((self->flags & RPMOSTREE_UNPACKER_FLAGS_OWNER) > 0)
|
||||
{
|
||||
struct passwd *pwent;
|
||||
struct group *grent;
|
||||
|
||||
pwent = getpwnam (fuser);
|
||||
if (pwent == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Unknown user '%s'", fuser);
|
||||
goto out;
|
||||
}
|
||||
owner_uid = pwent->pw_uid;
|
||||
|
||||
grent = getgrnam (fgroup);
|
||||
if (grent == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Unknown group '%s'", fgroup);
|
||||
goto out;
|
||||
}
|
||||
owner_gid = grent->gr_gid;
|
||||
}
|
||||
|
||||
hardlink = archive_entry_hardlink (entry);
|
||||
if (hardlink)
|
||||
{
|
||||
if (hardlink[0] == '/')
|
||||
hardlink++;
|
||||
if (linkat (rootfs_fd, hardlink, rootfs_fd, fn, 0) < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
g_hash_table_insert (hardlinks, g_strdup (hardlink), g_strdup (fn));
|
||||
continue;
|
||||
}
|
||||
else if (S_ISDIR (fmode))
|
||||
|
||||
/* Don't try to mkdir parents of "" (originally /) */
|
||||
if (fn[0])
|
||||
{
|
||||
g_assert (fn[0] != '/');
|
||||
if (!glnx_shutil_mkdir_p_at (rootfs_fd, fn, fmode, cancellable, error))
|
||||
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 (rpmfiFLink (self->fi), rootfs_fd, 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 = fsize;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -413,39 +480,55 @@ rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *self,
|
||||
}
|
||||
remain -= size;
|
||||
}
|
||||
if (remain > 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Unexpected end of RPM cpio stream reading '%s'", fn);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_FAILED,
|
||||
"RPM contains non-regular/non-symlink file %s",
|
||||
fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((self->flags & RPMOSTREE_UNPACKER_FLAGS_OWNER) > 0 &&
|
||||
fchownat (rootfs_fd, fn, owner_uid, owner_gid, AT_SYMLINK_NOFOLLOW) < 0)
|
||||
if (fi != NULL && (self->flags & RPMOSTREE_UNPACKER_FLAGS_OWNER) > 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
g_prefix_error (error, "fchownat: ");
|
||||
goto out;
|
||||
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))
|
||||
{
|
||||
g_assert (destfd != -1);
|
||||
|
||||
if ((self->flags & RPMOSTREE_UNPACKER_FLAGS_SUID_FSCAPS) == 0)
|
||||
fmode &= 0777;
|
||||
else
|
||||
else if (fi != NULL)
|
||||
{
|
||||
const char *fcaps = rpmfiFCaps (fi);
|
||||
if (fcaps != NULL && fcaps[0])
|
||||
{
|
||||
cap_t caps = cap_from_text (fcaps);
|
||||
@ -457,7 +540,7 @@ rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (fchmod (destfd, fmode) < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
@ -466,7 +549,451 @@ rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *self,
|
||||
}
|
||||
}
|
||||
|
||||
{ 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)
|
||||
{
|
||||
if (!self->ostree_branch)
|
||||
self->ostree_branch = _rpmostree_get_cache_branch_header (self->hdr);
|
||||
|
||||
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,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
glnx_unref_object GInputStream *uin = NULL;
|
||||
g_autofree char *buf = NULL;
|
||||
char *bufp;
|
||||
size_t bytes_remaining;
|
||||
|
||||
/* Inline a pread() based reader here to avoid affecting the file
|
||||
* offset since both librpm and libarchive have references.
|
||||
*/
|
||||
buf = g_malloc (self->cpio_offset);
|
||||
bufp = buf;
|
||||
bytes_remaining = self->cpio_offset;
|
||||
while (bytes_remaining > 0)
|
||||
{
|
||||
ssize_t bytes_read;
|
||||
do
|
||||
bytes_read = pread (self->fd, bufp, bytes_remaining, bufp - buf);
|
||||
while (bytes_read < 0 && errno == EINTR);
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
bufp += bytes_read;
|
||||
bytes_remaining -= bytes_read;
|
||||
}
|
||||
if (bytes_remaining > 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Failed to read %" G_GUINT64_FORMAT " bytes of metadata",
|
||||
bytes_remaining);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
*out_metadata = g_bytes_new_take (g_steal_pointer (&buf), self->cpio_offset);
|
||||
out:
|
||||
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;
|
||||
|
||||
g_variant_builder_init (&metadata_builder, (GVariantType*)"a{sv}");
|
||||
|
||||
rpmfi_overrides = build_rpmfi_overrides (self);
|
||||
|
||||
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;
|
||||
|
||||
g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.metadata",
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
goto out;
|
||||
|
||||
ostree_repo_transaction_set_ref (repo, NULL,
|
||||
rpmostree_unpacker_get_ostree_branch (self),
|
||||
commit_checksum);
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
*out_commit = g_steal_pointer (&commit_checksum);
|
||||
out:
|
||||
ostree_repo_abort_transaction (repo, cancellable, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <ostree.h>
|
||||
|
||||
#include "libglnx.h"
|
||||
#include <rpm/rpmlib.h>
|
||||
|
||||
typedef struct RpmOstreeUnpacker RpmOstreeUnpacker;
|
||||
|
||||
@ -36,7 +37,7 @@ typedef enum {
|
||||
RPMOSTREE_UNPACKER_FLAGS_SUID_FSCAPS = (1 << 0),
|
||||
RPMOSTREE_UNPACKER_FLAGS_OWNER = (1 << 1)
|
||||
} RpmOstreeUnpackerFlags;
|
||||
#define RPMOSTREE_UNPACKER_FLAGS_ALL = (RPMOSTREE_UNPACKER_SUID_FSCAPS | RPMOSTREE_UNPACKER_OWNER)
|
||||
#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);
|
||||
|
||||
@ -46,3 +47,19 @@ gboolean rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *unpacker,
|
||||
int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
rpmostree_unpacker_read_metainfo (int fd,
|
||||
Header *out_header,
|
||||
gsize *out_cpio_offset,
|
||||
rpmfi *out_fi,
|
||||
GError **error);
|
||||
|
||||
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);
|
||||
|
@ -66,6 +66,22 @@ _rpmostree_perror_fatal (const char *message)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
GVariant *
|
||||
_rpmostree_vardict_lookup_value_required (GVariantDict *dict,
|
||||
const char *key,
|
||||
const GVariantType *fmt,
|
||||
GError **error)
|
||||
{
|
||||
GVariant *r = g_variant_dict_lookup_value (dict, key, fmt);
|
||||
if (!r)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Failed to find metadata key %s (signature %s)", key, (char*)fmt);
|
||||
return NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
gboolean
|
||||
rpmostree_mkdtemp (const char *template,
|
||||
char **out_tmpdir,
|
||||
|
@ -37,6 +37,11 @@ _rpmostree_set_prefix_error_from_errno (GError **error,
|
||||
|
||||
void _rpmostree_perror_fatal (const char *message) __attribute__ ((noreturn));
|
||||
|
||||
GVariant *_rpmostree_vardict_lookup_value_required (GVariantDict *dict,
|
||||
const char *key,
|
||||
const GVariantType *fmt,
|
||||
GError **error);
|
||||
|
||||
gboolean rpmostree_mkdtemp (const char *template,
|
||||
char **out_tmpdir,
|
||||
int *out_tmpdir_dfd, /* allow-none */
|
||||
|
Loading…
Reference in New Issue
Block a user