rpm.c 13 KB

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