Import libarchive-2.5.4b.
[dragonfly.git] / contrib / libarchive-2 / libarchive / archive_write_set_compression_program.c
blobb8b20c86cf957d81684d4d26e558e1a8c112e01a
1 /*-
2 * Copyright (c) 2007 Joerg Sonnenberger
3 * All rights reserved.
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.
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.
26 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.1 2007/05/29 01:00:19 kientzle Exp $");
30 /* This capability is only available on POSIX systems. */
31 #if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL)
34 * On non-Posix systems, allow the program to build, but choke if
35 * this function is actually invoked.
37 int
38 archive_write_set_compression_program(struct archive *_a, const char *cmd)
40 archive_set_error(_a, -1,
41 "External compression programs not supported on this platform");
42 return (ARCHIVE_FATAL);
45 #else
47 #ifdef HAVE_SYS_WAIT_H
48 # include <sys/wait.h>
49 #endif
50 #ifdef HAVE_ERRNO_H
51 # include <errno.h>
52 #endif
53 #ifdef HAVE_FCNTL_H
54 # include <fcntl.h>
55 #endif
56 #ifdef HAVE_STDLIB_H
57 # include <stdlib.h>
58 #endif
59 #ifdef HAVE_STRING_H
60 # include <string.h>
61 #endif
63 #include "archive.h"
64 #include "archive_private.h"
65 #include "archive_write_private.h"
67 #include "filter_fork.h"
69 struct private_data {
70 char *description;
71 pid_t child;
72 int child_stdin, child_stdout;
74 char *child_buf;
75 size_t child_buf_len, child_buf_avail;
78 static int archive_compressor_program_finish(struct archive_write *);
79 static int archive_compressor_program_init(struct archive_write *);
80 static int archive_compressor_program_write(struct archive_write *,
81 const void *, size_t);
84 * Allocate, initialize and return a archive object.
86 int
87 archive_write_set_compression_program(struct archive *_a, const char *cmd)
89 struct archive_write *a = (struct archive_write *)_a;
90 __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
91 ARCHIVE_STATE_NEW, "archive_write_set_compression_program");
92 a->compressor.init = &archive_compressor_program_init;
93 a->compressor.config = strdup(cmd);
94 return (ARCHIVE_OK);
98 * Setup callback.
100 static int
101 archive_compressor_program_init(struct archive_write *a)
103 int ret;
104 struct private_data *state;
105 static const char *prefix = "Program: ";
106 char *cmd = a->compressor.config;
108 if (a->client_opener != NULL) {
109 ret = (a->client_opener)(&a->archive, a->client_data);
110 if (ret != ARCHIVE_OK)
111 return (ret);
114 state = (struct private_data *)malloc(sizeof(*state));
115 if (state == NULL) {
116 archive_set_error(&a->archive, ENOMEM,
117 "Can't allocate data for compression");
118 return (ARCHIVE_FATAL);
120 memset(state, 0, sizeof(*state));
122 a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
123 state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
124 strcpy(state->description, prefix);
125 strcat(state->description, cmd);
126 a->archive.compression_name = state->description;
128 state->child_buf_len = a->bytes_per_block;
129 state->child_buf_avail = 0;
130 state->child_buf = malloc(state->child_buf_len);
132 if (state->child_buf == NULL) {
133 archive_set_error(&a->archive, ENOMEM,
134 "Can't allocate data for compression buffer");
135 free(state);
136 return (ARCHIVE_FATAL);
139 if ((state->child = __archive_create_child(cmd,
140 &state->child_stdin, &state->child_stdout)) == -1) {
141 archive_set_error(&a->archive, EINVAL,
142 "Can't initialise filter");
143 free(state->child_buf);
144 free(state);
145 return (ARCHIVE_FATAL);
148 a->compressor.write = archive_compressor_program_write;
149 a->compressor.finish = archive_compressor_program_finish;
151 a->compressor.data = state;
152 return (0);
155 static ssize_t
156 child_write(struct archive_write *a, const char *buf, size_t buf_len)
158 struct private_data *state = a->compressor.data;
159 ssize_t ret;
161 if (state->child_stdin == -1)
162 return (-1);
164 if (buf_len == 0)
165 return (-1);
167 restart_write:
168 do {
169 ret = write(state->child_stdin, buf, buf_len);
170 } while (ret == -1 && errno == EINTR);
172 if (ret > 0)
173 return (ret);
174 if (ret == 0) {
175 close(state->child_stdin);
176 state->child_stdin = -1;
177 fcntl(state->child_stdout, F_SETFL, 0);
178 return (0);
180 if (ret == -1 && errno != EAGAIN)
181 return (-1);
183 do {
184 ret = read(state->child_stdout,
185 state->child_buf + state->child_buf_avail,
186 state->child_buf_len - state->child_buf_avail);
187 } while (ret == -1 && errno == EINTR);
189 if (ret == 0 || (ret == -1 && errno == EPIPE)) {
190 close(state->child_stdout);
191 state->child_stdout = -1;
192 fcntl(state->child_stdin, F_SETFL, 0);
193 goto restart_write;
195 if (ret == -1 && errno == EAGAIN) {
196 __archive_check_child(state->child_stdin, state->child_stdout);
197 goto restart_write;
199 if (ret == -1)
200 return (-1);
202 state->child_buf_avail += ret;
204 ret = (a->client_writer)(&a->archive, a->client_data,
205 state->child_buf, state->child_buf_avail);
206 if (ret <= 0)
207 return (-1);
209 if ((size_t)ret < state->child_buf_avail) {
210 memmove(state->child_buf, state->child_buf + ret,
211 state->child_buf_avail - ret);
213 state->child_buf_avail -= ret;
214 a->archive.raw_position += ret;
215 goto restart_write;
219 * Write data to the compressed stream.
221 static int
222 archive_compressor_program_write(struct archive_write *a, const void *buff,
223 size_t length)
225 struct private_data *state;
226 ssize_t ret;
227 const char *buf;
229 state = (struct private_data *)a->compressor.data;
230 if (a->client_writer == NULL) {
231 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
232 "No write callback is registered? "
233 "This is probably an internal programming error.");
234 return (ARCHIVE_FATAL);
237 buf = buff;
238 while (length > 0) {
239 ret = child_write(a, buf, length);
240 if (ret == -1 || ret == 0) {
241 archive_set_error(&a->archive, EIO,
242 "Can't write to filter");
243 return (ARCHIVE_FATAL);
245 length -= ret;
246 buf += ret;
249 a->archive.file_position += length;
250 return (ARCHIVE_OK);
255 * Finish the compression...
257 static int
258 archive_compressor_program_finish(struct archive_write *a)
260 int ret, status;
261 ssize_t bytes_read, bytes_written;
262 struct private_data *state;
264 state = (struct private_data *)a->compressor.data;
265 ret = 0;
266 if (a->client_writer == NULL) {
267 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
268 "No write callback is registered? "
269 "This is probably an internal programming error.");
270 ret = ARCHIVE_FATAL;
271 goto cleanup;
274 /* XXX pad compressed data. */
276 close(state->child_stdin);
277 state->child_stdin = -1;
278 fcntl(state->child_stdout, F_SETFL, 0);
280 for (;;) {
281 do {
282 bytes_read = read(state->child_stdout,
283 state->child_buf + state->child_buf_avail,
284 state->child_buf_len - state->child_buf_avail);
285 } while (bytes_read == -1 && errno == EINTR);
287 if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
288 break;
290 if (bytes_read == -1) {
291 archive_set_error(&a->archive, errno,
292 "Read from filter failed unexpectedly.");
293 ret = ARCHIVE_FATAL;
294 goto cleanup;
296 state->child_buf_avail += bytes_read;
298 bytes_written = (a->client_writer)(&a->archive, a->client_data,
299 state->child_buf, state->child_buf_avail);
300 if (bytes_written <= 0) {
301 ret = ARCHIVE_FATAL;
302 goto cleanup;
304 if ((size_t)bytes_written < state->child_buf_avail) {
305 memmove(state->child_buf,
306 state->child_buf + bytes_written,
307 state->child_buf_avail - bytes_written);
309 state->child_buf_avail -= bytes_written;
310 a->archive.raw_position += bytes_written;
313 /* XXX pad final compressed block. */
315 cleanup:
316 /* Shut down the child. */
317 if (state->child_stdin != -1)
318 close(state->child_stdin);
319 if (state->child_stdout != -1)
320 close(state->child_stdout);
321 while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
322 continue;
324 if (status != 0) {
325 archive_set_error(&a->archive, EIO,
326 "Filter exited with failure.");
327 ret = ARCHIVE_FATAL;
330 /* Release our configuration data. */
331 free(a->compressor.config);
332 a->compressor.config = NULL;
334 /* Release our private state data. */
335 free(state->child_buf);
336 free(state->description);
337 free(state);
338 return (ret);
341 #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */