source: MondoRescue/branches/3.3/mindi-busybox/archival/rpm.c@ 3884

Last change on this file since 3884 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 12.4 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 source tree.
8 */
9
10//config:config RPM
11//config: bool "rpm"
12//config: default y
13//config: help
14//config: Mini RPM applet - queries and extracts RPM packages.
15
16//applet:IF_RPM(APPLET(rpm, BB_DIR_BIN, BB_SUID_DROP))
17//kbuild:lib-$(CONFIG_RPM) += rpm.o
18
19//usage:#define rpm_trivial_usage
20//usage: "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
21//usage:#define rpm_full_usage "\n\n"
22//usage: "Manipulate RPM packages\n"
23//usage: "\nCommands:"
24//usage: "\n -i Install package"
25//usage: "\n -qp Query package"
26//usage: "\n -qpi Show information"
27//usage: "\n -qpl List contents"
28//usage: "\n -qpd List documents"
29//usage: "\n -qpc List config files"
30
31#include "libbb.h"
32#include "common_bufsiz.h"
33#include "bb_archive.h"
34#include "rpm.h"
35
36#define RPM_CHAR_TYPE 1
37#define RPM_INT8_TYPE 2
38#define RPM_INT16_TYPE 3
39#define RPM_INT32_TYPE 4
40/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */
41#define RPM_STRING_TYPE 6
42#define RPM_BIN_TYPE 7
43#define RPM_STRING_ARRAY_TYPE 8
44#define RPM_I18NSTRING_TYPE 9
45
46#define TAG_NAME 1000
47#define TAG_VERSION 1001
48#define TAG_RELEASE 1002
49#define TAG_SUMMARY 1004
50#define TAG_DESCRIPTION 1005
51#define TAG_BUILDTIME 1006
52#define TAG_BUILDHOST 1007
53#define TAG_SIZE 1009
54#define TAG_VENDOR 1011
55#define TAG_LICENSE 1014
56#define TAG_PACKAGER 1015
57#define TAG_GROUP 1016
58#define TAG_URL 1020
59#define TAG_PREIN 1023
60#define TAG_POSTIN 1024
61#define TAG_FILEFLAGS 1037
62#define TAG_FILEUSERNAME 1039
63#define TAG_FILEGROUPNAME 1040
64#define TAG_SOURCERPM 1044
65#define TAG_PREINPROG 1085
66#define TAG_POSTINPROG 1086
67#define TAG_PREFIXS 1098
68#define TAG_DIRINDEXES 1116
69#define TAG_BASENAMES 1117
70#define TAG_DIRNAMES 1118
71
72#define RPMFILE_CONFIG (1 << 0)
73#define RPMFILE_DOC (1 << 1)
74
75enum rpm_functions_e {
76 rpm_query = 1,
77 rpm_install = 2,
78 rpm_query_info = 4,
79 rpm_query_package = 8,
80 rpm_query_list = 16,
81 rpm_query_list_doc = 32,
82 rpm_query_list_config = 64
83};
84
85typedef struct {
86 uint32_t tag; /* 4 byte tag */
87 uint32_t type; /* 4 byte type */
88 uint32_t offset; /* 4 byte offset */
89 uint32_t count; /* 4 byte count */
90} rpm_index;
91
92struct globals {
93 void *map;
94 rpm_index **mytags;
95 int tagcount;
96} FIX_ALIASING;
97#define G (*(struct globals*)bb_common_bufsiz1)
98#define INIT_G() do { setup_common_bufsiz(); } while (0)
99
100static void extract_cpio(int fd, const char *source_rpm)
101{
102 archive_handle_t *archive_handle;
103
104 if (source_rpm != NULL) {
105 /* Binary rpm (it was built from some SRPM), install to root */
106 xchdir("/");
107 } /* else: SRPM, install to current dir */
108
109 /* Initialize */
110 archive_handle = init_handle();
111 archive_handle->seek = seek_by_read;
112 archive_handle->action_data = data_extract_all;
113#if 0 /* For testing (rpm -i only lists the files in internal cpio): */
114 archive_handle->action_header = header_list;
115 archive_handle->action_data = data_skip;
116#endif
117 archive_handle->ah_flags = ARCHIVE_RESTORE_DATE | ARCHIVE_CREATE_LEADING_DIRS
118 /* compat: overwrite existing files.
119 * try "rpm -i foo.src.rpm" few times in a row -
120 * standard rpm will not complain.
121 */
122 | ARCHIVE_REPLACE_VIA_RENAME;
123 archive_handle->src_fd = fd;
124 /*archive_handle->offset = 0; - init_handle() did it */
125
126 setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 1);
127 while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
128 continue;
129}
130
131static rpm_index **rpm_gettags(int fd, int *num_tags)
132{
133 /* We should never need more than 200 (shrink via realloc later) */
134 rpm_index **tags = xzalloc(200 * sizeof(tags[0]));
135 int pass, tagindex = 0;
136
137 xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
138
139 /* 1st pass is the signature headers, 2nd is the main stuff */
140 for (pass = 0; pass < 2; pass++) {
141 struct rpm_header header;
142 rpm_index *tmpindex;
143 int storepos;
144
145 xread(fd, &header, sizeof(header));
146 if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER))
147 return NULL; /* Invalid magic, or not version 1 */
148 header.size = ntohl(header.size);
149 header.entries = ntohl(header.entries);
150 storepos = xlseek(fd, 0, SEEK_CUR) + header.entries * 16;
151
152 while (header.entries--) {
153 tmpindex = tags[tagindex++] = xmalloc(sizeof(*tmpindex));
154 xread(fd, tmpindex, sizeof(*tmpindex));
155 tmpindex->tag = ntohl(tmpindex->tag);
156 tmpindex->type = ntohl(tmpindex->type);
157 tmpindex->count = ntohl(tmpindex->count);
158 tmpindex->offset = storepos + ntohl(tmpindex->offset);
159 if (pass == 0)
160 tmpindex->tag -= 743;
161 }
162 storepos = xlseek(fd, header.size, SEEK_CUR); /* Seek past store */
163 /* Skip padding to 8 byte boundary after reading signature headers */
164 if (pass == 0)
165 xlseek(fd, (-storepos) & 0x7, SEEK_CUR);
166 }
167 /* realloc tags to save space */
168 tags = xrealloc(tags, tagindex * sizeof(tags[0]));
169 *num_tags = tagindex;
170 /* All done, leave the file at the start of the gzipped cpio archive */
171 return tags;
172}
173
174static int bsearch_rpmtag(const void *key, const void *item)
175{
176 int *tag = (int *)key;
177 rpm_index **tmp = (rpm_index **) item;
178 return (*tag - tmp[0]->tag);
179}
180
181static int rpm_getcount(int tag)
182{
183 rpm_index **found;
184 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
185 if (!found)
186 return 0;
187 return found[0]->count;
188}
189
190static char *rpm_getstr(int tag, int itemindex)
191{
192 rpm_index **found;
193 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
194 if (!found || itemindex >= found[0]->count)
195 return NULL;
196 if (found[0]->type == RPM_STRING_TYPE
197 || found[0]->type == RPM_I18NSTRING_TYPE
198 || found[0]->type == RPM_STRING_ARRAY_TYPE
199 ) {
200 int n;
201 char *tmpstr = (char *) G.map + found[0]->offset;
202 for (n = 0; n < itemindex; n++)
203 tmpstr = tmpstr + strlen(tmpstr) + 1;
204 return tmpstr;
205 }
206 return NULL;
207}
208
209static int rpm_getint(int tag, int itemindex)
210{
211 rpm_index **found;
212 char *tmpint;
213
214 /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
215 * it's ok to ignore it because tag won't be used as a pointer */
216 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
217 if (!found || itemindex >= found[0]->count)
218 return -1;
219
220 tmpint = (char *) G.map + found[0]->offset;
221 if (found[0]->type == RPM_INT32_TYPE) {
222 tmpint += itemindex*4;
223 return ntohl(*(int32_t*)tmpint);
224 }
225 if (found[0]->type == RPM_INT16_TYPE) {
226 tmpint += itemindex*2;
227 return ntohs(*(int16_t*)tmpint);
228 }
229 if (found[0]->type == RPM_INT8_TYPE) {
230 tmpint += itemindex;
231 return *(int8_t*)tmpint;
232 }
233 return -1;
234}
235
236static void fileaction_dobackup(char *filename, int fileref)
237{
238 struct stat oldfile;
239 int stat_res;
240 char *newname;
241 if (rpm_getint(TAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) {
242 /* Only need to backup config files */
243 stat_res = lstat(filename, &oldfile);
244 if (stat_res == 0 && S_ISREG(oldfile.st_mode)) {
245 /* File already exists - really should check MD5's etc to see if different */
246 newname = xasprintf("%s.rpmorig", filename);
247 copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS);
248 remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE);
249 free(newname);
250 }
251 }
252}
253
254static void fileaction_setowngrp(char *filename, int fileref)
255{
256 /* real rpm warns: "user foo does not exist - using <you>" */
257 struct passwd *pw = getpwnam(rpm_getstr(TAG_FILEUSERNAME, fileref));
258 int uid = pw ? pw->pw_uid : getuid(); /* or euid? */
259 struct group *gr = getgrnam(rpm_getstr(TAG_FILEGROUPNAME, fileref));
260 int gid = gr ? gr->gr_gid : getgid();
261 chown(filename, uid, gid);
262}
263
264static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
265{
266 int count = 0;
267 while (rpm_getstr(filetag, count)) {
268 char* filename = xasprintf("%s%s",
269 rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, count)),
270 rpm_getstr(TAG_BASENAMES, count));
271 fileaction(filename, count++);
272 free(filename);
273 }
274}
275
276int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
277int rpm_main(int argc, char **argv)
278{
279 int opt, func = 0;
280 const unsigned pagesize = getpagesize();
281
282 while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
283 switch (opt) {
284 case 'i': /* First arg: Install mode, with q: Information */
285 if (!func) func = rpm_install;
286 else func |= rpm_query_info;
287 break;
288 case 'q': /* First arg: Query mode */
289 if (func) bb_show_usage();
290 func = rpm_query;
291 break;
292 case 'p': /* Query a package */
293 func |= rpm_query_package;
294 break;
295 case 'l': /* List files in a package */
296 func |= rpm_query_list;
297 break;
298 case 'd': /* List doc files in a package (implies list) */
299 func |= rpm_query_list;
300 func |= rpm_query_list_doc;
301 break;
302 case 'c': /* List config files in a package (implies list) */
303 func |= rpm_query_list;
304 func |= rpm_query_list_config;
305 break;
306 default:
307 bb_show_usage();
308 }
309 }
310 argv += optind;
311 //argc -= optind;
312 if (!argv[0]) {
313 bb_show_usage();
314 }
315
316 while (*argv) {
317 int rpm_fd;
318 unsigned mapsize;
319 const char *source_rpm;
320
321 rpm_fd = xopen(*argv++, O_RDONLY);
322 G.mytags = rpm_gettags(rpm_fd, &G.tagcount);
323 if (!G.mytags)
324 bb_error_msg_and_die("error reading rpm header");
325 mapsize = xlseek(rpm_fd, 0, SEEK_CUR);
326 mapsize = (mapsize + pagesize) & -(int)pagesize;
327 /* Some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */
328 G.map = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
329//FIXME: error check?
330
331 source_rpm = rpm_getstr(TAG_SOURCERPM, 0);
332
333 if (func & rpm_install) {
334 /* Backup any config files */
335 loop_through_files(TAG_BASENAMES, fileaction_dobackup);
336 /* Extact the archive */
337 extract_cpio(rpm_fd, source_rpm);
338 /* Set the correct file uid/gid's */
339 loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
340 }
341 else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
342 if (!(func & (rpm_query_info|rpm_query_list))) {
343 /* If just a straight query, just give package name */
344 printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0));
345 }
346 if (func & rpm_query_info) {
347 /* Do the nice printout */
348 time_t bdate_time;
349 struct tm *bdate_ptm;
350 char bdatestring[50];
351 const char *p;
352
353 printf("%-12s: %s\n", "Name" , rpm_getstr(TAG_NAME, 0));
354 /* TODO compat: add "Epoch" here */
355 printf("%-12s: %s\n", "Version" , rpm_getstr(TAG_VERSION, 0));
356 printf("%-12s: %s\n", "Release" , rpm_getstr(TAG_RELEASE, 0));
357 /* add "Architecture" */
358 printf("%-12s: %s\n", "Install Date", "(not installed)");
359 printf("%-12s: %s\n", "Group" , rpm_getstr(TAG_GROUP, 0));
360 printf("%-12s: %d\n", "Size" , rpm_getint(TAG_SIZE, 0));
361 printf("%-12s: %s\n", "License" , rpm_getstr(TAG_LICENSE, 0));
362 /* add "Signature" */
363 printf("%-12s: %s\n", "Source RPM" , source_rpm ? source_rpm : "(none)");
364 bdate_time = rpm_getint(TAG_BUILDTIME, 0);
365 bdate_ptm = localtime(&bdate_time);
366 strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm);
367 printf("%-12s: %s\n", "Build Date" , bdatestring);
368 printf("%-12s: %s\n", "Build Host" , rpm_getstr(TAG_BUILDHOST, 0));
369 p = rpm_getstr(TAG_PREFIXS, 0);
370 printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)");
371 /* add "Packager" */
372 p = rpm_getstr(TAG_VENDOR, 0);
373 printf("%-12s: %s\n", "Vendor" , p ? p : "(none)");
374 printf("%-12s: %s\n", "URL" , rpm_getstr(TAG_URL, 0));
375 printf("%-12s: %s\n", "Summary" , rpm_getstr(TAG_SUMMARY, 0));
376 printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0));
377 }
378 if (func & rpm_query_list) {
379 int count, it, flags;
380 count = rpm_getcount(TAG_BASENAMES);
381 for (it = 0; it < count; it++) {
382 flags = rpm_getint(TAG_FILEFLAGS, it);
383 switch (func & (rpm_query_list_doc|rpm_query_list_config)) {
384 case rpm_query_list_doc:
385 if (!(flags & RPMFILE_DOC)) continue;
386 break;
387 case rpm_query_list_config:
388 if (!(flags & RPMFILE_CONFIG)) continue;
389 break;
390 case rpm_query_list_doc|rpm_query_list_config:
391 if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue;
392 break;
393 }
394 printf("%s%s\n",
395 rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)),
396 rpm_getstr(TAG_BASENAMES, it));
397 }
398 }
399 }
400 munmap(G.map, mapsize);
401 free(G.mytags);
402 close(rpm_fd);
403 }
404 return 0;
405}
Note: See TracBrowser for help on using the repository browser.