/* vi: set sw=4 ts=4: */ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" #include "bb_archive.h" #define ZIPPED (ENABLE_FEATURE_SEAMLESS_LZMA \ || ENABLE_FEATURE_SEAMLESS_BZ2 \ || ENABLE_FEATURE_SEAMLESS_GZ \ /* || ENABLE_FEATURE_SEAMLESS_Z */ \ ) #if ZIPPED # include "bb_archive.h" #endif /* transformer(), more than meets the eye */ /* * On MMU machine, the transform_prog is removed by macro magic * in include/archive.h. On NOMMU, transformer is removed. */ void FAST_FUNC open_transformer(int fd, IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd), const char *transform_prog) { struct fd_pair fd_pipe; int pid; xpiped_pair(fd_pipe); pid = BB_MMU ? xfork() : xvfork(); if (pid == 0) { /* Child */ close(fd_pipe.rd); /* we don't want to read from the parent */ // FIXME: error check? #if BB_MMU transformer(fd, fd_pipe.wr); if (ENABLE_FEATURE_CLEAN_UP) { close(fd_pipe.wr); /* send EOF */ close(fd); } /* must be _exit! bug was actually seen here */ _exit(EXIT_SUCCESS); #else { char *argv[4]; xmove_fd(fd, 0); xmove_fd(fd_pipe.wr, 1); argv[0] = (char*)transform_prog; argv[1] = (char*)"-cf"; argv[2] = (char*)"-"; argv[3] = NULL; BB_EXECVP(transform_prog, argv); bb_perror_msg_and_die("can't execute '%s'", transform_prog); } #endif /* notreached */ } /* parent process */ close(fd_pipe.wr); /* don't want to write to the child */ xmove_fd(fd_pipe.rd, fd); } /* Used by e.g. rpm which gives us a fd without filename, * thus we can't guess the format from filename's extension. */ #if ZIPPED void FAST_FUNC setup_unzip_on_fd(int fd /*, int fail_if_not_detected*/) { const int fail_if_not_detected = 1; union { uint8_t b[4]; uint16_t b16[2]; uint32_t b32[1]; } magic; int offset = -2; # if BB_MMU IF_DESKTOP(long long) int FAST_FUNC (*xformer)(int src_fd, int dst_fd); enum { xformer_prog = 0 }; # else enum { xformer = 0 }; const char *xformer_prog; # endif /* .gz and .bz2 both have 2-byte signature, and their * unpack_XXX_stream wants this header skipped. */ xread(fd, magic.b16, sizeof(magic.b16[0])); if (ENABLE_FEATURE_SEAMLESS_GZ && magic.b16[0] == GZIP_MAGIC ) { # if BB_MMU xformer = unpack_gz_stream; # else xformer_prog = "gunzip"; # endif goto found_magic; } if (ENABLE_FEATURE_SEAMLESS_BZ2 && magic.b16[0] == BZIP2_MAGIC ) { # if BB_MMU xformer = unpack_bz2_stream; # else xformer_prog = "bunzip2"; # endif goto found_magic; } if (ENABLE_FEATURE_SEAMLESS_XZ && magic.b16[0] == XZ_MAGIC1 ) { offset = -6; xread(fd, magic.b32, sizeof(magic.b32[0])); if (magic.b32[0] == XZ_MAGIC2) { # if BB_MMU xformer = unpack_xz_stream; /* unpack_xz_stream wants fd at position 6, no need to seek */ //xlseek(fd, offset, SEEK_CUR); # else xformer_prog = "unxz"; # endif goto found_magic; } } /* No known magic seen */ if (fail_if_not_detected) bb_error_msg_and_die("no gzip" IF_FEATURE_SEAMLESS_BZ2("/bzip2") IF_FEATURE_SEAMLESS_XZ("/xz") " magic"); xlseek(fd, offset, SEEK_CUR); return; found_magic: # if !BB_MMU /* NOMMU version of open_transformer execs * an external unzipper that wants * file position at the start of the file */ xlseek(fd, offset, SEEK_CUR); # endif open_transformer(fd, xformer, xformer_prog); } #endif /* ZIPPED */ int FAST_FUNC open_zipped(const char *fname) { #if !ZIPPED return open(fname, O_RDONLY); #else char *sfx; int fd; fd = open(fname, O_RDONLY); if (fd < 0) return fd; sfx = strrchr(fname, '.'); if (sfx) { sfx++; if (ENABLE_FEATURE_SEAMLESS_LZMA && strcmp(sfx, "lzma") == 0) /* .lzma has no header/signature, just trust it */ open_transformer(fd, unpack_lzma_stream, "unlzma"); else if ((ENABLE_FEATURE_SEAMLESS_GZ && strcmp(sfx, "gz") == 0) || (ENABLE_FEATURE_SEAMLESS_BZ2 && strcmp(sfx, "bz2") == 0) || (ENABLE_FEATURE_SEAMLESS_XZ && strcmp(sfx, "xz") == 0) ) { setup_unzip_on_fd(fd /*, fail_if_not_detected: 1*/); } } return fd; #endif } void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) { int fd; char *image; fd = open_zipped(fname); if (fd < 0) return NULL; image = xmalloc_read(fd, maxsz_p); if (!image) bb_perror_msg("read error from '%s'", fname); close(fd); return image; }