xref: /aosp_15_r20/external/bcc/src/cc/bcc_zip.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker /*
2*387f9dfdSAndroid Build Coastguard Worker  * Copyright (c) Meta Platforms, Inc. and affiliates.
3*387f9dfdSAndroid Build Coastguard Worker  *
4*387f9dfdSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*387f9dfdSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*387f9dfdSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*387f9dfdSAndroid Build Coastguard Worker  *
8*387f9dfdSAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
9*387f9dfdSAndroid Build Coastguard Worker  *
10*387f9dfdSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*387f9dfdSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*387f9dfdSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*387f9dfdSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*387f9dfdSAndroid Build Coastguard Worker  * limitations under the License.
15*387f9dfdSAndroid Build Coastguard Worker  */
16*387f9dfdSAndroid Build Coastguard Worker 
17*387f9dfdSAndroid Build Coastguard Worker #include "bcc_zip.h"
18*387f9dfdSAndroid Build Coastguard Worker 
19*387f9dfdSAndroid Build Coastguard Worker #include <fcntl.h>
20*387f9dfdSAndroid Build Coastguard Worker #include <limits.h>
21*387f9dfdSAndroid Build Coastguard Worker #include <stdint.h>
22*387f9dfdSAndroid Build Coastguard Worker #include <stdlib.h>
23*387f9dfdSAndroid Build Coastguard Worker #include <string.h>
24*387f9dfdSAndroid Build Coastguard Worker #include <sys/mman.h>
25*387f9dfdSAndroid Build Coastguard Worker #include <unistd.h>
26*387f9dfdSAndroid Build Coastguard Worker 
27*387f9dfdSAndroid Build Coastguard Worker // Specification of ZIP file format can be found here:
28*387f9dfdSAndroid Build Coastguard Worker // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
29*387f9dfdSAndroid Build Coastguard Worker // For a high level overview of the structure of a ZIP file see
30*387f9dfdSAndroid Build Coastguard Worker // sections 4.3.1 - 4.3.6.
31*387f9dfdSAndroid Build Coastguard Worker 
32*387f9dfdSAndroid Build Coastguard Worker // Data structures appearing in ZIP files do not contain any
33*387f9dfdSAndroid Build Coastguard Worker // padding and they might be misaligned. To allow us to safely
34*387f9dfdSAndroid Build Coastguard Worker // operate on pointers to such structures and their members, without
35*387f9dfdSAndroid Build Coastguard Worker // worrying of platform specific alignment issues, we define
36*387f9dfdSAndroid Build Coastguard Worker // unaligned_uint16_t and unaligned_uint32_t types with no alignment
37*387f9dfdSAndroid Build Coastguard Worker // requirements.
38*387f9dfdSAndroid Build Coastguard Worker typedef struct {
39*387f9dfdSAndroid Build Coastguard Worker   uint8_t raw[2];
40*387f9dfdSAndroid Build Coastguard Worker } unaligned_uint16_t;
41*387f9dfdSAndroid Build Coastguard Worker 
unaligned_uint16_read(unaligned_uint16_t value)42*387f9dfdSAndroid Build Coastguard Worker static uint16_t unaligned_uint16_read(unaligned_uint16_t value) {
43*387f9dfdSAndroid Build Coastguard Worker   uint16_t return_value;
44*387f9dfdSAndroid Build Coastguard Worker   memcpy(&return_value, value.raw, sizeof(return_value));
45*387f9dfdSAndroid Build Coastguard Worker   return return_value;
46*387f9dfdSAndroid Build Coastguard Worker }
47*387f9dfdSAndroid Build Coastguard Worker 
48*387f9dfdSAndroid Build Coastguard Worker typedef struct {
49*387f9dfdSAndroid Build Coastguard Worker   uint8_t raw[4];
50*387f9dfdSAndroid Build Coastguard Worker } unaligned_uint32_t;
51*387f9dfdSAndroid Build Coastguard Worker 
unaligned_uint32_read(unaligned_uint32_t value)52*387f9dfdSAndroid Build Coastguard Worker static uint32_t unaligned_uint32_read(unaligned_uint32_t value) {
53*387f9dfdSAndroid Build Coastguard Worker   uint32_t return_value;
54*387f9dfdSAndroid Build Coastguard Worker   memcpy(&return_value, value.raw, sizeof(return_value));
55*387f9dfdSAndroid Build Coastguard Worker   return return_value;
56*387f9dfdSAndroid Build Coastguard Worker }
57*387f9dfdSAndroid Build Coastguard Worker 
58*387f9dfdSAndroid Build Coastguard Worker #define END_OF_CD_RECORD_MAGIC 0x06054b50
59*387f9dfdSAndroid Build Coastguard Worker 
60*387f9dfdSAndroid Build Coastguard Worker // See section 4.3.16 of the spec.
61*387f9dfdSAndroid Build Coastguard Worker struct end_of_central_directory_record {
62*387f9dfdSAndroid Build Coastguard Worker   // Magic value equal to END_OF_CD_RECORD_MAGIC
63*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t magic;
64*387f9dfdSAndroid Build Coastguard Worker 
65*387f9dfdSAndroid Build Coastguard Worker   // Number of the file containing this structure or 0xFFFF if ZIP64 archive.
66*387f9dfdSAndroid Build Coastguard Worker   // Zip archive might span multiple files (disks).
67*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t this_disk;
68*387f9dfdSAndroid Build Coastguard Worker 
69*387f9dfdSAndroid Build Coastguard Worker   // Number of the file containing the beginning of the central directory or
70*387f9dfdSAndroid Build Coastguard Worker   // 0xFFFF if ZIP64 archive.
71*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t cd_disk;
72*387f9dfdSAndroid Build Coastguard Worker 
73*387f9dfdSAndroid Build Coastguard Worker   // Number of central directory records on this disk or 0xFFFF if ZIP64
74*387f9dfdSAndroid Build Coastguard Worker   // archive.
75*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t cd_records;
76*387f9dfdSAndroid Build Coastguard Worker 
77*387f9dfdSAndroid Build Coastguard Worker   // Number of central directory records on all disks or 0xFFFF if ZIP64
78*387f9dfdSAndroid Build Coastguard Worker   // archive.
79*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t cd_records_total;
80*387f9dfdSAndroid Build Coastguard Worker 
81*387f9dfdSAndroid Build Coastguard Worker   // Size of the central directory recrod or 0xFFFFFFFF if ZIP64 archive.
82*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t cd_size;
83*387f9dfdSAndroid Build Coastguard Worker 
84*387f9dfdSAndroid Build Coastguard Worker   // Offset of the central directory from the beginning of the archive or
85*387f9dfdSAndroid Build Coastguard Worker   // 0xFFFFFFFF if ZIP64 archive.
86*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t cd_offset;
87*387f9dfdSAndroid Build Coastguard Worker 
88*387f9dfdSAndroid Build Coastguard Worker   // Length of comment data following end of central driectory record.
89*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t comment_length;
90*387f9dfdSAndroid Build Coastguard Worker 
91*387f9dfdSAndroid Build Coastguard Worker   // Up to 64k of arbitrary bytes.
92*387f9dfdSAndroid Build Coastguard Worker   // uint8_t comment[comment_length]
93*387f9dfdSAndroid Build Coastguard Worker };
94*387f9dfdSAndroid Build Coastguard Worker 
95*387f9dfdSAndroid Build Coastguard Worker #define CD_FILE_HEADER_MAGIC 0x02014b50
96*387f9dfdSAndroid Build Coastguard Worker #define FLAG_ENCRYPTED (1 << 0)
97*387f9dfdSAndroid Build Coastguard Worker #define FLAG_HAS_DATA_DESCRIPTOR (1 << 3)
98*387f9dfdSAndroid Build Coastguard Worker 
99*387f9dfdSAndroid Build Coastguard Worker // See section 4.3.12 of the spec.
100*387f9dfdSAndroid Build Coastguard Worker struct central_directory_file_header {
101*387f9dfdSAndroid Build Coastguard Worker   // Magic value equal to CD_FILE_HEADER_MAGIC.
102*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t magic;
103*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t version;
104*387f9dfdSAndroid Build Coastguard Worker   // Minimum zip version needed to extract the file.
105*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t min_version;
106*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t flags;
107*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t compression;
108*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t last_modified_time;
109*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t last_modified_date;
110*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t crc;
111*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t compressed_size;
112*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t uncompressed_size;
113*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t file_name_length;
114*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t extra_field_length;
115*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t file_comment_length;
116*387f9dfdSAndroid Build Coastguard Worker   // Number of the disk where the file starts or 0xFFFF if ZIP64 archive.
117*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t disk;
118*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t internal_attributes;
119*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t external_attributes;
120*387f9dfdSAndroid Build Coastguard Worker   // Offset from the start of the disk containing the local file header to the
121*387f9dfdSAndroid Build Coastguard Worker   // start of the local file header.
122*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t offset;
123*387f9dfdSAndroid Build Coastguard Worker };
124*387f9dfdSAndroid Build Coastguard Worker 
125*387f9dfdSAndroid Build Coastguard Worker #define LOCAL_FILE_HEADER_MAGIC 0x04034b50
126*387f9dfdSAndroid Build Coastguard Worker 
127*387f9dfdSAndroid Build Coastguard Worker // See section 4.3.7 of the spec.
128*387f9dfdSAndroid Build Coastguard Worker struct local_file_header {
129*387f9dfdSAndroid Build Coastguard Worker   // Magic value equal to LOCAL_FILE_HEADER_MAGIC.
130*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t magic;
131*387f9dfdSAndroid Build Coastguard Worker   // Minimum zip version needed to extract the file.
132*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t min_version;
133*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t flags;
134*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t compression;
135*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t last_modified_time;
136*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t last_modified_date;
137*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t crc;
138*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t compressed_size;
139*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint32_t uncompressed_size;
140*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t file_name_length;
141*387f9dfdSAndroid Build Coastguard Worker   unaligned_uint16_t extra_field_length;
142*387f9dfdSAndroid Build Coastguard Worker };
143*387f9dfdSAndroid Build Coastguard Worker 
144*387f9dfdSAndroid Build Coastguard Worker struct bcc_zip_archive {
145*387f9dfdSAndroid Build Coastguard Worker   void* data;
146*387f9dfdSAndroid Build Coastguard Worker   uint32_t size;
147*387f9dfdSAndroid Build Coastguard Worker   uint32_t cd_offset;
148*387f9dfdSAndroid Build Coastguard Worker   uint32_t cd_records;
149*387f9dfdSAndroid Build Coastguard Worker };
150*387f9dfdSAndroid Build Coastguard Worker 
check_access(struct bcc_zip_archive * archive,uint32_t offset,uint32_t size)151*387f9dfdSAndroid Build Coastguard Worker static void* check_access(struct bcc_zip_archive* archive, uint32_t offset,
152*387f9dfdSAndroid Build Coastguard Worker                           uint32_t size) {
153*387f9dfdSAndroid Build Coastguard Worker   if (offset + size > archive->size || offset > offset + size) {
154*387f9dfdSAndroid Build Coastguard Worker     return NULL;
155*387f9dfdSAndroid Build Coastguard Worker   }
156*387f9dfdSAndroid Build Coastguard Worker   return (char *) archive->data + offset;
157*387f9dfdSAndroid Build Coastguard Worker }
158*387f9dfdSAndroid Build Coastguard Worker 
159*387f9dfdSAndroid Build Coastguard Worker // Returns 0 on success, -1 on error and -2 if the eocd indicates
160*387f9dfdSAndroid Build Coastguard Worker // the archive uses features which are not supported.
try_parse_end_of_central_directory(struct bcc_zip_archive * archive,uint32_t offset)161*387f9dfdSAndroid Build Coastguard Worker static int try_parse_end_of_central_directory(struct bcc_zip_archive* archive,
162*387f9dfdSAndroid Build Coastguard Worker                                               uint32_t offset) {
163*387f9dfdSAndroid Build Coastguard Worker   struct end_of_central_directory_record* eocd = check_access(
164*387f9dfdSAndroid Build Coastguard Worker       archive, offset, sizeof(struct end_of_central_directory_record));
165*387f9dfdSAndroid Build Coastguard Worker   if (eocd == NULL ||
166*387f9dfdSAndroid Build Coastguard Worker       unaligned_uint32_read(eocd->magic) != END_OF_CD_RECORD_MAGIC) {
167*387f9dfdSAndroid Build Coastguard Worker     return -1;
168*387f9dfdSAndroid Build Coastguard Worker   }
169*387f9dfdSAndroid Build Coastguard Worker 
170*387f9dfdSAndroid Build Coastguard Worker   uint16_t comment_length = unaligned_uint16_read(eocd->comment_length);
171*387f9dfdSAndroid Build Coastguard Worker   if (offset + sizeof(struct end_of_central_directory_record) +
172*387f9dfdSAndroid Build Coastguard Worker           comment_length !=
173*387f9dfdSAndroid Build Coastguard Worker       archive->size) {
174*387f9dfdSAndroid Build Coastguard Worker     return -1;
175*387f9dfdSAndroid Build Coastguard Worker   }
176*387f9dfdSAndroid Build Coastguard Worker 
177*387f9dfdSAndroid Build Coastguard Worker   uint16_t cd_records = unaligned_uint16_read(eocd->cd_records);
178*387f9dfdSAndroid Build Coastguard Worker   if (unaligned_uint16_read(eocd->this_disk) != 0 ||
179*387f9dfdSAndroid Build Coastguard Worker       unaligned_uint16_read(eocd->cd_disk) != 0 ||
180*387f9dfdSAndroid Build Coastguard Worker       unaligned_uint16_read(eocd->cd_records_total) != cd_records) {
181*387f9dfdSAndroid Build Coastguard Worker     // This is a valid eocd, but we only support single-file non-ZIP64 archives.
182*387f9dfdSAndroid Build Coastguard Worker     return -2;
183*387f9dfdSAndroid Build Coastguard Worker   }
184*387f9dfdSAndroid Build Coastguard Worker 
185*387f9dfdSAndroid Build Coastguard Worker   uint32_t cd_offset = unaligned_uint32_read(eocd->cd_offset);
186*387f9dfdSAndroid Build Coastguard Worker   uint32_t cd_size = unaligned_uint32_read(eocd->cd_size);
187*387f9dfdSAndroid Build Coastguard Worker   if (check_access(archive, cd_offset, cd_size) == NULL) {
188*387f9dfdSAndroid Build Coastguard Worker     return -1;
189*387f9dfdSAndroid Build Coastguard Worker   }
190*387f9dfdSAndroid Build Coastguard Worker 
191*387f9dfdSAndroid Build Coastguard Worker   archive->cd_offset = cd_offset;
192*387f9dfdSAndroid Build Coastguard Worker   archive->cd_records = cd_records;
193*387f9dfdSAndroid Build Coastguard Worker   return 0;
194*387f9dfdSAndroid Build Coastguard Worker }
195*387f9dfdSAndroid Build Coastguard Worker 
find_central_directory(struct bcc_zip_archive * archive)196*387f9dfdSAndroid Build Coastguard Worker static int find_central_directory(struct bcc_zip_archive* archive) {
197*387f9dfdSAndroid Build Coastguard Worker   if (archive->size <= sizeof(struct end_of_central_directory_record)) {
198*387f9dfdSAndroid Build Coastguard Worker     return -1;
199*387f9dfdSAndroid Build Coastguard Worker   }
200*387f9dfdSAndroid Build Coastguard Worker 
201*387f9dfdSAndroid Build Coastguard Worker   int rc = -1;
202*387f9dfdSAndroid Build Coastguard Worker   // Because the end of central directory ends with a variable length array of
203*387f9dfdSAndroid Build Coastguard Worker   // up to 0xFFFF bytes we can't know exactly where it starts and need to
204*387f9dfdSAndroid Build Coastguard Worker   // search for it at the end of the file, scanning the (limit, offset] range.
205*387f9dfdSAndroid Build Coastguard Worker   int64_t offset =
206*387f9dfdSAndroid Build Coastguard Worker       (int64_t)archive->size - sizeof(struct end_of_central_directory_record);
207*387f9dfdSAndroid Build Coastguard Worker   int64_t limit = offset - (1 << 16);
208*387f9dfdSAndroid Build Coastguard Worker   for (; offset >= 0 && offset > limit && rc == -1; offset--) {
209*387f9dfdSAndroid Build Coastguard Worker     rc = try_parse_end_of_central_directory(archive, offset);
210*387f9dfdSAndroid Build Coastguard Worker   }
211*387f9dfdSAndroid Build Coastguard Worker 
212*387f9dfdSAndroid Build Coastguard Worker   return rc;
213*387f9dfdSAndroid Build Coastguard Worker }
214*387f9dfdSAndroid Build Coastguard Worker 
bcc_zip_archive_open(const char * path)215*387f9dfdSAndroid Build Coastguard Worker struct bcc_zip_archive* bcc_zip_archive_open(const char* path) {
216*387f9dfdSAndroid Build Coastguard Worker   int fd = open(path, O_RDONLY);
217*387f9dfdSAndroid Build Coastguard Worker   if (fd < 0) {
218*387f9dfdSAndroid Build Coastguard Worker     return NULL;
219*387f9dfdSAndroid Build Coastguard Worker   }
220*387f9dfdSAndroid Build Coastguard Worker 
221*387f9dfdSAndroid Build Coastguard Worker   off_t size = lseek(fd, 0, SEEK_END);
222*387f9dfdSAndroid Build Coastguard Worker   if (size == (off_t)-1 || size > UINT32_MAX) {
223*387f9dfdSAndroid Build Coastguard Worker     close(fd);
224*387f9dfdSAndroid Build Coastguard Worker     return NULL;
225*387f9dfdSAndroid Build Coastguard Worker   }
226*387f9dfdSAndroid Build Coastguard Worker 
227*387f9dfdSAndroid Build Coastguard Worker   void* data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
228*387f9dfdSAndroid Build Coastguard Worker   close(fd);
229*387f9dfdSAndroid Build Coastguard Worker 
230*387f9dfdSAndroid Build Coastguard Worker   if (data == MAP_FAILED) {
231*387f9dfdSAndroid Build Coastguard Worker     return NULL;
232*387f9dfdSAndroid Build Coastguard Worker   }
233*387f9dfdSAndroid Build Coastguard Worker 
234*387f9dfdSAndroid Build Coastguard Worker   struct bcc_zip_archive* archive = malloc(sizeof(struct bcc_zip_archive));
235*387f9dfdSAndroid Build Coastguard Worker   if (archive == NULL) {
236*387f9dfdSAndroid Build Coastguard Worker     munmap(data, size);
237*387f9dfdSAndroid Build Coastguard Worker     return NULL;
238*387f9dfdSAndroid Build Coastguard Worker   };
239*387f9dfdSAndroid Build Coastguard Worker 
240*387f9dfdSAndroid Build Coastguard Worker   archive->data = data;
241*387f9dfdSAndroid Build Coastguard Worker   archive->size = size;
242*387f9dfdSAndroid Build Coastguard Worker   if (find_central_directory(archive)) {
243*387f9dfdSAndroid Build Coastguard Worker     munmap(data, size);
244*387f9dfdSAndroid Build Coastguard Worker     free(archive);
245*387f9dfdSAndroid Build Coastguard Worker     archive = NULL;
246*387f9dfdSAndroid Build Coastguard Worker   }
247*387f9dfdSAndroid Build Coastguard Worker 
248*387f9dfdSAndroid Build Coastguard Worker   return archive;
249*387f9dfdSAndroid Build Coastguard Worker }
250*387f9dfdSAndroid Build Coastguard Worker 
bcc_zip_archive_close(struct bcc_zip_archive * archive)251*387f9dfdSAndroid Build Coastguard Worker void bcc_zip_archive_close(struct bcc_zip_archive* archive) {
252*387f9dfdSAndroid Build Coastguard Worker   munmap(archive->data, archive->size);
253*387f9dfdSAndroid Build Coastguard Worker   free(archive);
254*387f9dfdSAndroid Build Coastguard Worker }
255*387f9dfdSAndroid Build Coastguard Worker 
local_file_header_at_offset(struct bcc_zip_archive * archive,uint32_t offset)256*387f9dfdSAndroid Build Coastguard Worker static struct local_file_header* local_file_header_at_offset(
257*387f9dfdSAndroid Build Coastguard Worker     struct bcc_zip_archive* archive, uint32_t offset) {
258*387f9dfdSAndroid Build Coastguard Worker   struct local_file_header* lfh =
259*387f9dfdSAndroid Build Coastguard Worker       check_access(archive, offset, sizeof(struct local_file_header));
260*387f9dfdSAndroid Build Coastguard Worker   if (lfh == NULL ||
261*387f9dfdSAndroid Build Coastguard Worker       unaligned_uint32_read(lfh->magic) != LOCAL_FILE_HEADER_MAGIC) {
262*387f9dfdSAndroid Build Coastguard Worker     return NULL;
263*387f9dfdSAndroid Build Coastguard Worker   }
264*387f9dfdSAndroid Build Coastguard Worker   return lfh;
265*387f9dfdSAndroid Build Coastguard Worker }
266*387f9dfdSAndroid Build Coastguard Worker 
get_entry_at_offset(struct bcc_zip_archive * archive,uint32_t offset,struct bcc_zip_entry * out)267*387f9dfdSAndroid Build Coastguard Worker static int get_entry_at_offset(struct bcc_zip_archive* archive, uint32_t offset,
268*387f9dfdSAndroid Build Coastguard Worker                                struct bcc_zip_entry* out) {
269*387f9dfdSAndroid Build Coastguard Worker   struct local_file_header* lfh = local_file_header_at_offset(archive, offset);
270*387f9dfdSAndroid Build Coastguard Worker   offset += sizeof(struct local_file_header);
271*387f9dfdSAndroid Build Coastguard Worker   if (lfh == NULL) {
272*387f9dfdSAndroid Build Coastguard Worker     return -1;
273*387f9dfdSAndroid Build Coastguard Worker   };
274*387f9dfdSAndroid Build Coastguard Worker 
275*387f9dfdSAndroid Build Coastguard Worker   uint16_t flags = unaligned_uint16_read(lfh->flags);
276*387f9dfdSAndroid Build Coastguard Worker   if ((flags & FLAG_ENCRYPTED) || (flags & FLAG_HAS_DATA_DESCRIPTOR)) {
277*387f9dfdSAndroid Build Coastguard Worker     return -1;
278*387f9dfdSAndroid Build Coastguard Worker   }
279*387f9dfdSAndroid Build Coastguard Worker 
280*387f9dfdSAndroid Build Coastguard Worker   uint16_t name_length = unaligned_uint16_read(lfh->file_name_length);
281*387f9dfdSAndroid Build Coastguard Worker   const char* name = check_access(archive, offset, name_length);
282*387f9dfdSAndroid Build Coastguard Worker   offset += name_length;
283*387f9dfdSAndroid Build Coastguard Worker   if (name == NULL) {
284*387f9dfdSAndroid Build Coastguard Worker     return -1;
285*387f9dfdSAndroid Build Coastguard Worker   }
286*387f9dfdSAndroid Build Coastguard Worker 
287*387f9dfdSAndroid Build Coastguard Worker   uint16_t extra_field_length = unaligned_uint16_read(lfh->extra_field_length);
288*387f9dfdSAndroid Build Coastguard Worker   if (check_access(archive, offset, extra_field_length) == NULL) {
289*387f9dfdSAndroid Build Coastguard Worker     return -1;
290*387f9dfdSAndroid Build Coastguard Worker   }
291*387f9dfdSAndroid Build Coastguard Worker   offset += extra_field_length;
292*387f9dfdSAndroid Build Coastguard Worker 
293*387f9dfdSAndroid Build Coastguard Worker   uint32_t compressed_size = unaligned_uint32_read(lfh->compressed_size);
294*387f9dfdSAndroid Build Coastguard Worker   void* data = check_access(archive, offset, compressed_size);
295*387f9dfdSAndroid Build Coastguard Worker   if (data == NULL) {
296*387f9dfdSAndroid Build Coastguard Worker     return -1;
297*387f9dfdSAndroid Build Coastguard Worker   }
298*387f9dfdSAndroid Build Coastguard Worker 
299*387f9dfdSAndroid Build Coastguard Worker   out->compression = unaligned_uint16_read(lfh->compression);
300*387f9dfdSAndroid Build Coastguard Worker   out->name_length = name_length;
301*387f9dfdSAndroid Build Coastguard Worker   out->name = name;
302*387f9dfdSAndroid Build Coastguard Worker   out->data = data;
303*387f9dfdSAndroid Build Coastguard Worker   out->data_length = compressed_size;
304*387f9dfdSAndroid Build Coastguard Worker   out->data_offset = offset;
305*387f9dfdSAndroid Build Coastguard Worker 
306*387f9dfdSAndroid Build Coastguard Worker   return 0;
307*387f9dfdSAndroid Build Coastguard Worker }
308*387f9dfdSAndroid Build Coastguard Worker 
cd_file_header_at_offset(struct bcc_zip_archive * archive,uint32_t offset)309*387f9dfdSAndroid Build Coastguard Worker static struct central_directory_file_header* cd_file_header_at_offset(
310*387f9dfdSAndroid Build Coastguard Worker     struct bcc_zip_archive* archive, uint32_t offset) {
311*387f9dfdSAndroid Build Coastguard Worker   struct central_directory_file_header* cdfh = check_access(
312*387f9dfdSAndroid Build Coastguard Worker       archive, offset, sizeof(struct central_directory_file_header));
313*387f9dfdSAndroid Build Coastguard Worker   if (cdfh == NULL ||
314*387f9dfdSAndroid Build Coastguard Worker       unaligned_uint32_read(cdfh->magic) != CD_FILE_HEADER_MAGIC) {
315*387f9dfdSAndroid Build Coastguard Worker     return NULL;
316*387f9dfdSAndroid Build Coastguard Worker   }
317*387f9dfdSAndroid Build Coastguard Worker   return cdfh;
318*387f9dfdSAndroid Build Coastguard Worker }
319*387f9dfdSAndroid Build Coastguard Worker 
bcc_zip_archive_find_entry(struct bcc_zip_archive * archive,const char * file_name,struct bcc_zip_entry * out)320*387f9dfdSAndroid Build Coastguard Worker int bcc_zip_archive_find_entry(struct bcc_zip_archive* archive,
321*387f9dfdSAndroid Build Coastguard Worker                                const char* file_name,
322*387f9dfdSAndroid Build Coastguard Worker                                struct bcc_zip_entry* out) {
323*387f9dfdSAndroid Build Coastguard Worker   size_t file_name_length = strlen(file_name);
324*387f9dfdSAndroid Build Coastguard Worker 
325*387f9dfdSAndroid Build Coastguard Worker   uint32_t offset = archive->cd_offset;
326*387f9dfdSAndroid Build Coastguard Worker   for (uint32_t i = 0; i < archive->cd_records; ++i) {
327*387f9dfdSAndroid Build Coastguard Worker     struct central_directory_file_header* cdfh =
328*387f9dfdSAndroid Build Coastguard Worker         cd_file_header_at_offset(archive, offset);
329*387f9dfdSAndroid Build Coastguard Worker     offset += sizeof(struct central_directory_file_header);
330*387f9dfdSAndroid Build Coastguard Worker     if (cdfh == NULL) {
331*387f9dfdSAndroid Build Coastguard Worker       return -1;
332*387f9dfdSAndroid Build Coastguard Worker     }
333*387f9dfdSAndroid Build Coastguard Worker 
334*387f9dfdSAndroid Build Coastguard Worker     uint16_t cdfh_name_length = unaligned_uint16_read(cdfh->file_name_length);
335*387f9dfdSAndroid Build Coastguard Worker     const char* cdfh_name = check_access(archive, offset, cdfh_name_length);
336*387f9dfdSAndroid Build Coastguard Worker     if (cdfh_name == NULL) {
337*387f9dfdSAndroid Build Coastguard Worker       return -1;
338*387f9dfdSAndroid Build Coastguard Worker     }
339*387f9dfdSAndroid Build Coastguard Worker 
340*387f9dfdSAndroid Build Coastguard Worker     uint16_t cdfh_flags = unaligned_uint16_read(cdfh->flags);
341*387f9dfdSAndroid Build Coastguard Worker     if ((cdfh_flags & FLAG_ENCRYPTED) == 0 &&
342*387f9dfdSAndroid Build Coastguard Worker         (cdfh_flags & FLAG_HAS_DATA_DESCRIPTOR) == 0 &&
343*387f9dfdSAndroid Build Coastguard Worker         file_name_length == cdfh_name_length &&
344*387f9dfdSAndroid Build Coastguard Worker         memcmp(file_name, (char*) archive->data + offset, file_name_length) == 0) {
345*387f9dfdSAndroid Build Coastguard Worker       return get_entry_at_offset(archive, unaligned_uint32_read(cdfh->offset),
346*387f9dfdSAndroid Build Coastguard Worker                                  out);
347*387f9dfdSAndroid Build Coastguard Worker     }
348*387f9dfdSAndroid Build Coastguard Worker 
349*387f9dfdSAndroid Build Coastguard Worker     offset += cdfh_name_length;
350*387f9dfdSAndroid Build Coastguard Worker     offset += unaligned_uint16_read(cdfh->extra_field_length);
351*387f9dfdSAndroid Build Coastguard Worker     offset += unaligned_uint16_read(cdfh->file_comment_length);
352*387f9dfdSAndroid Build Coastguard Worker   }
353*387f9dfdSAndroid Build Coastguard Worker 
354*387f9dfdSAndroid Build Coastguard Worker   return -1;
355*387f9dfdSAndroid Build Coastguard Worker }
356*387f9dfdSAndroid Build Coastguard Worker 
bcc_zip_archive_find_entry_at_offset(struct bcc_zip_archive * archive,uint32_t target,struct bcc_zip_entry * out)357*387f9dfdSAndroid Build Coastguard Worker int bcc_zip_archive_find_entry_at_offset(struct bcc_zip_archive* archive,
358*387f9dfdSAndroid Build Coastguard Worker                                          uint32_t target,
359*387f9dfdSAndroid Build Coastguard Worker                                          struct bcc_zip_entry* out) {
360*387f9dfdSAndroid Build Coastguard Worker   uint32_t offset = archive->cd_offset;
361*387f9dfdSAndroid Build Coastguard Worker   for (uint32_t i = 0; i < archive->cd_records; ++i) {
362*387f9dfdSAndroid Build Coastguard Worker     struct central_directory_file_header* cdfh =
363*387f9dfdSAndroid Build Coastguard Worker         cd_file_header_at_offset(archive, offset);
364*387f9dfdSAndroid Build Coastguard Worker     offset += sizeof(struct central_directory_file_header);
365*387f9dfdSAndroid Build Coastguard Worker     if (cdfh == NULL) {
366*387f9dfdSAndroid Build Coastguard Worker       return -1;
367*387f9dfdSAndroid Build Coastguard Worker     }
368*387f9dfdSAndroid Build Coastguard Worker 
369*387f9dfdSAndroid Build Coastguard Worker     uint16_t cdfh_flags = unaligned_uint16_read(cdfh->flags);
370*387f9dfdSAndroid Build Coastguard Worker     if ((cdfh_flags & FLAG_ENCRYPTED) == 0 &&
371*387f9dfdSAndroid Build Coastguard Worker         (cdfh_flags & FLAG_HAS_DATA_DESCRIPTOR) == 0) {
372*387f9dfdSAndroid Build Coastguard Worker       if (get_entry_at_offset(archive, unaligned_uint32_read(cdfh->offset),
373*387f9dfdSAndroid Build Coastguard Worker                               out)) {
374*387f9dfdSAndroid Build Coastguard Worker         return -1;
375*387f9dfdSAndroid Build Coastguard Worker       }
376*387f9dfdSAndroid Build Coastguard Worker 
377*387f9dfdSAndroid Build Coastguard Worker       if ((char*) out->data <= (char*) archive->data + target &&
378*387f9dfdSAndroid Build Coastguard Worker           (char*) archive->data + target < (char*) out->data + out->data_length) {
379*387f9dfdSAndroid Build Coastguard Worker         return 0;
380*387f9dfdSAndroid Build Coastguard Worker       }
381*387f9dfdSAndroid Build Coastguard Worker     }
382*387f9dfdSAndroid Build Coastguard Worker 
383*387f9dfdSAndroid Build Coastguard Worker     offset += unaligned_uint16_read(cdfh->file_name_length);
384*387f9dfdSAndroid Build Coastguard Worker     offset += unaligned_uint16_read(cdfh->extra_field_length);
385*387f9dfdSAndroid Build Coastguard Worker     offset += unaligned_uint16_read(cdfh->file_comment_length);
386*387f9dfdSAndroid Build Coastguard Worker   }
387*387f9dfdSAndroid Build Coastguard Worker 
388*387f9dfdSAndroid Build Coastguard Worker   return -1;
389*387f9dfdSAndroid Build Coastguard Worker }
390*387f9dfdSAndroid Build Coastguard Worker 
bcc_zip_archive_open_and_find(const char * path,struct bcc_zip_entry * out)391*387f9dfdSAndroid Build Coastguard Worker struct bcc_zip_archive* bcc_zip_archive_open_and_find(
392*387f9dfdSAndroid Build Coastguard Worker     const char* path, struct bcc_zip_entry* out) {
393*387f9dfdSAndroid Build Coastguard Worker   struct bcc_zip_archive* archive = NULL;
394*387f9dfdSAndroid Build Coastguard Worker   const char* separator = strstr(path, "!/");
395*387f9dfdSAndroid Build Coastguard Worker   if (separator == NULL || separator - path >= PATH_MAX) {
396*387f9dfdSAndroid Build Coastguard Worker     return NULL;
397*387f9dfdSAndroid Build Coastguard Worker   }
398*387f9dfdSAndroid Build Coastguard Worker 
399*387f9dfdSAndroid Build Coastguard Worker   char archive_path[PATH_MAX];
400*387f9dfdSAndroid Build Coastguard Worker   strncpy(archive_path, path, separator - path);
401*387f9dfdSAndroid Build Coastguard Worker   archive_path[separator - path] = 0;
402*387f9dfdSAndroid Build Coastguard Worker   archive = bcc_zip_archive_open(archive_path);
403*387f9dfdSAndroid Build Coastguard Worker   if (archive == NULL) {
404*387f9dfdSAndroid Build Coastguard Worker     return NULL;
405*387f9dfdSAndroid Build Coastguard Worker   }
406*387f9dfdSAndroid Build Coastguard Worker 
407*387f9dfdSAndroid Build Coastguard Worker   if (bcc_zip_archive_find_entry(archive, separator + 2, out)) {
408*387f9dfdSAndroid Build Coastguard Worker     bcc_zip_archive_close(archive);
409*387f9dfdSAndroid Build Coastguard Worker     return NULL;
410*387f9dfdSAndroid Build Coastguard Worker   }
411*387f9dfdSAndroid Build Coastguard Worker 
412*387f9dfdSAndroid Build Coastguard Worker   return archive;
413*387f9dfdSAndroid Build Coastguard Worker }
414