source: MondoRescue/branches/stable/mindi-busybox/archival/rpm.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 11.8 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini rpm applet for busybox
4 *
5 * Copyright (C) 2001,2002 by Laurence Anderson
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9
10#include <stdio.h>
11#include <unistd.h>
12#include <signal.h>
13#include <stdlib.h>
14#include <fcntl.h>
15#include <netinet/in.h> /* For ntohl & htonl function */
16#include <string.h> /* For strncmp */
17#include <sys/mman.h> /* For mmap */
18#include <time.h> /* For ctime */
19
20#include "busybox.h"
21#include "unarchive.h"
22
23#define RPM_HEADER_MAGIC "\216\255\350"
24#define RPM_CHAR_TYPE       1
25#define RPM_INT8_TYPE       2
26#define RPM_INT16_TYPE      3
27#define RPM_INT32_TYPE      4
28/* #define RPM_INT64_TYPE   5   ---- These aren't supported (yet) */
29#define RPM_STRING_TYPE     6
30#define RPM_BIN_TYPE        7
31#define RPM_STRING_ARRAY_TYPE   8
32#define RPM_I18NSTRING_TYPE 9
33
34#define RPMTAG_NAME             1000
35#define RPMTAG_VERSION          1001
36#define RPMTAG_RELEASE          1002
37#define RPMTAG_SUMMARY          1004
38#define RPMTAG_DESCRIPTION      1005
39#define RPMTAG_BUILDTIME        1006
40#define RPMTAG_BUILDHOST        1007
41#define RPMTAG_SIZE         1009
42#define RPMTAG_VENDOR           1011
43#define RPMTAG_LICENSE          1014
44#define RPMTAG_PACKAGER         1015
45#define RPMTAG_GROUP            1016
46#define RPMTAG_URL          1020
47#define RPMTAG_PREIN            1023
48#define RPMTAG_POSTIN           1024
49#define RPMTAG_FILEFLAGS        1037
50#define RPMTAG_FILEUSERNAME     1039
51#define RPMTAG_FILEGROUPNAME        1040
52#define RPMTAG_SOURCERPM        1044
53#define RPMTAG_PREINPROG        1085
54#define RPMTAG_POSTINPROG       1086
55#define RPMTAG_PREFIXS          1098
56#define RPMTAG_DIRINDEXES       1116
57#define RPMTAG_BASENAMES        1117
58#define RPMTAG_DIRNAMES         1118
59#define RPMFILE_CONFIG          (1 << 0)
60#define RPMFILE_DOC         (1 << 1)
61
62enum rpm_functions_e {
63    rpm_query = 1,
64    rpm_install = 2,
65    rpm_query_info = 4,
66    rpm_query_package = 8,
67    rpm_query_list = 16,
68    rpm_query_list_doc = 32,
69    rpm_query_list_config = 64
70};
71
72typedef struct {
73    uint32_t tag; /* 4 byte tag */
74    uint32_t type; /* 4 byte type */
75    uint32_t offset; /* 4 byte offset */
76    uint32_t count; /* 4 byte count */
77} rpm_index;
78
79static void *map;
80static rpm_index **mytags;
81static int tagcount;
82
83void extract_cpio_gz(int fd);
84rpm_index **rpm_gettags(int fd, int *num_tags);
85int bsearch_rpmtag(const void *key, const void *item);
86char *rpm_getstring(int tag, int itemindex);
87int rpm_getint(int tag, int itemindex);
88int rpm_getcount(int tag);
89void exec_script(int progtag, int datatag, char *prefix);
90void fileaction_dobackup(char *filename, int fileref);
91void fileaction_setowngrp(char *filename, int fileref);
92void fileaction_list(char *filename, int itemno);
93void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref));
94
95int rpm_main(int argc, char **argv)
96{
97    int opt = 0, func = 0, rpm_fd, offset;
98
99    while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
100        switch (opt) {
101        case 'i': // First arg: Install mode, with q: Information
102            if (!func) func |= rpm_install;
103            else func |= rpm_query_info;
104            break;
105        case 'q': // First arg: Query mode
106            if (!func) func |= rpm_query;
107            else bb_show_usage();
108            break;
109        case 'p': // Query a package
110            func |= rpm_query_package;
111            break;
112        case 'l': // List files in a package
113            func |= rpm_query_list;
114            break;
115        case 'd': // List doc files in a package (implies list)
116            func |= rpm_query_list;
117            func |= rpm_query_list_doc;
118            break;
119        case 'c': // List config files in a package (implies list)
120            func |= rpm_query_list;
121            func |= rpm_query_list_config;
122            break;
123        default:
124            bb_show_usage();
125        }
126    }
127
128    if (optind == argc) bb_show_usage();
129    while (optind < argc) {
130        rpm_fd = bb_xopen(argv[optind], O_RDONLY);
131        mytags = rpm_gettags(rpm_fd, (int *) &tagcount);
132        offset = lseek(rpm_fd, 0, SEEK_CUR);
133        if (!mytags) { printf("Error reading rpm header\n"); exit(-1); }
134        map = mmap(0, offset > getpagesize() ? (offset + offset % getpagesize()) : getpagesize(), PROT_READ, MAP_PRIVATE, rpm_fd, 0); // Mimimum is one page
135        if (func & rpm_install) {
136            loop_through_files(RPMTAG_BASENAMES, fileaction_dobackup); /* Backup any config files */
137            extract_cpio_gz(rpm_fd); // Extact the archive
138            loop_through_files(RPMTAG_BASENAMES, fileaction_setowngrp); /* Set the correct file uid/gid's */
139        } else if (func & rpm_query && func & rpm_query_package) {
140            if (!((func & rpm_query_info) || (func & rpm_query_list))) { // If just a straight query, just give package name
141                printf("%s-%s-%s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_RELEASE, 0));
142            }
143            if (func & rpm_query_info) {
144                /* Do the nice printout */
145                time_t bdate_time;
146                struct tm *bdate;
147                char bdatestring[50];
148                printf("Name        : %-29sRelocations: %s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_PREFIXS, 0) ? rpm_getstring(RPMTAG_PREFIXS, 0) : "(not relocateable)");
149                printf("Version     : %-34sVendor: %s\n", rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_VENDOR, 0) ? rpm_getstring(RPMTAG_VENDOR, 0) : "(none)");
150                bdate_time = rpm_getint(RPMTAG_BUILDTIME, 0);
151                bdate = localtime((time_t *) &bdate_time);
152                strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate);
153                printf("Release     : %-30sBuild Date: %s\n", rpm_getstring(RPMTAG_RELEASE, 0), bdatestring);
154                printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstring(RPMTAG_BUILDHOST, 0));
155                printf("Group       : %-30sSource RPM: %s\n", rpm_getstring(RPMTAG_GROUP, 0), rpm_getstring(RPMTAG_SOURCERPM, 0));
156                printf("Size        : %-33dLicense: %s\n", rpm_getint(RPMTAG_SIZE, 0), rpm_getstring(RPMTAG_LICENSE, 0));
157                printf("URL         : %s\n", rpm_getstring(RPMTAG_URL, 0));
158                printf("Summary     : %s\n", rpm_getstring(RPMTAG_SUMMARY, 0));
159                printf("Description :\n%s\n", rpm_getstring(RPMTAG_DESCRIPTION, 0));
160            }
161            if (func & rpm_query_list) {
162                int count, it, flags;
163                count = rpm_getcount(RPMTAG_BASENAMES);
164                for (it = 0; it < count; it++) {
165                    flags = rpm_getint(RPMTAG_FILEFLAGS, it);
166                    switch ((func & rpm_query_list_doc) + (func & rpm_query_list_config))
167                    {
168                        case rpm_query_list_doc: if (!(flags & RPMFILE_DOC)) continue; break;
169                        case rpm_query_list_config: if (!(flags & RPMFILE_CONFIG)) continue; break;
170                        case rpm_query_list_doc + rpm_query_list_config: if (!((flags & RPMFILE_CONFIG) || (flags & RPMFILE_DOC))) continue; break;
171                    }
172                    printf("%s%s\n", rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES, it)), rpm_getstring(RPMTAG_BASENAMES, it));
173                }
174            }
175        }
176        optind++;
177        free (mytags);
178    }
179    return 0;
180}
181
182void extract_cpio_gz(int fd) {
183    archive_handle_t *archive_handle;
184    unsigned char magic[2];
185
186    /* Initialise */
187    archive_handle = init_handle();
188    archive_handle->seek = seek_by_char;
189    //archive_handle->action_header = header_list;
190    archive_handle->action_data = data_extract_all;
191    archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
192    archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
193    archive_handle->src_fd = fd;
194    archive_handle->offset = 0;
195
196    bb_xread_all(archive_handle->src_fd, &magic, 2);
197    if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
198        bb_error_msg_and_die("Invalid gzip magic");
199    }
200    check_header_gzip(archive_handle->src_fd);
201    bb_xchdir("/"); // Install RPM's to root
202
203    archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
204    archive_handle->offset = 0;
205    while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
206}
207
208
209rpm_index **rpm_gettags(int fd, int *num_tags)
210{
211    rpm_index **tags = xzalloc(200 * sizeof(struct rpmtag *)); /* We should never need mode than 200, and realloc later */
212    int pass, tagindex = 0;
213    lseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
214
215    for (pass = 0; pass < 2; pass++) { /* 1st pass is the signature headers, 2nd is the main stuff */
216        struct {
217            char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
218            uint8_t version; /* 1 byte version number */
219            uint32_t reserved; /* 4 bytes reserved */
220            uint32_t entries; /* Number of entries in header (4 bytes) */
221            uint32_t size; /* Size of store (4 bytes) */
222        } header;
223        rpm_index *tmpindex;
224        int storepos;
225
226        read(fd, &header, sizeof(header));
227        if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) return NULL; /* Invalid magic */
228        if (header.version != 1) return NULL; /* This program only supports v1 headers */
229        header.size = ntohl(header.size);
230        header.entries = ntohl(header.entries);
231        storepos = lseek(fd,0,SEEK_CUR) + header.entries * 16;
232
233        while (header.entries--) {
234            tmpindex = tags[tagindex++] = xmalloc(sizeof(rpm_index));
235            read(fd, tmpindex, sizeof(rpm_index));
236            tmpindex->tag = ntohl(tmpindex->tag); tmpindex->type = ntohl(tmpindex->type); tmpindex->count = ntohl(tmpindex->count);
237            tmpindex->offset = storepos + ntohl(tmpindex->offset);
238            if (pass==0) tmpindex->tag -= 743;
239        }
240        lseek(fd, header.size, SEEK_CUR); /* Seek past store */
241        if (pass==0) lseek(fd, (8 - (lseek(fd,0,SEEK_CUR) % 8)) % 8, SEEK_CUR); /* Skip padding to 8 byte boundary after reading signature headers */
242    }
243    tags = realloc(tags, tagindex * sizeof(struct rpmtag *)); /* realloc tags to save space */
244    *num_tags = tagindex;
245    return tags; /* All done, leave the file at the start of the gzipped cpio archive */
246}
247
248int bsearch_rpmtag(const void *key, const void *item)
249{
250    int *tag = (int *)key;
251    rpm_index **tmp = (rpm_index **) item;
252    return (*tag - tmp[0]->tag);
253}
254
255int rpm_getcount(int tag)
256{
257    rpm_index **found;
258    found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
259    if (!found) return 0;
260    else return found[0]->count;
261}
262
263char *rpm_getstring(int tag, int itemindex)
264{
265    rpm_index **found;
266    found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
267    if (!found || itemindex >= found[0]->count) return NULL;
268    if (found[0]->type == RPM_STRING_TYPE || found[0]->type == RPM_I18NSTRING_TYPE || found[0]->type == RPM_STRING_ARRAY_TYPE) {
269        int n;
270        char *tmpstr = (char *) (map + found[0]->offset);
271        for (n=0; n < itemindex; n++) tmpstr = tmpstr + strlen(tmpstr) + 1;
272        return tmpstr;
273    } else return NULL;
274}
275
276int rpm_getint(int tag, int itemindex)
277{
278    rpm_index **found;
279    int n, *tmpint;
280    /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
281     * it's ok to ignore it because tag won't be used as a pointer */
282    found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
283    if (!found || itemindex >= found[0]->count) return -1;
284    tmpint = (int *) (map + found[0]->offset);
285    if (found[0]->type == RPM_INT32_TYPE) {
286        for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 4);
287        return ntohl(*tmpint);
288    } else if (found[0]->type == RPM_INT16_TYPE) {
289        for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 2);
290        return ntohs(*tmpint);
291    } else if (found[0]->type == RPM_INT8_TYPE) {
292        for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 1);
293        return ntohs(*tmpint);
294    } else return -1;
295}
296
297void fileaction_dobackup(char *filename, int fileref)
298{
299    struct stat oldfile;
300    int stat_res;
301    char *newname;
302    if (rpm_getint(RPMTAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) { /* Only need to backup config files */
303        stat_res = lstat (filename, &oldfile);
304        if (stat_res == 0 && S_ISREG(oldfile.st_mode)) { /* File already exists  - really should check MD5's etc to see if different */
305            newname = bb_xstrdup(filename);
306            newname = strcat(newname, ".rpmorig");
307            copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS);
308            remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE);
309            free(newname);
310        }
311    }
312}
313
314void fileaction_setowngrp(char *filename, int fileref)
315{
316    int uid, gid;
317    uid = bb_xgetpwnam(rpm_getstring(RPMTAG_FILEUSERNAME, fileref));
318    gid = bb_xgetgrnam(rpm_getstring(RPMTAG_FILEGROUPNAME, fileref));
319    chown (filename, uid, gid);
320}
321
322void fileaction_list(char *filename, int ATTRIBUTE_UNUSED fileref)
323{
324    printf("%s\n", filename);
325}
326
327void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
328{
329    int count = 0;
330    while (rpm_getstring(filetag, count)) {
331        char * filename = bb_xasprintf("%s%s",
332            rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES,
333            count)), rpm_getstring(RPMTAG_BASENAMES, count));
334        fileaction(filename, count++);
335        free(filename);
336    }
337}
Note: See TracBrowser for help on using the repository browser.