busybox/archival/libarchive/open_transformer.c
<<
>>
Prefs
   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
   8void FAST_FUNC init_transformer_state(transformer_state_t *xstate)
   9{
  10        memset(xstate, 0, sizeof(*xstate));
  11}
  12
  13int 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_error_msg("invalid magic");
  19                        return -1;
  20                }
  21                xstate->signature_skipped = 2;
  22        }
  23        return 0;
  24}
  25
  26ssize_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_perror_msg("write");
  50                        nwrote = -1;
  51                        goto ret;
  52                }
  53        }
  54 ret:
  55        return nwrote;
  56}
  57
  58ssize_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
  67void 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
 100void 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
 105void 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 */
 160static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed)
 161{
 162        union {
 163                uint8_t b[4];
 164                uint16_t b16[2];
 165                uint32_t b32[1];
 166        } magic;
 167        transformer_state_t *xstate;
 168
 169        xstate = xzalloc(sizeof(*xstate));
 170        xstate->src_fd = fd;
 171        xstate->signature_skipped = 2;
 172
 173        /* .gz and .bz2 both have 2-byte signature, and their
 174         * unpack_XXX_stream wants this header skipped. */
 175        xread(fd, magic.b16, sizeof(magic.b16[0]));
 176        if (ENABLE_FEATURE_SEAMLESS_GZ
 177         && magic.b16[0] == GZIP_MAGIC
 178        ) {
 179                xstate->xformer = unpack_gz_stream;
 180                USE_FOR_NOMMU(xstate->xformer_prog = "gunzip";)
 181                goto found_magic;
 182        }
 183        if (ENABLE_FEATURE_SEAMLESS_Z
 184         && magic.b16[0] == COMPRESS_MAGIC
 185        ) {
 186                xstate->xformer = unpack_Z_stream;
 187                USE_FOR_NOMMU(xstate->xformer_prog = "uncompress";)
 188                goto found_magic;
 189        }
 190        if (ENABLE_FEATURE_SEAMLESS_BZ2
 191         && magic.b16[0] == BZIP2_MAGIC
 192        ) {
 193                xstate->xformer = unpack_bz2_stream;
 194                USE_FOR_NOMMU(xstate->xformer_prog = "bunzip2";)
 195                goto found_magic;
 196        }
 197        if (ENABLE_FEATURE_SEAMLESS_XZ
 198         && magic.b16[0] == XZ_MAGIC1
 199        ) {
 200                xstate->signature_skipped = 6;
 201                xread(fd, magic.b32, sizeof(magic.b32[0]));
 202                if (magic.b32[0] == XZ_MAGIC2) {
 203                        xstate->xformer = unpack_xz_stream;
 204                        USE_FOR_NOMMU(xstate->xformer_prog = "unxz";)
 205                        goto found_magic;
 206                }
 207        }
 208
 209        /* No known magic seen */
 210        if (fail_if_not_compressed)
 211                bb_error_msg_and_die("no gzip"
 212                        IF_FEATURE_SEAMLESS_BZ2("/bzip2")
 213                        IF_FEATURE_SEAMLESS_XZ("/xz")
 214                        " magic");
 215
 216        /* Some callers expect this function to "consume" fd
 217         * even if data is not compressed. In this case,
 218         * we return a state with trivial transformer.
 219         */
 220//      USE_FOR_MMU(xstate->xformer = copy_stream;)
 221//      USE_FOR_NOMMU(xstate->xformer_prog = "cat";)
 222
 223 found_magic:
 224        return xstate;
 225}
 226
 227static void fork_transformer_and_free(transformer_state_t *xstate)
 228{
 229# if BB_MMU
 230        fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
 231# else
 232        /* NOMMU version of fork_transformer execs
 233         * an external unzipper that wants
 234         * file position at the start of the file.
 235         */
 236        xlseek(xstate->src_fd, - xstate->signature_skipped, SEEK_CUR);
 237        xstate->signature_skipped = 0;
 238        fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog);
 239# endif
 240        free(xstate);
 241}
 242
 243/* Used by e.g. rpm which gives us a fd without filename,
 244 * thus we can't guess the format from filename's extension.
 245 */
 246int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
 247{
 248        transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
 249
 250        if (!xstate->xformer) {
 251                free(xstate);
 252                return 1;
 253        }
 254
 255        fork_transformer_and_free(xstate);
 256        return 0;
 257}
 258#if ENABLE_FEATURE_SEAMLESS_LZMA
 259/* ...and custom version for LZMA */
 260void FAST_FUNC setup_lzma_on_fd(int fd)
 261{
 262        transformer_state_t *xstate = xzalloc(sizeof(*xstate));
 263        xstate->src_fd = fd;
 264        xstate->xformer = unpack_lzma_stream;
 265        USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
 266        fork_transformer_and_free(xstate);
 267}
 268#endif
 269
 270static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed)
 271{
 272        transformer_state_t *xstate;
 273        int fd;
 274
 275        fd = open(fname, O_RDONLY);
 276        if (fd < 0)
 277                return NULL;
 278
 279        if (ENABLE_FEATURE_SEAMLESS_LZMA) {
 280                /* .lzma has no header/signature, can only detect it by extension */
 281                if (is_suffixed_with(fname, ".lzma")) {
 282                        xstate = xzalloc(sizeof(*xstate));
 283                        xstate->src_fd = fd;
 284                        xstate->xformer = unpack_lzma_stream;
 285                        USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
 286                        return xstate;
 287                }
 288        }
 289
 290        xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
 291
 292        return xstate;
 293}
 294
 295int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed)
 296{
 297        int fd;
 298        transformer_state_t *xstate;
 299
 300        xstate = open_transformer(fname, fail_if_not_compressed);
 301        if (!xstate)
 302                return -1;
 303
 304        fd = xstate->src_fd;
 305# if BB_MMU
 306        if (xstate->xformer) {
 307                fork_transformer_with_no_sig(fd, xstate->xformer);
 308        } else {
 309                /* the file is not compressed */
 310                xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
 311                xstate->signature_skipped = 0;
 312        }
 313# else
 314        /* NOMMU can't avoid the seek :( */
 315        xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
 316        xstate->signature_skipped = 0;
 317        if (xstate->xformer) {
 318                fork_transformer_with_sig(fd, xstate->xformer, xstate->xformer_prog);
 319        } /* else: the file is not compressed */
 320# endif
 321
 322        free(xstate);
 323        return fd;
 324}
 325
 326void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
 327{
 328# if 1
 329        transformer_state_t *xstate;
 330        char *image;
 331
 332        xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0);
 333        if (!xstate) /* file open error */
 334                return NULL;
 335
 336        image = NULL;
 337        if (xstate->xformer) {
 338                /* In-memory decompression */
 339                xstate->mem_output_size_max = maxsz_p ? *maxsz_p : (size_t)(INT_MAX - 4095);
 340                xstate->xformer(xstate);
 341                if (xstate->mem_output_buf) {
 342                        image = xstate->mem_output_buf;
 343                        if (maxsz_p)
 344                                *maxsz_p = xstate->mem_output_size;
 345                }
 346        } else {
 347                /* File is not compressed */
 348//FIXME: avoid seek
 349                xlseek(xstate->src_fd, - xstate->signature_skipped, SEEK_CUR);
 350                xstate->signature_skipped = 0;
 351                image = xmalloc_read(xstate->src_fd, maxsz_p);
 352        }
 353
 354        if (!image)
 355                bb_perror_msg("read error from '%s'", fname);
 356        close(xstate->src_fd);
 357        free(xstate);
 358        return image;
 359# else
 360        /* This version forks a subprocess - much more expensive */
 361        int fd;
 362        char *image;
 363
 364        fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0);
 365        if (fd < 0)
 366                return NULL;
 367
 368        image = xmalloc_read(fd, maxsz_p);
 369        if (!image)
 370                bb_perror_msg("read error from '%s'", fname);
 371        close(fd);
 372        return image;
 373# endif
 374}
 375
 376#endif /* SEAMLESS_COMPRESSION */
 377