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