update seamless uncompression code
[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 void FAST_FUNC init_transformer_aux_data(transformer_aux_data_t *aux)
10 {
11         memset(aux, 0, sizeof(*aux));
12 }
13
14 int FAST_FUNC check_signature16(transformer_aux_data_t *aux, int src_fd, unsigned magic16)
15 {
16         if (aux && aux->check_signature) {
17                 uint16_t magic2;
18                 if (full_read(src_fd, &magic2, 2) != 2 || magic2 != magic16) {
19                         bb_error_msg("invalid magic");
20 #if 0 /* possible future extension */
21                         if (aux->check_signature > 1)
22                                 xfunc_die();
23 #endif
24                         return -1;
25                 }
26         }
27         return 0;
28 }
29
30 /* transformer(), more than meets the eye */
31 #if BB_MMU
32 void FAST_FUNC open_transformer(int fd,
33         int check_signature,
34         IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd)
35 )
36 #else
37 void FAST_FUNC open_transformer(int fd, const char *transform_prog)
38 #endif
39 {
40         struct fd_pair fd_pipe;
41         int pid;
42
43         xpiped_pair(fd_pipe);
44         pid = BB_MMU ? xfork() : xvfork();
45         if (pid == 0) {
46                 /* Child */
47                 close(fd_pipe.rd); /* we don't want to read from the parent */
48                 // FIXME: error check?
49 #if BB_MMU
50                 {
51                         transformer_aux_data_t aux;
52                         init_transformer_aux_data(&aux);
53                         aux.check_signature = check_signature;
54                         transformer(&aux, fd, fd_pipe.wr);
55                         if (ENABLE_FEATURE_CLEAN_UP) {
56                                 close(fd_pipe.wr); /* send EOF */
57                                 close(fd);
58                         }
59                         /* must be _exit! bug was actually seen here */
60                         _exit(EXIT_SUCCESS);
61                 }
62 #else
63                 {
64                         char *argv[4];
65                         xmove_fd(fd, 0);
66                         xmove_fd(fd_pipe.wr, 1);
67                         argv[0] = (char*)transform_prog;
68                         argv[1] = (char*)"-cf";
69                         argv[2] = (char*)"-";
70                         argv[3] = NULL;
71                         BB_EXECVP(transform_prog, argv);
72                         bb_perror_msg_and_die("can't execute '%s'", transform_prog);
73                 }
74 #endif
75                 /* notreached */
76         }
77
78         /* parent process */
79         close(fd_pipe.wr); /* don't want to write to the child */
80         xmove_fd(fd_pipe.rd, fd);
81 }
82
83
84 #if SEAMLESS_COMPRESSION
85
86 /* Used by e.g. rpm which gives us a fd without filename,
87  * thus we can't guess the format from filename's extension.
88  */
89 int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_detected)
90 {
91         union {
92                 uint8_t b[4];
93                 uint16_t b16[2];
94                 uint32_t b32[1];
95         } magic;
96         int offset = -2;
97         USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd);)
98         USE_FOR_NOMMU(const char *xformer_prog;)
99
100         /* .gz and .bz2 both have 2-byte signature, and their
101          * unpack_XXX_stream wants this header skipped. */
102         xread(fd, magic.b16, sizeof(magic.b16[0]));
103         if (ENABLE_FEATURE_SEAMLESS_GZ
104          && magic.b16[0] == GZIP_MAGIC
105         ) {
106                 USE_FOR_MMU(xformer = unpack_gz_stream;)
107                 USE_FOR_NOMMU(xformer_prog = "gunzip";)
108                 goto found_magic;
109         }
110         if (ENABLE_FEATURE_SEAMLESS_BZ2
111          && magic.b16[0] == BZIP2_MAGIC
112         ) {
113                 USE_FOR_MMU(xformer = unpack_bz2_stream;)
114                 USE_FOR_NOMMU(xformer_prog = "bunzip2";)
115                 goto found_magic;
116         }
117         if (ENABLE_FEATURE_SEAMLESS_XZ
118          && magic.b16[0] == XZ_MAGIC1
119         ) {
120                 offset = -6;
121                 xread(fd, magic.b32, sizeof(magic.b32[0]));
122                 if (magic.b32[0] == XZ_MAGIC2) {
123                         USE_FOR_MMU(xformer = unpack_xz_stream;)
124                         USE_FOR_NOMMU(xformer_prog = "unxz";)
125                         goto found_magic;
126                 }
127         }
128
129         /* No known magic seen */
130         if (fail_if_not_detected)
131                 bb_error_msg_and_die("no gzip"
132                         IF_FEATURE_SEAMLESS_BZ2("/bzip2")
133                         IF_FEATURE_SEAMLESS_XZ("/xz")
134                         " magic");
135         xlseek(fd, offset, SEEK_CUR);
136         return 1;
137
138  found_magic:
139 # if BB_MMU
140         open_transformer_with_no_sig(fd, xformer);
141 # else
142         /* NOMMU version of open_transformer execs
143          * an external unzipper that wants
144          * file position at the start of the file */
145         xlseek(fd, offset, SEEK_CUR);
146         open_transformer_with_sig(fd, xformer, xformer_prog);
147 # endif
148         return 0;
149 }
150
151 int FAST_FUNC open_zipped(const char *fname)
152 {
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_with_sig(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 }
177
178 #endif /* SEAMLESS_COMPRESSION */
179
180 void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
181 {
182         int fd;
183         char *image;
184
185         fd = open_zipped(fname);
186         if (fd < 0)
187                 return NULL;
188
189         image = xmalloc_read(fd, maxsz_p);
190         if (!image)
191                 bb_perror_msg("read error from '%s'", fname);
192         close(fd);
193
194         return image;
195 }