2
* Copyright (c) 2007 Joerg Sonnenberger
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");
30
/* This capability is only available on POSIX systems. */
31
#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
32
!(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__))
36
* On non-Posix systems, allow the program to build, but choke if
37
* this function is actually invoked.
40
archive_write_set_compression_program(struct archive *_a, const char *cmd)
42
archive_set_error(_a, -1,
43
"External compression programs not supported on this platform");
44
return (ARCHIVE_FATAL);
49
#ifdef HAVE_SYS_WAIT_H
50
# include <sys/wait.h>
66
#include "archive_private.h"
67
#include "archive_write_private.h"
69
#include "filter_fork.h"
74
int child_stdin, child_stdout;
77
size_t child_buf_len, child_buf_avail;
80
static int archive_compressor_program_finish(struct archive_write *);
81
static int archive_compressor_program_init(struct archive_write *);
82
static int archive_compressor_program_write(struct archive_write *,
83
const void *, size_t);
86
* Allocate, initialize and return a archive object.
89
archive_write_set_compression_program(struct archive *_a, const char *cmd)
91
struct archive_write *a = (struct archive_write *)_a;
92
__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
93
ARCHIVE_STATE_NEW, "archive_write_set_compression_program");
94
a->compressor.init = &archive_compressor_program_init;
95
a->compressor.config = strdup(cmd);
103
archive_compressor_program_init(struct archive_write *a)
106
struct private_data *state;
107
static const char *prefix = "Program: ";
108
char *cmd = a->compressor.config;
110
if (a->client_opener != NULL) {
111
ret = (a->client_opener)(&a->archive, a->client_data);
112
if (ret != ARCHIVE_OK)
116
state = (struct private_data *)malloc(sizeof(*state));
118
archive_set_error(&a->archive, ENOMEM,
119
"Can't allocate data for compression");
120
return (ARCHIVE_FATAL);
122
memset(state, 0, sizeof(*state));
124
a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
125
state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
126
strcpy(state->description, prefix);
127
strcat(state->description, cmd);
128
a->archive.compression_name = state->description;
130
state->child_buf_len = a->bytes_per_block;
131
state->child_buf_avail = 0;
132
state->child_buf = malloc(state->child_buf_len);
134
if (state->child_buf == NULL) {
135
archive_set_error(&a->archive, ENOMEM,
136
"Can't allocate data for compression buffer");
138
return (ARCHIVE_FATAL);
141
if ((state->child = __archive_create_child(cmd,
142
&state->child_stdin, &state->child_stdout)) == -1) {
143
archive_set_error(&a->archive, EINVAL,
144
"Can't initialise filter");
145
free(state->child_buf);
147
return (ARCHIVE_FATAL);
150
a->compressor.write = archive_compressor_program_write;
151
a->compressor.finish = archive_compressor_program_finish;
153
a->compressor.data = state;
158
child_write(struct archive_write *a, const char *buf, size_t buf_len)
160
struct private_data *state = a->compressor.data;
163
if (state->child_stdin == -1)
171
ret = write(state->child_stdin, buf, buf_len);
172
} while (ret == -1 && errno == EINTR);
177
close(state->child_stdin);
178
state->child_stdin = -1;
179
fcntl(state->child_stdout, F_SETFL, 0);
182
if (ret == -1 && errno != EAGAIN)
185
if (state->child_stdout == -1) {
186
fcntl(state->child_stdin, F_SETFL, 0);
187
__archive_check_child(state->child_stdin, state->child_stdout);
192
ret = read(state->child_stdout,
193
state->child_buf + state->child_buf_avail,
194
state->child_buf_len - state->child_buf_avail);
195
} while (ret == -1 && errno == EINTR);
197
if (ret == 0 || (ret == -1 && errno == EPIPE)) {
198
close(state->child_stdout);
199
state->child_stdout = -1;
200
fcntl(state->child_stdin, F_SETFL, 0);
203
if (ret == -1 && errno == EAGAIN) {
204
__archive_check_child(state->child_stdin, state->child_stdout);
210
state->child_buf_avail += ret;
212
ret = (a->client_writer)(&a->archive, a->client_data,
213
state->child_buf, state->child_buf_avail);
217
if ((size_t)ret < state->child_buf_avail) {
218
memmove(state->child_buf, state->child_buf + ret,
219
state->child_buf_avail - ret);
221
state->child_buf_avail -= ret;
222
a->archive.raw_position += ret;
227
* Write data to the compressed stream.
230
archive_compressor_program_write(struct archive_write *a, const void *buff,
236
if (a->client_writer == NULL) {
237
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
238
"No write callback is registered? "
239
"This is probably an internal programming error.");
240
return (ARCHIVE_FATAL);
245
ret = child_write(a, buf, length);
246
if (ret == -1 || ret == 0) {
247
archive_set_error(&a->archive, EIO,
248
"Can't write to filter");
249
return (ARCHIVE_FATAL);
255
a->archive.file_position += length;
261
* Finish the compression...
264
archive_compressor_program_finish(struct archive_write *a)
267
ssize_t bytes_read, bytes_written;
268
struct private_data *state;
270
state = (struct private_data *)a->compressor.data;
272
if (a->client_writer == NULL) {
273
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
274
"No write callback is registered? "
275
"This is probably an internal programming error.");
280
/* XXX pad compressed data. */
282
close(state->child_stdin);
283
state->child_stdin = -1;
284
fcntl(state->child_stdout, F_SETFL, 0);
288
bytes_read = read(state->child_stdout,
289
state->child_buf + state->child_buf_avail,
290
state->child_buf_len - state->child_buf_avail);
291
} while (bytes_read == -1 && errno == EINTR);
293
if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
296
if (bytes_read == -1) {
297
archive_set_error(&a->archive, errno,
298
"Read from filter failed unexpectedly.");
302
state->child_buf_avail += bytes_read;
304
bytes_written = (a->client_writer)(&a->archive, a->client_data,
305
state->child_buf, state->child_buf_avail);
306
if (bytes_written <= 0) {
310
if ((size_t)bytes_written < state->child_buf_avail) {
311
memmove(state->child_buf,
312
state->child_buf + bytes_written,
313
state->child_buf_avail - bytes_written);
315
state->child_buf_avail -= bytes_written;
316
a->archive.raw_position += bytes_written;
319
/* XXX pad final compressed block. */
322
/* Shut down the child. */
323
if (state->child_stdin != -1)
324
close(state->child_stdin);
325
if (state->child_stdout != -1)
326
close(state->child_stdout);
327
while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
331
archive_set_error(&a->archive, EIO,
332
"Filter exited with failure.");
336
/* Release our configuration data. */
337
free(a->compressor.config);
338
a->compressor.config = NULL;
340
/* Release our private state data. */
341
free(state->child_buf);
342
free(state->description);
347
#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */