1 /* Copyright (C) 2021-2023 Free Software Foundation, Inc.
4 This file is part of GNU Binutils.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
22 #include <sys/types.h>
29 #include "DbeJarFile.h"
30 #include "Data_window.h"
34 get_u1 (unsigned char *b
)
36 return (uint32_t) ((b
)[0]);
40 get_u2 (unsigned char *b
)
42 return (get_u1 (b
+ 1) << 8) | get_u1 (b
);
46 get_u4 (unsigned char *b
)
48 return (get_u2 (b
+ 2) << 16) | get_u2 (b
);
52 get_u8 (unsigned char *b
)
54 return (((uint64_t) get_u4 (b
+ 4)) << 32) | get_u4 (b
);
59 END_CENT_DIR_SIZE
= 22,
60 LOC_FILE_HEADER_SIZE
= 30,
61 CENT_FILE_HEADER_SIZE
= 46,
62 ZIP64_LOCATOR_SIZE
= 20,
63 ZIP64_CENT_DIR_SIZE
= 56,
90 compare (ZipEntry
*ze
)
92 return dbe_strcmp (name
, ze
->name
);
95 char *name
; // entry name
96 int time
; // modification time
97 int64_t size
; // size of uncompressed data
98 int64_t csize
; // size of compressed data (zero if uncompressed)
99 uint32_t compressionMethod
;
100 int64_t offset
; // offset of LOC header
105 cmp_names (const void *a
, const void *b
)
107 ZipEntry
*e1
= *((ZipEntry
**) a
);
108 ZipEntry
*e2
= *((ZipEntry
**) b
);
109 return e1
->compare (e2
);
112 template<> void Vector
<ZipEntry
*>::dump (const char *msg
)
114 Dprintf (1, NTXT ("Vector<ZipEntry *> %s [%lld]\n"), msg
? msg
: NTXT (""), (long long) size ());
115 for (long i
= 0, sz
= size (); i
< sz
; i
++)
117 ZipEntry
*ze
= get (i
);
118 Dprintf (1, NTXT (" %lld offset:%lld (0x%llx) size: %lld --> %lld %s\n"),
119 (long long) i
, (long long) ze
->offset
, (long long) ze
->offset
,
120 (long long) ze
->csize
, (long long) ze
->size
, STR (ze
->name
));
124 DbeJarFile::DbeJarFile (const char *jarName
)
126 name
= strdup (jarName
);
128 dwin
= new Data_window (name
);
132 DbeJarFile::~DbeJarFile ()
139 DbeJarFile::get_entries ()
141 Dprintf (DUMP_JAR_FILE
, NTXT ("\nArchive: %s\n"), STR (name
));
142 if (dwin
->not_opened ())
144 append_msg (CMSG_ERROR
, GTXT ("Cannot open file `%s'"), name
);
147 struct EndCentDir endCentDir
;
148 if (get_EndCentDir (&endCentDir
) == 0)
151 if (endCentDir
.count
== 0)
153 append_msg (CMSG_WARN
, GTXT ("No files in %s"), name
);
156 unsigned char *b
= (unsigned char *) dwin
->bind (endCentDir
.offset
, endCentDir
.size
);
159 append_msg (CMSG_ERROR
, GTXT ("%s: cannot read the central directory record"), name
);
163 fnames
= new Vector
<ZipEntry
*>(endCentDir
.count
);
164 for (uint64_t i
= 0, offset
= endCentDir
.offset
, last
= endCentDir
.offset
+ endCentDir
.size
; i
< endCentDir
.count
; i
++)
166 if ((last
- offset
) < CENT_FILE_HEADER_SIZE
)
168 append_msg (CMSG_ERROR
, GTXT ("%s: cannot read the central file header (%lld (from %lld), offset=0x%016llx last=0x%016llx"),
169 name
, (long long) i
, (long long) endCentDir
.count
, (long long) offset
, (long long) last
);
172 b
= (unsigned char *) dwin
->bind (offset
, CENT_FILE_HEADER_SIZE
);
173 // Central file header
174 // Offset Bytes Description
175 // 0 4 central file header signature = 0x02014b50
176 // 4 2 version made by
177 // 6 2 version needed to extract
178 // 8 2 general purpose bit flag
179 // 10 2 compression method
180 // 12 2 last mod file time
181 // 14 2 last mod file date
183 // 20 4 compressed size
184 // 24 4 uncompressed size
185 // 28 2 file name length
186 // 30 2 extra field length
187 // 32 2 file comment length
188 // 34 2 disk number start
189 // 36 2 internal file attributes
190 // 38 4 external file attributes
191 // 42 4 relative offset of local header
192 // 46 file name (variable size)
193 // extra field (variable size)
194 // file comment (variable size)
195 uint32_t signature
= get_u4 (b
);
196 if (signature
!= 0x02014b50)
198 append_msg (CMSG_ERROR
, GTXT ("%s: wrong header signature (%lld (total %lld), offset=0x%016llx last=0x%016llx"),
199 name
, (long long) i
, (long long) endCentDir
.count
, (long long) offset
, (long long) last
);
202 ZipEntry
*ze
= new ZipEntry ();
204 uint32_t name_len
= get_u2 (b
+ 28);
205 uint32_t extra_len
= get_u2 (b
+ 30);
206 uint32_t comment_len
= get_u2 (b
+ 32);
207 ze
->compressionMethod
= get_u2 (b
+ 10);
208 ze
->csize
= get_u4 (b
+ 20);
209 ze
->size
= get_u4 (b
+ 24);
210 ze
->offset
= get_u4 (b
+ 42);
211 char *nm
= (char *) dwin
->bind (offset
+ 46, name_len
);
214 ze
->name
= (char *) malloc (name_len
+ 1);
215 strncpy (ze
->name
, nm
, name_len
);
216 ze
->name
[name_len
] = 0;
218 offset
+= CENT_FILE_HEADER_SIZE
+ name_len
+ extra_len
+ comment_len
;
220 fnames
->sort (cmp_names
);
222 fnames
->dump (get_basename (name
));
226 DbeJarFile::get_entry (const char *fname
)
230 ZipEntry zipEntry
, *ze
= &zipEntry
;
231 ze
->name
= (char *) fname
;
232 int ind
= fnames
->bisearch (0, -1, &ze
, cmp_names
);
238 DbeJarFile::copy (char *toFileNname
, int fromEntryNum
)
240 if (fromEntryNum
< 0 || fromEntryNum
>= VecSize (fnames
))
242 ZipEntry
*ze
= fnames
->get (fromEntryNum
);
243 if (ze
->data_offset
== 0)
246 // Offset Bytes Description
247 // 0 4 local file header signature = 0x04034b50
248 // 4 2 version needed to extract
249 // 6 2 general purpose bit flag
250 // 8 2 compression method
251 // 10 2 last mod file time
252 // 12 2 last mod file date
254 // 18 4 compressed size
255 // 22 4 uncompressed size
256 // 26 2 file name length
257 // 28 2 extra field length
258 // 30 2 file name (variable size)
259 // extra field (variable size)
260 unsigned char *b
= (unsigned char *) dwin
->bind (ze
->offset
, LOC_FILE_HEADER_SIZE
);
263 append_msg (CMSG_ERROR
,
264 GTXT ("%s: Cannot read a local file header (%s offset=0x%lld"),
265 name
, STR (ze
->name
), (long long) ze
->offset
);
268 uint32_t signature
= get_u4 (b
);
269 if (signature
!= 0x04034b50)
271 append_msg (CMSG_ERROR
,
272 GTXT ("%s: wrong local header signature ('%s' offset=%lld (0x%llx)"),
273 name
, STR (ze
->name
), (long long) ze
->offset
,
274 (long long) ze
->offset
);
277 ze
->data_offset
= ze
->offset
+ LOC_FILE_HEADER_SIZE
+ get_u2 (b
+ 26) + get_u2 (b
+ 28);
280 if (ze
->compressionMethod
== 0)
282 int fd
= open (toFileNname
, O_CREAT
| O_WRONLY
| O_LARGEFILE
, 0644);
285 append_msg (CMSG_ERROR
, GTXT ("Cannot create file %s (%s)"), toFileNname
, STR (strerror (errno
)));
288 long long len
= dwin
->copy_to_file (fd
, ze
->data_offset
, ze
->size
);
292 append_msg (CMSG_ERROR
, GTXT ("%s: Cannot write %lld bytes (only %lld)"),
293 toFileNname
, (long long) ze
->size
, (long long) len
);
294 unlink (toFileNname
);
300 unsigned char *b
= (unsigned char *) dwin
->bind (ze
->data_offset
, ze
->csize
);
303 append_msg (CMSG_ERROR
,
304 GTXT ("%s: Cannot extract file %s (offset=0x%lld csize=%lld)"),
305 name
, STR (ze
->name
), (long long) ze
->offset
,
306 (long long) ze
->csize
);
310 strm
.zalloc
= Z_NULL
;
312 strm
.opaque
= Z_NULL
;
313 strm
.next_in
= Z_NULL
;
315 if (inflateInit2 (&strm
, -MAX_WBITS
) != Z_OK
)
317 append_msg (CMSG_ERROR
, GTXT ("%s: inflateInit2 failed (%s)"), STR (ze
->name
), STR (strm
.msg
));
320 strm
.avail_in
= ze
->csize
;
322 int retval
= ze
->size
;
323 unsigned char *buf
= (unsigned char *) malloc (ze
->size
);
327 strm
.avail_out
= ze
->size
;
328 int ret
= inflate (&strm
, Z_SYNC_FLUSH
);
329 if ((ret
== Z_NEED_DICT
) || (ret
== Z_DATA_ERROR
) || (ret
== Z_MEM_ERROR
) || (ret
== Z_STREAM_ERROR
))
331 append_msg (CMSG_ERROR
, GTXT ("%s: inflate('%s') error %d (%s)"), name
, STR (ze
->name
), ret
, STR (strm
.msg
));
335 if (strm
.avail_out
!= 0)
341 int fd
= open (toFileNname
, O_CREAT
| O_WRONLY
| O_LARGEFILE
, 0644);
344 append_msg (CMSG_ERROR
, GTXT ("Cannot create file %s (%s)"), toFileNname
, STR (strerror (errno
)));
349 long long len
= write (fd
, buf
, ze
->size
);
352 append_msg (CMSG_ERROR
, GTXT ("%s: Cannot write %lld bytes (only %lld)"),
353 toFileNname
, (long long) strm
.avail_out
, (long long) len
);
364 DbeJarFile::get_EndCentDir (struct EndCentDir
*endCentDir
)
366 int64_t fsize
= dwin
->get_fsize ();
367 int64_t sz
= (fsize
< ZIP_BUF_SIZE
) ? fsize
: ZIP_BUF_SIZE
;
369 // Find the end of central directory record:
370 unsigned char *b
= (unsigned char *) dwin
->bind (fsize
- sz
, sz
);
373 append_msg (CMSG_ERROR
, GTXT ("%s: cannot find the central directory record (fsize=%lld)"),
374 name
, (long long) fsize
);
378 // End of central directory record:
379 // Offset Bytes Description
380 // 0 4 end of central directory signature = 0x06054b50
381 // 4 2 number of this disk
382 // 6 2 disk where central directory starts
383 // 8 2 number of central directory records on this disk
384 // 10 2 total number of central directory records
385 // 12 4 size of central directory(bytes)
386 // 16 4 offset of start of central directory, relative to start of archive
387 // 20 2 comment length(n)
390 endCentDir
->count
= 0;
391 endCentDir
->size
= 0;
392 endCentDir
->offset
= 0;
393 int64_t ecdrOffset
= fsize
;
394 for (int64_t i
= END_CENT_DIR_SIZE
; i
< sz
; i
++)
396 b
= (unsigned char *) dwin
->bind (fsize
- i
, END_CENT_DIR_SIZE
);
399 append_msg (CMSG_ERROR
, GTXT ("%s: read failed (offset:0x%llx bytes:%lld"),
400 name
, (long long) (fsize
- i
), (long long) END_CENT_DIR_SIZE
);
403 uint32_t signature
= get_u4 (b
);
404 if (signature
== 0x06054b50)
406 int64_t len_comment
= get_u2 (b
+ 20);
407 if (i
!= (len_comment
+ END_CENT_DIR_SIZE
))
409 ecdrOffset
= fsize
- i
;
410 endCentDir
->count
= get_u2 (b
+ 10);
411 endCentDir
->size
= get_u4 (b
+ 12);
412 endCentDir
->offset
= get_u4 (b
+ 16);
413 Dprintf (DUMP_JAR_FILE
,
414 " Zip archive file size: %10lld (0x%016llx)\n"
415 " end-cent-dir record offset: %10lld (0x%016llx)\n"
416 " cent-dir offset: %10lld (0x%016llx)\n"
417 " cent-dir size: %10lld (0x%016llx)\n"
418 " cent-dir entries: %10lld\n",
419 (long long) fsize
, (long long) fsize
,
420 (long long) ecdrOffset
, (long long) ecdrOffset
,
421 (long long) endCentDir
->offset
, (long long) endCentDir
->offset
,
422 (long long) endCentDir
->size
, (long long) endCentDir
->size
,
423 (long long) endCentDir
->count
);
427 if (ecdrOffset
== fsize
)
429 append_msg (CMSG_ERROR
,
430 GTXT ("%s: cannot find the central directory record"), name
);
433 if (endCentDir
->count
== 0xffff || endCentDir
->offset
== 0xffffffff
434 || endCentDir
->size
== 0xffffffff)
437 // Zip64 end of central directory record
438 // Zip64 end of central directory locator ( Can be absent )
439 // End of central directory record
440 b
= (unsigned char *) dwin
->bind (ecdrOffset
- ZIP64_LOCATOR_SIZE
,
444 append_msg (CMSG_ERROR
,
445 GTXT ("%s: cannot find the Zip64 central directory record"), name
);
448 uint32_t signature
= get_u4 (b
);
449 if (signature
== 0x07064b50)
450 { // Get an offset from the Zip64 cent-dir locator
451 // Zip64 end of central directory locator
452 // Offset Bytes Description
453 // 0 4 Zip64 end of central dir locator signature = 0x07064b50
454 // 4 4 number of the disk with the start of the zip64 end of central directory
455 // 8 8 relative offset of the Zip64 end of central directory record
456 // 12 4 total number of disks
457 Dprintf (DUMP_JAR_FILE
, " cent-dir locator offset %10lld (0x%016llx)\n",
458 (long long) (ecdrOffset
- ZIP64_LOCATOR_SIZE
), (long long) (ecdrOffset
- ZIP64_LOCATOR_SIZE
));
459 ecdrOffset
= get_u8 (b
+ 8);
461 else // the Zip64 end of central directory locator is absent
462 ecdrOffset
-= ZIP64_CENT_DIR_SIZE
;
463 Dprintf (DUMP_JAR_FILE
, NTXT (" Zip64 end-cent-dir record offset: %10lld (0x%016llx)\n"),
464 (long long) ecdrOffset
, (long long) ecdrOffset
);
466 b
= (unsigned char *) dwin
->bind (ecdrOffset
, ZIP64_CENT_DIR_SIZE
);
469 append_msg (CMSG_ERROR
,
470 GTXT ("%s: cannot find the Zip64 central directory record"), name
);
473 // Zip64 end of central directory record
474 // Offset Bytes Description
475 // 0 4 Zip64 end of central dir signature = 0x06064b50
476 // 4 8 size of zip64 end of central directory record
477 // 12 2 version made by
478 // 14 2 version needed to extract
479 // 16 4 number of this disk
480 // 20 4 number of the disk with the start of the central directory
481 // 24 8 total number of entries in the central directory on this disk
482 // 32 8 total number of entries in the central directory
483 // 40 8 size of the central directory
484 // 48 8 offset of start of centraldirectory with respect to the starting disk number
485 // 56 Zip64 extensible data sector (variable size)
486 signature
= get_u4 (b
);
487 if (signature
!= 0x06064b50)
489 append_msg (CMSG_ERROR
, GTXT ("%s: cannot find the Zip64 central directory record"), name
);
492 endCentDir
->count
= get_u8 (b
+ 32);
493 endCentDir
->size
= get_u8 (b
+ 40);
494 endCentDir
->offset
= get_u8 (b
+ 48);
495 Dprintf (DUMP_JAR_FILE
,
496 NTXT (" cent-dir offset: %10lld (0x%016llx)\n"
497 " cent-dir size: %10lld (0x%016llx)\n"
498 " cent-dir entries: %10lld\n"),
499 (long long) endCentDir
->offset
, (long long) endCentDir
->offset
,
500 (long long) endCentDir
->size
, (long long) endCentDir
->size
,
501 (long long) endCentDir
->count
);