archive_write_set_compression_program.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*-
  2. * Copyright (c) 2007 Joerg Sonnenberger
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
  15. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  16. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  17. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
  18. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  19. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  20. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  21. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  23. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "archive_platform.h"
  26. __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.3 2008/06/15 10:45:57 kientzle Exp $");
  27. /* This capability is only available on POSIX systems. */
  28. #if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
  29. !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__))
  30. #include "archive.h"
  31. /*
  32. * On non-Posix systems, allow the program to build, but choke if
  33. * this function is actually invoked.
  34. */
  35. int
  36. archive_write_set_compression_program(struct archive *_a, const char *cmd)
  37. {
  38. archive_set_error(_a, -1,
  39. "External compression programs not supported on this platform");
  40. return (ARCHIVE_FATAL);
  41. }
  42. #else
  43. #ifdef HAVE_SYS_WAIT_H
  44. # include <sys/wait.h>
  45. #endif
  46. #ifdef HAVE_ERRNO_H
  47. # include <errno.h>
  48. #endif
  49. #ifdef HAVE_FCNTL_H
  50. # include <fcntl.h>
  51. #endif
  52. #ifdef HAVE_STDLIB_H
  53. # include <stdlib.h>
  54. #endif
  55. #ifdef HAVE_STRING_H
  56. # include <string.h>
  57. #endif
  58. #include "archive.h"
  59. #include "archive_private.h"
  60. #include "archive_write_private.h"
  61. #include "filter_fork.h"
  62. struct private_data {
  63. char *description;
  64. pid_t child;
  65. int child_stdin, child_stdout;
  66. char *child_buf;
  67. size_t child_buf_len, child_buf_avail;
  68. };
  69. static int archive_compressor_program_finish(struct archive_write *);
  70. static int archive_compressor_program_init(struct archive_write *);
  71. static int archive_compressor_program_write(struct archive_write *,
  72. const void *, size_t);
  73. /*
  74. * Allocate, initialize and return a archive object.
  75. */
  76. int
  77. archive_write_set_compression_program(struct archive *_a, const char *cmd)
  78. {
  79. struct archive_write *a = (struct archive_write *)_a;
  80. __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
  81. ARCHIVE_STATE_NEW, "archive_write_set_compression_program");
  82. a->compressor.init = &archive_compressor_program_init;
  83. a->compressor.config = strdup(cmd);
  84. return (ARCHIVE_OK);
  85. }
  86. /*
  87. * Setup callback.
  88. */
  89. static int
  90. archive_compressor_program_init(struct archive_write *a)
  91. {
  92. int ret;
  93. struct private_data *state;
  94. static const char *prefix = "Program: ";
  95. char *cmd = a->compressor.config;
  96. if (a->client_opener != NULL) {
  97. ret = (a->client_opener)(&a->archive, a->client_data);
  98. if (ret != ARCHIVE_OK)
  99. return (ret);
  100. }
  101. state = (struct private_data *)malloc(sizeof(*state));
  102. if (state == NULL) {
  103. archive_set_error(&a->archive, ENOMEM,
  104. "Can't allocate data for compression");
  105. return (ARCHIVE_FATAL);
  106. }
  107. memset(state, 0, sizeof(*state));
  108. a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
  109. state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
  110. strcpy(state->description, prefix);
  111. strcat(state->description, cmd);
  112. a->archive.compression_name = state->description;
  113. state->child_buf_len = a->bytes_per_block;
  114. state->child_buf_avail = 0;
  115. state->child_buf = malloc(state->child_buf_len);
  116. if (state->child_buf == NULL) {
  117. archive_set_error(&a->archive, ENOMEM,
  118. "Can't allocate data for compression buffer");
  119. free(state);
  120. return (ARCHIVE_FATAL);
  121. }
  122. if ((state->child = __archive_create_child(cmd,
  123. &state->child_stdin, &state->child_stdout)) == -1) {
  124. archive_set_error(&a->archive, EINVAL,
  125. "Can't initialise filter");
  126. free(state->child_buf);
  127. free(state);
  128. return (ARCHIVE_FATAL);
  129. }
  130. a->compressor.write = archive_compressor_program_write;
  131. a->compressor.finish = archive_compressor_program_finish;
  132. a->compressor.data = state;
  133. return (0);
  134. }
  135. static ssize_t
  136. child_write(struct archive_write *a, const char *buf, size_t buf_len)
  137. {
  138. struct private_data *state = a->compressor.data;
  139. ssize_t ret;
  140. if (state->child_stdin == -1)
  141. return (-1);
  142. if (buf_len == 0)
  143. return (-1);
  144. restart_write:
  145. do {
  146. ret = write(state->child_stdin, buf, buf_len);
  147. } while (ret == -1 && errno == EINTR);
  148. if (ret > 0)
  149. return (ret);
  150. if (ret == 0) {
  151. close(state->child_stdin);
  152. state->child_stdin = -1;
  153. fcntl(state->child_stdout, F_SETFL, 0);
  154. return (0);
  155. }
  156. if (ret == -1 && errno != EAGAIN)
  157. return (-1);
  158. if (state->child_stdout == -1) {
  159. fcntl(state->child_stdin, F_SETFL, 0);
  160. __archive_check_child(state->child_stdin, state->child_stdout);
  161. goto restart_write;
  162. }
  163. do {
  164. ret = read(state->child_stdout,
  165. state->child_buf + state->child_buf_avail,
  166. state->child_buf_len - state->child_buf_avail);
  167. } while (ret == -1 && errno == EINTR);
  168. if (ret == 0 || (ret == -1 && errno == EPIPE)) {
  169. close(state->child_stdout);
  170. state->child_stdout = -1;
  171. fcntl(state->child_stdin, F_SETFL, 0);
  172. goto restart_write;
  173. }
  174. if (ret == -1 && errno == EAGAIN) {
  175. __archive_check_child(state->child_stdin, state->child_stdout);
  176. goto restart_write;
  177. }
  178. if (ret == -1)
  179. return (-1);
  180. state->child_buf_avail += ret;
  181. ret = (a->client_writer)(&a->archive, a->client_data,
  182. state->child_buf, state->child_buf_avail);
  183. if (ret <= 0)
  184. return (-1);
  185. if ((size_t)ret < state->child_buf_avail) {
  186. memmove(state->child_buf, state->child_buf + ret,
  187. state->child_buf_avail - ret);
  188. }
  189. state->child_buf_avail -= ret;
  190. a->archive.raw_position += ret;
  191. goto restart_write;
  192. }
  193. /*
  194. * Write data to the compressed stream.
  195. */
  196. static int
  197. archive_compressor_program_write(struct archive_write *a, const void *buff,
  198. size_t length)
  199. {
  200. struct private_data *state;
  201. ssize_t ret;
  202. const char *buf;
  203. state = (struct private_data *)a->compressor.data;
  204. if (a->client_writer == NULL) {
  205. archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
  206. "No write callback is registered? "
  207. "This is probably an internal programming error.");
  208. return (ARCHIVE_FATAL);
  209. }
  210. buf = buff;
  211. while (length > 0) {
  212. ret = child_write(a, buf, length);
  213. if (ret == -1 || ret == 0) {
  214. archive_set_error(&a->archive, EIO,
  215. "Can't write to filter");
  216. return (ARCHIVE_FATAL);
  217. }
  218. length -= ret;
  219. buf += ret;
  220. }
  221. a->archive.file_position += length;
  222. return (ARCHIVE_OK);
  223. }
  224. /*
  225. * Finish the compression...
  226. */
  227. static int
  228. archive_compressor_program_finish(struct archive_write *a)
  229. {
  230. int ret, status;
  231. ssize_t bytes_read, bytes_written;
  232. struct private_data *state;
  233. state = (struct private_data *)a->compressor.data;
  234. ret = 0;
  235. if (a->client_writer == NULL) {
  236. archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
  237. "No write callback is registered? "
  238. "This is probably an internal programming error.");
  239. ret = ARCHIVE_FATAL;
  240. goto cleanup;
  241. }
  242. /* XXX pad compressed data. */
  243. close(state->child_stdin);
  244. state->child_stdin = -1;
  245. fcntl(state->child_stdout, F_SETFL, 0);
  246. for (;;) {
  247. do {
  248. bytes_read = read(state->child_stdout,
  249. state->child_buf + state->child_buf_avail,
  250. state->child_buf_len - state->child_buf_avail);
  251. } while (bytes_read == -1 && errno == EINTR);
  252. if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
  253. break;
  254. if (bytes_read == -1) {
  255. archive_set_error(&a->archive, errno,
  256. "Read from filter failed unexpectedly.");
  257. ret = ARCHIVE_FATAL;
  258. goto cleanup;
  259. }
  260. state->child_buf_avail += bytes_read;
  261. bytes_written = (a->client_writer)(&a->archive, a->client_data,
  262. state->child_buf, state->child_buf_avail);
  263. if (bytes_written <= 0) {
  264. ret = ARCHIVE_FATAL;
  265. goto cleanup;
  266. }
  267. if ((size_t)bytes_written < state->child_buf_avail) {
  268. memmove(state->child_buf,
  269. state->child_buf + bytes_written,
  270. state->child_buf_avail - bytes_written);
  271. }
  272. state->child_buf_avail -= bytes_written;
  273. a->archive.raw_position += bytes_written;
  274. }
  275. /* XXX pad final compressed block. */
  276. cleanup:
  277. /* Shut down the child. */
  278. if (state->child_stdin != -1)
  279. close(state->child_stdin);
  280. if (state->child_stdout != -1)
  281. close(state->child_stdout);
  282. while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
  283. continue;
  284. if (status != 0) {
  285. archive_set_error(&a->archive, EIO,
  286. "Filter exited with failure.");
  287. ret = ARCHIVE_FATAL;
  288. }
  289. /* Release our configuration data. */
  290. free(a->compressor.config);
  291. a->compressor.config = NULL;
  292. /* Release our private state data. */
  293. free(state->child_buf);
  294. free(state->description);
  295. free(state);
  296. return (ret);
  297. }
  298. #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */