Initial commit
[nyanbb.git] / archival / libarchive / open_transformer.c
blobd9017d92c572360a056baaa930e08e768fb1bd43
1 /* vi: set sw=4 ts=4: */
2 /*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <fcntl.h>
10 #include "bb/private/config.h"
11 #include "bb/archival/libarchive/private.h"
13 #include "bb/lib/public.h"
14 #include "bb/archival/libarchive/public.h"
16 BB_STATIC void bb_archive_init_transformer_state(bb_archive_transformer_state_t *xstate)
18 memset(xstate, 0, sizeof(*xstate));
21 BB_STATIC int bb_archive_check_signature16(bb_archive_transformer_state_t *xstate, unsigned magic16)
23 if (!xstate->signature_skipped) {
24 uint16_t magic2;
25 if (bb_full_read(xstate->src_fd, &magic2, 2) != 2 || magic2 != magic16) {
26 bb_simple_error_msg("invalid magic");
27 return -1;
29 xstate->signature_skipped = 2;
31 return 0;
34 BB_STATIC ssize_t bb_archive_transformer_write(bb_archive_transformer_state_t *xstate,
35 const void *buf, size_t bufsize)
37 ssize_t nwrote;
39 if (xstate->mem_output_size_max != 0) {
40 size_t pos = xstate->mem_output_size;
41 size_t size;
43 size = (xstate->mem_output_size += bufsize);
44 if (size > xstate->mem_output_size_max) {
45 free(xstate->mem_output_buf);
46 xstate->mem_output_buf = NULL;
47 bb_perror_msg("buffer %u too small", (unsigned)xstate->mem_output_size_max);
48 nwrote = -1;
49 goto ret;
51 xstate->mem_output_buf = bb_xrealloc(xstate->mem_output_buf, size + 1);
52 memcpy(xstate->mem_output_buf + pos, buf, bufsize);
53 xstate->mem_output_buf[size] = '\0';
54 nwrote = bufsize;
55 } else {
56 nwrote = bb_full_write(xstate->dst_fd, buf, bufsize);
57 if (nwrote != (ssize_t)bufsize) {
58 bb_simple_perror_msg("write");
59 nwrote = -1;
60 goto ret;
63 ret:
64 return nwrote;
67 BB_STATIC ssize_t bb_archive_xtransformer_write(bb_archive_transformer_state_t *xstate,
68 const void *buf, size_t bufsize)
70 ssize_t nwrote = bb_archive_transformer_write(xstate, buf, bufsize);
71 if (nwrote != (ssize_t)bufsize) {
72 bb_xfunc_die();
74 return nwrote;
77 /* transformer(), more than meets the eye */
78 BB_STATIC void bb_archive_fork_transformer(int fd, int signature_skipped,
79 long (*transformer)(bb_archive_transformer_state_t *xstate))
81 struct bb_fd_pair fd_pipe;
82 int pid;
84 bb_xpiped_pair(fd_pipe);
85 pid = bb_xfork();
86 if (pid == 0) {
87 long r;
88 bb_archive_transformer_state_t xstate;
89 /* Child */
90 close(fd_pipe.rd); /* we don't want to read from the parent */
91 // FIXME: error check?
93 bb_archive_init_transformer_state(&xstate);
94 xstate.signature_skipped = signature_skipped;
95 xstate.src_fd = fd;
96 xstate.dst_fd = fd_pipe.wr;
97 r = transformer(&xstate);
98 /* must be _exit! bug was actually seen here */
99 _exit(/*error if:*/ r < 0);
100 /* notreached */
103 /* parent process */
104 close(fd_pipe.wr); /* don't want to write to the child */
105 bb_xmove_fd(fd_pipe.rd, fd);
107 /* Used by e.g. rpm which gives us a fd without filename,
108 * thus we can't guess the format from filename's extension.
110 BB_STATIC bb_archive_transformer_state_t *bb_archive_setup_transformer_on_fd(int fd, int fail_if_not_compressed)
112 bb_archive_transformer_state_t *xstate;
114 xstate = bb_xzalloc(sizeof(*xstate));
115 xstate->src_fd = fd;
117 /* .gz and .bz2 both have 2-byte signature, and their
118 * unpack_XXX_stream wants this header skipped. */
119 xstate->signature_skipped = 2;
120 bb_xread(fd, xstate->magic.b16, 2);
121 if (xstate->magic.b16[0] == BB_GZIP_MAGIC) {
122 xstate->xformer = bb_archive_unpack_gz_stream;
123 goto found_magic;
125 if (xstate->magic.b16[0] == BB_COMPRESS_MAGIC) {
126 xstate->xformer = bb_archive_unpack_Z_stream;
127 goto found_magic;
129 if (xstate->magic.b16[0] == BB_BZIP2_MAGIC) {
130 xstate->xformer = bb_archive_unpack_bz2_stream;
131 goto found_magic;
133 if (xstate->magic.b16[0] == BB_XZ_MAGIC1) {
134 uint32_t v32;
135 xstate->signature_skipped = 6;
136 bb_xread(fd, &xstate->magic.b16[1], 4);
137 v32 = *(uint32_t*)(&xstate->magic.b16[1]);
138 if (v32 == BB_XZ_MAGIC2) {
139 xstate->xformer = bb_archive_unpack_xz_stream;
140 goto found_magic;
144 /* No known magic seen */
145 if (fail_if_not_compressed)
146 bb_simple_error_msg_and_die("no gzip"
147 "/bzip2"
148 "/xz"
149 " magic");
151 /* Some callers expect this function to "consume" fd
152 * even if data is not compressed. In this case,
153 * we return a state with trivial transformer.
155 // USE_FOR_MMU(xstate->xformer = copy_stream;)
156 // USE_FOR_NOMMU(xstate->xformer_prog = "cat";)
158 found_magic:
159 return xstate;
162 BB_STATIC bb_archive_transformer_state_t *bb_archive_open_transformer(const char *fname,
163 int fail_if_not_compressed)
165 bb_archive_transformer_state_t *xstate;
166 int fd;
168 fd = open(fname, O_RDONLY);
169 if (fd < 0)
170 return NULL;
172 /* .lzma has no header/signature, can only detect it by extension */
173 if (bb_is_suffixed_with(fname, ".lzma")) {
174 xstate = bb_xzalloc(sizeof(*xstate));
175 xstate->src_fd = fd;
176 xstate->xformer = bb_archive_unpack_lzma_stream;
177 return xstate;
180 xstate = bb_archive_setup_transformer_on_fd(fd, fail_if_not_compressed);
182 return xstate;
185 BB_STATIC void bb_archive_fork_transformer_and_free(bb_archive_transformer_state_t *xstate)
187 bb_archive_fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
188 free(xstate);
191 /* Used by e.g. rpm which gives us a fd without filename,
192 * thus we can't guess the format from filename's extension.
194 BB_STATIC int bb_archive_setup_unzip_on_fd(int fd, int fail_if_not_compressed)
196 bb_archive_transformer_state_t *xstate = bb_archive_setup_transformer_on_fd(fd,
197 fail_if_not_compressed);
199 if (!xstate->xformer) {
200 free(xstate);
201 return 1;
204 bb_archive_fork_transformer_and_free(xstate);
205 return 0;
208 BB_STATIC int bb_archive_open_zipped(const char *fname, int fail_if_not_compressed)
210 int fd;
211 bb_archive_transformer_state_t *xstate;
213 xstate = bb_archive_open_transformer(fname, fail_if_not_compressed);
214 if (!xstate)
215 return -1;
217 fd = xstate->src_fd;
219 if (xstate->xformer) {
220 bb_archive_fork_transformer_with_no_sig(fd, xstate->xformer);
221 } else {
222 /* the file is not compressed */
223 bb_xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
224 xstate->signature_skipped = 0;
227 free(xstate);
228 return fd;