Major improvements to RPM Adds many new features to RPM including a 'database' of installed packages down /var/lib/rpm. See the comments in rpm.c and the usage help for more information. Signed-off-by: Andy Green --- archival/rpm.c | 2215 ++++++++++++++++++++++++++++++++++++++++++++++++++------ archival/rpm.h | 238 ++++++ include/usage.h | 26 3 files changed, 2241 insertions(+), 238 deletions(-) Index: busybox-1.6.0.18773/archival/rpm.c =================================================================== --- busybox-1.6.0.18773.orig/archival/rpm.c +++ busybox-1.6.0.18773/archival/rpm.c @@ -3,366 +3,2125 @@ * Mini rpm applet for busybox * * Copyright (C) 2001,2002 by Laurence Anderson + * Additional work to add more RPM functionality + * (C)2005-2007 Andy Green * * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ + +/* + * + * Additional features added to the original nice lightweight implementation + * include: + * + * - Maintains a collection of installed RPM headers in /var/lib/rpm. + * No init of this directory is needed. + * + * - Adds a -e Erase mode :-) + * + * - On updating, takes care to not remove files until the update is over, + * and to only remove files that were present in the old package but absent + * in the new one. As opposed for example to treating update as erase+install, + * which will not allow safe update of critical packages such as those that + * contain the /dev tree, etc. + * + * - Adds a -v verify mode that compares the filesystem files against the + * original package ones (including by md5) and reports differences + * + * - Adds a -F freshen mode that updates only installed packages, + * taking no action if the package is not already installed + * + * - Makes some cpio unpack fatal errors into nonfatal ones (preferable + * to getting half an archive) + * + * - supports pre/post install/uninstall scripts from the RPM, which are + * executed at the appropriate times; uninstall ones are done from the + * stored copy of the RPM header without needing the original RPM file + * + * - Checks requires are satisfied on install or update (currently + * does not check this on erase) + * + * - Adds a -C version string compare action. This is used as a lightweight + * test for if a given RPM is newer than the installed package and is intended + * for use in package management on top of RPM. Examples: + * + * # rpm -q -C 5.5-1 ncurses + * Same + * ncurses-5.5-1 + * # rpm -q -C 5.6-2 ncurses + * Newer + * ncurses-5.5-1 + * # rpm -q -C 5.1-0 ncurses + * Older + * ncurses-5.5-1 + * # rpm -q -C 5.1-0 xyz + * Package xyz not installed + * + */ + + +#include +#include /* For chmod */ +#include /* For chmod */ +#include /* For waitpid() */ #include "libbb.h" #include "unarchive.h" +#include "rpm.h" + +#define MAX_CMDLINE_PACKAGES 20 + +/* uncomment this to get some very verbose debug */ +// #define RPM_DEBUG -#define RPM_HEADER_MAGIC "\216\255\350" -#define RPM_CHAR_TYPE 1 -#define RPM_INT8_TYPE 2 -#define RPM_INT16_TYPE 3 -#define RPM_INT32_TYPE 4 -/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */ -#define RPM_STRING_TYPE 6 -#define RPM_BIN_TYPE 7 -#define RPM_STRING_ARRAY_TYPE 8 -#define RPM_I18NSTRING_TYPE 9 - -#define TAG_NAME 1000 -#define TAG_VERSION 1001 -#define TAG_RELEASE 1002 -#define TAG_SUMMARY 1004 -#define TAG_DESCRIPTION 1005 -#define TAG_BUILDTIME 1006 -#define TAG_BUILDHOST 1007 -#define TAG_SIZE 1009 -#define TAG_VENDOR 1011 -#define TAG_LICENSE 1014 -#define TAG_PACKAGER 1015 -#define TAG_GROUP 1016 -#define TAG_URL 1020 -#define TAG_PREIN 1023 -#define TAG_POSTIN 1024 -#define TAG_FILEFLAGS 1037 -#define TAG_FILEUSERNAME 1039 -#define TAG_FILEGROUPNAME 1040 -#define TAG_SOURCERPM 1044 -#define TAG_PREINPROG 1085 -#define TAG_POSTINPROG 1086 -#define TAG_PREFIXS 1098 -#define TAG_DIRINDEXES 1116 -#define TAG_BASENAMES 1117 -#define TAG_DIRNAMES 1118 -#define RPMFILE_CONFIG (1 << 0) -#define RPMFILE_DOC (1 << 1) - -enum rpm_functions_e { - rpm_query = 1, - rpm_install = 2, - rpm_query_info = 4, - rpm_query_package = 8, - rpm_query_list = 16, - rpm_query_list_doc = 32, - rpm_query_list_config = 64 +static char *pszDatabaseDirpath = NULL, **pszArgv; +static char szOriginalRootPath[] = "/"; /* the default root path */ +static char *pszEffectiveRoot = szOriginalRootPath; +/* by default the effective root path is the original / one */ +static int fdTemp = -1, optind_post_switches, nCopyArgc, nCountErrors = 0; +static const char * szaRelationship[] = { + "", "<", ">", "", "=", "<=", ">=", "" }; +static char *pszRoot, *szEmpty = ""; -typedef struct { - uint32_t tag; /* 4 byte tag */ - uint32_t type; /* 4 byte type */ - uint32_t offset; /* 4 byte offset */ - uint32_t count; /* 4 byte count */ -} rpm_index; - -static void *map; -static rpm_index **mytags; -static int tagcount; - -static void extract_cpio_gz(int fd); -static rpm_index **rpm_gettags(int fd, int *num_tags); -static int bsearch_rpmtag(const void *key, const void *item); -static char *rpm_getstr(int tag, int itemindex); -static int rpm_getint(int tag, int itemindex); -static int rpm_getcount(int tag); -static void fileaction_dobackup(char *filename, int fileref); -static void fileaction_setowngrp(char *filename, int fileref); -static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref)); -int rpm_main(int argc, char **argv); -int rpm_main(int argc, char **argv) +int +rpm_compare_versions(const char * sz1, const char * sz2) { - int opt = 0, func = 0, rpm_fd, offset; - const int pagesize = getpagesize(); + const char * psza[2] = { sz1, sz2 }; - while ((opt = getopt(argc, argv, "iqpldc")) != -1) { - switch (opt) { - case 'i': /* First arg: Install mode, with q: Information */ - if (!func) func = rpm_install; - else func |= rpm_query_info; - break; - case 'q': /* First arg: Query mode */ - if (func) bb_show_usage(); - func = rpm_query; - break; - case 'p': /* Query a package */ - func |= rpm_query_package; - break; - case 'l': /* List files in a package */ - func |= rpm_query_list; - break; - case 'd': /* List doc files in a package (implies list) */ - func |= rpm_query_list; - func |= rpm_query_list_doc; - break; - case 'c': /* List config files in a package (implies list) */ - func |= rpm_query_list; - func |= rpm_query_list_config; - break; + while (1) { + int naElement[2]; + unsigned char c; + + for (c = 0; c < 2; c++) { + naElement[c] = atoi(psza[c]); + while ((*psza[c] != ':') && (*psza[c] != '.') && + (*psza[c] != '-') && (*psza[c] != '\0')) + psza[c]++; + } + + /* if first had epoch, must be later */ + if ((*psza[0] == ':') && (*psza[1] != ':')) + return RPMSENSE_GREATER; + + /* if only second has epoch, that must be later */ + if ((*psza[0] != ':') && (*psza[1] == ':')) + return RPMSENSE_LESS; + + if (naElement[0] < naElement[1]) + return RPMSENSE_LESS; + + if (naElement[0] > naElement[1]) + return RPMSENSE_GREATER; + + /* equal - go on looking */ + + if ((*psza[0] == '\0') && (*psza[1] == '\0')) + /* end on both, must be equal to get here */ + return RPMSENSE_EQUAL; + + if ((*psza[0] == '\0') && (*psza[1] != '\0')) + /* first ran out but second has more */ + return RPMSENSE_LESS; + + if ((*psza[0] != '\0') && (*psza[1] == '\0')) + /* second ran out but first has more */ + return RPMSENSE_GREATER; + + psza[0]++; + psza[1]++; + } + return 0; +} + + + +/* + * opens an RPM from szcFilepath and prepares the + * rpm_scan_state at prss to work with it + */ + +int +rpm_open_header(const char * szcFilepath, rpm_scan_state * prss) +{ + if (prss->m_fd != -1) + close(prss->m_fd); + + prss->m_fd = open(szcFilepath, O_RDONLY); + + if (prss->m_fd != -1) { + prss->m_tags = rpm_gettags(prss->m_fd, + (int *) &prss->m_tagcount); + if ((prss->m_offset = lseek(prss->m_fd, 0, SEEK_CUR)) == + (off_t)-1) + perror("Seek error B"); + if (!prss->m_tags) { + printf("Error reading rpm header\n"); + exit(-1); + } + + /* one page minimum */ + + prss->m_map = mmap(0, prss->m_offset > getpagesize() ? + (prss->m_offset + prss->m_offset % getpagesize()) : + getpagesize(), PROT_READ, MAP_SHARED, prss->m_fd, 0); + + return 0; + } + + return 1; +} + +/* + * returns a pointer to a string naming the type of + * script tag given in the parameter + */ + +const char * +ScriptTagToName(int nTag) +{ + switch (nTag) { + case RPMTAG_PREIN: + return "Pre-Install"; + + case RPMTAG_POSTIN: + return "Post-Install"; + + case RPMTAG_PREUN: + return "Pre-uninstall"; + + case RPMTAG_POSTUN: + return "Post-uninstall"; default: - bb_show_usage(); + return "???"; + } +} + +/* + * extracts a given kind of script from the RPM, copies it to /tmp + * and executes it. After execution, deletes the /tmp script copy + * FIXME: Does not perform script chroot when -r is used + */ + +int +RunScripts(int nTagType, rpm_scan_state * prss) +{ + int it, count; + count = rpm_getcount(nTagType, prss); + + for (it = 0; it < count; it++) { + char *szRunCommand; + char *sz = rpm_getstring(nTagType, it, prss); + char * szFilepathTemp = tempnam("/tmp", "rpm_"); + int fd = open(szFilepathTemp, O_CREAT|O_TRUNC|O_WRONLY, 0700); + int nret; + + write(fd, sz, strlen(sz)); + close(fd); + + szRunCommand = xmalloc(strlen(szFilepathTemp) + 9); + strcpy(szRunCommand, "/bin/sh "); + strcpy(szRunCommand+8, szFilepathTemp); + +/* + * printf("(Running %s script %s)\n", ScriptTagToName(nTagType), + * szFilepathTemp); + */ + + nret = system(szRunCommand); + + free(szRunCommand); + unlink(szFilepathTemp); + + if (nret) { + printf(" Failed to execute RPM %s SCRIPT: %s, " + "retcode =%d, script=\n%s\n", + ScriptTagToName(nTagType), + szFilepathTemp, nret, sz); + return nret; + } + + } + return 0; +} + +/* + * copy out the list of provide entries from the rpm header + * into the provides temp file + */ + +void +DumpProvides(rpm_scan_state * prss) { + int it = 0, count = rpm_getcount(RPMTAG_PROVIDENAME, prss); + for (; it < count; it++) { + dependency dep; + strcpy(&dep.m_szPackage[0], + rpm_getstring(RPMTAG_PROVIDENAME, it, prss)); + dep.m_nFlags = rpm_getint(RPMTAG_PROVIDEFLAGS, it, prss); + + if (!(dep.m_nFlags & RPMSENSE_MISSINGOK)) { + strcpy(dep.m_szVersion, + rpm_getstring(RPMTAG_PROVIDEVERSION, it, prss)); } + write(fdTemp, &dep, sizeof(dep)); +// printf("prov %s %s\n", dep.m_szPackage, dep.m_szVersion); } - argv += optind; - argc -= optind; - if (!argc) bb_show_usage(); - - while (*argv) { - rpm_fd = xopen(*argv++, O_RDONLY); - mytags = rpm_gettags(rpm_fd, &tagcount); - if (!mytags) - bb_error_msg_and_die("error reading rpm header"); - offset = xlseek(rpm_fd, 0, SEEK_CUR); - /* Mimimum is one page */ - map = mmap(0, offset > pagesize ? (offset + offset % pagesize) : pagesize, PROT_READ, MAP_PRIVATE, rpm_fd, 0); - - if (func & rpm_install) { - /* Backup any config files */ - loop_through_files(TAG_BASENAMES, fileaction_dobackup); - /* Extact the archive */ - extract_cpio_gz(rpm_fd); - /* Set the correct file uid/gid's */ - loop_through_files(TAG_BASENAMES, fileaction_setowngrp); - } - else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) { - if (!(func & (rpm_query_info|rpm_query_list))) { - /* If just a straight query, just give package name */ - printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0)); +} + + +/* + * describe a dependency in a human-readable way, eg + * blah >= 1.01 or just blah + */ + +void +PrintDependency(dependency * pdep) +{ + if ((pdep->m_nFlags & + (RPMSENSE_LESS | RPMSENSE_GREATER | RPMSENSE_EQUAL)) == 0) { + printf("%s", pdep->m_szPackage); + } else { + printf("%s %s %s", pdep->m_szPackage, + szaRelationship[(pdep->m_nFlags & + (RPMSENSE_LESS | RPMSENSE_GREATER | RPMSENSE_EQUAL))>>1], + pdep->m_szVersion); + } +} + + + +int +rpm_main(int argc, char **argv) +{ + int opt = 0, func = 0, nPasses = 1, nPass = 0; + char * pszDbFilepath = NULL, * pszProvides = NULL, + *pszCompareString = NULL, *szFilepathTempRmList = NULL, + *szFilepathTempAddList = NULL, *cwd; + int fdRmList = 0, fdAddList = 0; + int naFuncEffective[MAX_CMDLINE_PACKAGES]; + + cwd = xmalloc(PATH_MAX); + getcwd(cwd, PATH_MAX); + + rpm_scan_state rss_rpm, rss_lib; + + rpm_construct_scan_state(&rss_rpm); + rpm_construct_scan_state(&rss_lib); + + pszArgv = argv; + nCopyArgc = argc; + + while ((opt = getopt(argc, argv, "iqpldcr:eofvuaFC:")) != -1) { + + switch (opt) { + + case 'i': /* INSTALL package(s), or info query */ + if (func & rpm_query) { + func |= rpm_query_info; + } else { + func |= rpm_install | rpm_package; + } + break; + case 'u': + /* + * UPDATE will allow preinstalled package + * to be overwritten with newer + */ + func |= rpm_update | rpm_package; + break; + case 'F': + /* + * FRESHEN: like Update, but will skip packages + * not already installed + */ + func |= rpm_erase | rpm_install | rpm_package | + rpm_skip_uninstalled; + break; + case 'e': + /* ERASE package */ + func |= rpm_erase; + break; + + case 'q': /* Query mode */ + if (!(func & (rpm_erase | rpm_install))) + func |= rpm_query; + // else bb_show_usage(); + break; + + case 'p': + /* + * Query a package + * (otherwise we query database) + */ + func |= rpm_package; + break; + case 'l': /* List files in a package */ + func |= rpm_query_list; + break; + case 'f': /* Force install, overriding requires */ + func |= rpm_force; + break; + case 'd': /* List doc files (implies list) */ + func |= rpm_query_list | rpm_query_list_doc; + break; + case 'c': /* List config files in a package */ + func |= rpm_query_list | rpm_query_list_config; + break; + case 'v': /* Verify MD5s against installed header */ + func |= rpm_query_verify | rpm_query; + break; + case 'a': /* Queries to ALL installed packages */ + func |= rpm_query_all; + break; + case 'r': /* Set effective root for file operations */ + pszEffectiveRoot = optarg; + /* + * change the effective root to point + * to the commandline arg in place + */ + break; + case 'o': + /* + * oldpackage, allow update of older package + * over newer already installed + */ + func |= rpm_oldpackage; + break; + case 'C': /* Compare version against installed */ + func |= rpm_query|rpm_query_compareverions; + pszCompareString = optarg; + break; + default: + bb_show_usage(); + } + } + + // deal with removing extra / at start of path display in default case + + if (strlen(pszEffectiveRoot) == 1) + pszRoot = szEmpty; + else + pszRoot = pszEffectiveRoot; + + + /* + * create database dir (no other init needed) + * if it is not present already + */ + + pszDatabaseDirpath = xmalloc(strlen(pszEffectiveRoot) + 4 +1); + strcpy(pszDatabaseDirpath, pszEffectiveRoot); + strcat(pszDatabaseDirpath, "/tmp"); + bb_make_directory(pszDatabaseDirpath, 0744, FILEUTILS_RECUR); + // ignore error, we expect it to exist already normally + + free(pszDatabaseDirpath); + pszDatabaseDirpath = xmalloc(strlen(pszEffectiveRoot) + + strlen(RPM_STATE_DIR)+1); + strcpy(pszDatabaseDirpath, pszEffectiveRoot); + strcat(pszDatabaseDirpath, RPM_STATE_DIR); + bb_make_directory(pszDatabaseDirpath, 0744, FILEUTILS_RECUR); + // ignore error, we expect it to exist already normally + + if (((func & (rpm_query|rpm_query_verify|rpm_package| + rpm_query_list)) == rpm_query) && (optind == argc)) { + loop_through_database_files(diraction_list_packages); + } + + if ((func & (rpm_query|rpm_query_verify|rpm_query_all| + rpm_query_list)) == (rpm_query|rpm_query_verify|rpm_query_all)) { + loop_through_database_files(diraction_verify_packages); + } + + if ((func & (rpm_query|rpm_query_verify|rpm_query_all| + rpm_query_list)) == (rpm_query|rpm_query_list|rpm_query_all)) { + loop_through_database_files(diraction_listcontents_packages); + } + + + if (func & (rpm_install | rpm_erase | rpm_update)) { + + /* + * create temp files to hold the lists of + * files that were "removed" ... + */ + + szFilepathTempRmList = tempnam("/tmp", "rpmr_"); + fdRmList = open(szFilepathTempRmList, + O_CREAT|O_TRUNC|O_RDWR, 0700); + + /* and the files that were "added" ... */ + + szFilepathTempAddList = tempnam("/tmp", "rpma_"); + fdAddList = open(szFilepathTempAddList, + O_CREAT|O_TRUNC|O_RDWR, 0700); + + } + + /* + * at the end we will try to work out what was only removed + * and not added (eg, in update) and delete it. As an + * optimization the RPM Header archive entry + * itself is added to these lists + */ + + optind_post_switches = optind; + + while (nPass < nPasses) { + + /* + * outermost loop is passes through the sequencer + * for install/update/erase + */ + + optind = optind_post_switches; + + while (optind < argc) { + + /* + * for each level through the sequencer, we visit + * all packages on the commandline at that level + */ + + char * pszBasename; + rpm_scan_state * prssActive = NULL; + int nFuncEffective = func; + + xchdir(cwd); + + if (optind >= MAX_CMDLINE_PACKAGES) + bb_error_msg_and_die( + "Too many packages in transaction set"); + + // init our per-package action flags + if (nPass == 0) + naFuncEffective[optind] = func; + + nFuncEffective = naFuncEffective[optind]; + + /* + * open either the given package filepath + * (rss_rpm) in -p style and/or the RPM library + * installed copy of package headers (rss_lib) + */ + + if (nFuncEffective & rpm_package) { + + /* operating on a given RPM */ + + if (rpm_open_header(argv[optind], &rss_rpm)) { + puts(argv[optind]); + bb_error_msg_and_die( + "Unable to open file"); + } + + pszBasename = rpm_getstring(RPMTAG_NAME, + 0, &rss_rpm); + pszDbFilepath = xmalloc(strlen(pszRoot) + + strlen(RPM_STATE_DIR) + + strlen(pszBasename)+1); + strcpy(pszDbFilepath, pszRoot); + strcat(pszDbFilepath, RPM_STATE_DIR); + strcat(pszDbFilepath, pszBasename); + + if (rpm_open_header(pszDbFilepath, &rss_lib)) { + + /* won't exist if not installed yet */ + + if (nFuncEffective & + (rpm_skip_uninstalled)) { + /* + * not already installed? + * we need to ignore it + */ + rpm_destruct_scan_state( + &rss_rpm); + rpm_destruct_scan_state( + &rss_lib); + + optind++; + continue; + } + + } else { + char cFail = 0; + int compVersion, compRelease; + + /* + * we have a version already installed + * turn install into update + * since package already exists + */ + + if (nFuncEffective & rpm_install) { + nFuncEffective &= ~rpm_install; + nFuncEffective |= rpm_update; + } + + /* + * update without --oldpackage? + * then insist it is newer + */ + + if ((nFuncEffective & + (rpm_update|rpm_oldpackage)) == + (rpm_update|rpm_oldpackage)) + goto good_enough; + + /* + * at some point an installed package + * starts to exist if we are installing + * it ;-) just complain at the start + */ + + if (nPass > 1) + goto good_enough; + + /* + * only allow newer + * package install + */ + + if (rpm_getint(RPMTAG_EPOCH, 0, + &rss_rpm) < rpm_getint(RPMTAG_EPOCH, + 0, &rss_lib)) + cFail = 1; + + compVersion = rpm_compare_versions( + rpm_getstring(RPMTAG_VERSION, 0, + &rss_rpm), rpm_getstring( + RPMTAG_VERSION, 0, &rss_lib)); + + compRelease = rpm_compare_versions( + rpm_getstring( RPMTAG_RELEASE, 0, + &rss_rpm), rpm_getstring( + RPMTAG_RELEASE, 0, &rss_lib)); + + if ((compVersion == RPMSENSE_EQUAL) && + (compRelease == RPMSENSE_EQUAL)) + cFail = 1; + + if ((compVersion == RPMSENSE_LESS) || + (compRelease == RPMSENSE_LESS)) + cFail = 1; + +good_enough: + if (cFail) { + printf( + "Same or Newer version " + "%d:%s-%s of %s " + "already " + "installed\n", + rpm_getint(RPMTAG_EPOCH, 0, + &rss_lib), rpm_getstring( + RPMTAG_VERSION, 0, + &rss_lib), rpm_getstring( + RPMTAG_RELEASE, 0, + &rss_lib), rpm_getstring( + RPMTAG_NAME, 0, &rss_lib)); + + /* + * we're not going to + * do this one + */ + + rpm_destruct_scan_state( + &rss_rpm); + rpm_destruct_scan_state( + &rss_lib); + + optind++; + continue; + + } + + } + + } else { /* not -p / package */ + + /* + * open only the installed copy + * of package headers + */ + + if (nPass < 4) { + + /* + * last time around is cleanup, none + * of this is needed (and it might no + * longer exist :-) ) + */ + + pszDbFilepath = xmalloc( + strlen(pszRoot) + + strlen(RPM_STATE_DIR) + + strlen(argv[optind]) + 1); + strcpy(pszDbFilepath, pszRoot); + strcat(pszDbFilepath, + RPM_STATE_DIR); + strcat(pszDbFilepath, + argv[optind]); + + if (rpm_open_header(pszDbFilepath, + &rss_lib)) { + printf("Package %s not " + "installed\n", + argv[optind]); + return 0; + } else { + if (nFuncEffective & + rpm_skip_uninstalled) + /* + * not already + * installed? we need + * to ignore it + */ + continue; + } + + } else { + + /* + * old copy of RPM header has been + * unlinked() by pass 4 + */ + + } // if pass <4 + + } // if package + + /* + * for when nFuncEffectivetions make sense on an + * explicit package or a Database package, + * use prssActive this allows the same code to + * handle either case + */ + + prssActive = &rss_rpm; + if (!(nFuncEffective & rpm_package)) + prssActive = &rss_lib; + + if (nFuncEffective & rpm_query_compareverions) { + int nResult; + char *szVersion = rpm_getstring(RPMTAG_VERSION, + 0, prssActive); + char *szRelease = rpm_getstring( + RPMTAG_RELEASE, 0, prssActive); + char * szTemp = xmalloc(strlen(szVersion) + + strlen(szRelease) + 2); + + strcpy(szTemp, szVersion); + strcat(szTemp, "-"); + strcat(szTemp, szRelease); + + nResult = rpm_compare_versions( + pszCompareString, szTemp); + + switch (nResult) { + case 2: + puts("Older"); + break; + case 4: + puts("Newer"); + break; + case 8: + puts("Same"); + break; + } + free(szTemp); } - if (func & rpm_query_info) { - /* Do the nice printout */ - time_t bdate_time; - struct tm *bdate; - char bdatestring[50]; - printf("Name : %-29sRelocations: %s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_PREFIXS, 0) ? rpm_getstr(TAG_PREFIXS, 0) : "(not relocateable)"); - printf("Version : %-34sVendor: %s\n", rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_VENDOR, 0) ? rpm_getstr(TAG_VENDOR, 0) : "(none)"); - bdate_time = rpm_getint(TAG_BUILDTIME, 0); - bdate = localtime((time_t *) &bdate_time); - strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate); - printf("Release : %-30sBuild Date: %s\n", rpm_getstr(TAG_RELEASE, 0), bdatestring); - printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstr(TAG_BUILDHOST, 0)); - printf("Group : %-30sSource RPM: %s\n", rpm_getstr(TAG_GROUP, 0), rpm_getstr(TAG_SOURCERPM, 0)); - printf("Size : %-33dLicense: %s\n", rpm_getint(TAG_SIZE, 0), rpm_getstr(TAG_LICENSE, 0)); - printf("URL : %s\n", rpm_getstr(TAG_URL, 0)); - printf("Summary : %s\n", rpm_getstr(TAG_SUMMARY, 0)); - printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0)); + + if (nFuncEffective & rpm_query_verify) { + prssActive->m_nUser = 0; + loop_through_files(RPMTAG_BASENAMES, + fileaction_verify, prssActive); } - if (func & rpm_query_list) { + + + if (nFuncEffective & + (rpm_install | rpm_erase | rpm_update)) { + off_t offsetKeep; + unsigned int nLen = rss_rpm.m_offset; + int fdDatabase, it, count; + char *sz; + unsigned char baBuffer[64]; + char fMoreRm; + int nPositionInRmList; + char *szFilepathRm, *szFilepathAdd; + + nPasses = 5; + + /* + * we need to do the full monty outer loop + * sequencing in such a case + */ + + switch (nPass) { + + case 0: + /* + * install: collect provides from + * given and installed packages; + * erase: collect provides + * only from given packages + */ + + /* + * turn update into install if + * package not already installed + */ + + if (nFuncEffective & rpm_update) { + int fd = open( + pszDbFilepath, + O_RDONLY); + if (fd == -1) { + /* + * not already + * installed + */ + naFuncEffective[ + optind] &= + ~rpm_update; + naFuncEffective[ + optind] |= + rpm_install; + printf( + "Changing action to" + " Install for " + "'%s'\n", + argv[optind]); + } else { + close(fd); + } + } + + /* + * confirm we don't have this + * package installed already + */ + + if (nFuncEffective & rpm_install) { + fdDatabase = open( + pszDbFilepath, + O_RDONLY); + if (fdDatabase != -1) { + /* + * for install, the + * package must not + * already be installed + */ + puts(rpm_getstring( + RPMTAG_NAME, 0, + &rss_lib)); + bb_error_msg_and_die( + "Package already " + "installed"); + } + } + + if (fdTemp != -1) { + DumpProvides(prssActive); + break; + } + + pszProvides = xmalloc( + strlen(pszEffectiveRoot) + + strlen(RPM_STATE_DIR) + + strlen(RPM_TEMPFILE_REQUIRES) + 1); + strcpy(pszProvides, pszEffectiveRoot); + strcat(pszProvides, + RPM_TEMPFILE_PROVIDES); + + fdTemp = xopen(pszProvides, + O_RDWR|O_CREAT|O_TRUNC); + + /* + * if install or update, start the + * provides list off with provides + * from all installed packages + */ + + if (nFuncEffective & + (rpm_install | rpm_update)) + loop_through_database_files( + diraction_append_provides); + + DumpProvides(prssActive); + break; + + + case 1: + /* + * look at the provides situation - + * install: do all the new packages' + * requires appear in the temp file? + * erase: does any other package + * require anything listed in the + * temp file? + */ + + if (! (nFuncEffective & + (rpm_install|rpm_update))) { + + /* not install or update */ + + /* just do this step once */ + optind = argc; + + if (fdTemp < 1) + bb_error_msg_and_die( + "case1 provides: " + "provides file " + "closed when still" + " needed"); + + loop_through_database_files( + diraction_check_if_dep); + + break; + } + + /* + * fdTemp file now has all provides, + * including those supposedly being + * installed. confirm that all the + * new guys' *requires* appear in + * that file somewhere + */ + + it = 0; + count = rpm_getcount(RPMTAG_REQUIRENAME, + &rss_rpm); + + /* for all the requires */ + + for (; it < count; it++) { + char *sz = rpm_getstring( + RPMTAG_REQUIRENAME, it, + &rss_rpm); + int nFlags = rpm_getint( + RPMTAG_REQUIREFLAGS, it, + &rss_rpm); + char *szVersion = rpm_getstring( + RPMTAG_REQUIREVERSION, it, + &rss_rpm); + char fSeen = 0; + dependency dep; + + /* + * script interpreter, + * currently ignore + */ + if (nFlags & RPMSENSE_INTERP) + continue; + + if ((nFlags & + (RPMSENSE_FIND_REQUIRES))) + /* + * file/lib deps not + * supported yet + */ + continue; + + if ((nFlags & RPMSENSE_RPMLIB)) + /* + * ignore RPM capability + * requires + */ + continue; + + /* + * move to start of + * provides list + */ + + if (lseek(fdTemp, 0, + SEEK_SET) == (off_t)-1) + perror("Seek error C"); + + while ((read(fdTemp, &dep, + sizeof(dep)) != 0) && + (!fSeen)) { + + if (strcmp(dep. + m_szPackage, sz) != + 0) + continue; + + /* + * match on package + * name, now check + * compliance with + * version needs + */ + + if (!(nFlags & + (RPMSENSE_LESS | + RPMSENSE_GREATER | + RPMSENSE_EQUAL))) { + /* + * just package + * presence was + * enough + */ + fSeen = 1; + continue; + } + + /* + * there is a comparison + * required! + */ + + if (nFlags & + rpm_compare_versions + (dep.m_szVersion, + szVersion)) { + fSeen = 1; + continue; + } + + printf( + "bad version seen: " + "%s vs dep %s\n", + szVersion, dep. + m_szVersion); + + } /* while */ + + if (fSeen) + continue; + + /* + * this then is a requirement + * from a new package not + * satisfied by any provides + */ + + nCountErrors++; + + printf("Package %s requires %s", + argv[optind], sz); + + if (!(nFlags & + (RPMSENSE_LESS | + RPMSENSE_GREATER | + RPMSENSE_EQUAL))) + continue; + + printf("%s %s", + szaRelationship[(nFlags & + (RPMSENSE_LESS | + RPMSENSE_GREATER | + RPMSENSE_EQUAL))>>1], + szVersion); + + } // for + + + break; + + + case 2: // install / erase action + + if ((nCountErrors) && + !(nFuncEffective & rpm_force)) + break; + + /* + * take no action if there + * were unsatisfied requires + */ + + if (!(nFuncEffective & + (rpm_erase | rpm_update))) + goto try_update; + + /* + * list all the files we want + * to remove, including the + * database file itself + */ + +#ifdef RPM_DEBUG + printf("RPM_DEBUG: Running " + "Pre-uninstall script\n"); +#endif + /* run the OLD uninstall script */ + RunScripts(RPMTAG_PREUN, &rss_lib); + +#ifdef RPM_DEBUG + printf("RPM_DEBUG: Pre-uninstall " + "script completed\n"); +#endif + /* + * add all the files that were in + * the OLD header to the remove list + */ + + count = rpm_getcount(RPMTAG_BASENAMES, + &rss_lib); +#ifdef RPM_DEBUG + printf("RPM_DEBUG: %d files " + "listed in old\n", count); +#endif + + for (it = 0; it < count; it++) { + + write(fdRmList, pszRoot, + strlen(pszRoot)); + sz = rpm_getstring( + RPMTAG_DIRNAMES, + rpm_getint( + RPMTAG_DIRINDEXES, it, + &rss_lib), &rss_lib); +#ifdef RPM_DEBUG + printf("RPM_DEBUG: dir=%s\n", + sz); +#endif + write(fdRmList, sz, strlen(sz)); + sz = rpm_getstring( + RPMTAG_BASENAMES, it, + &rss_lib); +#ifdef RPM_DEBUG + printf("RPM_DEBUG: file=%s\n", + sz); +#endif + write(fdRmList, sz, strlen(sz)); + write(fdRmList, "\n", 1); + } + +#ifdef RPM_DEBUG + printf("RPM_DEBUG: Running OLD " + "Post-uninstall script\n"); +#endif + if (nFuncEffective & rpm_update) + /* run the OLD post-uninstall */ + RunScripts(RPMTAG_POSTUN, + &rss_lib); +#ifdef RPM_DEBUG + printf("RPM_DEBUG: OLD Post-uninstall " + "script completed\n"); +#endif + + close(rss_lib.m_fd); + rss_lib.m_fd = -1; + + /* + * add the database entry for this now + * deleted package to the + * remove list too + */ + + write(fdRmList, pszDbFilepath, + strlen(pszDbFilepath)); + write(fdRmList, "\n", 1); + break; + + + +try_update: + if (!(nFuncEffective & + (rpm_install | rpm_update))) + break; + + /* + * perform install/update action + * from NEW package + */ + + RunScripts(RPMTAG_PREIN, &rss_rpm); + + /* + * Backup any config files, + * using Effective Root + */ + + loop_through_files(RPMTAG_BASENAMES, + fileaction_dobackup, &rss_rpm); + + /* + * Extract the archive to + * the effective root + */ + + extract_cpio_gz(rss_rpm.m_fd); + + + /* + * Set the correct file uid/gid's + * on the NEW files + */ + + loop_through_files(RPMTAG_BASENAMES, + fileaction_setowngrp, &rss_rpm); + + unlink(pszDbFilepath); + + + + /* + * copy the RPM header (only) en bloc + * into a "database" file named after + * the package name + * mode 644 here allows non-root + * to perform rpm -q + */ + + fdDatabase = open(pszDbFilepath, + O_WRONLY| O_CREAT, 0644); + + if (fdDatabase < 0) + bb_error_msg_and_die( + "Unable to open /var/lib" + "/rpm header for save"); + + if ((offsetKeep = lseek(rss_rpm.m_fd, + 0, SEEK_CUR)) == (off_t) - 1) + perror("Seek error D"); + + /* move to start of RPM */ + if (lseek(rss_rpm.m_fd, 0, SEEK_SET) == + (off_t)-1) + perror("Seek error E"); + + while (nLen) { + /* copy the header portion */ + unsigned int nDo = + sizeof(baBuffer); + if (nLen < sizeof(baBuffer)) + nDo = nLen; + read(rss_rpm.m_fd, + &baBuffer[0], nDo); + if (write(fdDatabase, + &baBuffer[0], nDo) < 0) { + close(fdDatabase); + unlink(pszDbFilepath); + bb_error_msg_and_die( + "Copying RPM header" + " failed to write"); + } + nLen -= nDo; + } + fchmod(fdDatabase, 0644); + close(fdDatabase); + /* move back to where we were */ + if (lseek(rss_rpm.m_fd, offsetKeep, + SEEK_SET) == (off_t) - 1) + perror("Seek error F"); + + /* + * add all the files that were in it + * to the added list + */ + + count = rpm_getcount(RPMTAG_BASENAMES, + &rss_rpm); + + for (it = 0; it < count; it++) { + + write(fdAddList, pszRoot, + strlen(pszRoot)); + sz = rpm_getstring( + RPMTAG_DIRNAMES, + rpm_getint( + RPMTAG_DIRINDEXES, it, + &rss_rpm), &rss_rpm); + write(fdAddList, sz, + strlen(sz)); + sz = rpm_getstring( + RPMTAG_BASENAMES, it, + &rss_rpm); + write(fdAddList, sz, + strlen(sz)); + write(fdAddList, "\n", 1); + } + + /* + * note that we added the + * database entry + */ + + write(fdAddList, pszDbFilepath, + strlen(pszDbFilepath)); + write(fdAddList, "\n", 1); + + break; + + + case 3: + /* + * do post-uninstall script, + * delete things that are removed + */ + + /* run the OLD post-uninstall */ + + if (nFuncEffective & (rpm_erase)) + RunScripts(RPMTAG_POSTUN, + &rss_lib); + + + /* + * time to settle up on any deferred + * file deletion actions + * + * compare the things added to the + * deleted list and the added list, + * delete things only on the deleted + * list + */ + + if (!fdRmList) + break; + + fMoreRm = 1; + nPositionInRmList = 0; + szFilepathRm = xmalloc( + RPM_MAX_FILEPATH + 1); + szFilepathAdd = xmalloc( + RPM_MAX_FILEPATH + 1); + + while (fMoreRm) { + int nFetchedLength = 0, n = 0; + char fMoreAdd = 1, fMatched = 0; + int nPositionInAddList = 0; + + /* + * get the next removed + * filepath in a safe, low + * memory way without buffering + * for everything that got + * removed... + */ + + if (lseek(fdRmList, + nPositionInRmList, + SEEK_SET) == (off_t)-1) + perror("Seek error A"); + nFetchedLength = read(fdRmList, + szFilepathRm, + RPM_MAX_FILEPATH); + while ((n < nFetchedLength) && + (szFilepathRm[n] != '\n')) + n++; + if (n >= nFetchedLength) { + fMoreRm = 0; + continue; + } + szFilepathRm[n] = '\0'; + // puts(szFilepathRm); + nPositionInRmList += n + 1; + + /* + * see if it was added back... + * if not, it needs to be + * actually removed + */ + + while (fMoreAdd) { + + if (lseek(fdAddList, + nPositionInAddList, + SEEK_SET) == + (off_t)-1) + perror( + "Seek " + "error G"); + nFetchedLength = read( + fdAddList, + szFilepathAdd, + RPM_MAX_FILEPATH); + n = 0; + while ((n < + nFetchedLength) && + (szFilepathAdd[n] != + '\n')) + n++; + if (n >= + nFetchedLength) { + fMoreAdd = 0; + continue; + } + szFilepathAdd[n] = '\0'; + nPositionInAddList += + n + 1; + + if (strcmp( + szFilepathAdd, + szFilepathRm) == + 0) { + fMatched = 1; + fMoreAdd = 0; + } + } + + if (!fMatched) { + printf( + "Deleting '%s'\n", + szFilepathRm); + unlink(szFilepathRm); + } + + } + + free(szFilepathRm); + free(szFilepathAdd); + + break; + + + case 4: + + /* run installed post-install scripts */ + + if (nFuncEffective & + (rpm_update|rpm_install)) + /* run the NEW post-install */ + RunScripts(RPMTAG_POSTIN, + &rss_rpm); + + if (fdTemp) { + close(fdTemp); + fdTemp = 0; + } + + if (pszProvides) { + unlink(pszProvides); + free(pszProvides); + pszProvides = NULL; + } + + break; + } + + + } // if erase, update, install + + + /* Query list files */ + + if ((func & (rpm_query|rpm_query_list| + rpm_query_verify)) == + (rpm_query | rpm_query_list)) { int count, it, flags; - count = rpm_getcount(TAG_BASENAMES); + + count = rpm_getcount(RPMTAG_BASENAMES, + prssActive); + for (it = 0; it < count; it++) { - flags = rpm_getint(TAG_FILEFLAGS, it); - switch (func & (rpm_query_list_doc|rpm_query_list_config)) { + flags = rpm_getint(RPMTAG_FILEFLAGS, + it, prssActive); + switch ((func & rpm_query_list_doc) + + (func & rpm_query_list_config)) { + case rpm_query_list_doc: - if (!(flags & RPMFILE_DOC)) continue; + if (!(flags & RPMFILE_DOC)) + continue; break; + case rpm_query_list_config: - if (!(flags & RPMFILE_CONFIG)) continue; + if (!(flags & RPMFILE_CONFIG)) + continue; break; - case rpm_query_list_doc|rpm_query_list_config: - if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue; + + case rpm_query_list_doc | + rpm_query_list_config: + if (!((flags & + RPMFILE_CONFIG) || (flags & + RPMFILE_DOC))) + continue; break; } - printf("%s%s\n", - rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)), - rpm_getstr(TAG_BASENAMES, it)); + printf("%s%s%s\n", + pszRoot, + rpm_getstring(RPMTAG_DIRNAMES, + rpm_getint( + RPMTAG_DIRINDEXES, it, + prssActive), prssActive), + rpm_getstring(RPMTAG_BASENAMES, + it, prssActive)); + } + } + /* + * count = rpm_getcount(RPMTAG_REQUIRENAME, &rss_rpm); + * for (it = 0; it < count; it++) { + * printf("Requires: %s\n", rpm_getstring( + * RPMTAG_REQUIRENAME, it, &rss_rpm)); + * } + */ + + /* + * bare query, giving package version info, + * or query header info + */ + + if ((func & (rpm_query|rpm_query_list| + rpm_query_verify|rpm_query_all)) == (rpm_query)) { + + printf("%s-%s-%s\n", + rpm_getstring(RPMTAG_NAME, 0, prssActive), + rpm_getstring(RPMTAG_VERSION, 0, + prssActive), rpm_getstring(RPMTAG_RELEASE, + 0, prssActive)); + + if (func & rpm_query_info) { + /* Do the nice printout */ + time_t timeTemp; + struct tm *tmTemp; + struct stat statInstalledFile; + char bdatestring[50], szInstall[50]; + + printf("Name : %-29s" + "Relocations: %s\n", + rpm_getstring(RPMTAG_NAME, 0, + prssActive), rpm_getstring( + RPMTAG_PREFIXS, 0, prssActive) ? + rpm_getstring(RPMTAG_PREFIXS, 0, + prssActive) : "(not relocateable)"); + printf("Version : %-34s" + "Vendor: %s\n", rpm_getstring( + RPMTAG_VERSION, 0, prssActive), + rpm_getstring(RPMTAG_VENDOR, 0, + prssActive) ? rpm_getstring( + RPMTAG_VENDOR, 0, prssActive) : + "(none)"); + timeTemp = rpm_getint( + RPMTAG_BUILDTIME, 0, prssActive); + tmTemp = localtime((time_t *) + &timeTemp); + strftime(bdatestring, + sizeof(bdatestring), + "%a %d %b %Y %T %Z", tmTemp); + printf("Release : %-30s" + "Build Date: %s\n", rpm_getstring( + RPMTAG_RELEASE, 0, prssActive), + bdatestring); + + strcpy(szInstall, "(not installed)"); + if (pszDbFilepath != NULL) { + if (stat(pszDbFilepath, + &statInstalledFile) == 0) { + strftime(szInstall, + sizeof(szInstall), + "%a %d %b %Y %T %Z", + localtime( + (time_t *) + &statInstalledFile. + st_mtime)); + } else + strcpy(szInstall, + "(stat failed)"); + + } else + strcpy(szInstall, + "(pszDbFilepath " + "NULL)"); + + printf("Install date: %-30s" + "Build Host: %s\n", szInstall, + rpm_getstring(RPMTAG_BUILDHOST, 0, + prssActive)); + printf("Group : %-30s" + "Source RPM: %s\n", rpm_getstring( + RPMTAG_GROUP, 0, prssActive), + rpm_getstring(RPMTAG_SOURCERPM, + 0, prssActive)); + printf("Size : %-33d" + "License: %s\n", rpm_getint( + RPMTAG_SIZE, 0, prssActive), + rpm_getstring(RPMTAG_LICENSE, 0, + prssActive)); + printf("URL : %s\n", + rpm_getstring(RPMTAG_URL, 0, + prssActive)); + printf("Summary : %s\n", + rpm_getstring(RPMTAG_SUMMARY, 0, + prssActive)); + printf("Description :\n%s\n", + rpm_getstring(RPMTAG_DESCRIPTION, 0, + prssActive)); } + } + + optind++; + + rpm_destruct_scan_state(&rss_rpm); + rpm_destruct_scan_state(&rss_lib); + + if (pszDbFilepath != NULL) { + free(pszDbFilepath); + pszDbFilepath = NULL; + } + } - free(mytags); + nPass++; + } + + + // clean up the temp lists + + if (fdRmList) { + close(fdRmList); + unlink(szFilepathTempRmList); + } + if (fdAddList) { + close(fdAddList); + unlink(szFilepathTempAddList); } + + + free(pszDatabaseDirpath); + + // since we are intended for flash-based systems, this will do no harm + + sync(); + return 0; } -static void extract_cpio_gz(int fd) + + +int +rpm_open_transformer(int src_fd, int (*transformer)(int src_fd, int dst_fd)) { + int fd_pipe[2]; + int pid; + + if (pipe(fd_pipe) != 0) { + bb_perror_msg_and_die("Can't create pipe"); + } + + pid = fork(); + if (pid == -1) { + bb_perror_msg_and_die("Fork failed"); + } + + if (pid == 0) { + /* child process */ + close(fd_pipe[0]); /* We don't wan't to read from the parent */ + transformer(src_fd, fd_pipe[1]); + close(fd_pipe[1]); /* Send EOF */ + close(src_fd); + _exit(0); + } + + /* parent process */ + close(fd_pipe[1]); /* Don't want to write to the child */ + + return fd_pipe[0]; +} + + + + + +/* + * performs the actual unpacking into the effective root + */ + +void extract_cpio_gz(int fd) { archive_handle_t *archive_handle; unsigned char magic[2]; /* Initialise */ + archive_handle = init_handle(); archive_handle->seek = seek_by_read; - //archive_handle->action_header = header_list; archive_handle->action_data = data_extract_all; - archive_handle->flags |= ARCHIVE_PRESERVE_DATE; - archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS; + archive_handle->flags |= ARCHIVE_PRESERVE_DATE | + ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_EXTRACT_UNCONDITIONAL; archive_handle->src_fd = fd; archive_handle->offset = 0; xread(archive_handle->src_fd, &magic, 2); - if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) { - bb_error_msg_and_die("invalid gzip magic"); - } + + if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) + bb_error_msg_and_die("Only RPMs with gzipped payload " + "currently supported"); + check_header_gzip_or_die(archive_handle->src_fd); - xchdir("/"); /* Install RPM's to root */ - archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip); + xchdir(pszEffectiveRoot); // Install RPM's to effective root + + archive_handle->src_fd = rpm_open_transformer( + archive_handle->src_fd, inflate_gunzip); + archive_handle->offset = 0; - while (get_header_cpio(archive_handle) == EXIT_SUCCESS) - /* loop */; + while (get_header_cpio(archive_handle) == EXIT_SUCCESS); + } -static rpm_index **rpm_gettags(int fd, int *num_tags) + + +rpm_index ** +rpm_gettags(int fd, int *num_tags) { + rpm_index **tags = calloc(200, sizeof(struct rpmtag *)); /* We should never need mode than 200, and realloc later */ - rpm_index **tags = xzalloc(200 * sizeof(struct rpmtag *)); int pass, tagindex = 0; + if (lseek(fd, 96, SEEK_CUR) == (off_t)-1) + perror("Seek error H"); /* Seek past the unused lead */ - xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */ - - /* 1st pass is the signature headers, 2nd is the main stuff */ for (pass = 0; pass < 2; pass++) { + /* + * 1st pass is the signature headers, + * 2nd is the main stuff + */ struct { char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */ uint8_t version; /* 1 byte version number */ uint32_t reserved; /* 4 bytes reserved */ - uint32_t entries; /* Number of entries in header (4 bytes) */ + uint32_t entries; /* No of entries in hdr (4 bytes) */ uint32_t size; /* Size of store (4 bytes) */ } header; rpm_index *tmpindex; int storepos; - xread(fd, &header, sizeof(header)); - if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) + read(fd, &header, sizeof(header)); + if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3)) return NULL; /* Invalid magic */ + if (header.version != 1) return NULL; /* This program only supports v1 headers */ + header.size = ntohl(header.size); header.entries = ntohl(header.entries); - storepos = xlseek(fd,0,SEEK_CUR) + header.entries * 16; + if ((storepos = lseek(fd, 0, SEEK_CUR)) == (off_t)-1) + perror("Seek error I"); + + storepos += header.entries << 4; /* x 16 */ while (header.entries--) { - tmpindex = tags[tagindex++] = xmalloc(sizeof(rpm_index)); - xread(fd, tmpindex, sizeof(rpm_index)); + tmpindex = tags[tagindex++] = malloc(sizeof(rpm_index)); + read(fd, tmpindex, sizeof(rpm_index)); tmpindex->tag = ntohl(tmpindex->tag); tmpindex->type = ntohl(tmpindex->type); tmpindex->count = ntohl(tmpindex->count); tmpindex->offset = storepos + ntohl(tmpindex->offset); - if (pass==0) + if (pass == 0) tmpindex->tag -= 743; } - xlseek(fd, header.size, SEEK_CUR); /* Seek past store */ - /* Skip padding to 8 byte boundary after reading signature headers */ - if (pass==0) - xlseek(fd, (8 - (xlseek(fd,0,SEEK_CUR) % 8)) % 8, SEEK_CUR); + if (lseek(fd, header.size, SEEK_CUR) == (off_t)-1) + perror("Seek error J"); /* Seek past store */ + + if (pass == 0) { + if (lseek(fd, (8 - (lseek(fd, 0, SEEK_CUR) % 8)) % 8, + SEEK_CUR) == (off_t)-1) + perror("Seek error J"); + /* + * Skip padding to 8 byte boundary after + * reading signature headers + */ + } } - tags = xrealloc(tags, tagindex * sizeof(struct rpmtag *)); /* realloc tags to save space */ + tags = realloc(tags, tagindex * sizeof(struct rpmtag *)); + /* realloc tags to save space */ + *num_tags = tagindex; - return tags; /* All done, leave the file at the start of the gzipped cpio archive */ + + /* All done, leave the file at the start of the gzipped cpio archive */ + + return tags; } -static int bsearch_rpmtag(const void *key, const void *item) +int +bsearch_rpmtag(const void *key, const void *item) { - int *tag = (int *)key; rpm_index **tmp = (rpm_index **) item; - return (*tag - tmp[0]->tag); + return ((int) key - tmp[0]->tag); } -static int rpm_getcount(int tag) + +// returns how many entries of a particular array tag type are present + +int +rpm_getcount(int tag, rpm_scan_state * pstate) { rpm_index **found; - found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + found = bsearch((void *) tag, pstate->m_tags, pstate->m_tagcount, + sizeof(struct rpmtag *), bsearch_rpmtag); if (!found) return 0; return found[0]->count; } -static char *rpm_getstr(int tag, int itemindex) +char * +rpm_getstring(int tag, int itemindex, rpm_scan_state * pstate) { rpm_index **found; - found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + found = bsearch((void *) tag, pstate->m_tags, pstate->m_tagcount, + sizeof(struct rpmtag *), bsearch_rpmtag); if (!found || itemindex >= found[0]->count) return NULL; - if (found[0]->type == RPM_STRING_TYPE || found[0]->type == RPM_I18NSTRING_TYPE || found[0]->type == RPM_STRING_ARRAY_TYPE) { + if (found[0]->type == RPM_STRING_TYPE || + found[0]->type == RPM_I18NSTRING_TYPE || + found[0]->type == RPM_STRING_ARRAY_TYPE) { int n; - char *tmpstr = (char *) (map + found[0]->offset); - for (n=0; n < itemindex; n++) + char *tmpstr = (char *) (pstate->m_map + found[0]->offset); + for (n = 0; n < itemindex; n++) tmpstr = tmpstr + strlen(tmpstr) + 1; return tmpstr; } return NULL; } -static int rpm_getint(int tag, int itemindex) +int +rpm_getint(int tag, int itemindex, rpm_scan_state * pstate) { rpm_index **found; - int *tmpint; /* NB: using int8_t* would be easier to code */ + int n, *tmpint; - /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ... - * it's ok to ignore it because tag won't be used as a pointer */ - found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); + found = bsearch((void *) tag, pstate->m_tags, + pstate->m_tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); if (!found || itemindex >= found[0]->count) return -1; - - tmpint = (int *) (map + found[0]->offset); + tmpint = (int *) (pstate->m_map + found[0]->offset); if (found[0]->type == RPM_INT32_TYPE) { - tmpint = (int *) ((char *) tmpint + itemindex*4); - /*return ntohl(*tmpint);*/ - /* int can be != int32_t */ - return ntohl(*(int32_t*)tmpint); + for (n = 0; n < itemindex; n++) + tmpint = (int *) ((void *) tmpint + 4); + return ntohl(*tmpint); } if (found[0]->type == RPM_INT16_TYPE) { - tmpint = (int *) ((char *) tmpint + itemindex*2); - /* ??? read int, and THEN ntohs() it?? */ - /*return ntohs(*tmpint);*/ - return ntohs(*(int16_t*)tmpint); + for (n = 0; n < itemindex; n++) + tmpint = (int *) ((void *) tmpint + 2); + return ntohs(*tmpint); } if (found[0]->type == RPM_INT8_TYPE) { - tmpint = (int *) ((char *) tmpint + itemindex); - /* ??? why we don't read byte here??? */ - /*return ntohs(*tmpint);*/ - return *(int8_t*)tmpint; + for (n = 0; n < itemindex; n++) + tmpint = (int *) ((void *) tmpint + 1); + return ntohs(*tmpint); } return -1; } -static void fileaction_dobackup(char *filename, int fileref) + +int +rpm_compare_md5_string_to_actual(const char * szFilepath, + const char * pszMD5) +{ + char * szResult; + + if (strlen(pszMD5) < 32) + return 0; + + szResult = (char *)hash_file(szFilepath, HASH_MD5); + if (szResult != NULL) { + + if (strcmp(szResult, pszMD5) != 0) + return 1; // mismatch + + return 0; // match + } else + return 3; // unable to compute hash + +} + + +/* + * called by RPM file list iterator, + * backs up extant config files on install/update + */ + +void +fileaction_dobackup(char *filename, int fileref, rpm_scan_state * pstate) { - struct stat oldfile; - int stat_res; - char *newname; - if (rpm_getint(TAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) { + char *newname, * szEffectiveFilename; + + if (rpm_getint(RPMTAG_FILEFLAGS, fileref, pstate) & RPMFILE_CONFIG) { + /* Only need to backup config files */ - stat_res = lstat(filename, &oldfile); - if (stat_res == 0 && S_ISREG(oldfile.st_mode)) { - /* File already exists - really should check MD5's etc to see if different */ - newname = xasprintf("%s.rpmorig", filename); - copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS); - remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE); + + szEffectiveFilename = filename; + if ((szEffectiveFilename[0] == '/') && + (szEffectiveFilename[1] == '/')) + szEffectiveFilename++; + + if (rpm_compare_md5_string_to_actual(szEffectiveFilename, + rpm_getstring(RPMTAG_FILEMD5S, fileref, pstate)) == 1) { + + /* file is changed */ + + newname = xmalloc(strlen(szEffectiveFilename)+10); + strcpy(newname, szEffectiveFilename); + strcat(newname, ".rpmorig"); + printf("Saving %s as %s\n", + szEffectiveFilename, newname); + unlink(newname); + copy_file(szEffectiveFilename, newname, + FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS); + remove_file(szEffectiveFilename, + FILEUTILS_RECUR | FILEUTILS_FORCE); free(newname); } + + } +} + +void +fileaction_verify(char *filename, int fileref, rpm_scan_state * pstate) +{ + + switch (rpm_compare_md5_string_to_actual(filename, + rpm_getstring(RPMTAG_FILEMD5S, fileref, pstate))) { + + case 1: + printf(" CHANGED %s\n", filename); + pstate->m_nUser++; + break; + case 2: + case 3: + case 4: + printf(" DELETED %s\n", filename); + pstate->m_nUser++; + break; + + default: + break; } + } -static void fileaction_setowngrp(char *filename, int fileref) + +void +fileaction_setowngrp(char *filename, int fileref, rpm_scan_state * pstate) { int uid, gid; - uid = xuname2uid(rpm_getstr(TAG_FILEUSERNAME, fileref)); - gid = xgroup2gid(rpm_getstr(TAG_FILEGROUPNAME, fileref)); + + uid = xuname2uid(rpm_getstring( + RPMTAG_FILEUSERNAME, fileref, pstate)); + gid = xgroup2gid(rpm_getstring( + RPMTAG_FILEGROUPNAME, fileref, pstate)); chown(filename, uid, gid); } -static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref)) + +void +fileaction_list(char *filename, int fileref, rpm_scan_state * pstate) +{ + printf("%s\n", filename); +} + +void +loop_through_files(int filetag, + void (*fileaction)(char *filename, int fileref, rpm_scan_state * pstate), + rpm_scan_state * pstate) { int count = 0; - while (rpm_getstr(filetag, count)) { - char* filename = xasprintf("%s%s", - rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, count)), - rpm_getstr(TAG_BASENAMES, count)); - fileaction(filename, count++); + char *filename, *tmp_dirname, *tmp_basename; + while (rpm_getstring(filetag, count, pstate)) { + /* 1st put on the directory */ + tmp_dirname = rpm_getstring(RPMTAG_DIRNAMES, + rpm_getint(RPMTAG_DIRINDEXES, count, pstate), pstate); + tmp_basename = rpm_getstring(RPMTAG_BASENAMES, count, pstate); + filename = xmalloc(strlen(pszEffectiveRoot) + + strlen(tmp_basename) + strlen(tmp_dirname) + 1); + + strcpy(filename, pszEffectiveRoot); // effective root dir + strcat(filename, tmp_dirname); // then the directory name + strcat(filename, tmp_basename); // then the filename + + fileaction(filename, count++, pstate); free(filename); } } + + +void +diraction_list_packages(struct dirent * pdirentry) +{ + rpm_scan_state rss; + char * pszFilename; + + rpm_construct_scan_state(&rss); + pszFilename = xmalloc(strlen(pszDatabaseDirpath) + + strlen(pdirentry->d_name) + 2); + strcpy(pszFilename, pszDatabaseDirpath); // effective root dir + strcat(pszFilename, pdirentry->d_name); // then the directory name + if (rpm_open_header(pszFilename, &rss)) { + printf("%s (not installed)\n", pszFilename); + free(pszFilename); + } else { + printf("%s-%s-%s\n", pdirentry->d_name, + rpm_getstring(RPMTAG_VERSION, 0, &rss), + rpm_getstring(RPMTAG_RELEASE, 0, &rss)); + free(pszFilename); + rpm_destruct_scan_state(&rss); + } +} + + +void +diraction_listcontents_packages(struct dirent * pdirentry) +{ + rpm_scan_state rss; + char * pszFilename; + int count, it; + + rpm_construct_scan_state(&rss); + pszFilename = xmalloc(strlen(pszDatabaseDirpath) + + strlen(pdirentry->d_name) + 2); + strcpy(pszFilename, pszDatabaseDirpath); // effective root dir + strcat(pszFilename, pdirentry->d_name); // then the directory name + + if (rpm_open_header(pszFilename, &rss)) { + printf("%s (not installed)\n", pszFilename); + return; + } + + count = rpm_getcount(RPMTAG_BASENAMES, &rss); + + for (it = 0; it < count; it++) { + + printf(" %s%s%s\n", + pszRoot, rpm_getstring(RPMTAG_DIRNAMES, + rpm_getint(RPMTAG_DIRINDEXES, it, &rss), &rss), + rpm_getstring(RPMTAG_BASENAMES, it, &rss)); + } + + free(pszFilename); + rpm_destruct_scan_state(&rss); +} + + +void +diraction_verify_packages(struct dirent * pdirentry) +{ + rpm_scan_state rss; + char * pszFilename; + + rpm_construct_scan_state(&rss); + pszFilename = xmalloc(strlen(pszDatabaseDirpath) + + strlen(pdirentry->d_name) + 2); + strcpy(pszFilename, pszDatabaseDirpath); // effective root dir + strcat(pszFilename, pdirentry->d_name); // then the directory name + if (rpm_open_header(pszFilename, &rss)) { + printf("%s (not installed)\n", pszFilename); + return; + } + + rpm_getstring(RPMTAG_RELEASE, 0, &rss); + rss.m_nUser = 0; + loop_through_files(RPMTAG_BASENAMES, fileaction_verify, &rss); + + free(pszFilename); + rpm_destruct_scan_state(&rss); +} + + +void +diraction_append_provides(struct dirent * pdirentry) +{ + rpm_scan_state rss; + char * pszFilename; + + rpm_construct_scan_state(&rss); + pszFilename = xmalloc(strlen(pszDatabaseDirpath) + + strlen(pdirentry->d_name) + 2); + strcpy(pszFilename, pszDatabaseDirpath); // first the effective root dir + strcat(pszFilename, pdirentry->d_name); // then the directory name + +// printf("diraction_append_provides %s\n", pszFilename); + + if (rpm_open_header(pszFilename, &rss)) { + printf("%s (not installed)\n", pszFilename); + return; + } + DumpProvides(&rss); + free(pszFilename); + rpm_destruct_scan_state(&rss); +} + +void +diraction_check_if_dep( + struct dirent * pdirentry) +{ + rpm_scan_state rss; + char * pszFilename, *szName; + int n; + char fSeen = 0; + + rpm_construct_scan_state(&rss); + pszFilename = xmalloc(strlen(pszDatabaseDirpath) + + strlen(pdirentry->d_name) + 2); + strcpy(pszFilename, pszDatabaseDirpath); // first the effective root dir + strcat(pszFilename, pdirentry->d_name); // then the directory name + + if (rpm_open_header(pszFilename, &rss)) { + printf("%s (not installed)\n", pszFilename); + return; + } + szName = rpm_getstring(RPMTAG_NAME, 0, &rss); + n = optind_post_switches; + while (n < nCopyArgc) { + if (strcmp(pszArgv[n], szName) == 0) + fSeen = 1; + n++; + } + if (!fSeen) { // this package is not one of those being deleted + int nCount = rpm_getcount(RPMTAG_REQUIRENAME, &rss); + + if (fdTemp < 1) + bb_error_msg_and_die( + "diraction_check_if_dep" + ": provides file closed when still needed"); + + /* for every require in this package, then */ + + for (n = 0; n < nCount; n++) { + dependency dep; + char * sz = rpm_getstring(RPMTAG_REQUIRENAME, n, &rss); + + /* move to start of provides list */ + if (lseek(fdTemp, 0, SEEK_SET) == (off_t)-1) + perror("Seek error K"); + + while ((read(fdTemp, &dep, sizeof(dep)) != 0) && + (!fSeen)) { + + // does this require appear in the list? + if (strcmp(dep.m_szPackage, sz) == 0) + fSeen = 1; + + } + + if (fSeen) { + + /* + * uh-uh... a package not being deleted + * required something that is being + * requested to be deleted... + */ + printf( + "Error: Package %s requires %s (0x%x)\n", + szName, dep.m_szPackage, dep.m_nFlags); + nCountErrors++; + } + } + } + free(pszFilename); + rpm_destruct_scan_state(&rss); +} + +void +loop_through_database_files(void (*diraction)(struct dirent * pdirentry)) +{ + DIR *pdir = opendir(pszDatabaseDirpath); + if (pdir != NULL) { + while (1) { + struct dirent *pdirentry = readdir(pdir); + if (pdirentry == NULL) { + closedir(pdir); + return; + } else { + if (pdirentry->d_name[0] != '.') + diraction(pdirentry); + } + } + } +} Index: busybox-1.6.0.18773/archival/rpm.h =================================================================== --- /dev/null +++ busybox-1.6.0.18773/archival/rpm.h @@ -0,0 +1,238 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rpm applet for busybox includefile + * + * Copyright (C) 2001,2002 by Laurence Anderson + * Additional work to add more RPM functionality (C)2005-2006 Andy Green + * + * 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. + * + */ + +// md5sum implementation: from busybox-1.1.2/coreutils/md5_sha1_sum.c +// forced the export to be non-static in that file so we can see it at linktime + +typedef enum { HASH_SHA1, HASH_MD5 } hash_algo_t; +extern uint8_t *hash_file(const char *filename, hash_algo_t hash_algo); + +////////// +// +// Implementation defines +// +////////// + + // where the database of headers is stored +#define RPM_STATE_DIR "/var/lib/rpm/" +#define RPM_TEMPFILE_REQUIRES "/tmp/_requires" +#define RPM_TEMPFILE_PROVIDES "/tmp/_provides" + // largest filepath that can come out of the cpio archive +#define RPM_MAX_FILEPATH 256 +#define MAX_VERSION_ELEMENTS 6 + // used in the dependency struct +#define MAX_BASENAME_LENGTH 64 +#define MAX_VERSION_LENGTH 16 + +////////// +// +// Option bitfield definitions +// +////////// + + +enum rpm_functions_e { + rpm_query = 1, + rpm_install = 2, + rpm_query_info = 4, + rpm_package = 8, + rpm_query_list = 16, + rpm_query_list_doc = 32, + rpm_query_list_config = 64, + rpm_update = 128, + rpm_erase = 256, + rpm_oldpackage = 512, + rpm_force = 1024, + rpm_query_verify = 2048, + rpm_query_all = 4096, + rpm_skip_uninstalled = 8192, + rpm_query_compareverions = 16384 +}; + + +////////// +// +// RPM-format specific defines +// +////////// + +#define RPM_HEADER_MAGIC "\216\255\350" +#define RPM_CHAR_TYPE 1 +#define RPM_INT8_TYPE 2 +#define RPM_INT16_TYPE 3 +#define RPM_INT32_TYPE 4 +/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */ +#define RPM_STRING_TYPE 6 +#define RPM_BIN_TYPE 7 +#define RPM_STRING_ARRAY_TYPE 8 +#define RPM_I18NSTRING_TYPE 9 + +#define RPMTAG_NAME 1000 +#define RPMTAG_VERSION 1001 +#define RPMTAG_RELEASE 1002 +#define RPMTAG_EPOCH 1003 +#define RPMTAG_SUMMARY 1004 +#define RPMTAG_DESCRIPTION 1005 +#define RPMTAG_BUILDTIME 1006 +#define RPMTAG_BUILDHOST 1007 +#define RPMTAG_SIZE 1009 +#define RPMTAG_VENDOR 1011 +#define RPMTAG_LICENSE 1014 +#define RPMTAG_PACKAGER 1015 +#define RPMTAG_GROUP 1016 +#define RPMTAG_URL 1020 +// pre/post IN are pre- and post-install scripts +#define RPMTAG_PREIN 1023 +#define RPMTAG_POSTIN 1024 +// pre/post UN are pre- and post-uninstall scripts +#define RPMTAG_PREUN 1025 +#define RPMTAG_POSTUN 1026 +#define RPMTAG_FILEMD5S 1035 /* s[] */ +#define RPMTAG_FILEFLAGS 1037 +#define RPMTAG_FILEUSERNAME 1039 +#define RPMTAG_FILEGROUPNAME 1040 +#define RPMTAG_SOURCERPM 1044 +#define RPMTAG_PROVIDENAME 1047 +#define RPMTAG_REQUIREFLAGS 1048 /* i */ +#define RPMTAG_REQUIRENAME 1049 /* s[] */ +#define RPMTAG_REQUIREVERSION 1050 /* s[] */ +#define RPMTAG_CONFLICTFLAGS 1053 /* i */ +#define RPMTAG_CONFLICTNAME 1054 /* s[] */ +#define RPMTAG_CONFLICTVERSION 1055 /* s[] */ +// pre/post PROG strings contain something like /bin/sh +#define RPMTAG_PREINPROG 1085 +#define RPMTAG_POSTINPROG 1086 +#define RPMTAG_PREFIXS 1098 +#define RPMTAG_PROVIDEFLAGS 1112 +#define RPMTAG_PROVIDEVERSION 1113 +#define RPMTAG_DIRINDEXES 1116 +#define RPMTAG_BASENAMES 1117 +#define RPMTAG_DIRNAMES 1118 + +#define RPMFILE_CONFIG (1 << 0) +#define RPMFILE_DOC (1 << 1) + + +enum { + RPMSENSE_ANY = 0, +/*@-enummemuse@*/ + RPMSENSE_SERIAL = (1 << 0), /*!< @todo Legacy. */ +/*@=enummemuse@*/ + RPMSENSE_LESS = (1 << 1), + RPMSENSE_GREATER = (1 << 2), + RPMSENSE_EQUAL = (1 << 3), + RPMSENSE_PROVIDES = (1 << 4), /* only used internally by builds */ + RPMSENSE_CONFLICTS = (1 << 5), /* only used internally by builds */ + /* bit 6 used to be RPMSENSE_PREREQ */ +#define RPMSENSE_PREREQ RPMSENSE_ANY + RPMSENSE_OBSOLETES = (1 << 7), /* only used internally by builds */ + RPMSENSE_INTERP = (1 << 8), /*!< Interpreter used by scriptlet. */ + RPMSENSE_SCRIPT_PRE = ((1 << 9)|RPMSENSE_PREREQ), /*!< %pre dependency. */ + RPMSENSE_SCRIPT_POST = ((1 << 10)|RPMSENSE_PREREQ), /*!< %post dependency. */ + RPMSENSE_SCRIPT_PREUN = ((1 << 11)|RPMSENSE_PREREQ), /*!< %preun dependency. */ + RPMSENSE_SCRIPT_POSTUN = ((1 << 12)|RPMSENSE_PREREQ), /*!< %postun dependency. */ + RPMSENSE_SCRIPT_VERIFY = (1 << 13), /*!< %verify dependency. */ + RPMSENSE_FIND_REQUIRES = (1 << 14), /*!< find-requires generated dependency. */ + RPMSENSE_FIND_PROVIDES = (1 << 15), /*!< find-provides generated dependency. */ + + RPMSENSE_TRIGGERIN = (1 << 16), /*!< %triggerin dependency. */ + RPMSENSE_TRIGGERUN = (1 << 17), /*!< %triggerun dependency. */ + RPMSENSE_TRIGGERPOSTUN = (1 << 18), /*!< %triggerpostun dependency. */ + RPMSENSE_MISSINGOK = (1 << 19), /*!< suggests/enhances hint. */ + RPMSENSE_SCRIPT_PREP = (1 << 20), /*!< %prep build dependency. */ + RPMSENSE_SCRIPT_BUILD = (1 << 21), /*!< %build build dependency. */ + RPMSENSE_SCRIPT_INSTALL = (1 << 22),/*!< %install build dependency. */ + RPMSENSE_SCRIPT_CLEAN = (1 << 23), /*!< %clean build dependency. */ + RPMSENSE_RPMLIB = ((1 << 24) | RPMSENSE_PREREQ), /*!< rpmlib(feature) dependency. */ +/*@-enummemuse@*/ + RPMSENSE_TRIGGERPREIN = (1 << 25), /*!< @todo Implement %triggerprein. */ +/*@=enummemuse@*/ + RPMSENSE_KEYRING = (1 << 26), + RPMSENSE_PATCHES = (1 << 27), + RPMSENSE_CONFIG = (1 << 28) +}; + + +////////// +// +// RPM-related structs +// +////////// + +typedef struct { + uint32_t tag; /* 4 byte tag */ + uint32_t type; /* 4 byte type */ + uint32_t offset; /* 4 byte offset */ + uint32_t count; /* 4 byte count */ +} rpm_index; + + + + // one of these is instantiated for each RPM header that we parse + // it contains all the parsing state information + +typedef struct { + int m_fd; + void * m_map; + rpm_index **m_tags; + int m_tagcount; + int m_offset; + int m_nUser; // available for storing temp states with the + //object during interation callbacks +} rpm_scan_state; + +void rpm_construct_scan_state(rpm_scan_state * prss) { prss->m_fd=-1; prss->m_tags=NULL; } +void rpm_destruct_scan_state(rpm_scan_state * prss) { + if(prss->m_fd!=-1) { close(prss->m_fd); prss->m_fd=-1; } + if(prss->m_tags!=NULL) { free(prss->m_tags); prss->m_tags=NULL; } +} +int rpm_open_header(const char * szcFilepath, rpm_scan_state * prss); + +typedef struct { + char m_szPackage[MAX_BASENAME_LENGTH+1]; + int m_nFlags; + char m_szVersion[MAX_VERSION_LENGTH+1]; +} dependency; + +////////// +// +// Forward refs +// +////////// + +void extract_cpio_gz(int fd); +rpm_index **rpm_gettags(int fd, int *num_tags); +int bsearch_rpmtag(const void *key, const void *item); +char *rpm_getstring(int tag, int itemindex, rpm_scan_state * pstate); +int rpm_getint(int tag, int itemindex, rpm_scan_state * pstate); +int rpm_getcount(int tag, rpm_scan_state *pstate); +void exec_script(int progtag, int datatag, char *prefix); +void fileaction_dobackup(char *filename, int fileref, rpm_scan_state * pstate); +void fileaction_setowngrp(char *filename, int fileref, rpm_scan_state * pstate); +void fileaction_list(char *filename, int itemno, rpm_scan_state * pstate); +void loop_through_files(int filetag, + void (*fileaction)(char *filename, int fileref, rpm_scan_state *pstate), + rpm_scan_state * pstate); +void loop_through_database_files(void (*diraction)(struct dirent * pdirentry)); +void diraction_list_packages(struct dirent * pdirentry); +void fileaction_verify(char *filename, int fileref, rpm_scan_state * pstate); +void diraction_verify_packages(struct dirent * pdirentry); +void diraction_append_provides(struct dirent * pdirentry); +void diraction_check_if_dep(struct dirent * pdirentry); +void diraction_listcontents_packages(struct dirent * pdirentry); Index: busybox-1.6.0.18773/include/usage.h =================================================================== --- busybox-1.6.0.18773.orig/include/usage.h +++ busybox-1.6.0.18773/include/usage.h @@ -2793,17 +2793,23 @@ " -A inet" USE_FEATURE_IPV6("{6}") " Select address family" #define rpm_trivial_usage \ - "-i -q[ildc]p package.rpm" + "-iuFeq [-fpldcofva] [-r rootpath] package.rpm" #define rpm_full_usage \ - "Manipulate RPM packages" \ - "\n\nOptions:" \ - "\n -i Install package" \ - "\n -q Query package" \ - "\n -p Query uninstalled package" \ - "\n -i Show information" \ - "\n -l List contents" \ - "\n -d List documents" \ - "\n -c List config files" + "Manipulates RPM packages" \ + "\n\nOptions:" \ + "\n\t-r Set effective root to " \ + "\n\t-i pkgname.rpm Install package" \ + "\n\t-e pkgname Erase (uninstall) package" \ + "\n\t-u pkgname.rpm Update (or install) package" \ + "\n\t-u -o pkgname.rpm Upgrade of newer package by older package" \ + "\n\t-F pkgname.rpm Freshen package" \ + "\n\t-v pkgname Verify installation against package files" \ + "\n\t-q -a Query all installed packages" \ + "\n\t-q -i pkgname Show information" \ + "\n\t-q -i -p pkgname.rpm Show information" \ + "\n\t-q -l pkgname List contents" \ + "\n\t-q -l -p pkgname.rpm List contents" \ + "\n\t-q -C vers pkgname Compare versions" #define rpm2cpio_trivial_usage \ "package.rpm"