zgz.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /*
  2. * Authors: Faidon Liambotis <paravoid@debian.org>
  3. * Josh Triplett <josh@joshtriplett.org>
  4. *
  5. * This is a zlib-based gzip that is heavily based on NetBSD's gzip,
  6. * developed by Matthew R. Green.
  7. *
  8. * This is suited for gzip regeneration and is part of pristine-tar.
  9. * As such, it adds some extra options which are needed to successfully
  10. * reproduce the gzips out there and removes features of the original
  11. * implementation that were not relevant (e.g. decompression)
  12. *
  13. * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
  14. * Copyright (c) 2007 Faidon Liambotis
  15. * Copyright (c) 2008 Josh Triplett
  16. * Copyright (c) 2010 Joey Hess
  17. * All rights reserved.
  18. *
  19. * Redistribution and use in source and binary forms, with or without
  20. * modification, are permitted provided that the following conditions
  21. * are met:
  22. * 1. Redistributions of source code must retain the above copyright
  23. * notice, this list of conditions and the following disclaimer.
  24. * 2. Redistributions in binary form must reproduce the above copyright
  25. * notice, this list of conditions and the following disclaimer in the
  26. * documentation and/or other materials provided with the distribution.
  27. * 3. The name of the author may not be used to endorse or promote products
  28. * derived from this software without specific prior written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  31. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  33. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  34. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  35. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  36. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  37. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  38. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  39. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  40. * SUCH DAMAGE.
  41. */
  42. /*
  43. * gzip.c -- GPL free gzip using zlib.
  44. *
  45. * RFC 1950 covers the zlib format
  46. * RFC 1951 covers the deflate format
  47. * RFC 1952 covers the gzip format
  48. *
  49. */
  50. #define _GNU_SOURCE
  51. #include <sys/param.h>
  52. #include <sys/stat.h>
  53. #include <sys/time.h>
  54. #include <inttypes.h>
  55. #include <unistd.h>
  56. #include <stdio.h>
  57. #include <string.h>
  58. #include <stdlib.h>
  59. #include <err.h>
  60. #include <errno.h>
  61. #include <fcntl.h>
  62. #include <zlib.h>
  63. #include <libgen.h>
  64. #include <stdarg.h>
  65. #include <getopt.h>
  66. #include <time.h>
  67. extern void gnuzip(int in, int out, char *origname, unsigned long timestamp, int level, int osflag, int rsync, int rsync14, int rsync16);
  68. #define BUFLEN (64 * 1024)
  69. #define GZIP_MAGIC0 0x1F
  70. #define GZIP_MAGIC1 0x8B
  71. #define HEAD_CRC 0x02
  72. #define EXTRA_FIELD 0x04
  73. #define ORIG_NAME 0x08
  74. #define COMMENT 0x10
  75. #define GZIP_OS_UNIX 3 /* Unix */
  76. #define GZIP_OS_NTFS 11 /* NTFS */
  77. static const char gzip_version[] = "zgz 20100613 based on NetBSD gzip 20060927, GNU gzip 1.3.12";
  78. static const char gzip_copyright[] = \
  79. " Authors: Faidon Liambotis <paravoid@debian.org>\n"
  80. " Josh Triplett <josh@joshtriplett.org>\n"
  81. "\n"
  82. " Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
  83. " Copyright (c) 2007 Faidon Liambotis\n"
  84. " Copyright (c) 2008 Josh Triplett\n"
  85. " * All rights reserved.\n"
  86. " *\n"
  87. " * Redistribution and use in source and binary forms, with or without\n"
  88. " * modification, are permitted provided that the following conditions\n"
  89. " * are met:\n"
  90. " * 1. Redistributions of source code must retain the above copyright\n"
  91. " * notice, this list of conditions and the following disclaimer.\n"
  92. " * 2. Redistributions in binary form must reproduce the above copyright\n"
  93. " * notice, this list of conditions and the following disclaimer in the\n"
  94. " * documentation and/or other materials provided with the distribution.\n"
  95. " * 3. The name of the author may not be used to endorse or promote products\n"
  96. " * derived from this software without specific prior written permission.\n"
  97. " *\n"
  98. " * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
  99. " * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
  100. " * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
  101. " * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
  102. " * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
  103. " * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
  104. " * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
  105. " * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
  106. " * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
  107. " * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
  108. " * SUCH DAMAGE.";
  109. static int qflag; /* quiet mode */
  110. static void maybe_err(const char *fmt, ...)
  111. __attribute__((__format__(__printf__, 1, 2),noreturn));
  112. static void maybe_errx(const char *fmt, ...)
  113. __attribute__((__format__(__printf__, 1, 2),noreturn));
  114. static void gz_compress(int, int, const char *, uint32_t, int, int, int, int, int);
  115. static void usage(void);
  116. static void display_version(void);
  117. static void display_license(void);
  118. int main(int, char **p);
  119. static const struct option longopts[] = {
  120. { "stdout", no_argument, 0, 'c' },
  121. { "to-stdout", no_argument, 0, 'c' },
  122. { "decompress", no_argument, 0, 'd' },
  123. { "uncompress", no_argument, 0, 'd' },
  124. { "force", no_argument, 0, 'f' },
  125. { "help", no_argument, 0, 'h' },
  126. { "no-name", no_argument, 0, 'n' },
  127. { "name", no_argument, 0, 'N' },
  128. { "quiet", no_argument, 0, 'q' },
  129. { "fast", no_argument, 0, '1' },
  130. { "best", no_argument, 0, '9' },
  131. { "ascii", no_argument, 0, 'a' },
  132. /* new options */
  133. { "gnu", no_argument, 0, 'G' },
  134. { "zlib", no_argument, 0, 'Z' },
  135. { "rsyncable", no_argument, 0, 'R' },
  136. { "new-rsyncable", no_argument, 0, 'r' },
  137. { "16-rsyncable", no_argument, 0, 'D' },
  138. { "no-timestamp", no_argument, 0, 'm' },
  139. { "force-timestamp", no_argument, 0, 'M' },
  140. { "timestamp", required_argument, 0, 'T' },
  141. { "osflag", required_argument, 0, 's' },
  142. { "original-name", required_argument, 0, 'o' },
  143. { "filename", required_argument, 0, 'F' },
  144. { "quirk", required_argument, 0, 'k' },
  145. /* end */
  146. { "version", no_argument, 0, 'V' },
  147. { "license", no_argument, 0, 'L' },
  148. { NULL, no_argument, 0, 0 },
  149. };
  150. int
  151. main(int argc, char **argv)
  152. {
  153. const char *progname = argv[0];
  154. int gnu = 0;
  155. int quirks = 0;
  156. char *origname = NULL;
  157. uint32_t timestamp = 0;
  158. int memlevel = 8; /* zlib's default */
  159. int nflag = 0;
  160. int mflag = 0;
  161. int fflag = 0;
  162. int xflag = -1;
  163. int ntfs_quirk = 0;
  164. int level = 6;
  165. int osflag = GZIP_OS_UNIX;
  166. int rsync = 0;
  167. int rsync14 = 0;
  168. int rsync16 = 0;
  169. int ch;
  170. if (strcmp(progname, "gunzip") == 0 ||
  171. strcmp(progname, "zcat") == 0 ||
  172. strcmp(progname, "gzcat") == 0) {
  173. fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
  174. usage();
  175. }
  176. #define OPT_LIST "123456789acdfhF:GLNnMmqRrT:Vo:k:s:Z"
  177. while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
  178. switch (ch) {
  179. case 'G':
  180. gnu = 1;
  181. break;
  182. case 'Z':
  183. break;
  184. case '1': case '2': case '3':
  185. case '4': case '5': case '6':
  186. case '7': case '8': case '9':
  187. level = ch - '0';
  188. break;
  189. case 'c':
  190. /* Ignored for compatibility; zgz always uses -c */
  191. break;
  192. case 'f':
  193. fflag = 1;
  194. break;
  195. case 'N':
  196. nflag = 0;
  197. break;
  198. case 'n':
  199. nflag = 1;
  200. /* no break, n implies m */
  201. case 'm':
  202. mflag = 1;
  203. break;
  204. case 'M':
  205. mflag = 0;
  206. break;
  207. case 'q':
  208. qflag = 1;
  209. break;
  210. case 's':
  211. osflag = atoi(optarg);
  212. break;
  213. case 'F':
  214. case 'o':
  215. origname = optarg;
  216. break;
  217. case 'k':
  218. quirks = 1;
  219. if (strcmp(optarg, "buggy-bsd") == 0) {
  220. /* certain archives made with older versions of
  221. * BSD variants of gzip */
  222. /* no name or timestamp information */
  223. nflag = 1;
  224. mflag = 1;
  225. /* maximum compression but without indicating so */
  226. level = 9;
  227. xflag = 0;
  228. } else if (strcmp(optarg, "ntfs") == 0) {
  229. ntfs_quirk = 1;
  230. /* no name or timestamp information */
  231. nflag = 1;
  232. mflag = 1;
  233. /* osflag is NTFS */
  234. osflag = GZIP_OS_NTFS;
  235. } else if (strcmp(optarg, "perl") == 0) {
  236. /* Perl's Compress::Raw::Zlib */
  237. memlevel = 9;
  238. /* no name or timestamp information */
  239. nflag = 1;
  240. mflag = 1;
  241. /* maximum compression but without indicating so */
  242. level = 9;
  243. xflag = 0;
  244. } else {
  245. fprintf(stderr, "%s: unknown quirk!\n", progname);
  246. usage();
  247. }
  248. break;
  249. case 'T':
  250. timestamp = atoi(optarg);
  251. break;
  252. case 'R':
  253. rsync = 1;
  254. break;
  255. case 'r':
  256. rsync14 = 1;
  257. break;
  258. case 'D':
  259. rsync16 = 1;
  260. break;
  261. case 'd':
  262. fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
  263. usage();
  264. break;
  265. case 'a':
  266. fprintf(stderr, "%s: option --ascii ignored on this version\n", progname);
  267. break;
  268. case 'V':
  269. display_version();
  270. /* NOTREACHED */
  271. case 'L':
  272. display_license();
  273. /* NOT REACHED */
  274. default:
  275. usage();
  276. /* NOTREACHED */
  277. }
  278. }
  279. argv += optind;
  280. argc -= optind;
  281. if (argc != 0) {
  282. fprintf(stderr, "%s: filenames not supported; use stdin and stdout\n", progname);
  283. return 1;
  284. }
  285. if (fflag == 0 && isatty(STDOUT_FILENO))
  286. maybe_errx("standard output is a terminal -- ignoring");
  287. if (nflag)
  288. origname = NULL;
  289. if (mflag)
  290. timestamp = 0;
  291. if (gnu) {
  292. if (quirks) {
  293. fprintf(stderr, "%s: quirks not supported with --gnu\n", progname);
  294. return 1;
  295. }
  296. gnuzip(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, osflag, rsync, rsync14, rsync16);
  297. } else {
  298. if (rsync || rsync14 || rsync16) {
  299. fprintf(stderr, "%s: --rsyncable not supported with --zlib\n", progname);
  300. return 1;
  301. }
  302. gz_compress(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, memlevel, osflag, xflag, ntfs_quirk);
  303. }
  304. return 0;
  305. }
  306. /* maybe print an error */
  307. void
  308. maybe_err(const char *fmt, ...)
  309. {
  310. va_list ap;
  311. if (qflag == 0) {
  312. va_start(ap, fmt);
  313. vwarn(fmt, ap);
  314. va_end(ap);
  315. }
  316. exit(1);
  317. }
  318. /* ... without an errno. */
  319. void
  320. maybe_errx(const char *fmt, ...)
  321. {
  322. va_list ap;
  323. if (qflag == 0) {
  324. va_start(ap, fmt);
  325. vwarnx(fmt, ap);
  326. va_end(ap);
  327. }
  328. exit(1);
  329. }
  330. /* compress input to output. */
  331. static void
  332. gz_compress(int in, int out, const char *origname, uint32_t mtime, int level, int memlevel, int osflag, int xflag, int ntfs_quirk)
  333. {
  334. z_stream z;
  335. char *outbufp, *inbufp;
  336. off_t in_tot = 0;
  337. ssize_t in_size;
  338. int i, error;
  339. uLong crc;
  340. outbufp = malloc(BUFLEN);
  341. inbufp = malloc(BUFLEN);
  342. if (outbufp == NULL || inbufp == NULL)
  343. maybe_err("malloc failed");
  344. memset(&z, 0, sizeof z);
  345. z.zalloc = Z_NULL;
  346. z.zfree = Z_NULL;
  347. z.opaque = 0;
  348. i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
  349. GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
  350. origname ? ORIG_NAME : 0,
  351. mtime & 0xff,
  352. (mtime >> 8) & 0xff,
  353. (mtime >> 16) & 0xff,
  354. (mtime >> 24) & 0xff,
  355. xflag >= 0 ? xflag :
  356. level == 1 ? 4 : level == 9 ? 2 : 0,
  357. osflag, origname ? origname : "");
  358. if (i >= BUFLEN)
  359. /* this need PATH_MAX > BUFLEN ... */
  360. maybe_err("snprintf");
  361. if (origname)
  362. i++;
  363. z.next_out = (unsigned char *)outbufp + i;
  364. z.avail_out = BUFLEN - i;
  365. error = deflateInit2(&z, level, Z_DEFLATED,
  366. (-MAX_WBITS), memlevel, Z_DEFAULT_STRATEGY);
  367. if (error != Z_OK)
  368. maybe_err("deflateInit2 failed");
  369. crc = crc32(0L, Z_NULL, 0);
  370. for (;;) {
  371. if (z.avail_out == 0) {
  372. if (write(out, outbufp, BUFLEN) != BUFLEN)
  373. maybe_err("write");
  374. z.next_out = (unsigned char *)outbufp;
  375. z.avail_out = BUFLEN;
  376. }
  377. if (z.avail_in == 0) {
  378. in_size = read(in, inbufp, BUFLEN);
  379. if (in_size < 0)
  380. maybe_err("read");
  381. if (in_size == 0)
  382. break;
  383. crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
  384. in_tot += in_size;
  385. z.next_in = (unsigned char *)inbufp;
  386. z.avail_in = in_size;
  387. }
  388. error = deflate(&z, Z_NO_FLUSH);
  389. if (error != Z_OK && error != Z_STREAM_END)
  390. maybe_errx("deflate failed");
  391. }
  392. /* clean up */
  393. for (;;) {
  394. size_t len;
  395. ssize_t w;
  396. error = deflate(&z, Z_FINISH);
  397. if (error != Z_OK && error != Z_STREAM_END)
  398. maybe_errx("deflate failed");
  399. len = (char *)z.next_out - outbufp;
  400. /* for a really strange reason, that
  401. * particular byte is decremented */
  402. if (ntfs_quirk)
  403. outbufp[10]--;
  404. w = write(out, outbufp, len);
  405. if (w == -1 || (size_t)w != len)
  406. maybe_err("write");
  407. z.next_out = (unsigned char *)outbufp;
  408. z.avail_out = BUFLEN;
  409. if (error == Z_STREAM_END)
  410. break;
  411. }
  412. if (deflateEnd(&z) != Z_OK)
  413. maybe_errx("deflateEnd failed");
  414. if (ntfs_quirk) {
  415. /* write NTFS tail magic (?) */
  416. i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c",
  417. 0x00, 0x00, 0xff, 0xff, 0x03, 0x00);
  418. if (i != 6)
  419. maybe_err("snprintf");
  420. if (write(out, outbufp, i) != i)
  421. maybe_err("write");
  422. }
  423. /* write CRC32 and input size (ISIZE) at the tail */
  424. i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
  425. (int)crc & 0xff,
  426. (int)(crc >> 8) & 0xff,
  427. (int)(crc >> 16) & 0xff,
  428. (int)(crc >> 24) & 0xff,
  429. (int)in_tot & 0xff,
  430. (int)(in_tot >> 8) & 0xff,
  431. (int)(in_tot >> 16) & 0xff,
  432. (int)(in_tot >> 24) & 0xff);
  433. if (i != 8)
  434. maybe_err("snprintf");
  435. if (write(out, outbufp, i) != i)
  436. maybe_err("write");
  437. free(inbufp);
  438. free(outbufp);
  439. }
  440. /* display usage */
  441. static void
  442. usage(void)
  443. {
  444. fprintf(stderr, "%s\n", gzip_version);
  445. fprintf(stderr,
  446. "usage: zgz [-" OPT_LIST "] < <file> > <file>\n"
  447. " -G --gnu use GNU gzip implementation\n"
  448. " -Z --zlib use zlib's implementation (default)\n"
  449. " -1 --fast fastest (worst) compression\n"
  450. " -2 .. -8 set compression level\n"
  451. " -9 --best best (slowest) compression\n"
  452. " -f --force force writing compressed data to a terminal\n"
  453. " -N --name save or restore original file name and time stamp\n"
  454. " -n --no-name don't save original file name or time stamp\n"
  455. " -m --no-timestamp don't save original time stamp\n"
  456. " -M --force-timestemp save the timestamp even if -n was passed\n"
  457. " -q --quiet output no warnings\n"
  458. " -V --version display program version\n"
  459. " -h --help display this help\n"
  460. " -o NAME\n"
  461. " --original-name NAME use NAME as the original file name\n"
  462. " -F NAME --filename NAME same as --original-name\n"
  463. " -s --osflag set the OS flag to something different than 03 (Unix)\n"
  464. " -T --timestamp SECONDS set the timestamp to the specified number of seconds\n"
  465. " \ngnu-specific options:\n"
  466. " -R --rsyncable make rsync-friendly archive\n"
  467. " -r --new-rsyncable make rsync-friendly archive (new version)\n"
  468. " \nzlib-specific options:\n"
  469. " -k --quirk QUIRK enable a format quirk (buggy-bsd, ntfs, perl)\n");
  470. exit(0);
  471. }
  472. /* display the license information of NetBSD gzip */
  473. static void
  474. display_license(void)
  475. {
  476. fprintf(stderr, "%s\n", gzip_version);
  477. fprintf(stderr, "%s\n", gzip_copyright);
  478. exit(0);
  479. }
  480. /* display the version of NetBSD gzip */
  481. static void
  482. display_version(void)
  483. {
  484. fprintf(stderr, "%s\n", gzip_version);
  485. exit(0);
  486. }