HardenedBSD src tree
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

402 lines
11KB

  1. /*-
  2. * Copyright (c) 2003-2007 Tim Kientzle
  3. * Copyright (c) 2012 Michihiro NAKAJIMA
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
  16. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  17. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  18. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
  19. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  24. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "archive_platform.h"
  27. __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $");
  28. #ifdef HAVE_ERRNO_H
  29. #include <errno.h>
  30. #endif
  31. #include <stdio.h>
  32. #ifdef HAVE_STDLIB_H
  33. #include <stdlib.h>
  34. #endif
  35. #ifdef HAVE_STRING_H
  36. #include <string.h>
  37. #endif
  38. #ifdef HAVE_BZLIB_H
  39. #include <bzlib.h>
  40. #endif
  41. #include "archive.h"
  42. #include "archive_private.h"
  43. #include "archive_write_private.h"
  44. #if ARCHIVE_VERSION_NUMBER < 4000000
  45. int
  46. archive_write_set_compression_bzip2(struct archive *a)
  47. {
  48. __archive_write_filters_free(a);
  49. return (archive_write_add_filter_bzip2(a));
  50. }
  51. #endif
  52. struct private_data {
  53. int compression_level;
  54. #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
  55. bz_stream stream;
  56. int64_t total_in;
  57. char *compressed;
  58. size_t compressed_buffer_size;
  59. #else
  60. struct archive_write_program_data *pdata;
  61. #endif
  62. };
  63. static int archive_compressor_bzip2_close(struct archive_write_filter *);
  64. static int archive_compressor_bzip2_free(struct archive_write_filter *);
  65. static int archive_compressor_bzip2_open(struct archive_write_filter *);
  66. static int archive_compressor_bzip2_options(struct archive_write_filter *,
  67. const char *, const char *);
  68. static int archive_compressor_bzip2_write(struct archive_write_filter *,
  69. const void *, size_t);
  70. /*
  71. * Add a bzip2 compression filter to this write handle.
  72. */
  73. int
  74. archive_write_add_filter_bzip2(struct archive *_a)
  75. {
  76. struct archive_write *a = (struct archive_write *)_a;
  77. struct archive_write_filter *f = __archive_write_allocate_filter(_a);
  78. struct private_data *data;
  79. archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
  80. ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");
  81. data = calloc(1, sizeof(*data));
  82. if (data == NULL) {
  83. archive_set_error(&a->archive, ENOMEM, "Out of memory");
  84. return (ARCHIVE_FATAL);
  85. }
  86. data->compression_level = 9; /* default */
  87. f->data = data;
  88. f->options = &archive_compressor_bzip2_options;
  89. f->close = &archive_compressor_bzip2_close;
  90. f->free = &archive_compressor_bzip2_free;
  91. f->open = &archive_compressor_bzip2_open;
  92. f->code = ARCHIVE_FILTER_BZIP2;
  93. f->name = "bzip2";
  94. #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
  95. return (ARCHIVE_OK);
  96. #else
  97. data->pdata = __archive_write_program_allocate("bzip2");
  98. if (data->pdata == NULL) {
  99. free(data);
  100. archive_set_error(&a->archive, ENOMEM, "Out of memory");
  101. return (ARCHIVE_FATAL);
  102. }
  103. data->compression_level = 0;
  104. archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
  105. "Using external bzip2 program");
  106. return (ARCHIVE_WARN);
  107. #endif
  108. }
  109. /*
  110. * Set write options.
  111. */
  112. static int
  113. archive_compressor_bzip2_options(struct archive_write_filter *f,
  114. const char *key, const char *value)
  115. {
  116. struct private_data *data = (struct private_data *)f->data;
  117. if (strcmp(key, "compression-level") == 0) {
  118. if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
  119. value[1] != '\0')
  120. return (ARCHIVE_WARN);
  121. data->compression_level = value[0] - '0';
  122. /* Make '0' be a synonym for '1'. */
  123. /* This way, bzip2 compressor supports the same 0..9
  124. * range of levels as gzip. */
  125. if (data->compression_level < 1)
  126. data->compression_level = 1;
  127. return (ARCHIVE_OK);
  128. }
  129. /* Note: The "warn" return is just to inform the options
  130. * supervisor that we didn't handle it. It will generate
  131. * a suitable error if no one used this option. */
  132. return (ARCHIVE_WARN);
  133. }
  134. #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
  135. /* Don't compile this if we don't have bzlib. */
  136. /*
  137. * Yuck. bzlib.h is not const-correct, so I need this one bit
  138. * of ugly hackery to convert a const * pointer to a non-const pointer.
  139. */
  140. #define SET_NEXT_IN(st,src) \
  141. (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
  142. static int drive_compressor(struct archive_write_filter *,
  143. struct private_data *, int finishing);
  144. /*
  145. * Setup callback.
  146. */
  147. static int
  148. archive_compressor_bzip2_open(struct archive_write_filter *f)
  149. {
  150. struct private_data *data = (struct private_data *)f->data;
  151. int ret;
  152. if (data->compressed == NULL) {
  153. size_t bs = 65536, bpb;
  154. if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
  155. /* Buffer size should be a multiple number of the of bytes
  156. * per block for performance. */
  157. bpb = archive_write_get_bytes_per_block(f->archive);
  158. if (bpb > bs)
  159. bs = bpb;
  160. else if (bpb != 0)
  161. bs -= bs % bpb;
  162. }
  163. data->compressed_buffer_size = bs;
  164. data->compressed
  165. = (char *)malloc(data->compressed_buffer_size);
  166. if (data->compressed == NULL) {
  167. archive_set_error(f->archive, ENOMEM,
  168. "Can't allocate data for compression buffer");
  169. return (ARCHIVE_FATAL);
  170. }
  171. }
  172. memset(&data->stream, 0, sizeof(data->stream));
  173. data->stream.next_out = data->compressed;
  174. data->stream.avail_out = data->compressed_buffer_size;
  175. f->write = archive_compressor_bzip2_write;
  176. /* Initialize compression library */
  177. ret = BZ2_bzCompressInit(&(data->stream),
  178. data->compression_level, 0, 30);
  179. if (ret == BZ_OK) {
  180. f->data = data;
  181. return (ARCHIVE_OK);
  182. }
  183. /* Library setup failed: clean up. */
  184. archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
  185. "Internal error initializing compression library");
  186. /* Override the error message if we know what really went wrong. */
  187. switch (ret) {
  188. case BZ_PARAM_ERROR:
  189. archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
  190. "Internal error initializing compression library: "
  191. "invalid setup parameter");
  192. break;
  193. case BZ_MEM_ERROR:
  194. archive_set_error(f->archive, ENOMEM,
  195. "Internal error initializing compression library: "
  196. "out of memory");
  197. break;
  198. case BZ_CONFIG_ERROR:
  199. archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
  200. "Internal error initializing compression library: "
  201. "mis-compiled library");
  202. break;
  203. }
  204. return (ARCHIVE_FATAL);
  205. }
  206. /*
  207. * Write data to the compressed stream.
  208. *
  209. * Returns ARCHIVE_OK if all data written, error otherwise.
  210. */
  211. static int
  212. archive_compressor_bzip2_write(struct archive_write_filter *f,
  213. const void *buff, size_t length)
  214. {
  215. struct private_data *data = (struct private_data *)f->data;
  216. /* Update statistics */
  217. data->total_in += length;
  218. /* Compress input data to output buffer */
  219. SET_NEXT_IN(data, buff);
  220. data->stream.avail_in = length;
  221. if (drive_compressor(f, data, 0))
  222. return (ARCHIVE_FATAL);
  223. return (ARCHIVE_OK);
  224. }
  225. /*
  226. * Finish the compression.
  227. */
  228. static int
  229. archive_compressor_bzip2_close(struct archive_write_filter *f)
  230. {
  231. struct private_data *data = (struct private_data *)f->data;
  232. int ret;
  233. /* Finish compression cycle. */
  234. ret = drive_compressor(f, data, 1);
  235. if (ret == ARCHIVE_OK) {
  236. /* Write the last block */
  237. ret = __archive_write_filter(f->next_filter,
  238. data->compressed,
  239. data->compressed_buffer_size - data->stream.avail_out);
  240. }
  241. switch (BZ2_bzCompressEnd(&(data->stream))) {
  242. case BZ_OK:
  243. break;
  244. default:
  245. archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
  246. "Failed to clean up compressor");
  247. ret = ARCHIVE_FATAL;
  248. }
  249. return ret;
  250. }
  251. static int
  252. archive_compressor_bzip2_free(struct archive_write_filter *f)
  253. {
  254. struct private_data *data = (struct private_data *)f->data;
  255. free(data->compressed);
  256. free(data);
  257. f->data = NULL;
  258. return (ARCHIVE_OK);
  259. }
  260. /*
  261. * Utility function to push input data through compressor, writing
  262. * full output blocks as necessary.
  263. *
  264. * Note that this handles both the regular write case (finishing ==
  265. * false) and the end-of-archive case (finishing == true).
  266. */
  267. static int
  268. drive_compressor(struct archive_write_filter *f,
  269. struct private_data *data, int finishing)
  270. {
  271. int ret;
  272. for (;;) {
  273. if (data->stream.avail_out == 0) {
  274. ret = __archive_write_filter(f->next_filter,
  275. data->compressed,
  276. data->compressed_buffer_size);
  277. if (ret != ARCHIVE_OK) {
  278. /* TODO: Handle this write failure */
  279. return (ARCHIVE_FATAL);
  280. }
  281. data->stream.next_out = data->compressed;
  282. data->stream.avail_out = data->compressed_buffer_size;
  283. }
  284. /* If there's nothing to do, we're done. */
  285. if (!finishing && data->stream.avail_in == 0)
  286. return (ARCHIVE_OK);
  287. ret = BZ2_bzCompress(&(data->stream),
  288. finishing ? BZ_FINISH : BZ_RUN);
  289. switch (ret) {
  290. case BZ_RUN_OK:
  291. /* In non-finishing case, did compressor
  292. * consume everything? */
  293. if (!finishing && data->stream.avail_in == 0)
  294. return (ARCHIVE_OK);
  295. break;
  296. case BZ_FINISH_OK: /* Finishing: There's more work to do */
  297. break;
  298. case BZ_STREAM_END: /* Finishing: all done */
  299. /* Only occurs in finishing case */
  300. return (ARCHIVE_OK);
  301. default:
  302. /* Any other return value indicates an error */
  303. archive_set_error(f->archive,
  304. ARCHIVE_ERRNO_PROGRAMMER,
  305. "Bzip2 compression failed;"
  306. " BZ2_bzCompress() returned %d",
  307. ret);
  308. return (ARCHIVE_FATAL);
  309. }
  310. }
  311. }
  312. #else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
  313. static int
  314. archive_compressor_bzip2_open(struct archive_write_filter *f)
  315. {
  316. struct private_data *data = (struct private_data *)f->data;
  317. struct archive_string as;
  318. int r;
  319. archive_string_init(&as);
  320. archive_strcpy(&as, "bzip2");
  321. /* Specify compression level. */
  322. if (data->compression_level > 0) {
  323. archive_strcat(&as, " -");
  324. archive_strappend_char(&as, '0' + data->compression_level);
  325. }
  326. f->write = archive_compressor_bzip2_write;
  327. r = __archive_write_program_open(f, data->pdata, as.s);
  328. archive_string_free(&as);
  329. return (r);
  330. }
  331. static int
  332. archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,
  333. size_t length)
  334. {
  335. struct private_data *data = (struct private_data *)f->data;
  336. return __archive_write_program_write(f, data->pdata, buff, length);
  337. }
  338. static int
  339. archive_compressor_bzip2_close(struct archive_write_filter *f)
  340. {
  341. struct private_data *data = (struct private_data *)f->data;
  342. return __archive_write_program_close(f, data->pdata);
  343. }
  344. static int
  345. archive_compressor_bzip2_free(struct archive_write_filter *f)
  346. {
  347. struct private_data *data = (struct private_data *)f->data;
  348. __archive_write_program_free(data->pdata);
  349. free(data);
  350. return (ARCHIVE_OK);
  351. }
  352. #endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */