Merge branch 'vendor/OPENSSL'
[dragonfly.git] / contrib / libarchive / libarchive / archive_write_set_compression_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
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.3 2008/06/15 10:45:57 kientzle Exp $");
29
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__))
33 #include "archive.h"
34
35 /*
36  * On non-Posix systems, allow the program to build, but choke if
37  * this function is actually invoked.
38  */
39 int
40 archive_write_set_compression_program(struct archive *_a, const char *cmd)
41 {
42         archive_set_error(_a, -1,
43             "External compression programs not supported on this platform");
44         return (ARCHIVE_FATAL);
45 }
46
47 #else
48
49 #ifdef HAVE_SYS_WAIT_H
50 #  include <sys/wait.h>
51 #endif
52 #ifdef HAVE_ERRNO_H
53 #  include <errno.h>
54 #endif
55 #ifdef HAVE_FCNTL_H
56 #  include <fcntl.h>
57 #endif
58 #ifdef HAVE_STDLIB_H
59 #  include <stdlib.h>
60 #endif
61 #ifdef HAVE_STRING_H
62 #  include <string.h>
63 #endif
64
65 #include "archive.h"
66 #include "archive_private.h"
67 #include "archive_write_private.h"
68
69 #include "filter_fork.h"
70
71 struct private_data {
72         char            *description;
73         pid_t            child;
74         int              child_stdin, child_stdout;
75
76         char            *child_buf;
77         size_t           child_buf_len, child_buf_avail;
78 };
79
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);
84
85 /*
86  * Allocate, initialize and return a archive object.
87  */
88 int
89 archive_write_set_compression_program(struct archive *_a, const char *cmd)
90 {
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);
96         return (ARCHIVE_OK);
97 }
98
99 /*
100  * Setup callback.
101  */
102 static int
103 archive_compressor_program_init(struct archive_write *a)
104 {
105         int ret;
106         struct private_data *state;
107         static const char *prefix = "Program: ";
108         char *cmd = a->compressor.config;
109
110         if (a->client_opener != NULL) {
111                 ret = (a->client_opener)(&a->archive, a->client_data);
112                 if (ret != ARCHIVE_OK)
113                         return (ret);
114         }
115
116         state = (struct private_data *)malloc(sizeof(*state));
117         if (state == NULL) {
118                 archive_set_error(&a->archive, ENOMEM,
119                     "Can't allocate data for compression");
120                 return (ARCHIVE_FATAL);
121         }
122         memset(state, 0, sizeof(*state));
123
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;
129
130         state->child_buf_len = a->bytes_per_block;
131         state->child_buf_avail = 0;
132         state->child_buf = malloc(state->child_buf_len);
133
134         if (state->child_buf == NULL) {
135                 archive_set_error(&a->archive, ENOMEM,
136                     "Can't allocate data for compression buffer");
137                 free(state);
138                 return (ARCHIVE_FATAL);
139         }
140
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);
146                 free(state);
147                 return (ARCHIVE_FATAL);
148         }
149
150         a->compressor.write = archive_compressor_program_write;
151         a->compressor.finish = archive_compressor_program_finish;
152
153         a->compressor.data = state;
154         return (0);
155 }
156
157 static ssize_t
158 child_write(struct archive_write *a, const char *buf, size_t buf_len)
159 {
160         struct private_data *state = a->compressor.data;
161         ssize_t ret;
162
163         if (state->child_stdin == -1)
164                 return (-1);
165
166         if (buf_len == 0)
167                 return (-1);
168
169 restart_write:
170         do {
171                 ret = write(state->child_stdin, buf, buf_len);
172         } while (ret == -1 && errno == EINTR);
173
174         if (ret > 0)
175                 return (ret);
176         if (ret == 0) {
177                 close(state->child_stdin);
178                 state->child_stdin = -1;
179                 fcntl(state->child_stdout, F_SETFL, 0);
180                 return (0);
181         }
182         if (ret == -1 && errno != EAGAIN)
183                 return (-1);
184
185         if (state->child_stdout == -1) {
186                 fcntl(state->child_stdin, F_SETFL, 0);
187                 __archive_check_child(state->child_stdin, state->child_stdout);
188                 goto restart_write;
189         }
190
191         do {
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);
196
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);
201                 goto restart_write;
202         }
203         if (ret == -1 && errno == EAGAIN) {
204                 __archive_check_child(state->child_stdin, state->child_stdout);
205                 goto restart_write;
206         }
207         if (ret == -1)
208                 return (-1);
209
210         state->child_buf_avail += ret;
211
212         ret = (a->client_writer)(&a->archive, a->client_data,
213             state->child_buf, state->child_buf_avail);
214         if (ret <= 0)
215                 return (-1);
216
217         if ((size_t)ret < state->child_buf_avail) {
218                 memmove(state->child_buf, state->child_buf + ret,
219                     state->child_buf_avail - ret);
220         }
221         state->child_buf_avail -= ret;
222         a->archive.raw_position += ret;
223         goto restart_write;
224 }
225
226 /*
227  * Write data to the compressed stream.
228  */
229 static int
230 archive_compressor_program_write(struct archive_write *a, const void *buff,
231     size_t length)
232 {
233         struct private_data *state;
234         ssize_t ret;
235         const char *buf;
236
237         state = (struct private_data *)a->compressor.data;
238         if (a->client_writer == NULL) {
239                 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
240                     "No write callback is registered?  "
241                     "This is probably an internal programming error.");
242                 return (ARCHIVE_FATAL);
243         }
244
245         buf = buff;
246         while (length > 0) {
247                 ret = child_write(a, buf, length);
248                 if (ret == -1 || ret == 0) {
249                         archive_set_error(&a->archive, EIO,
250                             "Can't write to filter");
251                         return (ARCHIVE_FATAL);
252                 }
253                 length -= ret;
254                 buf += ret;
255         }
256
257         a->archive.file_position += length;
258         return (ARCHIVE_OK);
259 }
260
261
262 /*
263  * Finish the compression...
264  */
265 static int
266 archive_compressor_program_finish(struct archive_write *a)
267 {
268         int ret, status;
269         ssize_t bytes_read, bytes_written;
270         struct private_data *state;
271
272         state = (struct private_data *)a->compressor.data;
273         ret = 0;
274         if (a->client_writer == NULL) {
275                 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
276                     "No write callback is registered?  "
277                     "This is probably an internal programming error.");
278                 ret = ARCHIVE_FATAL;
279                 goto cleanup;
280         }
281
282         /* XXX pad compressed data. */
283
284         close(state->child_stdin);
285         state->child_stdin = -1;
286         fcntl(state->child_stdout, F_SETFL, 0);
287
288         for (;;) {
289                 do {
290                         bytes_read = read(state->child_stdout,
291                             state->child_buf + state->child_buf_avail,
292                             state->child_buf_len - state->child_buf_avail);
293                 } while (bytes_read == -1 && errno == EINTR);
294
295                 if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
296                         break;
297
298                 if (bytes_read == -1) {
299                         archive_set_error(&a->archive, errno,
300                             "Read from filter failed unexpectedly.");
301                         ret = ARCHIVE_FATAL;
302                         goto cleanup;
303                 }
304                 state->child_buf_avail += bytes_read;
305
306                 bytes_written = (a->client_writer)(&a->archive, a->client_data,
307                     state->child_buf, state->child_buf_avail);
308                 if (bytes_written <= 0) {
309                         ret = ARCHIVE_FATAL;
310                         goto cleanup;
311                 }
312                 if ((size_t)bytes_written < state->child_buf_avail) {
313                         memmove(state->child_buf,
314                             state->child_buf + bytes_written,
315                             state->child_buf_avail - bytes_written);
316                 }
317                 state->child_buf_avail -= bytes_written;
318                 a->archive.raw_position += bytes_written;
319         }
320
321         /* XXX pad final compressed block. */
322
323 cleanup:
324         /* Shut down the child. */
325         if (state->child_stdin != -1)
326                 close(state->child_stdin);
327         if (state->child_stdout != -1)
328                 close(state->child_stdout);
329         while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
330                 continue;
331
332         if (status != 0) {
333                 archive_set_error(&a->archive, EIO,
334                     "Filter exited with failure.");
335                 ret = ARCHIVE_FATAL;
336         }
337
338         /* Release our configuration data. */
339         free(a->compressor.config);
340         a->compressor.config = NULL;
341
342         /* Release our private state data. */
343         free(state->child_buf);
344         free(state->description);
345         free(state);
346         return (ret);
347 }
348
349 #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */