unzip.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini unzip implementation for busybox
  4. *
  5. * Copyright (C) 2004 by Ed Clark
  6. *
  7. * Loosely based on original busybox unzip applet by Laurence Anderson.
  8. * All options and features should work in this version.
  9. *
  10. * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  11. */
  12. /* For reference see
  13. * http://www.pkware.com/company/standards/appnote/
  14. * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
  15. */
  16. /* TODO
  17. * Endian issues
  18. * Zip64 + other methods
  19. * Improve handling of zip format, ie.
  20. * - deferred CRC, comp. & uncomp. lengths (zip header flags bit 3)
  21. * - unix file permissions, etc.
  22. * - central directory
  23. */
  24. #include <fcntl.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <unistd.h>
  28. #include <errno.h>
  29. #include "unarchive.h"
  30. #include "busybox.h"
  31. #if BB_BIG_ENDIAN
  32. static inline unsigned short
  33. __swap16(unsigned short x) {
  34. return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8);
  35. }
  36. static inline uint32_t
  37. __swap32(uint32_t x) {
  38. return (((x & 0xFF) << 24) |
  39. ((x & 0xFF00) << 8) |
  40. ((x & 0xFF0000) >> 8) |
  41. ((x & 0xFF000000) >> 24));
  42. }
  43. #else /* it's little-endian */
  44. # define __swap16(x) (x)
  45. # define __swap32(x) (x)
  46. #endif /* BB_BIG_ENDIAN */
  47. #define ZIP_FILEHEADER_MAGIC __swap32(0x04034b50)
  48. #define ZIP_CDS_MAGIC __swap32(0x02014b50)
  49. #define ZIP_CDS_END_MAGIC __swap32(0x06054b50)
  50. #define ZIP_DD_MAGIC __swap32(0x08074b50)
  51. extern unsigned int gunzip_crc;
  52. extern unsigned int gunzip_bytes_out;
  53. typedef union {
  54. unsigned char raw[26];
  55. struct {
  56. unsigned short version; /* 0-1 */
  57. unsigned short flags; /* 2-3 */
  58. unsigned short method; /* 4-5 */
  59. unsigned short modtime; /* 6-7 */
  60. unsigned short moddate; /* 8-9 */
  61. unsigned int crc32 ATTRIBUTE_PACKED; /* 10-13 */
  62. unsigned int cmpsize ATTRIBUTE_PACKED; /* 14-17 */
  63. unsigned int ucmpsize ATTRIBUTE_PACKED; /* 18-21 */
  64. unsigned short filename_len; /* 22-23 */
  65. unsigned short extra_len; /* 24-25 */
  66. } formated ATTRIBUTE_PACKED;
  67. } zip_header_t;
  68. static void unzip_skip(int fd, off_t skip)
  69. {
  70. if (lseek(fd, skip, SEEK_CUR) == (off_t)-1) {
  71. if ((errno != ESPIPE) || (bb_copyfd_size(fd, -1, skip) != skip)) {
  72. bb_error_msg_and_die("Seek failure");
  73. }
  74. }
  75. }
  76. static void unzip_read(int fd, void *buf, size_t count)
  77. {
  78. if (bb_xread(fd, buf, count) != count) {
  79. bb_error_msg_and_die("Read failure");
  80. }
  81. }
  82. static void unzip_create_leading_dirs(char *fn)
  83. {
  84. /* Create all leading directories */
  85. char *name = bb_xstrdup(fn);
  86. if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
  87. bb_error_msg_and_die("Failed to create directory");
  88. }
  89. free(name);
  90. }
  91. static int unzip_extract(zip_header_t *zip_header, int src_fd, int dst_fd)
  92. {
  93. if (zip_header->formated.method == 0) {
  94. /* Method 0 - stored (not compressed) */
  95. int size = zip_header->formated.ucmpsize;
  96. if (size && (bb_copyfd_size(src_fd, dst_fd, size) != size)) {
  97. bb_error_msg_and_die("Cannot complete extraction");
  98. }
  99. } else {
  100. /* Method 8 - inflate */
  101. inflate_init(zip_header->formated.cmpsize);
  102. inflate_unzip(src_fd, dst_fd);
  103. inflate_cleanup();
  104. /* Validate decompression - crc */
  105. if (zip_header->formated.crc32 != (gunzip_crc ^ 0xffffffffL)) {
  106. bb_error_msg("Invalid compressed data--crc error");
  107. return 1;
  108. }
  109. /* Validate decompression - size */
  110. if (zip_header->formated.ucmpsize != gunzip_bytes_out) {
  111. bb_error_msg("Invalid compressed data--length error");
  112. return 1;
  113. }
  114. }
  115. return 0;
  116. }
  117. int unzip_main(int argc, char **argv)
  118. {
  119. zip_header_t zip_header;
  120. enum {v_silent, v_normal, v_list} verbosity = v_normal;
  121. enum {o_prompt, o_never, o_always} overwrite = o_prompt;
  122. unsigned int total_size = 0;
  123. unsigned int total_entries = 0;
  124. int src_fd = -1, dst_fd = -1;
  125. char *src_fn = NULL, *dst_fn = NULL;
  126. llist_t *zaccept = NULL;
  127. llist_t *zreject = NULL;
  128. char *base_dir = NULL;
  129. int failed, i, opt, opt_range = 0, list_header_done = 0;
  130. char key_buf[512];
  131. struct stat stat_buf;
  132. while((opt = getopt(argc, argv, "-d:lnopqx")) != -1) {
  133. switch(opt_range) {
  134. case 0: /* Options */
  135. switch(opt) {
  136. case 'l': /* List */
  137. verbosity = v_list;
  138. break;
  139. case 'n': /* Never overwrite existing files */
  140. overwrite = o_never;
  141. break;
  142. case 'o': /* Always overwrite existing files */
  143. overwrite = o_always;
  144. break;
  145. case 'p': /* Extract files to stdout and fall through to set verbosity */
  146. dst_fd = STDOUT_FILENO;
  147. case 'q': /* Be quiet */
  148. verbosity = (verbosity == v_normal) ? v_silent : verbosity;
  149. break;
  150. case 1 : /* The zip file */
  151. src_fn = bb_xstrndup(optarg, strlen(optarg)+4);
  152. opt_range++;
  153. break;
  154. default:
  155. bb_show_usage();
  156. }
  157. break;
  158. case 1: /* Include files */
  159. if (opt == 1) {
  160. zaccept = llist_add_to(zaccept, optarg);
  161. } else if (opt == 'd') {
  162. base_dir = optarg;
  163. opt_range += 2;
  164. } else if (opt == 'x') {
  165. opt_range++;
  166. } else {
  167. bb_show_usage();
  168. }
  169. break;
  170. case 2 : /* Exclude files */
  171. if (opt == 1) {
  172. zreject = llist_add_to(zreject, optarg);
  173. } else if (opt == 'd') { /* Extract to base directory */
  174. base_dir = optarg;
  175. opt_range++;
  176. } else {
  177. bb_show_usage();
  178. }
  179. break;
  180. default:
  181. bb_show_usage();
  182. }
  183. }
  184. if (src_fn == NULL) {
  185. bb_show_usage();
  186. }
  187. /* Open input file */
  188. if (strcmp("-", src_fn) == 0) {
  189. src_fd = STDIN_FILENO;
  190. /* Cannot use prompt mode since zip data is arriving on STDIN */
  191. overwrite = (overwrite == o_prompt) ? o_never : overwrite;
  192. } else {
  193. static const char *const extn[] = {"", ".zip", ".ZIP"};
  194. int orig_src_fn_len = strlen(src_fn);
  195. for(i = 0; (i < 3) && (src_fd == -1); i++) {
  196. strcpy(src_fn + orig_src_fn_len, extn[i]);
  197. src_fd = open(src_fn, O_RDONLY);
  198. }
  199. if (src_fd == -1) {
  200. src_fn[orig_src_fn_len] = 0;
  201. bb_error_msg_and_die("Cannot open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn);
  202. }
  203. }
  204. /* Change dir if necessary */
  205. if (base_dir && chdir(base_dir)) {
  206. bb_perror_msg_and_die("Cannot chdir");
  207. }
  208. if (verbosity != v_silent)
  209. printf("Archive: %s\n", src_fn);
  210. failed = 0;
  211. while (1) {
  212. unsigned int magic;
  213. /* Check magic number */
  214. unzip_read(src_fd, &magic, 4);
  215. if (magic == ZIP_CDS_MAGIC) {
  216. break;
  217. } else if (magic != ZIP_FILEHEADER_MAGIC) {
  218. bb_error_msg_and_die("Invalid zip magic %08X", magic);
  219. }
  220. /* Read the file header */
  221. unzip_read(src_fd, zip_header.raw, 26);
  222. #if BB_BIG_ENDIAN
  223. zip_header.formated.version = __swap16(zip_header.formated.version);
  224. zip_header.formated.flags = __swap16(zip_header.formated.flags);
  225. zip_header.formated.method = __swap16(zip_header.formated.method);
  226. zip_header.formated.modtime = __swap16(zip_header.formated.modtime);
  227. zip_header.formated.moddate = __swap16(zip_header.formated.moddate);
  228. zip_header.formated.crc32 = __swap32(zip_header.formated.crc32);
  229. zip_header.formated.cmpsize = __swap32(zip_header.formated.cmpsize);
  230. zip_header.formated.ucmpsize = __swap32(zip_header.formated.ucmpsize);
  231. zip_header.formated.filename_len = __swap16(zip_header.formated.filename_len);
  232. zip_header.formated.extra_len = __swap16(zip_header.formated.extra_len);
  233. #endif /* BB_BIG_ENDIAN */
  234. if ((zip_header.formated.method != 0) && (zip_header.formated.method != 8)) {
  235. bb_error_msg_and_die("Unsupported compression method %d", zip_header.formated.method);
  236. }
  237. /* Read filename */
  238. free(dst_fn);
  239. dst_fn = xmalloc(zip_header.formated.filename_len + 1);
  240. unzip_read(src_fd, dst_fn, zip_header.formated.filename_len);
  241. dst_fn[zip_header.formated.filename_len] = 0;
  242. /* Skip extra header bytes */
  243. unzip_skip(src_fd, zip_header.formated.extra_len);
  244. if ((verbosity == v_list) && !list_header_done){
  245. printf(" Length Date Time Name\n");
  246. printf(" -------- ---- ---- ----\n");
  247. list_header_done = 1;
  248. }
  249. /* Filter zip entries */
  250. if (find_list_entry(zreject, dst_fn) ||
  251. (zaccept && !find_list_entry(zaccept, dst_fn))) { /* Skip entry */
  252. i = 'n';
  253. } else { /* Extract entry */
  254. total_size += zip_header.formated.ucmpsize;
  255. if (verbosity == v_list) { /* List entry */
  256. unsigned int dostime = zip_header.formated.modtime | (zip_header.formated.moddate << 16);
  257. printf("%9u %02u-%02u-%02u %02u:%02u %s\n",
  258. zip_header.formated.ucmpsize,
  259. (dostime & 0x01e00000) >> 21,
  260. (dostime & 0x001f0000) >> 16,
  261. (((dostime & 0xfe000000) >> 25) + 1980) % 100,
  262. (dostime & 0x0000f800) >> 11,
  263. (dostime & 0x000007e0) >> 5,
  264. dst_fn);
  265. total_entries++;
  266. i = 'n';
  267. } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
  268. i = -1;
  269. } else if (last_char_is(dst_fn, '/')) { /* Extract directory */
  270. if (stat(dst_fn, &stat_buf) == -1) {
  271. if (errno != ENOENT) {
  272. bb_perror_msg_and_die("Cannot stat '%s'",dst_fn);
  273. }
  274. if (verbosity == v_normal) {
  275. printf(" creating: %s\n", dst_fn);
  276. }
  277. unzip_create_leading_dirs(dst_fn);
  278. if (bb_make_directory(dst_fn, 0777, 0)) {
  279. bb_error_msg_and_die("Failed to create directory");
  280. }
  281. } else {
  282. if (!S_ISDIR(stat_buf.st_mode)) {
  283. bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
  284. }
  285. }
  286. i = 'n';
  287. } else { /* Extract file */
  288. _check_file:
  289. if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
  290. if (errno != ENOENT) {
  291. bb_perror_msg_and_die("Cannot stat '%s'",dst_fn);
  292. }
  293. i = 'y';
  294. } else { /* File already exists */
  295. if (overwrite == o_never) {
  296. i = 'n';
  297. } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
  298. if (overwrite == o_always) {
  299. i = 'y';
  300. } else {
  301. printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
  302. if (!fgets(key_buf, 512, stdin)) {
  303. bb_perror_msg_and_die("Cannot read input");
  304. }
  305. i = key_buf[0];
  306. }
  307. } else { /* File is not regular file */
  308. bb_error_msg_and_die("'%s' exists but is not regular file",dst_fn);
  309. }
  310. }
  311. }
  312. }
  313. switch (i) {
  314. case 'A':
  315. overwrite = o_always;
  316. case 'y': /* Open file and fall into unzip */
  317. unzip_create_leading_dirs(dst_fn);
  318. dst_fd = bb_xopen(dst_fn, O_WRONLY | O_CREAT);
  319. case -1: /* Unzip */
  320. if (verbosity == v_normal) {
  321. printf(" inflating: %s\n", dst_fn);
  322. }
  323. if (unzip_extract(&zip_header, src_fd, dst_fd)) {
  324. failed = 1;
  325. }
  326. if (dst_fd != STDOUT_FILENO) {
  327. /* closing STDOUT is potentially bad for future business */
  328. close(dst_fd);
  329. }
  330. break;
  331. case 'N':
  332. overwrite = o_never;
  333. case 'n':
  334. /* Skip entry data */
  335. unzip_skip(src_fd, zip_header.formated.cmpsize);
  336. break;
  337. case 'r':
  338. /* Prompt for new name */
  339. printf("new name: ");
  340. if (!fgets(key_buf, 512, stdin)) {
  341. bb_perror_msg_and_die("Cannot read input");
  342. }
  343. free(dst_fn);
  344. dst_fn = bb_xstrdup(key_buf);
  345. chomp(dst_fn);
  346. goto _check_file;
  347. default:
  348. printf("error: invalid response [%c]\n",(char)i);
  349. goto _check_file;
  350. }
  351. /* Data descriptor section */
  352. if (zip_header.formated.flags & 4) {
  353. /* skip over duplicate crc, compressed size and uncompressed size */
  354. unzip_skip(src_fd, 12);
  355. }
  356. }
  357. if (verbosity == v_list) {
  358. printf(" -------- -------\n"
  359. "%9d %d files\n", total_size, total_entries);
  360. }
  361. return failed;
  362. }
  363. /* END CODE */
  364. /*
  365. Local Variables:
  366. c-file-style: "linux"
  367. c-basic-offset: 4
  368. tab-width: 4
  369. End:
  370. */