preparatory cleanups for seamless uncompression improvements
[oweals/busybox.git] / archival / libarchive / open_transformer.c
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 #define ZIPPED (ENABLE_FEATURE_SEAMLESS_LZMA \
10         || ENABLE_FEATURE_SEAMLESS_BZ2 \
11         || ENABLE_FEATURE_SEAMLESS_GZ \
12         /* || ENABLE_FEATURE_SEAMLESS_Z */ \
13 )
14
15 #if ZIPPED
16 # include "bb_archive.h"
17 #endif
18
19 /* transformer(), more than meets the eye */
20 /*
21  * On MMU machine, the transform_prog is removed by macro magic
22  * in include/archive.h. On NOMMU, transformer is removed.
23  */
24 void FAST_FUNC open_transformer(int fd,
25         IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd),
26         const char *transform_prog)
27 {
28         struct fd_pair fd_pipe;
29         int pid;
30
31         xpiped_pair(fd_pipe);
32         pid = BB_MMU ? xfork() : xvfork();
33         if (pid == 0) {
34                 /* Child */
35                 close(fd_pipe.rd); /* we don't want to read from the parent */
36                 // FIXME: error check?
37 #if BB_MMU
38                 transformer(fd, fd_pipe.wr);
39                 if (ENABLE_FEATURE_CLEAN_UP) {
40                         close(fd_pipe.wr); /* send EOF */
41                         close(fd);
42                 }
43                 /* must be _exit! bug was actually seen here */
44                 _exit(EXIT_SUCCESS);
45 #else
46                 {
47                         char *argv[4];
48                         xmove_fd(fd, 0);
49                         xmove_fd(fd_pipe.wr, 1);
50                         argv[0] = (char*)transform_prog;
51                         argv[1] = (char*)"-cf";
52                         argv[2] = (char*)"-";
53                         argv[3] = NULL;
54                         BB_EXECVP(transform_prog, argv);
55                         bb_perror_msg_and_die("can't execute '%s'", transform_prog);
56                 }
57 #endif
58                 /* notreached */
59         }
60
61         /* parent process */
62         close(fd_pipe.wr); /* don't want to write to the child */
63         xmove_fd(fd_pipe.rd, fd);
64 }
65
66
67 /* Used by e.g. rpm which gives us a fd without filename,
68  * thus we can't guess the format from filename's extension.
69  */
70 #if ZIPPED
71 void FAST_FUNC setup_unzip_on_fd(int fd /*, int fail_if_not_detected*/)
72 {
73         const int fail_if_not_detected = 1;
74         union {
75                 uint8_t b[4];
76                 uint16_t b16[2];
77                 uint32_t b32[1];
78         } magic;
79         int offset = -2;
80 # if BB_MMU
81         IF_DESKTOP(long long) int FAST_FUNC (*xformer)(int src_fd, int dst_fd);
82         enum { xformer_prog = 0 };
83 # else
84         enum { xformer = 0 };
85         const char *xformer_prog;
86 # endif
87
88         /* .gz and .bz2 both have 2-byte signature, and their
89          * unpack_XXX_stream wants this header skipped. */
90         xread(fd, magic.b16, sizeof(magic.b16[0]));
91         if (ENABLE_FEATURE_SEAMLESS_GZ
92          && magic.b16[0] == GZIP_MAGIC
93         ) {
94 # if BB_MMU
95                 xformer = unpack_gz_stream;
96 # else
97                 xformer_prog = "gunzip";
98 # endif
99                 goto found_magic;
100         }
101         if (ENABLE_FEATURE_SEAMLESS_BZ2
102          && magic.b16[0] == BZIP2_MAGIC
103         ) {
104 # if BB_MMU
105                 xformer = unpack_bz2_stream;
106 # else
107                 xformer_prog = "bunzip2";
108 # endif
109                 goto found_magic;
110         }
111         if (ENABLE_FEATURE_SEAMLESS_XZ
112          && magic.b16[0] == XZ_MAGIC1
113         ) {
114                 offset = -6;
115                 xread(fd, magic.b32, sizeof(magic.b32[0]));
116                 if (magic.b32[0] == XZ_MAGIC2) {
117 # if BB_MMU
118                         xformer = unpack_xz_stream;
119                         /* unpack_xz_stream wants fd at position 6, no need to seek */
120                         //xlseek(fd, offset, SEEK_CUR);
121 # else
122                         xformer_prog = "unxz";
123 # endif
124                         goto found_magic;
125                 }
126         }
127
128         /* No known magic seen */
129         if (fail_if_not_detected)
130                 bb_error_msg_and_die("no gzip"
131                         IF_FEATURE_SEAMLESS_BZ2("/bzip2")
132                         IF_FEATURE_SEAMLESS_XZ("/xz")
133                         " magic");
134         xlseek(fd, offset, SEEK_CUR);
135         return;
136
137  found_magic:
138 # if !BB_MMU
139         /* NOMMU version of open_transformer execs
140          * an external unzipper that wants
141          * file position at the start of the file */
142         xlseek(fd, offset, SEEK_CUR);
143 # endif
144         open_transformer(fd, xformer, xformer_prog);
145 }
146 #endif /* ZIPPED */
147
148 int FAST_FUNC open_zipped(const char *fname)
149 {
150 #if !ZIPPED
151         return open(fname, O_RDONLY);
152 #else
153         char *sfx;
154         int fd;
155
156         fd = open(fname, O_RDONLY);
157         if (fd < 0)
158                 return fd;
159
160         sfx = strrchr(fname, '.');
161         if (sfx) {
162                 sfx++;
163                 if (ENABLE_FEATURE_SEAMLESS_LZMA && strcmp(sfx, "lzma") == 0)
164                         /* .lzma has no header/signature, just trust it */
165                         open_transformer(fd, unpack_lzma_stream, "unlzma");
166                 else
167                 if ((ENABLE_FEATURE_SEAMLESS_GZ && strcmp(sfx, "gz") == 0)
168                  || (ENABLE_FEATURE_SEAMLESS_BZ2 && strcmp(sfx, "bz2") == 0)
169                  || (ENABLE_FEATURE_SEAMLESS_XZ && strcmp(sfx, "xz") == 0)
170                 ) {
171                         setup_unzip_on_fd(fd /*, fail_if_not_detected: 1*/);
172                 }
173         }
174
175         return fd;
176 #endif
177 }
178
179 void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
180 {
181         int fd;
182         char *image;
183
184         fd = open_zipped(fname);
185         if (fd < 0)
186                 return NULL;
187
188         image = xmalloc_read(fd, maxsz_p);
189         if (!image)
190                 bb_perror_msg("read error from '%s'", fname);
191         close(fd);
192
193         return image;
194 }