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 tarball for details.
11 #include "unarchive.h"
13 #define RPM_HEADER_MAGIC "\216\255\350"
14 #define RPM_CHAR_TYPE 1
15 #define RPM_INT8_TYPE 2
16 #define RPM_INT16_TYPE 3
17 #define RPM_INT32_TYPE 4
18 /* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */
19 #define RPM_STRING_TYPE 6
20 #define RPM_BIN_TYPE 7
21 #define RPM_STRING_ARRAY_TYPE 8
22 #define RPM_I18NSTRING_TYPE 9
25 #define TAG_VERSION 1001
26 #define TAG_RELEASE 1002
27 #define TAG_SUMMARY 1004
28 #define TAG_DESCRIPTION 1005
29 #define TAG_BUILDTIME 1006
30 #define TAG_BUILDHOST 1007
32 #define TAG_VENDOR 1011
33 #define TAG_LICENSE 1014
34 #define TAG_PACKAGER 1015
35 #define TAG_GROUP 1016
37 #define TAG_PREIN 1023
38 #define TAG_POSTIN 1024
39 #define TAG_FILEFLAGS 1037
40 #define TAG_FILEUSERNAME 1039
41 #define TAG_FILEGROUPNAME 1040
42 #define TAG_SOURCERPM 1044
43 #define TAG_PREINPROG 1085
44 #define TAG_POSTINPROG 1086
45 #define TAG_PREFIXS 1098
46 #define TAG_DIRINDEXES 1116
47 #define TAG_BASENAMES 1117
48 #define TAG_DIRNAMES 1118
49 #define RPMFILE_CONFIG (1 << 0)
50 #define RPMFILE_DOC (1 << 1)
52 enum rpm_functions_e
{
56 rpm_query_package
= 8,
58 rpm_query_list_doc
= 32,
59 rpm_query_list_config
= 64
63 uint32_t tag
; /* 4 byte tag */
64 uint32_t type
; /* 4 byte type */
65 uint32_t offset
; /* 4 byte offset */
66 uint32_t count
; /* 4 byte count */
70 static rpm_index
**mytags
;
73 static void extract_cpio_gz(int fd
);
74 static rpm_index
**rpm_gettags(int fd
, int *num_tags
);
75 static int bsearch_rpmtag(const void *key
, const void *item
);
76 static char *rpm_getstr(int tag
, int itemindex
);
77 static int rpm_getint(int tag
, int itemindex
);
78 static int rpm_getcount(int tag
);
79 static void fileaction_dobackup(char *filename
, int fileref
);
80 static void fileaction_setowngrp(char *filename
, int fileref
);
81 static void loop_through_files(int filetag
, void (*fileaction
)(char *filename
, int fileref
));
83 int rpm_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
84 int rpm_main(int argc
, char **argv
)
86 int opt
= 0, func
= 0, rpm_fd
, offset
;
87 const int pagesize
= getpagesize();
89 while ((opt
= getopt(argc
, argv
, "iqpldc")) != -1) {
91 case 'i': /* First arg: Install mode, with q: Information */
92 if (!func
) func
= rpm_install
;
93 else func
|= rpm_query_info
;
95 case 'q': /* First arg: Query mode */
96 if (func
) bb_show_usage();
99 case 'p': /* Query a package */
100 func
|= rpm_query_package
;
102 case 'l': /* List files in a package */
103 func
|= rpm_query_list
;
105 case 'd': /* List doc files in a package (implies list) */
106 func
|= rpm_query_list
;
107 func
|= rpm_query_list_doc
;
109 case 'c': /* List config files in a package (implies list) */
110 func
|= rpm_query_list
;
111 func
|= rpm_query_list_config
;
119 if (!argv
[0]) bb_show_usage();
122 rpm_fd
= xopen(*argv
++, O_RDONLY
);
123 mytags
= rpm_gettags(rpm_fd
, &tagcount
);
125 bb_error_msg_and_die("error reading rpm header");
126 offset
= xlseek(rpm_fd
, 0, SEEK_CUR
);
127 /* Mimimum is one page */
128 map
= mmap(0, offset
> pagesize
? (offset
+ offset
% pagesize
) : pagesize
, PROT_READ
, MAP_PRIVATE
, rpm_fd
, 0);
130 if (func
& rpm_install
) {
131 /* Backup any config files */
132 loop_through_files(TAG_BASENAMES
, fileaction_dobackup
);
133 /* Extact the archive */
134 extract_cpio_gz(rpm_fd
);
135 /* Set the correct file uid/gid's */
136 loop_through_files(TAG_BASENAMES
, fileaction_setowngrp
);
138 else if ((func
& (rpm_query
|rpm_query_package
)) == (rpm_query
|rpm_query_package
)) {
139 if (!(func
& (rpm_query_info
|rpm_query_list
))) {
140 /* If just a straight query, just give package name */
141 printf("%s-%s-%s\n", rpm_getstr(TAG_NAME
, 0), rpm_getstr(TAG_VERSION
, 0), rpm_getstr(TAG_RELEASE
, 0));
143 if (func
& rpm_query_info
) {
144 /* Do the nice printout */
147 char bdatestring
[50];
148 printf("Name : %-29sRelocations: %s\n", rpm_getstr(TAG_NAME
, 0), rpm_getstr(TAG_PREFIXS
, 0) ? rpm_getstr(TAG_PREFIXS
, 0) : "(not relocateable)");
149 printf("Version : %-34sVendor: %s\n", rpm_getstr(TAG_VERSION
, 0), rpm_getstr(TAG_VENDOR
, 0) ? rpm_getstr(TAG_VENDOR
, 0) : "(none)");
150 bdate_time
= rpm_getint(TAG_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_getstr(TAG_RELEASE
, 0), bdatestring
);
154 printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstr(TAG_BUILDHOST
, 0));
155 printf("Group : %-30sSource RPM: %s\n", rpm_getstr(TAG_GROUP
, 0), rpm_getstr(TAG_SOURCERPM
, 0));
156 printf("Size : %-33dLicense: %s\n", rpm_getint(TAG_SIZE
, 0), rpm_getstr(TAG_LICENSE
, 0));
157 printf("URL : %s\n", rpm_getstr(TAG_URL
, 0));
158 printf("Summary : %s\n", rpm_getstr(TAG_SUMMARY
, 0));
159 printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION
, 0));
161 if (func
& rpm_query_list
) {
162 int count
, it
, flags
;
163 count
= rpm_getcount(TAG_BASENAMES
);
164 for (it
= 0; it
< count
; it
++) {
165 flags
= rpm_getint(TAG_FILEFLAGS
, it
);
166 switch (func
& (rpm_query_list_doc
|rpm_query_list_config
)) {
167 case rpm_query_list_doc
:
168 if (!(flags
& RPMFILE_DOC
)) continue;
170 case rpm_query_list_config
:
171 if (!(flags
& RPMFILE_CONFIG
)) continue;
173 case rpm_query_list_doc
|rpm_query_list_config
:
174 if (!(flags
& (RPMFILE_CONFIG
|RPMFILE_DOC
))) continue;
178 rpm_getstr(TAG_DIRNAMES
, rpm_getint(TAG_DIRINDEXES
, it
)),
179 rpm_getstr(TAG_BASENAMES
, it
));
188 static void extract_cpio_gz(int fd
)
190 archive_handle_t
*archive_handle
;
191 unsigned char magic
[2];
193 USE_DESKTOP(long long) int FAST_FUNC (*xformer
)(int src_fd
, int dst_fd
);
194 enum { xformer_prog
= 0 };
196 enum { xformer
= 0 };
197 const char *xformer_prog
;
201 archive_handle
= init_handle();
202 archive_handle
->seek
= seek_by_read
;
203 //archive_handle->action_header = header_list;
204 archive_handle
->action_data
= data_extract_all
;
205 archive_handle
->ah_flags
= ARCHIVE_PRESERVE_DATE
| ARCHIVE_CREATE_LEADING_DIRS
206 /* compat: overwrite existing files.
207 * try "rpm -i foo.src.rpm" few times in a row -
208 * standard rpm will not complain.
209 * (TODO? real rpm creates "file;1234" and then renames it) */
210 | ARCHIVE_EXTRACT_UNCONDITIONAL
;
211 archive_handle
->src_fd
= fd
;
212 /*archive_handle->offset = 0; - init_handle() did it */
214 // TODO: open_zipped does the same
216 xread(archive_handle
->src_fd
, &magic
, 2);
218 xformer
= unpack_gz_stream
;
220 xformer_prog
= "gunzip";
222 if (magic
[0] != 0x1f || magic
[1] != 0x8b) {
223 if (!ENABLE_FEATURE_SEAMLESS_BZ2
224 || magic
[0] != 'B' || magic
[1] != 'Z'
226 bb_error_msg_and_die("no gzip"
227 USE_FEATURE_SEAMLESS_BZ2("/bzip2")
231 xformer
= unpack_bz2_stream
;
233 xformer_prog
= "bunzip2";
237 /* NOMMU version of open_transformer execs an external unzipper that should
238 * have the file position at the start of the file */
239 xlseek(archive_handle
->src_fd
, 0, SEEK_SET
);
243 xchdir("/"); /* Install RPM's to root */
244 open_transformer(archive_handle
->src_fd
, xformer
, xformer_prog
);
245 archive_handle
->offset
= 0;
246 while (get_header_cpio(archive_handle
) == EXIT_SUCCESS
)
251 static rpm_index
**rpm_gettags(int fd
, int *num_tags
)
253 /* We should never need mode than 200, and realloc later */
254 rpm_index
**tags
= xzalloc(200 * sizeof(tags
[0]));
255 int pass
, tagindex
= 0;
257 xlseek(fd
, 96, SEEK_CUR
); /* Seek past the unused lead */
259 /* 1st pass is the signature headers, 2nd is the main stuff */
260 for (pass
= 0; pass
< 2; pass
++) {
262 char magic
[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
263 uint8_t version
; /* 1 byte version number */
264 uint32_t reserved
; /* 4 bytes reserved */
265 uint32_t entries
; /* Number of entries in header (4 bytes) */
266 uint32_t size
; /* Size of store (4 bytes) */
269 char BUG_header
[sizeof(header
) == 16 ? 1 : -1];
274 xread(fd
, &header
, sizeof(header
));
275 if (strncmp((char *) &header
.magic
, RPM_HEADER_MAGIC
, 3) != 0)
276 return NULL
; /* Invalid magic */
277 if (header
.version
!= 1)
278 return NULL
; /* This program only supports v1 headers */
279 header
.size
= ntohl(header
.size
);
280 header
.entries
= ntohl(header
.entries
);
281 storepos
= xlseek(fd
,0,SEEK_CUR
) + header
.entries
* 16;
283 while (header
.entries
--) {
284 tmpindex
= tags
[tagindex
++] = xmalloc(sizeof(*tmpindex
));
285 xread(fd
, tmpindex
, sizeof(*tmpindex
));
286 tmpindex
->tag
= ntohl(tmpindex
->tag
);
287 tmpindex
->type
= ntohl(tmpindex
->type
);
288 tmpindex
->count
= ntohl(tmpindex
->count
);
289 tmpindex
->offset
= storepos
+ ntohl(tmpindex
->offset
);
291 tmpindex
->tag
-= 743;
293 xlseek(fd
, header
.size
, SEEK_CUR
); /* Seek past store */
294 /* Skip padding to 8 byte boundary after reading signature headers */
296 xlseek(fd
, (8 - (xlseek(fd
,0,SEEK_CUR
) % 8)) % 8, SEEK_CUR
);
298 tags
= xrealloc(tags
, tagindex
* sizeof(tags
[0])); /* realloc tags to save space */
299 *num_tags
= tagindex
;
300 return tags
; /* All done, leave the file at the start of the gzipped cpio archive */
303 static int bsearch_rpmtag(const void *key
, const void *item
)
305 int *tag
= (int *)key
;
306 rpm_index
**tmp
= (rpm_index
**) item
;
307 return (*tag
- tmp
[0]->tag
);
310 static int rpm_getcount(int tag
)
313 found
= bsearch(&tag
, mytags
, tagcount
, sizeof(struct rpmtag
*), bsearch_rpmtag
);
316 return found
[0]->count
;
319 static char *rpm_getstr(int tag
, int itemindex
)
322 found
= bsearch(&tag
, mytags
, tagcount
, sizeof(struct rpmtag
*), bsearch_rpmtag
);
323 if (!found
|| itemindex
>= found
[0]->count
)
325 if (found
[0]->type
== RPM_STRING_TYPE
|| found
[0]->type
== RPM_I18NSTRING_TYPE
|| found
[0]->type
== RPM_STRING_ARRAY_TYPE
) {
327 char *tmpstr
= (char *) (map
+ found
[0]->offset
);
328 for (n
=0; n
< itemindex
; n
++)
329 tmpstr
= tmpstr
+ strlen(tmpstr
) + 1;
335 static int rpm_getint(int tag
, int itemindex
)
338 int *tmpint
; /* NB: using int8_t* would be easier to code */
340 /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
341 * it's ok to ignore it because tag won't be used as a pointer */
342 found
= bsearch(&tag
, mytags
, tagcount
, sizeof(struct rpmtag
*), bsearch_rpmtag
);
343 if (!found
|| itemindex
>= found
[0]->count
)
346 tmpint
= (int *) (map
+ found
[0]->offset
);
348 if (found
[0]->type
== RPM_INT32_TYPE
) {
349 tmpint
= (int *) ((char *) tmpint
+ itemindex
*4);
350 /*return ntohl(*tmpint);*/
351 /* int can be != int32_t */
352 return ntohl(*(int32_t*)tmpint
);
354 if (found
[0]->type
== RPM_INT16_TYPE
) {
355 tmpint
= (int *) ((char *) tmpint
+ itemindex
*2);
356 /* ??? read int, and THEN ntohs() it?? */
357 /*return ntohs(*tmpint);*/
358 return ntohs(*(int16_t*)tmpint
);
360 if (found
[0]->type
== RPM_INT8_TYPE
) {
361 tmpint
= (int *) ((char *) tmpint
+ itemindex
);
362 /* ??? why we don't read byte here??? */
363 /*return ntohs(*tmpint);*/
364 return *(int8_t*)tmpint
;
369 static void fileaction_dobackup(char *filename
, int fileref
)
374 if (rpm_getint(TAG_FILEFLAGS
, fileref
) & RPMFILE_CONFIG
) {
375 /* Only need to backup config files */
376 stat_res
= lstat(filename
, &oldfile
);
377 if (stat_res
== 0 && S_ISREG(oldfile
.st_mode
)) {
378 /* File already exists - really should check MD5's etc to see if different */
379 newname
= xasprintf("%s.rpmorig", filename
);
380 copy_file(filename
, newname
, FILEUTILS_RECUR
| FILEUTILS_PRESERVE_STATUS
);
381 remove_file(filename
, FILEUTILS_RECUR
| FILEUTILS_FORCE
);
387 static void fileaction_setowngrp(char *filename
, int fileref
)
389 /* real rpm warns: "user foo does not exist - using <you>" */
390 struct passwd
*pw
= getpwnam(rpm_getstr(TAG_FILEUSERNAME
, fileref
));
391 int uid
= pw
? pw
->pw_uid
: getuid(); /* or euid? */
392 struct group
*gr
= getgrnam(rpm_getstr(TAG_FILEGROUPNAME
, fileref
));
393 int gid
= gr
? gr
->gr_gid
: getgid();
394 chown(filename
, uid
, gid
);
397 static void loop_through_files(int filetag
, void (*fileaction
)(char *filename
, int fileref
))
400 while (rpm_getstr(filetag
, count
)) {
401 char* filename
= xasprintf("%s%s",
402 rpm_getstr(TAG_DIRNAMES
, rpm_getint(TAG_DIRINDEXES
, count
)),
403 rpm_getstr(TAG_BASENAMES
, count
));
404 fileaction(filename
, count
++);