1 /* vi: set sw=4 ts=4: */
3 * Mini rpm applet for busybox
5 * Copyright (C) 2001,2002 by Laurence Anderson
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 //usage:#define rpm_trivial_usage
11 //usage: "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
12 //usage:#define rpm_full_usage "\n\n"
13 //usage: "Manipulate RPM packages\n"
14 //usage: "\nCommands:"
15 //usage: "\n -i Install package"
16 //usage: "\n -qp Query package"
17 //usage: "\n -i Show information"
18 //usage: "\n -l List contents"
19 //usage: "\n -d List documents"
20 //usage: "\n -c List config files"
23 #include "bb_archive.h"
26 #define RPM_CHAR_TYPE 1
27 #define RPM_INT8_TYPE 2
28 #define RPM_INT16_TYPE 3
29 #define RPM_INT32_TYPE 4
30 /* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */
31 #define RPM_STRING_TYPE 6
32 #define RPM_BIN_TYPE 7
33 #define RPM_STRING_ARRAY_TYPE 8
34 #define RPM_I18NSTRING_TYPE 9
37 #define TAG_VERSION 1001
38 #define TAG_RELEASE 1002
39 #define TAG_SUMMARY 1004
40 #define TAG_DESCRIPTION 1005
41 #define TAG_BUILDTIME 1006
42 #define TAG_BUILDHOST 1007
44 #define TAG_VENDOR 1011
45 #define TAG_LICENSE 1014
46 #define TAG_PACKAGER 1015
47 #define TAG_GROUP 1016
49 #define TAG_PREIN 1023
50 #define TAG_POSTIN 1024
51 #define TAG_FILEFLAGS 1037
52 #define TAG_FILEUSERNAME 1039
53 #define TAG_FILEGROUPNAME 1040
54 #define TAG_SOURCERPM 1044
55 #define TAG_PREINPROG 1085
56 #define TAG_POSTINPROG 1086
57 #define TAG_PREFIXS 1098
58 #define TAG_DIRINDEXES 1116
59 #define TAG_BASENAMES 1117
60 #define TAG_DIRNAMES 1118
62 #define RPMFILE_CONFIG (1 << 0)
63 #define RPMFILE_DOC (1 << 1)
65 enum rpm_functions_e
{
69 rpm_query_package
= 8,
71 rpm_query_list_doc
= 32,
72 rpm_query_list_config
= 64
76 uint32_t tag
; /* 4 byte tag */
77 uint32_t type
; /* 4 byte type */
78 uint32_t offset
; /* 4 byte offset */
79 uint32_t count
; /* 4 byte count */
83 static rpm_index
**mytags
;
86 static void extract_cpio(int fd
, const char *source_rpm
);
87 static rpm_index
**rpm_gettags(int fd
, int *num_tags
);
88 static int bsearch_rpmtag(const void *key
, const void *item
);
89 static char *rpm_getstr(int tag
, int itemindex
);
90 static int rpm_getint(int tag
, int itemindex
);
91 static int rpm_getcount(int tag
);
92 static void fileaction_dobackup(char *filename
, int fileref
);
93 static void fileaction_setowngrp(char *filename
, int fileref
);
94 static void loop_through_files(int filetag
, void (*fileaction
)(char *filename
, int fileref
));
96 int rpm_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
97 int rpm_main(int argc
, char **argv
)
99 int opt
= 0, func
= 0, rpm_fd
, offset
;
100 const int pagesize
= getpagesize();
102 while ((opt
= getopt(argc
, argv
, "iqpldc")) != -1) {
104 case 'i': /* First arg: Install mode, with q: Information */
105 if (!func
) func
= rpm_install
;
106 else func
|= rpm_query_info
;
108 case 'q': /* First arg: Query mode */
109 if (func
) bb_show_usage();
112 case 'p': /* Query a package */
113 func
|= rpm_query_package
;
115 case 'l': /* List files in a package */
116 func
|= rpm_query_list
;
118 case 'd': /* List doc files in a package (implies list) */
119 func
|= rpm_query_list
;
120 func
|= rpm_query_list_doc
;
122 case 'c': /* List config files in a package (implies list) */
123 func
|= rpm_query_list
;
124 func
|= rpm_query_list_config
;
137 const char *source_rpm
;
139 rpm_fd
= xopen(*argv
++, O_RDONLY
);
140 mytags
= rpm_gettags(rpm_fd
, &tagcount
);
142 bb_error_msg_and_die("error reading rpm header");
143 offset
= xlseek(rpm_fd
, 0, SEEK_CUR
);
144 /* Mimimum is one page */
145 map
= mmap(0, offset
> pagesize
? (offset
+ offset
% pagesize
) : pagesize
, PROT_READ
, MAP_PRIVATE
, rpm_fd
, 0);
147 source_rpm
= rpm_getstr(TAG_SOURCERPM
, 0);
149 if (func
& rpm_install
) {
150 /* Backup any config files */
151 loop_through_files(TAG_BASENAMES
, fileaction_dobackup
);
152 /* Extact the archive */
153 extract_cpio(rpm_fd
, source_rpm
);
154 /* Set the correct file uid/gid's */
155 loop_through_files(TAG_BASENAMES
, fileaction_setowngrp
);
157 else if ((func
& (rpm_query
|rpm_query_package
)) == (rpm_query
|rpm_query_package
)) {
158 if (!(func
& (rpm_query_info
|rpm_query_list
))) {
159 /* If just a straight query, just give package name */
160 printf("%s-%s-%s\n", rpm_getstr(TAG_NAME
, 0), rpm_getstr(TAG_VERSION
, 0), rpm_getstr(TAG_RELEASE
, 0));
162 if (func
& rpm_query_info
) {
163 /* Do the nice printout */
165 struct tm
*bdate_ptm
;
166 char bdatestring
[50];
169 p
= rpm_getstr(TAG_PREFIXS
, 0);
170 if (!p
) p
= "(not relocateable)";
171 printf("Name : %-29sRelocations: %s\n", rpm_getstr(TAG_NAME
, 0), p
);
172 p
= rpm_getstr(TAG_VENDOR
, 0);
173 if (!p
) p
= "(none)";
174 printf("Version : %-34sVendor: %s\n", rpm_getstr(TAG_VERSION
, 0), p
);
175 bdate_time
= rpm_getint(TAG_BUILDTIME
, 0);
176 bdate_ptm
= localtime(&bdate_time
);
177 strftime(bdatestring
, 50, "%a %d %b %Y %T %Z", bdate_ptm
);
178 printf("Release : %-30sBuild Date: %s\n", rpm_getstr(TAG_RELEASE
, 0), bdatestring
);
179 printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstr(TAG_BUILDHOST
, 0));
180 printf("Group : %-30sSource RPM: %s\n", rpm_getstr(TAG_GROUP
, 0), source_rpm
);
181 printf("Size : %-33dLicense: %s\n", rpm_getint(TAG_SIZE
, 0), rpm_getstr(TAG_LICENSE
, 0));
182 printf("URL : %s\n", rpm_getstr(TAG_URL
, 0));
183 printf("Summary : %s\n", rpm_getstr(TAG_SUMMARY
, 0));
184 printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION
, 0));
186 if (func
& rpm_query_list
) {
187 int count
, it
, flags
;
188 count
= rpm_getcount(TAG_BASENAMES
);
189 for (it
= 0; it
< count
; it
++) {
190 flags
= rpm_getint(TAG_FILEFLAGS
, it
);
191 switch (func
& (rpm_query_list_doc
|rpm_query_list_config
)) {
192 case rpm_query_list_doc
:
193 if (!(flags
& RPMFILE_DOC
)) continue;
195 case rpm_query_list_config
:
196 if (!(flags
& RPMFILE_CONFIG
)) continue;
198 case rpm_query_list_doc
|rpm_query_list_config
:
199 if (!(flags
& (RPMFILE_CONFIG
|RPMFILE_DOC
))) continue;
203 rpm_getstr(TAG_DIRNAMES
, rpm_getint(TAG_DIRINDEXES
, it
)),
204 rpm_getstr(TAG_BASENAMES
, it
));
213 static void extract_cpio(int fd
, const char *source_rpm
)
215 archive_handle_t
*archive_handle
;
217 if (source_rpm
!= NULL
) {
218 /* Binary rpm (it was built from some SRPM), install to root */
220 } /* else: SRPM, install to current dir */
223 archive_handle
= init_handle();
224 archive_handle
->seek
= seek_by_read
;
225 archive_handle
->action_data
= data_extract_all
;
226 #if 0 /* For testing (rpm -i only lists the files in internal cpio): */
227 archive_handle
->action_header
= header_list
;
228 archive_handle
->action_data
= data_skip
;
230 archive_handle
->ah_flags
= ARCHIVE_RESTORE_DATE
| ARCHIVE_CREATE_LEADING_DIRS
231 /* compat: overwrite existing files.
232 * try "rpm -i foo.src.rpm" few times in a row -
233 * standard rpm will not complain.
234 * (TODO? real rpm creates "file;1234" and then renames it) */
235 | ARCHIVE_UNLINK_OLD
;
236 archive_handle
->src_fd
= fd
;
237 /*archive_handle->offset = 0; - init_handle() did it */
239 setup_unzip_on_fd(archive_handle
->src_fd
, /*fail_if_not_detected:*/ 1);
240 while (get_header_cpio(archive_handle
) == EXIT_SUCCESS
)
244 static rpm_index
**rpm_gettags(int fd
, int *num_tags
)
246 /* We should never need more than 200 (shrink via realloc later) */
247 rpm_index
**tags
= xzalloc(200 * sizeof(tags
[0]));
248 int pass
, tagindex
= 0;
250 xlseek(fd
, 96, SEEK_CUR
); /* Seek past the unused lead */
252 /* 1st pass is the signature headers, 2nd is the main stuff */
253 for (pass
= 0; pass
< 2; pass
++) {
254 struct rpm_header header
;
258 xread(fd
, &header
, sizeof(header
));
259 if (header
.magic_and_ver
!= htonl(RPM_HEADER_MAGICnVER
))
260 return NULL
; /* Invalid magic, or not version 1 */
261 header
.size
= ntohl(header
.size
);
262 header
.entries
= ntohl(header
.entries
);
263 storepos
= xlseek(fd
, 0, SEEK_CUR
) + header
.entries
* 16;
265 while (header
.entries
--) {
266 tmpindex
= tags
[tagindex
++] = xmalloc(sizeof(*tmpindex
));
267 xread(fd
, tmpindex
, sizeof(*tmpindex
));
268 tmpindex
->tag
= ntohl(tmpindex
->tag
);
269 tmpindex
->type
= ntohl(tmpindex
->type
);
270 tmpindex
->count
= ntohl(tmpindex
->count
);
271 tmpindex
->offset
= storepos
+ ntohl(tmpindex
->offset
);
273 tmpindex
->tag
-= 743;
275 storepos
= xlseek(fd
, header
.size
, SEEK_CUR
); /* Seek past store */
276 /* Skip padding to 8 byte boundary after reading signature headers */
278 xlseek(fd
, (-storepos
) & 0x7, SEEK_CUR
);
280 /* realloc tags to save space */
281 tags
= xrealloc(tags
, tagindex
* sizeof(tags
[0]));
282 *num_tags
= tagindex
;
283 /* All done, leave the file at the start of the gzipped cpio archive */
287 static int bsearch_rpmtag(const void *key
, const void *item
)
289 int *tag
= (int *)key
;
290 rpm_index
**tmp
= (rpm_index
**) item
;
291 return (*tag
- tmp
[0]->tag
);
294 static int rpm_getcount(int tag
)
297 found
= bsearch(&tag
, mytags
, tagcount
, sizeof(struct rpmtag
*), bsearch_rpmtag
);
300 return found
[0]->count
;
303 static char *rpm_getstr(int tag
, int itemindex
)
306 found
= bsearch(&tag
, mytags
, tagcount
, sizeof(struct rpmtag
*), bsearch_rpmtag
);
307 if (!found
|| itemindex
>= found
[0]->count
)
309 if (found
[0]->type
== RPM_STRING_TYPE
310 || found
[0]->type
== RPM_I18NSTRING_TYPE
311 || found
[0]->type
== RPM_STRING_ARRAY_TYPE
314 char *tmpstr
= (char *) map
+ found
[0]->offset
;
315 for (n
= 0; n
< itemindex
; n
++)
316 tmpstr
= tmpstr
+ strlen(tmpstr
) + 1;
322 static int rpm_getint(int tag
, int itemindex
)
325 int *tmpint
; /* NB: using int8_t* would be easier to code */
327 /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
328 * it's ok to ignore it because tag won't be used as a pointer */
329 found
= bsearch(&tag
, mytags
, tagcount
, sizeof(struct rpmtag
*), bsearch_rpmtag
);
330 if (!found
|| itemindex
>= found
[0]->count
)
333 tmpint
= (int *) ((char *) map
+ found
[0]->offset
);
335 if (found
[0]->type
== RPM_INT32_TYPE
) {
336 tmpint
= (int *) ((char *) tmpint
+ itemindex
*4);
337 /*return ntohl(*tmpint);*/
338 /* int can be != int32_t */
339 return ntohl(*(int32_t*)tmpint
);
341 if (found
[0]->type
== RPM_INT16_TYPE
) {
342 tmpint
= (int *) ((char *) tmpint
+ itemindex
*2);
343 /* ??? read int, and THEN ntohs() it?? */
344 /*return ntohs(*tmpint);*/
345 return ntohs(*(int16_t*)tmpint
);
347 if (found
[0]->type
== RPM_INT8_TYPE
) {
348 tmpint
= (int *) ((char *) tmpint
+ itemindex
);
349 /* ??? why we don't read byte here??? */
350 /*return ntohs(*tmpint);*/
351 return *(int8_t*)tmpint
;
356 static void fileaction_dobackup(char *filename
, int fileref
)
361 if (rpm_getint(TAG_FILEFLAGS
, fileref
) & RPMFILE_CONFIG
) {
362 /* Only need to backup config files */
363 stat_res
= lstat(filename
, &oldfile
);
364 if (stat_res
== 0 && S_ISREG(oldfile
.st_mode
)) {
365 /* File already exists - really should check MD5's etc to see if different */
366 newname
= xasprintf("%s.rpmorig", filename
);
367 copy_file(filename
, newname
, FILEUTILS_RECUR
| FILEUTILS_PRESERVE_STATUS
);
368 remove_file(filename
, FILEUTILS_RECUR
| FILEUTILS_FORCE
);
374 static void fileaction_setowngrp(char *filename
, int fileref
)
376 /* real rpm warns: "user foo does not exist - using <you>" */
377 struct passwd
*pw
= getpwnam(rpm_getstr(TAG_FILEUSERNAME
, fileref
));
378 int uid
= pw
? pw
->pw_uid
: getuid(); /* or euid? */
379 struct group
*gr
= getgrnam(rpm_getstr(TAG_FILEGROUPNAME
, fileref
));
380 int gid
= gr
? gr
->gr_gid
: getgid();
381 chown(filename
, uid
, gid
);
384 static void loop_through_files(int filetag
, void (*fileaction
)(char *filename
, int fileref
))
387 while (rpm_getstr(filetag
, count
)) {
388 char* filename
= xasprintf("%s%s",
389 rpm_getstr(TAG_DIRNAMES
, rpm_getint(TAG_DIRINDEXES
, count
)),
390 rpm_getstr(TAG_BASENAMES
, count
));
391 fileaction(filename
, count
++);