[PATCH 2/7] Add rpm extraction routines (use librpm and libarchive)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 anaconda.spec.in    |    3 +
 loader/Makefile.am  |    6 +-
 loader/rpmextract.c |  325 +++++++++++++++++++++++++++++++++++++++++++++++++++
 loader/rpmextract.h |   45 +++++++
 4 files changed, 377 insertions(+), 2 deletions(-)
 create mode 100644 loader/rpmextract.c
 create mode 100644 loader/rpmextract.h

diff --git a/anaconda.spec.in b/anaconda.spec.in
index c1689ae..8133451 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -55,6 +55,7 @@ BuildRequires: gettext >= %{gettextver}
 BuildRequires: gtk2-devel
 BuildRequires: intltool >= %{intltoolver}
 BuildRequires: isomd5sum-devel
+BuildRequires: libarchive-devel
 BuildRequires: libX11-devel
 BuildRequires: libXt-devel
 BuildRequires: libXxf86misc-devel
@@ -69,6 +70,7 @@ BuildRequires: pango-devel
 BuildRequires: pykickstart >= %{pykickstartver}
 BuildRequires: python-devel
 BuildRequires: python-urlgrabber
+BuildRequires: rpm-devel
 BuildRequires: rpm-python >= %{rpmpythonver}
 BuildRequires: slang-devel >= %{slangver}
 BuildRequires: xmlto
@@ -103,6 +105,7 @@ Requires: device-mapper-libs >= %{dmver}
 Requires: dosfstools
 Requires: e2fsprogs >= %{e2fsver}
 Requires: gzip
+Requires: libarchive
 %ifarch %{ix86} x86_64 ia64
 Requires: dmidecode
 %endif
diff --git a/loader/Makefile.am b/loader/Makefile.am
index e5d1b7f..e9e12c9 100644
--- a/loader/Makefile.am
+++ b/loader/Makefile.am
@@ -45,13 +45,15 @@ loader_CFLAGS      = $(COMMON_CFLAGS) $(GLIB_CFLAGS) $(LIBNM_GLIB_CFLAGS) \
 loader_LDADD       = $(NEWT_LIBS) $(GLIB_LIBS) $(LIBNL_LIBS) \
                      $(LIBNM_GLIB_LIBS) $(CHECKISOMD5_LIBS) \
                      $(LIBCURL_LIBS) \
-                     $(ISCSI_LIBS) $(top_srcdir)/isys/libisys.la
+                     $(ISCSI_LIBS) $(top_srcdir)/isys/libisys.la \
+                     -lrpm -lrpmio -larchive
 loader_SOURCES     = loader.c copy.c log.c moduleinfo.c loadermisc.c \
                      modules.c windows.c lang.c kbd.c driverdisk.c \
                      selinux.c mediacheck.c kickstart.c driverselect.c \
                      getparts.c dirbrowser.c fwloader.c ibft.c hardware.c \
                      method.c cdinstall.c hdinstall.c nfsinstall.c \
-                     urlinstall.c net.c urls.c telnet.c telnetd.c
+                     urlinstall.c net.c urls.c telnet.c telnetd.c \
+                     rpmextract.c
 
 init_CFLAGS        = $(COMMON_CFLAGS)
 init_SOURCES       = init.c undomounts.c shutdown.c copy.c
diff --git a/loader/rpmextract.c b/loader/rpmextract.c
new file mode 100644
index 0000000..64d5d7d
--- /dev/null
+++ b/loader/rpmextract.c
@@ -0,0 +1,325 @@
+/* unpack the payload of RPM package to the current directory
+ * 
+ * File name: rpmextract.c
+ * Date:      2009/12/18
+ * Author:    Martin Sivak <msivak at redhat dot com>
+ * 
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rpm/rpmlib.h>		/* rpmReadPackageFile .. */
+#include <rpm/rpmtag.h>
+#include <rpm/rpmio.h>
+#include <rpm/rpmpgp.h>
+
+#include <rpm/rpmts.h>
+
+#include <stdio.h>
+#include <archive.h>
+#include <archive_entry.h>
+
+#include "loader.h"
+#include "log.h"
+#include "rpmextract.h"
+
+/*
+ * internal structure to pass to libarchive callbacks
+ */
+
+struct cpio_mydata {
+    FD_t gzdi;
+    char *buffer;
+};
+
+/*
+ * libarchive callbacks
+ */
+
+ssize_t rpm_myread(struct archive *a, void *client_data, const void **buff)
+{
+    struct cpio_mydata *mydata = client_data;
+    *buff = mydata->buffer;
+    return Fread(mydata->buffer, BUFFERSIZE, 1, mydata->gzdi);
+}
+
+int rpm_myclose(struct archive *a, void *client_data)
+{
+    struct cpio_mydata *mydata = client_data;
+    if (mydata->gzdi > 0)
+        Fclose(mydata->gzdi);
+    return ARCHIVE_OK;
+}
+
+/* read data from RPM header */
+
+const char * headerGetString(Header h, rpmTag tag)
+{
+    const char *res = NULL;
+    struct rpmtd_s td;
+
+    if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
+        if (rpmtdCount(&td) == 1) {
+            res = rpmtdGetString(&td);
+        }
+        rpmtdFreeData(&td);
+    }
+    return res;
+}
+
+/*
+ * explode source RPM into the current directory
+ * use filters to skip packages and files we do not need
+ */
+int explodeRPM(const char *source,
+        filterfunc filter,
+        dependencyfunc provides,
+        dependencyfunc deps,
+        void* userptr)
+{
+    char buffer[BUFFERSIZE+1]; /* make space for trailing \0 */
+    FD_t fdi;
+    Header h;
+    char * rpmio_flags = NULL;
+    rpmRC rc;
+    FD_t gzdi;
+    struct archive *cpio;
+    struct archive_entry *cpio_entry;
+    struct cpio_mydata cpio_mydata;
+
+    rpmts ts;
+    rpmVSFlags vsflags;
+    const char *compr;
+
+    if (strcmp(source, "-") == 0)
+        fdi = fdDup(STDIN_FILENO);
+    else
+        fdi = Fopen(source, "r.ufdio");
+
+    if (Ferror(fdi)) {
+        const char *srcname = (strcmp(source, "-") == 0) ? "<stdin>" : source;
+        logMessage(ERROR, "%s: %s\n", srcname, Fstrerror(fdi));
+        return EXIT_FAILURE;
+    }
+    rpmReadConfigFiles(NULL, NULL);
+
+    /* Initialize RPM transaction */
+    ts = rpmtsCreate();
+    vsflags = 0;
+
+    /* Do not check digests, signatures or headers */
+    vsflags |= _RPMVSF_NODIGESTS;
+    vsflags |= _RPMVSF_NOSIGNATURES;
+    vsflags |= RPMVSF_NOHDRCHK;
+    (void) rpmtsSetVSFlags(ts, vsflags);
+
+    rc = rpmReadPackageFile(ts, fdi, "rpm2dir", &h);
+
+    ts = rpmtsFree(ts);
+
+    switch (rc) {
+        case RPMRC_OK:
+        case RPMRC_NOKEY:
+        case RPMRC_NOTTRUSTED:
+            break;
+        case RPMRC_NOTFOUND:
+            logMessage(ERROR, "%s is not an RPM package", source);
+            return EXIT_FAILURE;
+            break;
+        case RPMRC_FAIL:
+        default:
+            logMessage(ERROR, "error reading header from %s package\n", source);
+            return EXIT_FAILURE;
+            break;
+    }
+
+    /* Retrieve all dependencies and run them through deps function */
+    while (deps) {
+        struct rpmtd_s td;
+        const char *depname;
+
+        if (!headerGet(h, RPMTAG_REQUIRENAME, &td, HEADERGET_MINMEM))
+            break;
+
+        /* iterator */
+        while ((depname = rpmtdNextString(&td))) {
+            if (deps(depname, userptr)) {
+                Fclose(fdi);
+                return EXIT_BADDEPS;
+            }
+        }
+        rpmtdFreeData(&td);
+        break;
+    }
+
+    /* Retrieve all provides and run them through provides function */
+    while (provides) {
+        struct rpmtd_s td;
+        const char *depname;
+        int found = 0;
+
+        if (!headerGet(h, RPMTAG_PROVIDES, &td, HEADERGET_MINMEM))
+            break;
+
+        /* iterator */
+        while ((depname = rpmtdNextString(&td))) {
+            if (!provides(depname, userptr)) {
+                found++;
+            }
+        }
+        rpmtdFreeData(&td);
+        if (found<=0)
+            return EXIT_BADDEPS;
+        break;
+    }
+
+    /* Retrieve type of payload compression. */
+    compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
+    if (compr && strcmp(compr, "gzip")) {
+        checked_asprintf(&rpmio_flags, "r.%sdio", compr);
+    }
+    else {
+        checked_asprintf(&rpmio_flags, "r.gzdio");
+    }
+
+    /* Open uncompressed cpio stream */
+    gzdi = Fdopen(fdi, rpmio_flags);
+    free(rpmio_flags);
+
+    if (gzdi == NULL) {
+        logMessage(ERROR, "cannot re-open payload: %s\n", Fstrerror(gzdi));
+        return EXIT_FAILURE;
+    }
+
+    /* initialize cpio decompressor */
+    cpio = archive_read_new();
+    if (cpio==NULL) {
+        Fclose(gzdi);
+        return -1;
+    }
+
+    cpio_mydata.gzdi = gzdi;
+    cpio_mydata.buffer = buffer;
+    archive_read_support_compression_all(cpio);
+    archive_read_support_format_all(cpio);
+    rc = archive_read_open(cpio, &cpio_mydata, NULL, rpm_myread, rpm_myclose);
+
+    /* check the status of archive_open */
+    if (rc != ARCHIVE_OK){
+        Fclose(gzdi);
+        return -1;
+    }
+
+    /* read all files in cpio archive */
+    while ((rc = archive_read_next_header(cpio, &cpio_entry)) == ARCHIVE_OK){
+        const struct stat *fstat;
+        int64_t fsize;
+        const char* filename;
+        int needskip = 1; /* do we need to read the data to get to the next header? */
+        int offset = 0;
+        int towrite = 0;
+
+        filename = archive_entry_pathname(cpio_entry);
+        fstat = archive_entry_stat(cpio_entry);
+        fsize = archive_entry_size(cpio_entry);
+
+        /* Strip leading slashes */
+        while (filename[offset] == '/')
+            offset+=1;
+
+        /* Strip leading ./ */
+        while (filename[offset] == '.' && filename[offset+1] == '/')
+            offset+=2;
+
+        /* Other file type - we do not care except special cases */
+        if (!S_ISREG(fstat->st_mode))
+            towrite = 1;
+        else
+            towrite = 2;
+
+        if (filter && filter(filename+offset, fstat, userptr)) {
+            /* filter this file */
+            towrite = 0;
+        }
+
+        /* Create directories */
+        char* dirname = strdup(filename+offset);
+
+        /* If the dup fails, let's hope the dirs already exist */
+        if (dirname){
+            char* dirptr = dirname;
+            while (dirptr && *dirptr) {
+                dirptr = strchr(dirptr, '/');
+                if (dirptr) {
+                    *dirptr = 0;
+                    mkdir(dirname, 0700);
+                    *dirptr = '/';
+                    dirptr++;
+                }
+            }
+            free(dirname);
+        }
+
+        /* Regular file */
+        if (towrite>=2) {
+            FILE *fdout = fopen(filename+offset, "w");
+
+            if (fdout==NULL){
+                free((void*)filename);
+                rc = 33;
+                break;
+            }
+            
+            rc = archive_read_data_into_fd(cpio, fileno(fdout));
+            if (rc!=ARCHIVE_OK) {
+                /* XXX We didn't get the file.. well.. */
+                needskip = 0;
+            } else {
+                needskip = 0;
+                fclose(fdout);
+            }
+        }
+
+        /* symlink, we assume that the path contained in symlink
+         * is shorter than BUFFERSIZE */
+        while (towrite && S_ISLNK(fstat->st_mode)) {
+            char symlinkbuffer[BUFFERSIZE-1];
+
+            needskip = 0;
+            if ((rc = archive_read_data(cpio, symlinkbuffer, fsize))!=ARCHIVE_OK) {
+                /* XXX We didn't get the file.. well.. */
+                break;
+            }
+
+            if (symlink(buffer, filename+offset)) {
+                logMessage(ERROR, "Failed to create symlink %s -> %s", filename+offset, buffer);
+            }
+
+            break;
+        }
+
+        if(needskip)
+            archive_read_data_skip(cpio);
+    }
+
+    archive_read_finish(cpio);
+
+    return rc != ARCHIVE_OK;
+}
diff --git a/loader/rpmextract.h b/loader/rpmextract.h
new file mode 100644
index 0000000..53a90cf
--- /dev/null
+++ b/loader/rpmextract.h
@@ -0,0 +1,45 @@
+/*
+   File name: rpmextract.h
+   Date:      2009/09/16
+   Author:    msivak
+
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef __RPMEXTRACT_H__
+#define __RPMEXTRACT_H__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define EXIT_BADDEPS 4
+#define BUFFERSIZE 1024
+
+/* both filter functions return 0 - match, 1 - match not found */
+typedef int (*filterfunc)(const char* name, const struct stat *fstat, void *userptr);
+typedef int (*dependencyfunc)(const char* depends, void *userptr);
+
+int explodeRPM(const char* file,
+               filterfunc filter,
+               dependencyfunc provides,
+               dependencyfunc deps,
+               void* userptr);
+
+#endif
+
+/* end of rpmextract.h */
-- 
1.6.4.4

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list

[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux