rpm: code shrink
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 9 Aug 2017 16:20:01 +0000 (18:20 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 9 Aug 2017 16:20:01 +0000 (18:20 +0200)
function                                             old     new   delta
rpm_getstr0                                            -       7      +7
rpm_getstr                                           112     110      -2
rpm_getint                                           120     118      -2
bsearch_rpmtag                                        15      13      -2
shell_builtin_read                                  1334    1320     -14
rpm_main                                            1548    1474     -74
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/5 up/down: 7/-94)             Total: -87 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
archival/rpm.c
archival/rpm2cpio.c

index 98039d49901f8dc203077d58e05013844c0fc052..89b36dd46ef165e7cd7469c8fe841f68143c1295 100644 (file)
 //applet:IF_RPM(APPLET(rpm, BB_DIR_BIN, BB_SUID_DROP))
 //kbuild:lib-$(CONFIG_RPM) += rpm.o
 
-//usage:#define rpm_trivial_usage
-//usage:       "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
-//usage:#define rpm_full_usage "\n\n"
-//usage:       "Manipulate RPM packages\n"
-//usage:     "\nCommands:"
-//usage:     "\n       -i      Install package"
-//usage:     "\n       -qp     Query package"
-//usage:     "\n       -qpi    Show information"
-//usage:     "\n       -qpl    List contents"
-//usage:     "\n       -qpd    List documents"
-//usage:     "\n       -qpc    List config files"
-
 #include "libbb.h"
 #include "common_bufsiz.h"
 #include "bb_archive.h"
@@ -91,8 +79,9 @@ typedef struct {
 
 struct globals {
        void *map;
-       rpm_index **mytags;
+       rpm_index *mytags;
        int tagcount;
+       unsigned mapsize, pagesize;
 } FIX_ALIASING;
 #define G (*(struct globals*)bb_common_bufsiz1)
 #define INIT_G() do { setup_common_bufsiz(); } while (0)
@@ -128,105 +117,122 @@ static void extract_cpio(int fd, const char *source_rpm)
                continue;
 }
 
-static rpm_index **rpm_gettags(int fd, int *num_tags)
+static rpm_index *rpm_gettags(int fd)
 {
-       /* We should never need more than 200 (shrink via realloc later) */
-       rpm_index **tags = xzalloc(200 * sizeof(tags[0]));
-       int pass, tagindex = 0;
+       rpm_index *tags;
+       unsigned pass, idx;
+       unsigned storepos;
 
-       xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
+       storepos = xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
 
+       G.tagcount = 0;
+       tags = NULL;
+       idx = 0;
        /* 1st pass is the signature headers, 2nd is the main stuff */
        for (pass = 0; pass < 2; pass++) {
                struct rpm_header header;
-               rpm_index *tmpindex;
-               int storepos;
+               unsigned cnt;
 
                xread(fd, &header, sizeof(header));
                if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER))
                        return NULL; /* Invalid magic, or not version 1 */
                header.size = ntohl(header.size);
-               header.entries = ntohl(header.entries);
-               storepos = xlseek(fd, 0, SEEK_CUR) + header.entries * 16;
-
-               while (header.entries--) {
-                       tmpindex = tags[tagindex++] = xmalloc(sizeof(*tmpindex));
-                       xread(fd, tmpindex, sizeof(*tmpindex));
-                       tmpindex->tag = ntohl(tmpindex->tag);
-                       tmpindex->type = ntohl(tmpindex->type);
-                       tmpindex->count = ntohl(tmpindex->count);
-                       tmpindex->offset = storepos + ntohl(tmpindex->offset);
+               cnt = ntohl(header.entries);
+               storepos += sizeof(header) + cnt * 16;
+
+               G.tagcount += cnt;
+               tags = xrealloc(tags, G.tagcount * sizeof(tags[0]));
+               xread(fd, &tags[idx], sizeof(tags[0]) * cnt);
+               while (cnt--) {
+                       rpm_index *tag = &tags[idx];
+                       tag->tag = ntohl(tag->tag);
+                       tag->type = ntohl(tag->type);
+                       tag->count = ntohl(tag->count);
+                       tag->offset = storepos + ntohl(tag->offset);
                        if (pass == 0)
-                               tmpindex->tag -= 743;
+                               tag->tag -= 743;
+                       idx++;
                }
-               storepos = xlseek(fd, header.size, SEEK_CUR); /* Seek past store */
                /* Skip padding to 8 byte boundary after reading signature headers */
                if (pass == 0)
-                       xlseek(fd, (-storepos) & 0x7, SEEK_CUR);
+                       while (header.size & 7)
+                               header.size++;
+               /* Seek past store */
+               storepos = xlseek(fd, header.size, SEEK_CUR);
        }
-       /* realloc tags to save space */
-       tags = xrealloc(tags, tagindex * sizeof(tags[0]));
-       *num_tags = tagindex;
-       /* All done, leave the file at the start of the gzipped cpio archive */
+
+       /* Map the store */
+       storepos = (storepos + G.pagesize) & -(int)G.pagesize;
+       /* remember size for munmap */
+       G.mapsize = storepos;
+       /* some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */
+       G.map = mmap(0, storepos, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (G.map == MAP_FAILED)
+               return NULL; /* error */
+
        return tags;
 }
 
 static int bsearch_rpmtag(const void *key, const void *item)
 {
        int *tag = (int *)key;
-       rpm_index **tmp = (rpm_index **) item;
-       return (*tag - tmp[0]->tag);
+       rpm_index *tmp = (rpm_index *) item;
+       return (*tag - tmp->tag);
 }
 
 static int rpm_getcount(int tag)
 {
-       rpm_index **found;
-       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+       rpm_index *found;
+       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag);
        if (!found)
                return 0;
-       return found[0]->count;
+       return found->count;
 }
 
 static char *rpm_getstr(int tag, int itemindex)
 {
-       rpm_index **found;
-       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
-       if (!found || itemindex >= found[0]->count)
+       rpm_index *found;
+       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag);
+       if (!found || itemindex >= found->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->type == RPM_STRING_TYPE
+        || found->type == RPM_I18NSTRING_TYPE
+        || found->type == RPM_STRING_ARRAY_TYPE
        ) {
                int n;
-               char *tmpstr = (char *) G.map + found[0]->offset;
+               char *tmpstr = (char *) G.map + found->offset;
                for (n = 0; n < itemindex; n++)
                        tmpstr = tmpstr + strlen(tmpstr) + 1;
                return tmpstr;
        }
        return NULL;
 }
+static char *rpm_getstr0(int tag)
+{
+       return rpm_getstr(tag, 0);
+}
 
 static int rpm_getint(int tag, int itemindex)
 {
-       rpm_index **found;
+       rpm_index *found;
        char *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, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
-       if (!found || itemindex >= found[0]->count)
+       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag);
+       if (!found || itemindex >= found->count)
                return -1;
 
-       tmpint = (char *) G.map + found[0]->offset;
-       if (found[0]->type == RPM_INT32_TYPE) {
+       tmpint = (char *) G.map + found->offset;
+       if (found->type == RPM_INT32_TYPE) {
                tmpint += itemindex*4;
                return ntohl(*(int32_t*)tmpint);
        }
-       if (found[0]->type == RPM_INT16_TYPE) {
+       if (found->type == RPM_INT16_TYPE) {
                tmpint += itemindex*2;
                return ntohs(*(int16_t*)tmpint);
        }
-       if (found[0]->type == RPM_INT8_TYPE) {
+       if (found->type == RPM_INT8_TYPE) {
                tmpint += itemindex;
                return *(int8_t*)tmpint;
        }
@@ -273,11 +279,71 @@ static void loop_through_files(int filetag, void (*fileaction)(char *filename, i
        }
 }
 
+#if 0 //DEBUG
+static void print_all_tags(void)
+{
+       unsigned i = 0;
+       while (i < G.tagcount) {
+               rpm_index *tag = &G.mytags[i];
+               if (tag->type == RPM_STRING_TYPE
+                || tag->type == RPM_I18NSTRING_TYPE
+                || tag->type == RPM_STRING_ARRAY_TYPE
+               ) {
+                       unsigned n;
+                       char *str = (char *) G.map + tag->offset;
+
+                       printf("tag[%u] %08x type %08x offset %08x count %d '%s'\n",
+                               i, tag->tag, tag->type, tag->offset, tag->count, str
+                       );
+                       for (n = 1; n < tag->count; n++) {
+                               str += strlen(str) + 1;
+                               printf("\t'%s'\n", str);
+                       }
+               }
+               i++;
+       }
+}
+#else
+#define print_all_tags() ((void)0)
+#endif
+
+//usage:#define rpm_trivial_usage
+//usage:       "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
+//usage:#define rpm_full_usage "\n\n"
+//usage:       "Manipulate RPM packages\n"
+//usage:     "\nCommands:"
+//usage:     "\n       -i      Install package"
+//usage:     "\n       -qp     Query package"
+//usage:     "\n       -qpi    Show information"
+//usage:     "\n       -qpl    List contents"
+//usage:     "\n       -qpd    List documents"
+//usage:     "\n       -qpc    List config files"
+
+/* RPM version 4.13.0.1:
+ * Unlike -q, -i seems to imply -p: -i, -ip and -pi work the same.
+ * OTOH, with -q order is important: "-piq FILE.rpm" works as -qp, not -qpi
+ * (IOW: shows only package name, not package info).
+ * "-iq ARG" works as -q: treats ARG as package name, not a file.
+ *
+ * "man rpm" on -l option and options implying it:
+ * -l, --list          List files in package.
+ * -c, --configfiles   List only configuration files (implies -l).
+ * -d, --docfiles      List only documentation files (implies -l).
+ * -L, --licensefiles  List only license files (implies -l).
+ * --dump      Dump file information as follows (implies -l):
+ *             path size mtime digest mode owner group isconfig isdoc rdev symlink
+ * -s, --state Display the states of files in the package (implies -l).
+ *             The state of each file is one of normal, not installed, or replaced.
+ *
+ * Looks like we can switch to getopt32 here: in practice, people
+ * do place -q first if they intend to use it (misinterpreting "-piq" wouldn't matter).
+ */
 int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int rpm_main(int argc, char **argv)
 {
        int opt, func = 0;
-       const unsigned pagesize = getpagesize();
+
+       G.pagesize = getpagesize();
 
        while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
                switch (opt) {
@@ -289,17 +355,17 @@ int rpm_main(int argc, char **argv)
                        if (func) bb_show_usage();
                        func = rpm_query;
                        break;
-               case 'p': /* Query a package */
+               case 'p': /* Query a package (IOW: .rpm file, we are not querying RPMDB) */
                        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) */
+               case 'd': /* List doc files in a package (implies -l) */
                        func |= rpm_query_list;
                        func |= rpm_query_list_doc;
                        break;
-               case 'c': /* List config files in a package (implies list) */
+               case 'c': /* List config files in a package (implies -l) */
                        func |= rpm_query_list;
                        func |= rpm_query_list_config;
                        break;
@@ -313,24 +379,22 @@ int rpm_main(int argc, char **argv)
                bb_show_usage();
        }
 
-       while (*argv) {
+       for (;;) {
                int rpm_fd;
-               unsigned mapsize;
                const char *source_rpm;
 
-               rpm_fd = xopen(*argv++, O_RDONLY);
-               G.mytags = rpm_gettags(rpm_fd, &G.tagcount);
+               rpm_fd = xopen(*argv, O_RDONLY);
+               G.mytags = rpm_gettags(rpm_fd);
                if (!G.mytags)
-                       bb_error_msg_and_die("error reading rpm header");
-               mapsize = xlseek(rpm_fd, 0, SEEK_CUR);
-               mapsize = (mapsize + pagesize) & -(int)pagesize;
-               /* Some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */
-               G.map = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
-//FIXME: error check?
+                       bb_error_msg_and_die("error reading rpm header from '%s'", *argv);
+
+               print_all_tags();
 
-               source_rpm = rpm_getstr(TAG_SOURCERPM, 0);
+               source_rpm = rpm_getstr0(TAG_SOURCERPM);
 
                if (func & rpm_install) {
+                       /* -i (and not -qi) */
+
                        /* Backup any config files */
                        loop_through_files(TAG_BASENAMES, fileaction_dobackup);
                        /* Extact the archive */
@@ -338,10 +402,13 @@ int rpm_main(int argc, char **argv)
                        /* 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)) {
+               else
+               if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
+                       /* -qp */
+
                        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));
+                               printf("%s-%s-%s\n", rpm_getstr0(TAG_NAME), rpm_getstr0(TAG_VERSION), rpm_getstr0(TAG_RELEASE));
                        }
                        if (func & rpm_query_info) {
                                /* Do the nice printout */
@@ -350,30 +417,33 @@ int rpm_main(int argc, char **argv)
                                char bdatestring[50];
                                const char *p;
 
-                               printf("%-12s: %s\n", "Name"        , rpm_getstr(TAG_NAME, 0));
+                               printf("%-12s: %s\n", "Name"        , rpm_getstr0(TAG_NAME));
                                /* TODO compat: add "Epoch" here */
-                               printf("%-12s: %s\n", "Version"     , rpm_getstr(TAG_VERSION, 0));
-                               printf("%-12s: %s\n", "Release"     , rpm_getstr(TAG_RELEASE, 0));
+                               printf("%-12s: %s\n", "Version"     , rpm_getstr0(TAG_VERSION));
+                               printf("%-12s: %s\n", "Release"     , rpm_getstr0(TAG_RELEASE));
                                /* add "Architecture" */
-                               printf("%-12s: %s\n", "Install Date", "(not installed)");
-                               printf("%-12s: %s\n", "Group"       , rpm_getstr(TAG_GROUP, 0));
+                               /* printf("%-12s: %s\n", "Install Date", "(not installed)"); - we don't know */
+                               printf("%-12s: %s\n", "Group"       , rpm_getstr0(TAG_GROUP));
                                printf("%-12s: %d\n", "Size"        , rpm_getint(TAG_SIZE, 0));
-                               printf("%-12s: %s\n", "License"     , rpm_getstr(TAG_LICENSE, 0));
+                               printf("%-12s: %s\n", "License"     , rpm_getstr0(TAG_LICENSE));
                                /* add "Signature" */
                                printf("%-12s: %s\n", "Source RPM"  , source_rpm ? source_rpm : "(none)");
                                bdate_time = rpm_getint(TAG_BUILDTIME, 0);
                                bdate_ptm = localtime(&bdate_time);
                                strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm);
                                printf("%-12s: %s\n", "Build Date"  , bdatestring);
-                               printf("%-12s: %s\n", "Build Host"  , rpm_getstr(TAG_BUILDHOST, 0));
-                               p = rpm_getstr(TAG_PREFIXS, 0);
+                               printf("%-12s: %s\n", "Build Host"  , rpm_getstr0(TAG_BUILDHOST));
+                               p = rpm_getstr0(TAG_PREFIXS);
                                printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)");
                                /* add "Packager" */
-                               p = rpm_getstr(TAG_VENDOR, 0);
-                               printf("%-12s: %s\n", "Vendor"      , p ? p : "(none)");
-                               printf("%-12s: %s\n", "URL"         , rpm_getstr(TAG_URL, 0));
-                               printf("%-12s: %s\n", "Summary"     , rpm_getstr(TAG_SUMMARY, 0));
-                               printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0));
+                               p = rpm_getstr0(TAG_VENDOR);
+                               if (p) /* rpm 4.13.0.1 does not show "(none)" for Vendor: */
+                               printf("%-12s: %s\n", "Vendor"      , p);
+                               p = rpm_getstr0(TAG_URL);
+                               if (p) /* rpm 4.13.0.1 does not show "(none)"/"(null)" for URL: */
+                               printf("%-12s: %s\n", "URL"         , p);
+                               printf("%-12s: %s\n", "Summary"     , rpm_getstr0(TAG_SUMMARY));
+                               printf("Description :\n%s\n", rpm_getstr0(TAG_DESCRIPTION));
                        }
                        if (func & rpm_query_list) {
                                int count, it, flags;
@@ -396,10 +466,16 @@ int rpm_main(int argc, char **argv)
                                                rpm_getstr(TAG_BASENAMES, it));
                                }
                        }
+               } else {
+                       /* Unsupported (help text shows what we support) */
+                       bb_show_usage();
                }
-               munmap(G.map, mapsize);
+               if (!*++argv)
+                       break;
+               munmap(G.map, G.mapsize);
                free(G.mytags);
                close(rpm_fd);
        }
+
        return 0;
 }
index 3e4a6a249404b82b4b74526f2a82382424deb71c..a6db19c13a0c7d60ba42654383a6cab05447c21d 100644 (file)
@@ -75,11 +75,14 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
        /* Skip the main header */
        skip_header();
 
-       //if (SEAMLESS_COMPRESSION)
+       //if (SEAMLESS_COMPRESSION) - we do this at the end instead.
        //      /* We need to know whether child (gzip/bzip/etc) exits abnormally */
        //      signal(SIGCHLD, check_errors_in_children);
 
-       /* This works, but doesn't report uncompress errors (they happen in child) */
+//TODO: look for rpm tag RPMTAG_PAYLOADCOMPRESSOR (dec 1125, hex 0x465),
+// if the value is "lzma", set up decompressor without detection
+// (lzma can't be detected).
+
        setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1);
        if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0)
                bb_error_msg_and_die("error unpacking");