]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libarchive/archive_read_support_compression_program.c
Record that base/vendor/file/dist@186675 was merged.
[FreeBSD/FreeBSD.git] / lib / libarchive / archive_read_support_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 __FBSDID("$FreeBSD$");
28
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))
33
34 #include "archive.h"
35
36 /*
37  * On non-Posix systems, allow the program to build, but choke if
38  * this function is actually invoked.
39  */
40 int
41 archive_read_support_compression_program(struct archive *_a, const char *cmd)
42 {
43         archive_set_error(_a, -1,
44             "External compression programs not supported on this platform");
45         return (ARCHIVE_FATAL);
46 }
47
48 #else
49
50 #ifdef HAVE_SYS_WAIT_H
51 #  include <sys/wait.h>
52 #endif
53 #ifdef HAVE_ERRNO_H
54 #  include <errno.h>
55 #endif
56 #ifdef HAVE_FCNTL_H
57 #  include <fcntl.h>
58 #endif
59 #ifdef HAVE_LIMITS_H
60 #  include <limits.h>
61 #endif
62 #ifdef HAVE_STDLIB_H
63 #  include <stdlib.h>
64 #endif
65 #ifdef HAVE_STRING_H
66 #  include <string.h>
67 #endif
68 #ifdef HAVE_UNISTD_H
69 #  include <unistd.h>
70 #endif
71
72 #include "archive.h"
73 #include "archive_private.h"
74 #include "archive_read_private.h"
75
76 #include "filter_fork.h"
77
78 struct program_reader {
79         char *cmd;
80         int bid;
81 };
82
83 struct program_source {
84         char            *description;
85         pid_t            child;
86         int              child_stdin, child_stdout;
87
88         char            *out_buf;
89         size_t           out_buf_len;
90
91         const char      *child_in_buf;
92         size_t           child_in_buf_avail;
93 };
94
95 static int      program_reader_bid(struct archive_reader *,
96                     const void *, size_t);
97 static struct archive_read_source *program_reader_init(struct archive_read *,
98     struct archive_reader *, struct archive_read_source *,
99     const void *, size_t);
100 static int      program_reader_free(struct archive_reader *);
101
102 static ssize_t  program_source_read(struct archive_read_source *,
103     const void **);
104 static int      program_source_close(struct archive_read_source *);
105
106
107 int
108 archive_read_support_compression_program(struct archive *_a, const char *cmd)
109 {
110         struct archive_read *a = (struct archive_read *)_a;
111         struct archive_reader *reader = __archive_read_get_reader(a);
112         struct program_reader *state;
113
114         state = (struct program_reader *)calloc(sizeof (*state), 1);
115
116         if (state == NULL)
117                 return (ARCHIVE_FATAL);
118         if (reader == NULL)
119                 return (ARCHIVE_FATAL);
120
121         state->cmd = strdup(cmd);
122         state->bid = INT_MAX;
123
124         reader->data = state;
125         reader->bid = program_reader_bid;
126         reader->init = program_reader_init;
127         reader->free = program_reader_free;
128         return (ARCHIVE_OK);
129 }
130
131 static int
132 program_reader_free(struct archive_reader *self)
133 {
134         free(self->data);
135         return (ARCHIVE_OK);
136 }
137
138 /*
139  * If the user used us to register, they must really want us to
140  * handle it, so we always bid INT_MAX the first time we're called.
141  * After that, we always return zero, lest we end up instantiating
142  * an infinite pipeline.
143  */
144 static int
145 program_reader_bid(struct archive_reader *self, const void *buff, size_t len)
146 {
147         struct program_reader *state = self->data;
148         int bid = state->bid;
149
150         (void)buff; /* UNUSED */
151         (void)len; /* UNUSED */
152
153         state->bid = 0; /* Don't bid again on this pipeline. */
154
155         return (bid); /* Default: We'll take it if we haven't yet bid. */
156 }
157
158 /*
159  * Use select() to decide whether the child is ready for read or write.
160  */
161
162 static ssize_t
163 child_read(struct archive_read_source *self, char *buf, size_t buf_len)
164 {
165         struct program_source *state = self->data;
166         ssize_t ret, requested;
167         const void *child_buf;
168
169         if (state->child_stdout == -1)
170                 return (-1);
171
172         if (buf_len == 0)
173                 return (-1);
174
175 restart_read:
176         requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
177
178         do {
179                 ret = read(state->child_stdout, buf, requested);
180         } while (ret == -1 && errno == EINTR);
181
182         if (ret > 0)
183                 return (ret);
184         if (ret == 0 || (ret == -1 && errno == EPIPE)) {
185                 close(state->child_stdout);
186                 state->child_stdout = -1;
187                 return (0);
188         }
189         if (ret == -1 && errno != EAGAIN)
190                 return (-1);
191
192         if (state->child_in_buf_avail == 0) {
193                 child_buf = state->child_in_buf;
194                 ret = (self->upstream->read)(self->upstream, &child_buf);
195                 state->child_in_buf = (const char *)child_buf;
196
197                 if (ret < 0) {
198                         close(state->child_stdin);
199                         state->child_stdin = -1;
200                         fcntl(state->child_stdout, F_SETFL, 0);
201                         return (-1);
202                 }
203                 if (ret == 0) {
204                         close(state->child_stdin);
205                         state->child_stdin = -1;
206                         fcntl(state->child_stdout, F_SETFL, 0);
207                         goto restart_read;
208                 }
209                 state->child_in_buf_avail = ret;
210         }
211
212         if (state->child_stdin == -1) {
213                 fcntl(state->child_stdout, F_SETFL, 0);
214                 __archive_check_child(state->child_stdin, state->child_stdout);
215                 goto restart_read;
216         }
217
218         do {
219                 ret = write(state->child_stdin, state->child_in_buf,
220                     state->child_in_buf_avail);
221         } while (ret == -1 && errno == EINTR);
222
223         if (ret > 0) {
224                 state->child_in_buf += ret;
225                 state->child_in_buf_avail -= ret;
226                 goto restart_read;
227         } else if (ret == -1 && errno == EAGAIN) {
228                 __archive_check_child(state->child_stdin, state->child_stdout);
229                 goto restart_read;
230         } else if (ret == 0 || (ret == -1 && errno == EPIPE)) {
231                 close(state->child_stdin);
232                 state->child_stdin = -1;
233                 fcntl(state->child_stdout, F_SETFL, 0);
234                 goto restart_read;
235         } else {
236                 close(state->child_stdin);
237                 state->child_stdin = -1;
238                 fcntl(state->child_stdout, F_SETFL, 0);
239                 return (-1);
240         }
241 }
242
243 static struct archive_read_source *
244 program_reader_init(struct archive_read *a, struct archive_reader *reader,
245     struct archive_read_source *upstream, const void *buff, size_t n)
246 {
247         struct program_source   *state;
248         struct program_reader   *reader_state;
249         struct archive_read_source *self;
250         static const size_t out_buf_len = 65536;
251         char *out_buf;
252         char *description;
253         const char *prefix = "Program: ";
254
255
256         reader_state = (struct program_reader *)reader->data;
257
258         self = (struct archive_read_source *)malloc(sizeof(*self));
259         state = (struct program_source *)malloc(sizeof(*state));
260         out_buf = (char *)malloc(out_buf_len);
261         description = (char *)malloc(strlen(prefix) + strlen(reader_state->cmd) + 1);
262         if (self == NULL
263             || state == NULL
264             || out_buf == NULL
265             || description == NULL)
266         {
267                 archive_set_error(&a->archive, ENOMEM,
268                     "Can't allocate input data");
269                 free(self);
270                 free(state);
271                 free(out_buf);
272                 free(description);
273                 return (NULL);
274         }
275
276         a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
277         state->description = description;
278         strcpy(state->description, prefix);
279         strcat(state->description, reader_state->cmd);
280         a->archive.compression_name = state->description;
281
282         state->out_buf = out_buf;
283         state->out_buf_len = out_buf_len;
284
285         state->child_in_buf = buff;
286         state->child_in_buf_avail = n;
287
288         if ((state->child = __archive_create_child(reader_state->cmd,
289                  &state->child_stdin, &state->child_stdout)) == -1) {
290                 free(state->out_buf);
291                 free(state);
292                 archive_set_error(&a->archive, EINVAL,
293                     "Can't initialise filter");
294                 return (NULL);
295         }
296
297         self->data = state;
298         self->read = program_source_read;
299         self->skip = NULL;
300         self->close = program_source_close;
301         self->upstream = upstream;
302         self->archive = a;
303
304         /* XXX Check that we can read at least one byte? */
305         return (self);
306 }
307
308 static ssize_t
309 program_source_read(struct archive_read_source *self, const void **buff)
310 {
311         struct program_source *state;
312         ssize_t bytes, total;
313         char *p;
314
315         state = (struct program_source *)self->data;
316
317         total = 0;
318         p = state->out_buf;
319         while (state->child_stdout != -1) {
320                 bytes = child_read(self, p, state->out_buf_len - total);
321                 if (bytes < 0)
322                         return (bytes);
323                 if (bytes == 0)
324                         break;
325                 total += bytes;
326 /* TODO: fix this */ /* a->archive.raw_position += bytes_read; */
327         }
328
329         *buff = state->out_buf;
330         return (total);
331 }
332
333 static int
334 program_source_close(struct archive_read_source *self)
335 {
336         struct program_source   *state;
337         int status;
338
339         state = (struct program_source *)self->data;
340
341         /* Shut down the child. */
342         if (state->child_stdin != -1)
343                 close(state->child_stdin);
344         if (state->child_stdout != -1)
345                 close(state->child_stdout);
346         while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
347                 continue;
348
349         /* Release our private data. */
350         free(state->out_buf);
351         free(state->description);
352         free(state);
353         free(self);
354
355         return (ARCHIVE_OK);
356 }
357
358 #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */