Start 1.33.0 development cycle
[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 #include "libbb.h"
6 #include "bb_archive.h"
7
8 void FAST_FUNC init_transformer_state(transformer_state_t *xstate)
9 {
10         memset(xstate, 0, sizeof(*xstate));
11 }
12
13 int FAST_FUNC check_signature16(transformer_state_t *xstate, unsigned magic16)
14 {
15         if (!xstate->signature_skipped) {
16                 uint16_t magic2;
17                 if (full_read(xstate->src_fd, &magic2, 2) != 2 || magic2 != magic16) {
18                         bb_simple_error_msg("invalid magic");
19                         return -1;
20                 }
21                 xstate->signature_skipped = 2;
22         }
23         return 0;
24 }
25
26 ssize_t FAST_FUNC transformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
27 {
28         ssize_t nwrote;
29
30         if (xstate->mem_output_size_max != 0) {
31                 size_t pos = xstate->mem_output_size;
32                 size_t size;
33
34                 size = (xstate->mem_output_size += bufsize);
35                 if (size > xstate->mem_output_size_max) {
36                         free(xstate->mem_output_buf);
37                         xstate->mem_output_buf = NULL;
38                         bb_perror_msg("buffer %u too small", (unsigned)xstate->mem_output_size_max);
39                         nwrote = -1;
40                         goto ret;
41                 }
42                 xstate->mem_output_buf = xrealloc(xstate->mem_output_buf, size + 1);
43                 memcpy(xstate->mem_output_buf + pos, buf, bufsize);
44                 xstate->mem_output_buf[size] = '\0';
45                 nwrote = bufsize;
46         } else {
47                 nwrote = full_write(xstate->dst_fd, buf, bufsize);
48                 if (nwrote != (ssize_t)bufsize) {
49                         bb_simple_perror_msg("write");
50                         nwrote = -1;
51                         goto ret;
52                 }
53         }
54  ret:
55         return nwrote;
56 }
57
58 ssize_t FAST_FUNC xtransformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
59 {
60         ssize_t nwrote = transformer_write(xstate, buf, bufsize);
61         if (nwrote != (ssize_t)bufsize) {
62                 xfunc_die();
63         }
64         return nwrote;
65 }
66
67 void check_errors_in_children(int signo)
68 {
69         int status;
70
71         if (!signo) {
72                 /* block waiting for any child */
73                 if (wait(&status) < 0)
74 //FIXME: check EINTR?
75                         return; /* probably there are no children */
76                 goto check_status;
77         }
78
79         /* Wait for any child without blocking */
80         for (;;) {
81                 if (wait_any_nohang(&status) < 0)
82 //FIXME: check EINTR?
83                         /* wait failed?! I'm confused... */
84                         return;
85  check_status:
86                 /*if (WIFEXITED(status) && WEXITSTATUS(status) == 0)*/
87                 /* On Linux, the above can be checked simply as: */
88                 if (status == 0)
89                         /* this child exited with 0 */
90                         continue;
91                 /* Cannot happen:
92                 if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???;
93                  */
94                 bb_got_signal = 1;
95         }
96 }
97
98 /* transformer(), more than meets the eye */
99 #if BB_MMU
100 void FAST_FUNC fork_transformer(int fd,
101         int signature_skipped,
102         IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_state_t *xstate)
103 )
104 #else
105 void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
106 #endif
107 {
108         struct fd_pair fd_pipe;
109         int pid;
110
111         xpiped_pair(fd_pipe);
112         pid = BB_MMU ? xfork() : xvfork();
113         if (pid == 0) {
114                 /* Child */
115                 close(fd_pipe.rd); /* we don't want to read from the parent */
116                 // FIXME: error check?
117 #if BB_MMU
118                 {
119                         IF_DESKTOP(long long) int r;
120                         transformer_state_t xstate;
121                         init_transformer_state(&xstate);
122                         xstate.signature_skipped = signature_skipped;
123                         xstate.src_fd = fd;
124                         xstate.dst_fd = fd_pipe.wr;
125                         r = transformer(&xstate);
126                         if (ENABLE_FEATURE_CLEAN_UP) {
127                                 close(fd_pipe.wr); /* send EOF */
128                                 close(fd);
129                         }
130                         /* must be _exit! bug was actually seen here */
131                         _exit(/*error if:*/ r < 0);
132                 }
133 #else
134                 {
135                         char *argv[4];
136                         xmove_fd(fd, 0);
137                         xmove_fd(fd_pipe.wr, 1);
138                         argv[0] = (char*)transform_prog;
139                         argv[1] = (char*)"-cf";
140                         argv[2] = (char*)"-";
141                         argv[3] = NULL;
142                         BB_EXECVP(transform_prog, argv);
143                         bb_perror_msg_and_die("can't execute '%s'", transform_prog);
144                 }
145 #endif
146                 /* notreached */
147         }
148
149         /* parent process */
150         close(fd_pipe.wr); /* don't want to write to the child */
151         xmove_fd(fd_pipe.rd, fd);
152 }
153
154
155 #if SEAMLESS_COMPRESSION
156
157 /* Used by e.g. rpm which gives us a fd without filename,
158  * thus we can't guess the format from filename's extension.
159  */
160 static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed)
161 {
162         transformer_state_t *xstate;
163
164         xstate = xzalloc(sizeof(*xstate));
165         xstate->src_fd = fd;
166
167         /* .gz and .bz2 both have 2-byte signature, and their
168          * unpack_XXX_stream wants this header skipped. */
169         xstate->signature_skipped = 2;
170         xread(fd, xstate->magic.b16, 2);
171         if (ENABLE_FEATURE_SEAMLESS_GZ
172          && xstate->magic.b16[0] == GZIP_MAGIC
173         ) {
174                 xstate->xformer = unpack_gz_stream;
175                 USE_FOR_NOMMU(xstate->xformer_prog = "gunzip";)
176                 goto found_magic;
177         }
178         if (ENABLE_FEATURE_SEAMLESS_Z
179          && xstate->magic.b16[0] == COMPRESS_MAGIC
180         ) {
181                 xstate->xformer = unpack_Z_stream;
182                 USE_FOR_NOMMU(xstate->xformer_prog = "uncompress";)
183                 goto found_magic;
184         }
185         if (ENABLE_FEATURE_SEAMLESS_BZ2
186          && xstate->magic.b16[0] == BZIP2_MAGIC
187         ) {
188                 xstate->xformer = unpack_bz2_stream;
189                 USE_FOR_NOMMU(xstate->xformer_prog = "bunzip2";)
190                 goto found_magic;
191         }
192         if (ENABLE_FEATURE_SEAMLESS_XZ
193          && xstate->magic.b16[0] == XZ_MAGIC1
194         ) {
195                 uint32_t v32;
196                 xstate->signature_skipped = 6;
197                 xread(fd, &xstate->magic.b16[1], 4);
198                 move_from_unaligned32(v32, &xstate->magic.b16[1]);
199                 if (v32 == 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_simple_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
220  found_magic:
221         return xstate;
222 }
223
224 static void fork_transformer_and_free(transformer_state_t *xstate)
225 {
226 # if BB_MMU
227         fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
228 # else
229         /* NOMMU version of fork_transformer execs
230          * an external unzipper that wants
231          * file position at the start of the file.
232          */
233         xlseek(xstate->src_fd, - xstate->signature_skipped, SEEK_CUR);
234         xstate->signature_skipped = 0;
235         fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog);
236 # endif
237         free(xstate);
238 }
239
240 /* Used by e.g. rpm which gives us a fd without filename,
241  * thus we can't guess the format from filename's extension.
242  */
243 int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
244 {
245         transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
246
247         if (!xstate->xformer) {
248                 free(xstate);
249                 return 1;
250         }
251
252         fork_transformer_and_free(xstate);
253         return 0;
254 }
255 #if ENABLE_FEATURE_SEAMLESS_LZMA
256 /* ...and custom version for LZMA */
257 void FAST_FUNC setup_lzma_on_fd(int fd)
258 {
259         transformer_state_t *xstate = xzalloc(sizeof(*xstate));
260         xstate->src_fd = fd;
261         xstate->xformer = unpack_lzma_stream;
262         USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
263         fork_transformer_and_free(xstate);
264 }
265 #endif
266
267 static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed)
268 {
269         transformer_state_t *xstate;
270         int fd;
271
272         fd = open(fname, O_RDONLY);
273         if (fd < 0)
274                 return NULL;
275
276         if (ENABLE_FEATURE_SEAMLESS_LZMA) {
277                 /* .lzma has no header/signature, can only detect it by extension */
278                 if (is_suffixed_with(fname, ".lzma")) {
279                         xstate = xzalloc(sizeof(*xstate));
280                         xstate->src_fd = fd;
281                         xstate->xformer = unpack_lzma_stream;
282                         USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
283                         return xstate;
284                 }
285         }
286
287         xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
288
289         return xstate;
290 }
291
292 int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed)
293 {
294         int fd;
295         transformer_state_t *xstate;
296
297         xstate = open_transformer(fname, fail_if_not_compressed);
298         if (!xstate)
299                 return -1;
300
301         fd = xstate->src_fd;
302 # if BB_MMU
303         if (xstate->xformer) {
304                 fork_transformer_with_no_sig(fd, xstate->xformer);
305         } else {
306                 /* the file is not compressed */
307                 xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
308                 xstate->signature_skipped = 0;
309         }
310 # else
311         /* NOMMU can't avoid the seek :( */
312         xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
313         xstate->signature_skipped = 0;
314         if (xstate->xformer) {
315                 fork_transformer_with_sig(fd, xstate->xformer, xstate->xformer_prog);
316         } /* else: the file is not compressed */
317 # endif
318
319         free(xstate);
320         return fd;
321 }
322
323 void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
324 {
325 # if 1
326         transformer_state_t *xstate;
327         char *image;
328
329         xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0);
330         if (!xstate) /* file open error */
331                 return NULL;
332
333         image = NULL;
334         if (xstate->xformer) {
335                 /* In-memory decompression */
336                 xstate->mem_output_size_max = maxsz_p ? *maxsz_p : (size_t)(INT_MAX - 4095);
337                 xstate->xformer(xstate);
338                 if (xstate->mem_output_buf) {
339                         image = xstate->mem_output_buf;
340                         if (maxsz_p)
341                                 *maxsz_p = xstate->mem_output_size;
342                 }
343         } else {
344                 /* File is not compressed.
345                  * We already read first few bytes, account for that.
346                  * Example where it happens:
347                  * "modinfo MODULE.ko" (not compressed)
348                  *   open("MODULE.ko", O_RDONLY|O_LARGEFILE) = 4
349                  *   read(4, "\177E", 2)                     = 2
350                  *   fstat64(4, ...)
351                  *   mmap(...)
352                  *   read(4, "LF\2\1\1\0\0\0\0"...
353                  * ...and we avoided seeking on the fd! :)
354                  */
355                 image = xmalloc_read_with_initial_buf(
356                         xstate->src_fd,
357                         maxsz_p,
358                         xmemdup(&xstate->magic, xstate->signature_skipped),
359                         xstate->signature_skipped
360                 );
361                 xstate->signature_skipped = 0;
362         }
363
364         if (!image)
365                 bb_perror_msg("read error from '%s'", fname);
366         close(xstate->src_fd);
367         free(xstate);
368         return image;
369 # else
370         /* This version forks a subprocess - much more expensive */
371         int fd;
372         char *image;
373
374         fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0);
375         if (fd < 0)
376                 return NULL;
377
378         image = xmalloc_read(fd, maxsz_p);
379         if (!image)
380                 bb_perror_msg("read error from '%s'", fname);
381         close(fd);
382         return image;
383 # endif
384 }
385
386 #endif /* SEAMLESS_COMPRESSION */