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
   6#include "libbb.h"
   7#include "bb_archive.h"
   8
   9void FAST_FUNC init_transformer_state(transformer_state_t *xstate)
  10{
  11        memset(xstate, 0, sizeof(*xstate));
  12}
  13
  14int 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
  27ssize_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
  59ssize_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
  68void 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
 101void 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
 106void 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 */
 161static 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 */
 231int 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
 255static 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
 281int 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
 312void* 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