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        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_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
 224static 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 */
 243int 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 */
 257void 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
 267static 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
 292int 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
 323void* 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 */
 387