- adds the code to unpack RPMs which provide specific Provide: - adds the code to unpack cpio directly from the RPM - adds the dependency on librpm --- anaconda.spec.in | 1 + loader/Makefile.am | 6 +- loader/cpio.c | 192 +++++++++++++++++++++++++++++++++++++++++++ loader/cpio.h | 116 ++++++++++++++++++++++++++ loader/rpmextract.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++ loader/rpmextract.h | 47 +++++++++++ 6 files changed, 587 insertions(+), 2 deletions(-) create mode 100644 loader/cpio.c create mode 100644 loader/cpio.h create mode 100644 loader/rpmextract.c create mode 100644 loader/rpmextract.h diff --git a/anaconda.spec.in b/anaconda.spec.in index 862e306..8344105 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -69,6 +69,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 diff --git a/loader/Makefile.am b/loader/Makefile.am index e5d1b7f..2d3b39b 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 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 \ + cpio.c rpmextract.c init_CFLAGS = $(COMMON_CFLAGS) init_SOURCES = init.c undomounts.c shutdown.c copy.c diff --git a/loader/cpio.c b/loader/cpio.c new file mode 100644 index 0000000..36335fc --- /dev/null +++ b/loader/cpio.c @@ -0,0 +1,192 @@ +/** \ingroup payload + * \file lib/cpio.c + * Handle cpio payloads within rpm packages. + * + * \warning FIXME: We don't translate between cpio and system mode bits! These + * should both be the same, but really odd things are going to happen if + * that's not true! + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#include <rpm/rpmio.h> +#include <rpm/rpmlog.h> +#include <rpm/header.h> + +#include "cpio.h" + + +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; +} + +/** + * Convert string to unsigned integer (with buffer size check). + * @param str input string + * @retval endptr address of 1st character not processed + * @param base numerical conversion base + * @param num max no. of bytes to read + * @return converted integer + */ +static unsigned long strntoul(const char *str,char **endptr, int base, size_t num) +{ + char buf[num+1], * end; + unsigned long ret; + + strncpy(buf, str, num); + buf[num] = '\0'; + + ret = strtoul(buf, &end, base); + if (*end != '\0') + *endptr = ((char *)str) + (end - buf); /* XXX discards const */ + else + *endptr = ((char *)str) + strlen(buf); + + return ret; +} + +#define GET_NUM_FIELD(phys, log) \ + \ + log = strntoul(phys, &end, 16, sizeof(phys)); \ + \ + if ( (end - phys) != sizeof(phys) ) return CPIOERR_BAD_HEADER; +#define SET_NUM_FIELD(phys, val, space) \ + sprintf(space, "%8.8lx", (unsigned long) (val)); \ + \ + memcpy(phys, space, 8) \ + +int cpioReadFileHdr(FD_t fd, struct stat * st, char** name) +{ + struct cpioCrcPhysicalHeader hdr; + int nameSize; + char * end; + unsigned int major, minor; + int rc = 0; + char paddingBuffer[4]; + + rc = Fread(&hdr, PHYS_HDR_SIZE, 1, fd); + if(rc!=1) return CPIOERR_BAD_HEADER; + + if (strncmp(CPIO_CRC_MAGIC, hdr.magic, sizeof(CPIO_CRC_MAGIC)-1) && + strncmp(CPIO_NEWC_MAGIC, hdr.magic, sizeof(CPIO_NEWC_MAGIC)-1)) + return CPIOERR_BAD_MAGIC; + + GET_NUM_FIELD(hdr.inode, st->st_ino); + GET_NUM_FIELD(hdr.mode, st->st_mode); + GET_NUM_FIELD(hdr.uid, st->st_uid); + GET_NUM_FIELD(hdr.gid, st->st_gid); + GET_NUM_FIELD(hdr.nlink, st->st_nlink); + GET_NUM_FIELD(hdr.mtime, st->st_mtime); + GET_NUM_FIELD(hdr.filesize, st->st_size); + + GET_NUM_FIELD(hdr.devMajor, major); + GET_NUM_FIELD(hdr.devMinor, minor); + st->st_dev = makedev(major, minor); + + GET_NUM_FIELD(hdr.rdevMajor, major); + GET_NUM_FIELD(hdr.rdevMinor, minor); + st->st_rdev = makedev(major, minor); + + GET_NUM_FIELD(hdr.namesize, nameSize); + //if (nameSize >= fsm->wrsize) + // return CPIOERR_BAD_HEADER; + + long padding = (nameSize+PHYS_HDR_SIZE)%4; + if(padding>0) padding = 4-padding; + + { char * t = malloc(nameSize + 1); + rc = Fread(t, nameSize, 1, fd); + if (rc != 1) { + free(t); + return CPIOERR_BAD_HEADER; + } + t[nameSize] = '\0'; + *name = t; + } + + if(padding){ + rc = Fread(paddingBuffer, padding, 1, fd); + if (rc != 1) { + free(*name); + return CPIOERR_BAD_HEADER; + } + } + + return (st->st_ino == 0 && st->st_size == 0 && st->st_mode == 0)?CPIOERR_HDR_TRAILER:0; +} + +const char * cpioStrerror(int rc) +{ + static char msg[256]; + const char *s; + int myerrno = errno; + size_t l; + + strcpy(msg, "cpio: "); + switch (rc) { + default: { + char *t = msg + strlen(msg); + sprintf(t, "(error 0x%x)", (unsigned)rc); + s = NULL; + break; + } + case CPIOERR_BAD_MAGIC: s = "Bad magic"; break; + case CPIOERR_BAD_HEADER: s = "Bad/unreadable header";break; + + case CPIOERR_OPEN_FAILED: s = "open"; break; + case CPIOERR_CHMOD_FAILED: s = "chmod"; break; + case CPIOERR_CHOWN_FAILED: s = "chown"; break; + case CPIOERR_WRITE_FAILED: s = "write"; break; + case CPIOERR_UTIME_FAILED: s = "utime"; break; + case CPIOERR_UNLINK_FAILED: s = "unlink"; break; + case CPIOERR_RENAME_FAILED: s = "rename"; break; + case CPIOERR_SYMLINK_FAILED: s = "symlink"; break; + case CPIOERR_STAT_FAILED: s = "stat"; break; + case CPIOERR_LSTAT_FAILED: s = "lstat"; break; + case CPIOERR_MKDIR_FAILED: s = "mkdir"; break; + case CPIOERR_RMDIR_FAILED: s = "rmdir"; break; + case CPIOERR_MKNOD_FAILED: s = "mknod"; break; + case CPIOERR_MKFIFO_FAILED: s = "mkfifo"; break; + case CPIOERR_LINK_FAILED: s = "link"; break; + case CPIOERR_READLINK_FAILED: s = "readlink"; break; + case CPIOERR_READ_FAILED: s = "read"; break; + case CPIOERR_COPY_FAILED: s = "copy"; break; + case CPIOERR_LSETFCON_FAILED: s = "lsetfilecon"; break; + case CPIOERR_SETCAP_FAILED: s = "cap_set_file"; break; + + case CPIOERR_HDR_SIZE: s = "Header size too big"; break; + case CPIOERR_UNKNOWN_FILETYPE: s = "Unknown file type"; break; + case CPIOERR_MISSING_HARDLINK: s = "Missing hard link(s)"; break; + case CPIOERR_DIGEST_MISMATCH: s = "Digest mismatch"; break; + case CPIOERR_INTERNAL: s = "Internal error"; break; + case CPIOERR_UNMAPPED_FILE: s = "Archive file not in header"; break; + case CPIOERR_ENOENT: s = strerror(ENOENT); break; + case CPIOERR_ENOTEMPTY: s = strerror(ENOTEMPTY); break; + } + + l = sizeof(msg) - strlen(msg) - 1; + if (s != NULL) { + if (l > 0) strncat(msg, s, l); + l -= strlen(s); + } + if ((rc & CPIOERR_CHECK_ERRNO) && myerrno) { + s = " failed - "; + if (l > 0) strncat(msg, s, l); + l -= strlen(s); + if (l > 0) strncat(msg, strerror(myerrno), l); + } + return msg; +} diff --git a/loader/cpio.h b/loader/cpio.h new file mode 100644 index 0000000..7e70cce --- /dev/null +++ b/loader/cpio.h @@ -0,0 +1,116 @@ +#ifndef H_CPIO +#define H_CPIO + +/** \ingroup payload + * \file lib/cpio.h + * Structures used to handle cpio payloads within rpm packages. + * + * @warning Rpm's cpio implementation may be different than standard cpio. + * The implementation is pretty close, but it has some behaviors which are + * more to RPM's liking. I tried to document the differing behavior in cpio.c, + * but I may have missed some (ewt). + * + */ + +/** \ingroup payload + * @note CPIO_CHECK_ERRNO bit is set only if errno is valid. + */ +#define CPIOERR_CHECK_ERRNO 0x00008000 + +/** \ingroup payload + */ +enum cpioErrorReturns { + CPIOERR_BAD_MAGIC = 2, + CPIOERR_BAD_HEADER = 3, + CPIOERR_OPEN_FAILED = 4 | CPIOERR_CHECK_ERRNO, + CPIOERR_CHMOD_FAILED = 5 | CPIOERR_CHECK_ERRNO, + CPIOERR_CHOWN_FAILED = 6 | CPIOERR_CHECK_ERRNO, + CPIOERR_WRITE_FAILED = 7 | CPIOERR_CHECK_ERRNO, + CPIOERR_UTIME_FAILED = 8 | CPIOERR_CHECK_ERRNO, + CPIOERR_UNLINK_FAILED = 9 | CPIOERR_CHECK_ERRNO, + CPIOERR_RENAME_FAILED = 10 | CPIOERR_CHECK_ERRNO, + CPIOERR_SYMLINK_FAILED = 11 | CPIOERR_CHECK_ERRNO, + CPIOERR_STAT_FAILED = 12 | CPIOERR_CHECK_ERRNO, + CPIOERR_LSTAT_FAILED = 13 | CPIOERR_CHECK_ERRNO, + CPIOERR_MKDIR_FAILED = 14 | CPIOERR_CHECK_ERRNO, + CPIOERR_RMDIR_FAILED = 15 | CPIOERR_CHECK_ERRNO, + CPIOERR_MKNOD_FAILED = 16 | CPIOERR_CHECK_ERRNO, + CPIOERR_MKFIFO_FAILED = 17 | CPIOERR_CHECK_ERRNO, + CPIOERR_LINK_FAILED = 18 | CPIOERR_CHECK_ERRNO, + CPIOERR_READLINK_FAILED = 19 | CPIOERR_CHECK_ERRNO, + CPIOERR_READ_FAILED = 20 | CPIOERR_CHECK_ERRNO, + CPIOERR_COPY_FAILED = 21 | CPIOERR_CHECK_ERRNO, + CPIOERR_LSETFCON_FAILED = 22 | CPIOERR_CHECK_ERRNO, + CPIOERR_HDR_SIZE = 23, + CPIOERR_HDR_TRAILER = 24, + CPIOERR_UNKNOWN_FILETYPE= 25, + CPIOERR_MISSING_HARDLINK= 26, + CPIOERR_DIGEST_MISMATCH = 27, + CPIOERR_INTERNAL = 28, + CPIOERR_UNMAPPED_FILE = 29, + CPIOERR_ENOENT = 30, + CPIOERR_ENOTEMPTY = 31, + CPIOERR_SETCAP_FAILED = 32 | CPIOERR_CHECK_ERRNO, +}; + +/* + * Size limit for individual files in "new ascii format" cpio archives. + * The max size of the entire archive is unlimited from cpio POV, + * but subject to filesystem limitations. + */ +#define CPIO_FILESIZE_MAX UINT32_MAX + +#define CPIO_NEWC_MAGIC "070701" +#define CPIO_CRC_MAGIC "070702" +#define CPIO_TRAILER "TRAILER!!!" + +/** \ingroup payload + * Cpio archive header information. + */ +struct cpioCrcPhysicalHeader { + char magic[6]; + char inode[8]; + char mode[8]; + char uid[8]; + char gid[8]; + char nlink[8]; + char mtime[8]; + char filesize[8]; + char devMajor[8]; + char devMinor[8]; + char rdevMajor[8]; + char rdevMinor[8]; + char namesize[8]; + char checksum[8]; /* ignored !! */ +}; + +#define PHYS_HDR_SIZE 110 /* Don't depend on sizeof(struct) */ + +#ifdef __cplusplus +extern "C" { +#endif + +const char * headerGetString(Header h, rpmTag tag); + +/** + * Read cpio header. + * @retval fsm file path and stat info + * @retval st + * @return 0 on success + */ +RPM_GNUC_INTERNAL +int cpioReadFileHdr(FD_t fd, struct stat * st, char** name); + +/** \ingroup payload + * Return formatted error message on payload handling failure. + * @param rc error code + * @return formatted error string + */ +/* XXX should be RPM_GNUC_INTERNAL too but build/pack.c uses */ +const char * cpioStrerror(int rc); + +#ifdef __cplusplus +} +#endif + +#endif /* H_CPIO */ diff --git a/loader/rpmextract.c b/loader/rpmextract.c new file mode 100644 index 0000000..ca741ad --- /dev/null +++ b/loader/rpmextract.c @@ -0,0 +1,227 @@ +/* rpm2dir: unpack the payload of RPM package to the current directory*/ +/* Based on rpm2cpio */ + +#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 "cpio.h" +#include "rpmextract.h" + +/* + * 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; + + if (strcmp(source, "-") == 0) + fdi = fdDup(STDIN_FILENO); + else + fdi = Fopen(source, "r.ufdio"); + + if (Ferror(fdi)) { + logMessage(ERROR, "%s: %s\n", (strcmp(source, "-") == 0 ? "<stdin>" : source), Fstrerror(fdi)); + return EXIT_FAILURE; + } + rpmReadConfigFiles(NULL, NULL); + + { rpmts ts = rpmtsCreate(); + rpmVSFlags vsflags = 0; + + /* XXX retain the ageless behavior of rpm2cpio */ + 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, "argument is not an RPM package\n"); + return EXIT_FAILURE; + break; + case RPMRC_FAIL: + default: + logMessage(ERROR, "error reading header from package\n"); + 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. */ + { + const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); + if(compr && strcmp(compr, "gzip")) rpmio_flags = rstrscat(NULL, "r.", compr, "dio", NULL); + else rpmio_flags = rstrscat(NULL, "r.", "gzdio", NULL); + } + + gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ + free(rpmio_flags); + + if (gzdi == NULL) { + logMessage(ERROR, "cannot re-open payload: %s\n", Fstrerror(gzdi)); + return EXIT_FAILURE; + } + + while(!rc){ + char *filename = NULL; + struct stat fstat; + int offset = 0; + int towrite = 1; + + rc = cpioReadFileHdr(gzdi, &fstat, &filename); + if(rc == CPIOERR_HDR_TRAILER){ + rc = 0; + break; + } else if(rc != 0){ + break; + } + + /* 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); + char* dirptr = dirname; + while(dirptr!=NULL && *dirptr!=0){ + dirptr = strchr(dirptr, '/'); + if(dirptr){ + *dirptr = 0; + mkdir(dirname, 0700); + *dirptr = '/'; + dirptr++; + } + } + free(dirname); + + /* Regular file */ + long readbytes = 0; + long padding = fstat.st_size%4; + FILE *fdout = NULL; + if(padding>0) padding = 4-padding; + + if(towrite>=2){ + fdout = fopen(filename+offset, "w"); + + if(fdout==NULL){ + free(filename); + rc = 33; + break; + } + } + + while(1){ + long toread = (BUFFERSIZE>fstat.st_size) ? fstat.st_size : BUFFERSIZE; + if(toread<=0) break; + readbytes = Fread(buffer, toread, 1, gzdi); + if(readbytes<=0){ + rc = 34; //truncated archive? set error flag... + break; + } + else{ + fstat.st_size-=toread; + if(towrite>=2) + if(fwrite(buffer, toread, 1, fdout)!=1){ + /* TODO: error handling */ + } + buffer[toread] = 0; + } + } + + /* symlink, we assume that the path comtained in symlink + * is shorter than BUFFERSIZE */ + if(towrite && !rc && S_ISLNK(fstat.st_mode)){ + if(symlink(buffer, filename+offset)){ + /* TODO: error handling */ + } + } + + free(filename); + if(towrite>=2) fclose(fdout); + + if(padding) readbytes = Fread(buffer, padding, 1, gzdi); + } + + + Fclose(gzdi); /* XXX gzdi == fdi */ + + return rc; +} diff --git a/loader/rpmextract.h b/loader/rpmextract.h new file mode 100644 index 0000000..0348b49 --- /dev/null +++ b/loader/rpmextract.h @@ -0,0 +1,47 @@ +/* + File name: rpmextract.h + Date: 2009/09/16 + Author: msivak + + Copyright (C) 2009 msivak + + 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 + in a file called COPYING along with this program; if not, write to + the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA + 02139, USA. +*/ + + +#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, 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