Merge pull request #204 from cgwalters/unprivileged
Add testing-only `internals` subcommand, with `unpack` verb
This commit is contained in:
commit
5b7e34f427
@ -35,6 +35,8 @@ librpmostreepriv_la_SOURCES = \
|
||||
src/libpriv/rpmostree-cleanup.h \
|
||||
src/libpriv/rpmostree-rpm-util.c \
|
||||
src/libpriv/rpmostree-rpm-util.h \
|
||||
src/libpriv/rpmostree-unpacker.c \
|
||||
src/libpriv/rpmostree-unpacker.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)
|
||||
|
@ -28,12 +28,14 @@ rpm_ostree_SOURCES = src/app/main.c \
|
||||
src/app/rpmostree-builtin-deploy.c \
|
||||
src/app/rpmostree-builtin-rebase.c \
|
||||
src/app/rpmostree-builtin-status.c \
|
||||
src/app/rpmostree-builtin-internals.c \
|
||||
src/app/rpmostree-builtin-db.c \
|
||||
src/app/rpmostree-db-builtin-diff.c \
|
||||
src/app/rpmostree-db-builtin-list.c \
|
||||
src/app/rpmostree-db-builtin-version.c \
|
||||
src/app/rpmostree-dbus-helpers.c \
|
||||
src/app/rpmostree-dbus-helpers.h \
|
||||
src/app/rpmostree-internals-builtin-unpack.c \
|
||||
src/app/rpmostree-libbuiltin.c \
|
||||
src/app/rpmostree-libbuiltin.h \
|
||||
$(NULL)
|
||||
|
2
libglnx
2
libglnx
@ -1 +1 @@
|
||||
Subproject commit 91e060699f5559c49335ce19f4b23ba70dfd6bb3
|
||||
Subproject commit 3c470803d08477dab9c7faada29a5f4c59dd519e
|
@ -43,6 +43,7 @@ static RpmOstreeCommand commands[] = {
|
||||
{ "rollback", rpmostree_builtin_rollback },
|
||||
{ "status", rpmostree_builtin_status },
|
||||
{ "upgrade", rpmostree_builtin_upgrade },
|
||||
{ "internals", rpmostree_builtin_internals },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -74,7 +75,8 @@ option_context_new_with_commands (void)
|
||||
|
||||
while (command->name != NULL)
|
||||
{
|
||||
g_string_append_printf (summary, "\n %s", command->name);
|
||||
if (strcmp (command->name, "internals") != 0)
|
||||
g_string_append_printf (summary, "\n %s", command->name);
|
||||
command++;
|
||||
}
|
||||
|
||||
|
150
src/app/rpmostree-builtin-internals.c
Normal file
150
src/app/rpmostree-builtin-internals.c
Normal file
@ -0,0 +1,150 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "rpmostree-internals-builtins.h"
|
||||
#include "rpmostree-rpm-util.h"
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
int (*fn) (int argc, char **argv, GCancellable *cancellable, GError **error);
|
||||
} RpmOstreeInternalsCommand;
|
||||
|
||||
static RpmOstreeInternalsCommand internals_subcommands[] = {
|
||||
{ "unpack", rpmostree_internals_builtin_unpack },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
static GOptionEntry global_entries[] = {
|
||||
{ NULL }
|
||||
};
|
||||
*/
|
||||
|
||||
static GOptionContext *
|
||||
internals_option_context_new_with_commands (void)
|
||||
{
|
||||
RpmOstreeInternalsCommand *command = internals_subcommands;
|
||||
GOptionContext *context;
|
||||
GString *summary;
|
||||
|
||||
context = g_option_context_new ("COMMAND");
|
||||
|
||||
summary = g_string_new ("Builtin \"internals\" Commands:");
|
||||
|
||||
while (command->name != NULL)
|
||||
{
|
||||
g_string_append_printf (summary, "\n %s", command->name);
|
||||
command++;
|
||||
}
|
||||
|
||||
g_option_context_set_summary (context, summary->str);
|
||||
|
||||
g_string_free (summary, TRUE);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
int
|
||||
rpmostree_builtin_internals (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
RpmOstreeInternalsCommand *subcommand;
|
||||
const char *subcommand_name = NULL;
|
||||
gs_free char *prgname = NULL;
|
||||
int exit_status = EXIT_SUCCESS;
|
||||
int in, out;
|
||||
|
||||
for (in = 1, out = 1; in < argc; in++, out++)
|
||||
{
|
||||
/* The non-option is the command, take it out of the arguments */
|
||||
if (argv[in][0] != '-')
|
||||
{
|
||||
if (subcommand_name == NULL)
|
||||
{
|
||||
subcommand_name = argv[in];
|
||||
out--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
else if (g_str_equal (argv[in], "--"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
argv[out] = argv[in];
|
||||
}
|
||||
|
||||
argc = out;
|
||||
|
||||
subcommand = internals_subcommands;
|
||||
while (subcommand->name)
|
||||
{
|
||||
if (g_strcmp0 (subcommand_name, subcommand->name) == 0)
|
||||
break;
|
||||
subcommand++;
|
||||
}
|
||||
|
||||
if (!subcommand->name)
|
||||
{
|
||||
GOptionContext *context;
|
||||
gs_free char *help = NULL;
|
||||
|
||||
context = internals_option_context_new_with_commands ();
|
||||
|
||||
/* This will not return for some options (e.g. --version). */
|
||||
if (rpmostree_option_context_parse (context, NULL,
|
||||
&argc, &argv,
|
||||
RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
|
||||
cancellable,
|
||||
NULL,
|
||||
error))
|
||||
{
|
||||
if (subcommand_name == NULL)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No \"internals\" subcommand specified");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Unknown \"internals\" subcommand '%s'", subcommand_name);
|
||||
}
|
||||
exit_status = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
help = g_option_context_get_help (context, FALSE, NULL);
|
||||
g_printerr ("%s", help);
|
||||
|
||||
g_option_context_free (context);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
prgname = g_strdup_printf ("%s %s", g_get_prgname (), subcommand_name);
|
||||
g_set_prgname (prgname);
|
||||
|
||||
exit_status = subcommand->fn (argc, argv, cancellable, error);
|
||||
|
||||
out:
|
||||
return exit_status;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ BUILTINPROTO(rebase);
|
||||
BUILTINPROTO(rollback);
|
||||
BUILTINPROTO(status);
|
||||
BUILTINPROTO(db);
|
||||
BUILTINPROTO(internals);
|
||||
|
||||
#undef BUILTINPROTO
|
||||
|
||||
|
103
src/app/rpmostree-internals-builtin-unpack.c
Normal file
103
src/app/rpmostree-internals-builtin-unpack.c
Normal file
@ -0,0 +1,103 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <glib-unix.h>
|
||||
#include <gio/gunixoutputstream.h>
|
||||
#include <libhif.h>
|
||||
#include <libhif/hif-utils.h>
|
||||
#include <rpm/rpmts.h>
|
||||
#include <stdio.h>
|
||||
#include <libglnx.h>
|
||||
#include <rpm/rpmmacro.h>
|
||||
|
||||
#include "rpmostree-internals-builtins.h"
|
||||
#include "rpmostree-util.h"
|
||||
#include "rpmostree-hif.h"
|
||||
#include "rpmostree-cleanup.h"
|
||||
#include "rpmostree-libbuiltin.h"
|
||||
#include "rpmostree-rpm-util.h"
|
||||
#include "rpmostree-unpacker.h"
|
||||
|
||||
#include "libgsystem.h"
|
||||
|
||||
gboolean opt_suid_fcaps = FALSE;
|
||||
gboolean opt_owner = 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 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
int
|
||||
rpmostree_internals_builtin_unpack (int argc,
|
||||
char **argv,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
int exit_status = EXIT_FAILURE;
|
||||
GOptionContext *context = g_option_context_new ("ROOT RPM");
|
||||
RpmOstreeUnpackerFlags flags = 0;
|
||||
glnx_unref_object RpmOstreeUnpacker *unpacker = NULL;
|
||||
const char *rpmpath;
|
||||
glnx_fd_close int rootfs_fd = -1;
|
||||
|
||||
if (!rpmostree_option_context_parse (context,
|
||||
option_entries,
|
||||
&argc, &argv,
|
||||
RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
|
||||
cancellable,
|
||||
NULL,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
rpmostree_usage_error (context, "ROOT and RPM must be specified", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!glnx_opendirat (AT_FDCWD, argv[1], TRUE, &rootfs_fd, error))
|
||||
goto out;
|
||||
|
||||
rpmpath = argv[2];
|
||||
|
||||
/* 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;
|
||||
|
||||
unpacker = rpmostree_unpacker_new_at (AT_FDCWD, rpmpath, flags, error);
|
||||
if (!unpacker)
|
||||
goto out;
|
||||
|
||||
if (!rpmostree_unpacker_unpack_to_dfd (unpacker, rootfs_fd, cancellable, error))
|
||||
goto out;
|
||||
|
||||
exit_status = EXIT_SUCCESS;
|
||||
out:
|
||||
return exit_status;
|
||||
}
|
32
src/app/rpmostree-internals-builtins.h
Normal file
32
src/app/rpmostree-internals-builtins.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2015 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostree.h>
|
||||
|
||||
#include "rpmostree-builtins.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean rpmostree_internals_builtin_unpack (int argc, char **argv, GCancellable *cancellable, GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
472
src/libpriv/rpmostree-unpacker.c
Normal file
472
src/libpriv/rpmostree-unpacker.c
Normal file
@ -0,0 +1,472 @@
|
||||
/* -*- 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 <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/capability.h>
|
||||
#include "rpmostree-unpacker.h"
|
||||
#include <rpm/rpmlib.h>
|
||||
#include <rpm/rpmlog.h>
|
||||
#include <rpm/rpmfi.h>
|
||||
#include <rpm/rpmts.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef GObjectClass RpmOstreeUnpackerClass;
|
||||
|
||||
struct RpmOstreeUnpacker
|
||||
{
|
||||
GObject parent_instance;
|
||||
struct archive *archive;
|
||||
int fd;
|
||||
gboolean owns_fd;
|
||||
Header hdr;
|
||||
rpmfi fi;
|
||||
GHashTable *fscaps;
|
||||
RpmOstreeUnpackerFlags flags;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(RpmOstreeUnpacker, rpmostree_unpacker, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
propagate_libarchive_error (GError **error,
|
||||
struct archive *a)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
archive_error_string (a));
|
||||
}
|
||||
|
||||
static void
|
||||
rpmostree_unpacker_finalize (GObject *object)
|
||||
{
|
||||
RpmOstreeUnpacker *self = (RpmOstreeUnpacker*)object;
|
||||
if (self->archive)
|
||||
archive_read_free (self->archive);
|
||||
if (self->fi)
|
||||
(void) rpmfiFree (self->fi);
|
||||
if (self->owns_fd)
|
||||
(void) close (self->fd);
|
||||
|
||||
G_OBJECT_CLASS (rpmostree_unpacker_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
rpmostree_unpacker_class_init (RpmOstreeUnpackerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = rpmostree_unpacker_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
rpmostree_unpacker_init (RpmOstreeUnpacker *p)
|
||||
{
|
||||
}
|
||||
|
||||
typedef int(*archive_setup_func)(struct archive *);
|
||||
|
||||
/**
|
||||
* rpmostree_rpm2cpio:
|
||||
* @fd: An open file descriptor for an RPM package
|
||||
* @error: GError
|
||||
*
|
||||
* Parse CPIO content of @fd via libarchive. Note that the CPIO data
|
||||
* does not capture all relevant filesystem content; for example,
|
||||
* filesystem capabilities are part of a separate header, etc.
|
||||
*/
|
||||
static struct archive *
|
||||
rpm2cpio (int fd, GError **error)
|
||||
{
|
||||
gboolean success = FALSE;
|
||||
struct archive *ret = NULL;
|
||||
guint i;
|
||||
|
||||
ret = archive_read_new ();
|
||||
g_assert (ret);
|
||||
|
||||
/* We only do the subset necessary for RPM */
|
||||
{ archive_setup_func archive_setup_funcs[] =
|
||||
{ archive_read_support_filter_rpm,
|
||||
archive_read_support_filter_lzma,
|
||||
archive_read_support_filter_gzip,
|
||||
archive_read_support_filter_xz,
|
||||
archive_read_support_filter_bzip2,
|
||||
archive_read_support_format_cpio };
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (archive_setup_funcs); i++)
|
||||
{
|
||||
if (archive_setup_funcs[i](ret) != ARCHIVE_OK)
|
||||
{
|
||||
propagate_libarchive_error (error, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (archive_read_open_fd (ret, fd, 10240) != ARCHIVE_OK)
|
||||
{
|
||||
propagate_libarchive_error (error, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
success = TRUE;
|
||||
out:
|
||||
if (success)
|
||||
return g_steal_pointer (&ret);
|
||||
else
|
||||
{
|
||||
if (ret)
|
||||
(void) archive_read_free (ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rpm_parse_hdr_fi (int fd, rpmfi *out_fi, Header *out_header,
|
||||
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;
|
||||
int r;
|
||||
|
||||
ts = rpmtsCreate ();
|
||||
rpmtsSetVSFlags (ts, _RPMVSF_NOSIGNATURES);
|
||||
|
||||
/* librpm needs Fopenfd */
|
||||
rpmfd = Fopen (abspath, "r.fdio");
|
||||
if (rpmfd == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Failed to open %s", abspath);
|
||||
goto out;
|
||||
}
|
||||
if (Ferror (rpmfd))
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_FAILED,
|
||||
"Opening %s: %s",
|
||||
abspath,
|
||||
Fstrerror (rpmfd));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = rpmReadPackageFile (ts, rpmfd, abspath, &hdr)) != RPMRC_OK)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_FAILED,
|
||||
"Verification of %s failed",
|
||||
abspath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (fi != NULL)
|
||||
rpmfiFree (fi);
|
||||
if (hdr != NULL)
|
||||
headerFree (hdr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RpmOstreeUnpacker *
|
||||
rpmostree_unpacker_new_fd (int fd, RpmOstreeUnpackerFlags flags, GError **error)
|
||||
{
|
||||
RpmOstreeUnpacker *ret = NULL;
|
||||
Header hdr = NULL;
|
||||
rpmfi fi = NULL;
|
||||
struct archive *archive;
|
||||
|
||||
archive = rpm2cpio (fd, error);
|
||||
if (archive == NULL)
|
||||
goto out;
|
||||
|
||||
rpm_parse_hdr_fi (fd, &fi, &hdr, error);
|
||||
if (fi == NULL)
|
||||
goto out;
|
||||
|
||||
ret = g_object_new (RPMOSTREE_TYPE_UNPACKER, NULL);
|
||||
ret->fd = fd;
|
||||
ret->fi = g_steal_pointer (&fi);
|
||||
ret->archive = g_steal_pointer (&archive);
|
||||
ret->flags = flags;
|
||||
|
||||
out:
|
||||
if (archive)
|
||||
archive_read_free (archive);
|
||||
if (hdr)
|
||||
headerFree (hdr);
|
||||
if (fi)
|
||||
rpmfiFree (fi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RpmOstreeUnpacker *
|
||||
rpmostree_unpacker_new_at (int dfd, const char *path, RpmOstreeUnpackerFlags flags, GError **error)
|
||||
{
|
||||
RpmOstreeUnpacker *ret = NULL;
|
||||
glnx_fd_close int fd = -1;
|
||||
|
||||
fd = openat (dfd, path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
|
||||
if (fd < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
g_prefix_error (error, "Opening %s: ", path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = rpmostree_unpacker_new_fd (fd, flags, error);
|
||||
if (ret == NULL)
|
||||
goto out;
|
||||
|
||||
ret->owns_fd = TRUE;
|
||||
fd = -1;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
rpmostree_unpacker_unpack_to_dfd (RpmOstreeUnpacker *self,
|
||||
int rootfs_fd,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
while (rpmfiNext (self->fi) >= 0)
|
||||
{
|
||||
int r;
|
||||
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;
|
||||
uid_t owner_uid = 0;
|
||||
gid_t owner_gid = 0;
|
||||
glnx_fd_close int destfd = -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (S_ISDIR (fmode))
|
||||
{
|
||||
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)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (S_ISREG (fmode))
|
||||
{
|
||||
size_t remain = fsize;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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_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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
48
src/libpriv/rpmostree-unpacker.h
Normal file
48
src/libpriv/rpmostree-unpacker.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* -*- 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 "libglnx.h"
|
||||
|
||||
typedef struct RpmOstreeUnpacker RpmOstreeUnpacker;
|
||||
|
||||
#define RPMOSTREE_TYPE_UNPACKER (rpmostree_unpacker_get_type ())
|
||||
#define RPMOSTREE_UNPACKER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), RPMOSTREE_TYPE_UNPACKER, RpmOstreeUnpacker))
|
||||
#define RPMOSTREE_IS_UNPACKER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), RPMOSTREE_TYPE_UNPACKER))
|
||||
|
||||
GType rpmostree_unpacker_get_type (void);
|
||||
|
||||
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)
|
||||
|
||||
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);
|
Loading…
Reference in New Issue
Block a user