libarchive: add capability to unpack to mem.buffer
[oweals/busybox.git] / archival / libarchive / open_transformer.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4  */
5
6 #include "libbb.h"
7 #include "bb_archive.h"
8
9 void FAST_FUNC init_transformer_state(transformer_state_t *xstate)
10 {
11         memset(xstate, 0, sizeof(*xstate));
12 }
13
14 int FAST_FUNC check_signature16(transformer_state_t *xstate, unsigned magic16)
15 {
16         if (xstate->check_signature) {
17                 uint16_t magic2;
18                 if (full_read(xstate->src_fd, &magic2, 2) != 2 || magic2 != magic16) {
19                         bb_error_msg("invalid magic");
20 #if 0 /* possible future extension */
21                         if (xstate->check_signature > 1)
22                                 xfunc_die();
23 #endif
24                         return -1;
25                 }
26         }
27         return 0;
28 }
29
30 ssize_t FAST_FUNC transformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
31 {
32         ssize_t nwrote;
33
34         if (xstate->mem_output_size_max != 0) {
35                 size_t pos = xstate->mem_output_size;
36                 size_t size;
37
38                 size = (xstate->mem_output_size += bufsize);
39                 if (size > xstate->mem_output_size_max) {
40                         free(xstate->mem_output_buf);
41                         xstate->mem_output_buf = NULL;
42                         bb_perror_msg("buffer %u too small", (unsigned)xstate->mem_output_size_max);
43                         nwrote = -1;
44                         goto ret;
45                 }
46                 xstate->mem_output_buf = xrealloc(xstate->mem_output_buf, size);
47                 memcpy(xstate->mem_output_buf + pos, buf, bufsize);
48                 nwrote = bufsize;
49         } else {
50                 nwrote = full_write(xstate->dst_fd, buf, bufsize);
51                 if (nwrote != (ssize_t)bufsize) {
52                         bb_perror_msg("write");
53                         nwrote = -1;
54                         goto ret;
55                 }
56         }
57  ret:
58         return nwrote;
59 }
60
61 ssize_t FAST_FUNC xtransformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
62 {
63         ssize_t nwrote = transformer_write(xstate, buf, bufsize);
64         if (nwrote != (ssize_t)bufsize) {
65                 xfunc_die();
66         }
67         return nwrote;
68 }
69
70 void check_errors_in_children(int signo)
71 {
72         int status;
73
74         if (!signo) {
75                 /* block waiting for any child */
76                 if (wait(&status) < 0)
77 //FIXME: check EINTR?
78                         return; /* probably there are no children */
79                 goto check_status;
80         }
81
82         /* Wait for any child without blocking */
83         for (;;) {
84                 if (wait_any_nohang(&status) < 0)
85 //FIXME: check EINTR?
86                         /* wait failed?! I'm confused... */
87                         return;
88  check_status:
89                 /*if (WIFEXITED(status) && WEXITSTATUS(status) == 0)*/
90                 /* On Linux, the above can be checked simply as: */
91                 if (status == 0)
92                         /* this child exited with 0 */
93                         continue;
94                 /* Cannot happen:
95                 if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???;
96                  */
97                 bb_got_signal = 1;
98         }
99 }
100
101 /* transformer(), more than meets the eye */
102 #if BB_MMU
103 void FAST_FUNC fork_transformer(int fd,
104         int check_signature,
105         IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_state_t *xstate)
106 )
107 #else
108 void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
109 #endif
110 {
111         struct fd_pair fd_pipe;
112         int pid;
113
114         xpiped_pair(fd_pipe);
115         pid = BB_MMU ? xfork() : xvfork();
116         if (pid == 0) {
117                 /* Child */
118                 close(fd_pipe.rd); /* we don't want to read from the parent */
119                 // FIXME: error check?
120 #if BB_MMU
121                 {
122                         IF_DESKTOP(long long) int r;
123                         transformer_state_t xstate;
124                         init_transformer_state(&xstate);
125                         xstate.check_signature = check_signature;
126                         xstate.src_fd = fd;
127                         xstate.dst_fd = fd_pipe.wr;
128                         r = transformer(&xstate);
129                         if (ENABLE_FEATURE_CLEAN_UP) {
130                                 close(fd_pipe.wr); /* send EOF */
131                                 close(fd);
132                         }
133                         /* must be _exit! bug was actually seen here */
134                         _exit(/*error if:*/ r < 0);
135                 }
136 #else
137                 {
138                         char *argv[4];
139                         xmove_fd(fd, 0);
140                         xmove_fd(fd_pipe.wr, 1);
141                         argv[0] = (char*)transform_prog;
142                         argv[1] = (char*)"-cf";
143                         argv[2] = (char*)"-";
144                         argv[3] = NULL;
145                         BB_EXECVP(transform_prog, argv);
146                         bb_perror_msg_and_die("can't execute '%s'", transform_prog);
147                 }
148 #endif
149                 /* notreached */
150         }
151
152         /* parent process */
153         close(fd_pipe.wr); /* don't want to write to the child */
154         xmove_fd(fd_pipe.rd, fd);
155 }
156
157
158 #if SEAMLESS_COMPRESSION
159
160 /* Used by e.g. rpm which gives us a fd without filename,
161  * thus we can't guess the format from filename's extension.
162  */
163 static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed)
164 {
165         union {
166                 uint8_t b[4];
167                 uint16_t b16[2];
168                 uint32_t b32[1];
169         } magic;
170         int offset;
171         transformer_state_t *xstate;
172
173         offset = -2;
174         xstate = xzalloc(sizeof(*xstate));
175         xstate->src_fd = fd;
176
177         /* .gz and .bz2 both have 2-byte signature, and their
178          * unpack_XXX_stream wants this header skipped. */
179         xread(fd, magic.b16, sizeof(magic.b16[0]));
180         if (ENABLE_FEATURE_SEAMLESS_GZ
181          && magic.b16[0] == GZIP_MAGIC
182         ) {
183                 xstate->xformer = unpack_gz_stream;
184                 USE_FOR_NOMMU(xstate->xformer_prog = "gunzip";)
185                 goto found_magic;
186         }
187         if (ENABLE_FEATURE_SEAMLESS_BZ2
188          && magic.b16[0] == BZIP2_MAGIC
189         ) {
190                 xstate->xformer = unpack_bz2_stream;
191                 USE_FOR_NOMMU(xstate->xformer_prog = "bunzip2";)
192                 goto found_magic;
193         }
194         if (ENABLE_FEATURE_SEAMLESS_XZ
195          && magic.b16[0] == XZ_MAGIC1
196         ) {
197                 offset = -6;
198                 xread(fd, magic.b32, sizeof(magic.b32[0]));
199                 if (magic.b32[0] == XZ_MAGIC2) {
200                         xstate->xformer = unpack_xz_stream;
201                         USE_FOR_NOMMU(xstate->xformer_prog = "unxz";)
202                         goto found_magic;
203                 }
204         }
205
206         /* No known magic seen */
207         if (fail_if_not_compressed)
208                 bb_error_msg_and_die("no gzip"
209                         IF_FEATURE_SEAMLESS_BZ2("/bzip2")
210                         IF_FEATURE_SEAMLESS_XZ("/xz")
211                         " magic");
212
213         /* Some callers expect this function to "consume" fd
214          * even if data is not compressed. In this case,
215          * we return a state with trivial transformer.
216          */
217 //      USE_FOR_MMU(xstate->xformer = copy_stream;)
218 //      USE_FOR_NOMMU(xstate->xformer_prog = "cat";)
219         /* fall through to seeking bck over bytes we read earlier */
220
221  USE_FOR_NOMMU(found_magic:)
222         /* NOMMU version of fork_transformer execs
223          * an external unzipper that wants
224          * file position at the start of the file.
225          */
226         xlseek(fd, offset, SEEK_CUR);
227
228  USE_FOR_MMU(found_magic:)
229         /* In MMU case, if magic was found, seeking back is not necessary */
230
231         return xstate;
232 }
233
234 /* Used by e.g. rpm which gives us a fd without filename,
235  * thus we can't guess the format from filename's extension.
236  */
237 int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
238 {
239         transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
240
241         if (!xstate || !xstate->xformer) {
242                 free(xstate);
243                 return 1;
244         }
245
246 # if BB_MMU
247         fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
248 # else
249         fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog);
250 # endif
251         free(xstate);
252         return 0;
253 }
254
255 static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed)
256 {
257         transformer_state_t *xstate;
258         int fd;
259
260         fd = open(fname, O_RDONLY);
261         if (fd < 0)
262                 return NULL;
263
264         if (ENABLE_FEATURE_SEAMLESS_LZMA) {
265                 /* .lzma has no header/signature, can only detect it by extension */
266                 char *sfx = strrchr(fname, '.');
267                 if (sfx && strcmp(sfx+1, "lzma") == 0) {
268                         xstate = xzalloc(sizeof(*xstate));
269                         xstate->src_fd = fd;
270                         xstate->xformer = unpack_lzma_stream;
271                         USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
272                         return xstate;
273                 }
274         }
275
276         xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
277
278         return xstate;
279 }
280
281 int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed)
282 {
283         int fd;
284         transformer_state_t *xstate;
285
286         xstate = open_transformer(fname, fail_if_not_compressed);
287         if (!xstate)
288                 return -1;
289
290         fd = xstate->src_fd;
291         if (xstate->xformer) {
292 # if BB_MMU
293                 fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
294 # else
295                 fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog);
296 # endif
297         }
298         /* else: the file is not compressed */
299
300         free(xstate);
301         return fd;
302 }
303
304 void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
305 {
306 # if 1
307         transformer_state_t *xstate;
308         char *image;
309
310         xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0);
311         if (!xstate) /* file open error */
312                 return NULL;
313
314         image = NULL;
315         if (xstate->xformer) {
316                 /* In-memory decompression */
317                 xstate->mem_output_size_max = maxsz_p ? *maxsz_p : (size_t)(INT_MAX - 4095);
318                 xstate->xformer(xstate);
319                 if (xstate->mem_output_buf) {
320                         image = xstate->mem_output_buf;
321                         if (maxsz_p)
322                                 *maxsz_p = xstate->mem_output_size;
323                 }
324         } else {
325                 /* File is not compressed */
326                 image = xmalloc_read(xstate->src_fd, maxsz_p);
327         }
328
329         if (!image)
330                 bb_perror_msg("read error from '%s'", fname);
331         close(xstate->src_fd);
332         free(xstate);
333         return image;
334 # else
335         /* This version forks a subprocess - much more expensive */
336         int fd;
337         char *image;
338
339         fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0);
340         if (fd < 0)
341                 return NULL;
342
343         image = xmalloc_read(fd, maxsz_p);
344         if (!image)
345                 bb_perror_msg("read error from '%s'", fname);
346         close(fd);
347         return image;
348 # endif
349 }
350
351 #endif /* SEAMLESS_COMPRESSION */