Import libarchive-3.0.4.
[dragonfly.git] / contrib / libarchive / libarchive / archive_write_add_filter_program.c
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
26 #include "archive_platform.h"
27 __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");
28
29 #ifdef HAVE_SYS_WAIT_H
30 #  include <sys/wait.h>
31 #endif
32 #ifdef HAVE_ERRNO_H
33 #  include <errno.h>
34 #endif
35 #ifdef HAVE_FCNTL_H
36 #  include <fcntl.h>
37 #endif
38 #ifdef HAVE_STDLIB_H
39 #  include <stdlib.h>
40 #endif
41 #ifdef HAVE_STRING_H
42 #  include <string.h>
43 #endif
44
45 #include "archive.h"
46 #include "archive_private.h"
47 #include "archive_write_private.h"
48
49 #if ARCHIVE_VERSION_NUMBER < 4000000
50 int
51 archive_write_set_compression_program(struct archive *a, const char *cmd)
52 {
53         __archive_write_filters_free(a);
54         return (archive_write_add_filter_program(a, cmd));
55 }
56 #endif
57
58 /* This capability is only available on POSIX systems. */
59 #if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \
60     !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__))
61
62 /*
63  * On non-Posix systems, allow the program to build, but choke if
64  * this function is actually invoked.
65  */
66 int
67 archive_write_add_filter_program(struct archive *_a, const char *cmd)
68 {
69         archive_set_error(_a, -1,
70             "External compression programs not supported on this platform");
71         return (ARCHIVE_FATAL);
72 }
73
74 #else
75
76 #include "filter_fork.h"
77
78 struct private_data {
79         char            *cmd;
80         char            *description;
81         pid_t            child;
82         int              child_stdin, child_stdout;
83
84         char            *child_buf;
85         size_t           child_buf_len, child_buf_avail;
86 };
87
88 static int archive_compressor_program_open(struct archive_write_filter *);
89 static int archive_compressor_program_write(struct archive_write_filter *,
90                     const void *, size_t);
91 static int archive_compressor_program_close(struct archive_write_filter *);
92 static int archive_compressor_program_free(struct archive_write_filter *);
93
94 /*
95  * Add a filter to this write handle that passes all data through an
96  * external program.
97  */
98 int
99 archive_write_add_filter_program(struct archive *_a, const char *cmd)
100 {
101         struct archive_write_filter *f = __archive_write_allocate_filter(_a);
102         struct archive_write *a = (struct archive_write *)_a;
103         struct private_data *data;
104         static const char *prefix = "Program: ";
105         archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
106             ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
107         data = calloc(1, sizeof(*data));
108         if (data == NULL) {
109                 archive_set_error(&a->archive, ENOMEM, "Out of memory");
110                 return (ARCHIVE_FATAL);
111         }
112         data->cmd = strdup(cmd);
113         data->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
114         strcpy(data->description, prefix);
115         strcat(data->description, cmd);
116
117         f->name = data->description;
118         f->data = data;
119         f->open = &archive_compressor_program_open;
120         f->code = ARCHIVE_COMPRESSION_PROGRAM;
121         return (ARCHIVE_OK);
122 }
123
124 /*
125  * Setup callback.
126  */
127 static int
128 archive_compressor_program_open(struct archive_write_filter *f)
129 {
130         struct private_data *data = (struct private_data *)f->data;
131         int ret;
132
133         ret = __archive_write_open_filter(f->next_filter);
134         if (ret != ARCHIVE_OK)
135                 return (ret);
136
137         if (data->child_buf == NULL) {
138                 data->child_buf_len = 65536;
139                 data->child_buf_avail = 0;
140                 data->child_buf = malloc(data->child_buf_len);
141
142                 if (data->child_buf == NULL) {
143                         archive_set_error(f->archive, ENOMEM,
144                             "Can't allocate compression buffer");
145                         return (ARCHIVE_FATAL);
146                 }
147         }
148
149         if ((data->child = __archive_create_child(data->cmd,
150                  &data->child_stdin, &data->child_stdout)) == -1) {
151                 archive_set_error(f->archive, EINVAL,
152                     "Can't initialise filter");
153                 return (ARCHIVE_FATAL);
154         }
155
156         f->write = archive_compressor_program_write;
157         f->close = archive_compressor_program_close;
158         f->free = archive_compressor_program_free;
159         return (0);
160 }
161
162 static ssize_t
163 child_write(struct archive_write_filter *f, const char *buf, size_t buf_len)
164 {
165         struct private_data *data = f->data;
166         ssize_t ret;
167
168         if (data->child_stdin == -1)
169                 return (-1);
170
171         if (buf_len == 0)
172                 return (-1);
173
174         for (;;) {
175                 do {
176                         ret = write(data->child_stdin, buf, buf_len);
177                 } while (ret == -1 && errno == EINTR);
178
179                 if (ret > 0)
180                         return (ret);
181                 if (ret == 0) {
182                         close(data->child_stdin);
183                         data->child_stdin = -1;
184                         fcntl(data->child_stdout, F_SETFL, 0);
185                         return (0);
186                 }
187                 if (ret == -1 && errno != EAGAIN)
188                         return (-1);
189
190                 if (data->child_stdout == -1) {
191                         fcntl(data->child_stdin, F_SETFL, 0);
192                         __archive_check_child(data->child_stdin,
193                                 data->child_stdout);
194                         continue;
195                 }
196
197                 do {
198                         ret = read(data->child_stdout,
199                             data->child_buf + data->child_buf_avail,
200                             data->child_buf_len - data->child_buf_avail);
201                 } while (ret == -1 && errno == EINTR);
202
203                 if (ret == 0 || (ret == -1 && errno == EPIPE)) {
204                         close(data->child_stdout);
205                         data->child_stdout = -1;
206                         fcntl(data->child_stdin, F_SETFL, 0);
207                         continue;
208                 }
209                 if (ret == -1 && errno == EAGAIN) {
210                         __archive_check_child(data->child_stdin,
211                                 data->child_stdout);
212                         continue;
213                 }
214                 if (ret == -1)
215                         return (-1);
216
217                 data->child_buf_avail += ret;
218
219                 ret = __archive_write_filter(f->next_filter,
220                     data->child_buf, data->child_buf_avail);
221                 if (ret <= 0)
222                         return (-1);
223
224                 if ((size_t)ret < data->child_buf_avail) {
225                         memmove(data->child_buf, data->child_buf + ret,
226                             data->child_buf_avail - ret);
227                 }
228                 data->child_buf_avail -= ret;
229         }
230 }
231
232 /*
233  * Write data to the compressed stream.
234  */
235 static int
236 archive_compressor_program_write(struct archive_write_filter *f,
237     const void *buff, size_t length)
238 {
239         ssize_t ret;
240         const char *buf;
241
242         buf = buff;
243         while (length > 0) {
244                 ret = child_write(f, buf, length);
245                 if (ret == -1 || ret == 0) {
246                         archive_set_error(f->archive, EIO,
247                             "Can't write to filter");
248                         return (ARCHIVE_FATAL);
249                 }
250                 length -= ret;
251                 buf += ret;
252         }
253         return (ARCHIVE_OK);
254 }
255
256
257 /*
258  * Finish the compression...
259  */
260 static int
261 archive_compressor_program_close(struct archive_write_filter *f)
262 {
263         struct private_data *data = (struct private_data *)f->data;
264         int ret, r1, status;
265         ssize_t bytes_read;
266
267         ret = 0;
268         close(data->child_stdin);
269         data->child_stdin = -1;
270         fcntl(data->child_stdout, F_SETFL, 0);
271
272         for (;;) {
273                 do {
274                         bytes_read = read(data->child_stdout,
275                             data->child_buf + data->child_buf_avail,
276                             data->child_buf_len - data->child_buf_avail);
277                 } while (bytes_read == -1 && errno == EINTR);
278
279                 if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
280                         break;
281
282                 if (bytes_read == -1) {
283                         archive_set_error(f->archive, errno,
284                             "Read from filter failed unexpectedly.");
285                         ret = ARCHIVE_FATAL;
286                         goto cleanup;
287                 }
288                 data->child_buf_avail += bytes_read;
289
290                 ret = __archive_write_filter(f->next_filter,
291                     data->child_buf, data->child_buf_avail);
292                 if (ret != ARCHIVE_OK) {
293                         ret = ARCHIVE_FATAL;
294                         goto cleanup;
295                 }
296                 data->child_buf_avail = 0;
297         }
298
299 cleanup:
300         /* Shut down the child. */
301         if (data->child_stdin != -1)
302                 close(data->child_stdin);
303         if (data->child_stdout != -1)
304                 close(data->child_stdout);
305         while (waitpid(data->child, &status, 0) == -1 && errno == EINTR)
306                 continue;
307
308         if (status != 0) {
309                 archive_set_error(f->archive, EIO,
310                     "Filter exited with failure.");
311                 ret = ARCHIVE_FATAL;
312         }
313         r1 = __archive_write_close_filter(f->next_filter);
314         return (r1 < ret ? r1 : ret);
315 }
316
317 static int
318 archive_compressor_program_free(struct archive_write_filter *f)
319 {
320         struct private_data *data = (struct private_data *)f->data;
321         free(data->cmd);
322         free(data->description);
323         free(data->child_buf);
324         free(data);
325         f->data = NULL;
326         return (ARCHIVE_OK);
327 }
328
329 #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */