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
This commit is contained in:
Jonathan Lebon 2016-05-20 14:14:47 -04:00 committed by Atomic Bot
parent 2a036cf8b4
commit ca162dece2
8 changed files with 349 additions and 1158 deletions

View File

@ -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)

View File

@ -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,13 +58,14 @@ 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,
@ -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:

View File

@ -1,185 +0,0 @@
/* -*- 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;
}

View File

@ -1,65 +0,0 @@
/* -*- 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

View File

@ -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 <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;
}

View File

@ -1,44 +0,0 @@
/* -*- 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);

View File

@ -36,7 +36,6 @@
#include "rpmostree-unpacker.h"
#include "rpmostree-core.h"
#include "rpmostree-rpm-util.h"
#include "rpmostree-ostree-libarchive-copynpaste.h"
#include <rpm/rpmlib.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmfi.h>
@ -47,6 +46,7 @@
#include <string.h>
#include <stdlib.h>
#include <selinux/selinux.h>
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;
@ -87,6 +87,8 @@ rpmostree_unpacker_finalize (GObject *object)
(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)
{
@ -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);
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;
/* 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;
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)
/* 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))
{
struct archive_entry *entry;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"path \"%s\" marked as %s%s%s)",
path, user, group ? ":" : "", group ?: "");
}
if (!next_archive_entry (self->archive, &entry, error))
goto out;
if (entry == NULL)
break;
/* 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 (!import_one_libarchive_entry_to_ostree (self, rpmfi_overrides, repo,
sepolicy, entry, mtree,
default_dir_checksum,
cancellable, error))
goto out;
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;

View File

@ -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);