jarfile.c (25806B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * JARFILE 7 * 8 * Parsing of a Jar file 9 */ 10 #define JAR_SIZE 256 11 12 #include "jar.h" 13 #include "jarint.h" 14 #include "jarfile.h" 15 16 /* commercial compression */ 17 #include "jzlib.h" 18 19 #if defined(XP_UNIX) 20 #include "sys/stat.h" 21 #endif 22 23 #include "sechash.h" /* for HASH_GetHashObject() */ 24 25 PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral)); 26 PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal)); 27 PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd)); 28 PR_STATIC_ASSERT(512 == sizeof(union TarEntry)); 29 30 /* extracting */ 31 static int 32 jar_guess_jar(const char *filename, JAR_FILE fp); 33 34 static int 35 jar_inflate_memory(unsigned int method, long *length, long expected_out_len, 36 char **data); 37 38 static int 39 jar_physical_extraction(JAR_FILE fp, char *outpath, unsigned long offset, 40 unsigned long length); 41 42 static int 43 jar_physical_inflate(JAR_FILE fp, char *outpath, unsigned long offset, 44 unsigned long length, unsigned int method); 45 46 static int 47 jar_verify_extract(JAR *jar, char *path, char *physical_path); 48 49 static JAR_Physical * 50 jar_get_physical(JAR *jar, char *pathname); 51 52 static int 53 jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp); 54 55 static int 56 jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext); 57 58 /* indexing */ 59 static int 60 jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp); 61 62 static int 63 jar_listtar(JAR *jar, JAR_FILE fp); 64 65 static int 66 jar_listzip(JAR *jar, JAR_FILE fp); 67 68 /* conversions */ 69 static int 70 dosdate(char *date, const char *s); 71 72 static int 73 dostime(char *time, const char *s); 74 75 #ifdef NSS_X86_OR_X64 76 /* The following macros throw up warnings. */ 77 #if defined(__GNUC__) && !defined(NSS_NO_GCC48) 78 #pragma GCC diagnostic ignored "-Wstrict-aliasing" 79 #endif 80 #define x86ShortToUint32(ii) ((const PRUint32) * ((const PRUint16 *)(ii))) 81 #define x86LongToUint32(ii) (*(const PRUint32 *)(ii)) 82 #else 83 static PRUint32 84 x86ShortToUint32(const void *ii); 85 86 static PRUint32 87 x86LongToUint32(const void *ll); 88 #endif 89 90 static long 91 octalToLong(const char *s); 92 93 /* 94 * J A R _ p a s s _ a r c h i v e 95 * 96 * For use by naive clients. Slam an entire archive file 97 * into this function. We extract manifests, parse, index 98 * the archive file, and do whatever nastiness. 99 * 100 */ 101 int 102 JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url) 103 { 104 JAR_FILE fp; 105 int status = 0; 106 107 if (filename == NULL) 108 return JAR_ERR_GENERAL; 109 110 if ((fp = JAR_FOPEN(filename, "rb")) != NULL) { 111 if (format == jarArchGuess) 112 format = (jarArch)jar_guess_jar(filename, fp); 113 114 jar->format = format; 115 jar->url = url ? PORT_Strdup(url) : NULL; 116 jar->filename = PORT_Strdup(filename); 117 118 status = jar_gen_index(jar, format, fp); 119 if (status == 0) 120 status = jar_extract_manifests(jar, format, fp); 121 122 JAR_FCLOSE(fp); 123 if (status < 0) 124 return status; 125 126 /* people were expecting it this way */ 127 return jar->valid; 128 } 129 /* file not found */ 130 return JAR_ERR_FNF; 131 } 132 133 /* 134 * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d 135 * 136 * Same as JAR_pass_archive, but doesn't parse signatures. 137 * 138 */ 139 int 140 JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, 141 const char *url) 142 { 143 JAR_FILE fp; 144 int status = 0; 145 146 if (filename == NULL) { 147 return JAR_ERR_GENERAL; 148 } 149 150 if ((fp = JAR_FOPEN(filename, "rb")) != NULL) { 151 if (format == jarArchGuess) { 152 format = (jarArch)jar_guess_jar(filename, fp); 153 } 154 155 jar->format = format; 156 jar->url = url ? PORT_Strdup(url) : NULL; 157 jar->filename = PORT_Strdup(filename); 158 159 status = jar_gen_index(jar, format, fp); 160 if (status == 0) { 161 status = jar_extract_mf(jar, format, fp, "mf"); 162 } 163 164 JAR_FCLOSE(fp); 165 if (status < 0) { 166 return status; 167 } 168 169 /* people were expecting it this way */ 170 return jar->valid; 171 } 172 /* file not found */ 173 return JAR_ERR_FNF; 174 } 175 176 /* 177 * J A R _ v e r i f i e d _ e x t r a c t 178 * 179 * Optimization: keep a file descriptor open 180 * inside the JAR structure, so we don't have to 181 * open the file 25 times to run java. 182 * 183 */ 184 185 int 186 JAR_verified_extract(JAR *jar, char *path, char *outpath) 187 { 188 int status = JAR_extract(jar, path, outpath); 189 190 if (status >= 0) 191 return jar_verify_extract(jar, path, outpath); 192 return status; 193 } 194 195 int 196 JAR_extract(JAR *jar, char *path, char *outpath) 197 { 198 int result; 199 JAR_Physical *phy; 200 201 if (jar->fp == NULL && jar->filename) { 202 jar->fp = (FILE *)JAR_FOPEN(jar->filename, "rb"); 203 } 204 if (jar->fp == NULL) { 205 /* file not found */ 206 return JAR_ERR_FNF; 207 } 208 209 phy = jar_get_physical(jar, path); 210 if (phy) { 211 if (phy->compression == 0) { 212 result = jar_physical_extraction((PRFileDesc *)jar->fp, outpath, phy->offset, phy->length); 213 } else { 214 /* compression methods other than 8 are unsupported, 215 * but for historical reasons, jar_physical_inflate will be called for 216 * unsupported compression method constants too. */ 217 result = jar_physical_inflate((PRFileDesc *)jar->fp, outpath, 218 phy->offset, phy->length, 219 (unsigned int)phy->compression); 220 } 221 222 #if defined(XP_UNIX) 223 if (phy->mode) 224 chmod(outpath, 0400 | (mode_t)phy->mode); 225 #endif 226 } else { 227 /* pathname not found in archive */ 228 result = JAR_ERR_PNF; 229 } 230 return result; 231 } 232 233 /* 234 * p h y s i c a l _ e x t r a c t i o n 235 * 236 * This needs to be done in chunks of say 32k, instead of 237 * in one bulk calloc. (Necessary under Win16 platform.) 238 * This is done for uncompressed entries only. 239 * 240 */ 241 242 #define CHUNK 32768 243 244 static int 245 jar_physical_extraction(JAR_FILE fp, char *outpath, unsigned long offset, 246 unsigned long length) 247 { 248 JAR_FILE out; 249 char *buffer = (char *)PORT_ZAlloc(CHUNK); 250 int status = 0; 251 252 if (buffer == NULL) 253 return JAR_ERR_MEMORY; 254 255 if ((out = JAR_FOPEN(outpath, "wb")) != NULL) { 256 unsigned long at = 0; 257 258 JAR_FSEEK(fp, offset, (PRSeekWhence)0); 259 while (at < length) { 260 long chunk = (at + CHUNK <= length) ? CHUNK : length - at; 261 if (JAR_FREAD(fp, buffer, chunk) != chunk) { 262 status = JAR_ERR_DISK; 263 break; 264 } 265 at += chunk; 266 if (JAR_FWRITE(out, buffer, chunk) < chunk) { 267 /* most likely a disk full error */ 268 status = JAR_ERR_DISK; 269 break; 270 } 271 } 272 JAR_FCLOSE(out); 273 } else { 274 /* error opening output file */ 275 status = JAR_ERR_DISK; 276 } 277 PORT_Free(buffer); 278 return status; 279 } 280 281 /* 282 * j a r _ p h y s i c a l _ i n f l a t e 283 * 284 * Inflate a range of bytes in a file, writing the inflated 285 * result to "outpath". Chunk based. 286 * 287 */ 288 /* input and output chunks differ, assume 4x compression */ 289 290 #define ICHUNK 8192 291 #define OCHUNK 32768 292 293 static int 294 jar_physical_inflate(JAR_FILE fp, char *outpath, unsigned long offset, unsigned long length, 295 unsigned int method) 296 { 297 char *inbuf, *outbuf; 298 int status = 0; 299 z_stream zs; 300 JAR_FILE out; 301 302 /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ 303 if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL) 304 return JAR_ERR_MEMORY; 305 306 if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) { 307 status = JAR_ERR_MEMORY; 308 goto loser; 309 } 310 311 PORT_Memset(&zs, 0, sizeof(zs)); 312 status = inflateInit2(&zs, -MAX_WBITS); 313 if (status != Z_OK) { 314 status = JAR_ERR_GENERAL; 315 goto loser; 316 } 317 318 if ((out = JAR_FOPEN(outpath, "wb")) != NULL) { 319 int status2 = 0; 320 unsigned long at = 0; 321 322 JAR_FSEEK(fp, offset, (PRSeekWhence)0); 323 while (at < length) { 324 unsigned long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at; 325 unsigned long tin; 326 327 if (JAR_FREAD(fp, inbuf, chunk) != chunk) { 328 /* incomplete read */ 329 JAR_FCLOSE(out); 330 status = JAR_ERR_CORRUPT; 331 break; 332 } 333 at += chunk; 334 if (at == length) { 335 /* add an extra dummy byte at the end */ 336 inbuf[chunk++] = 0xDD; 337 } 338 zs.next_in = (Bytef *)inbuf; 339 zs.avail_in = chunk; 340 zs.avail_out = OCHUNK; 341 tin = zs.total_in; 342 while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) { 343 unsigned long prev_total = zs.total_out; 344 unsigned long ochunk; 345 346 zs.next_out = (Bytef *)outbuf; 347 zs.avail_out = OCHUNK; 348 status = inflate(&zs, Z_NO_FLUSH); 349 if (status != Z_OK && status != Z_STREAM_END) { 350 /* error during decompression */ 351 JAR_FCLOSE(out); 352 status = JAR_ERR_CORRUPT; 353 break; 354 } 355 ochunk = zs.total_out - prev_total; 356 if (JAR_FWRITE(out, outbuf, ochunk) < (long)ochunk) { 357 /* most likely a disk full error */ 358 status = JAR_ERR_DISK; 359 break; 360 } 361 if (status == Z_STREAM_END) 362 break; 363 } 364 if (status != Z_OK) { 365 break; 366 } 367 } 368 JAR_FCLOSE(out); 369 status2 = inflateEnd(&zs); 370 if (status == Z_OK) { 371 status = status2; 372 } 373 } else { 374 /* error opening output file */ 375 status = JAR_ERR_DISK; 376 } 377 loser: 378 if (inbuf) { 379 PORT_Free(inbuf); 380 } 381 if (outbuf) { 382 PORT_Free(outbuf); 383 } 384 return status; 385 } 386 387 /* 388 * j a r _ i n f l a t e _ m e m o r y 389 * 390 * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd, 391 * and thus appears to operate inplace to the caller. 392 * 393 */ 394 static int 395 jar_inflate_memory(unsigned int method, long *length, long expected_out_len, 396 char **data) 397 { 398 char *inbuf = *data; 399 char *outbuf = (char *)PORT_ZAlloc(expected_out_len); 400 long insz = *length; 401 int status; 402 z_stream zs; 403 404 if (outbuf == NULL) 405 return JAR_ERR_MEMORY; 406 407 PORT_Memset(&zs, 0, sizeof zs); 408 status = inflateInit2(&zs, -MAX_WBITS); 409 if (status < 0) { 410 /* error initializing zlib stream */ 411 PORT_Free(outbuf); 412 return JAR_ERR_GENERAL; 413 } 414 415 zs.next_in = (Bytef *)inbuf; 416 zs.next_out = (Bytef *)outbuf; 417 zs.avail_in = insz; 418 zs.avail_out = expected_out_len; 419 420 status = inflate(&zs, Z_FINISH); 421 if (status != Z_OK && status != Z_STREAM_END) { 422 /* error during deflation */ 423 PORT_Free(outbuf); 424 return JAR_ERR_GENERAL; 425 } 426 427 status = inflateEnd(&zs); 428 if (status != Z_OK) { 429 /* error during deflation */ 430 PORT_Free(outbuf); 431 return JAR_ERR_GENERAL; 432 } 433 PORT_Free(*data); 434 *data = outbuf; 435 *length = zs.total_out; 436 return 0; 437 } 438 439 /* 440 * v e r i f y _ e x t r a c t 441 * 442 * Validate signature on the freshly extracted file. 443 * 444 */ 445 static int 446 jar_verify_extract(JAR *jar, char *path, char *physical_path) 447 { 448 int status; 449 JAR_Digest dig; 450 451 PORT_Memset(&dig, 0, sizeof dig); 452 status = JAR_digest_file(physical_path, &dig); 453 if (!status) 454 status = JAR_verify_digest(jar, path, &dig); 455 return status; 456 } 457 458 /* 459 * g e t _ p h y s i c a l 460 * 461 * Let's get physical. 462 * Obtains the offset and length of this file in the jar file. 463 * 464 */ 465 static JAR_Physical * 466 jar_get_physical(JAR *jar, char *pathname) 467 { 468 ZZLink *link; 469 ZZList *list = jar->phy; 470 471 if (ZZ_ListEmpty(list)) 472 return NULL; 473 474 for (link = ZZ_ListHead(list); 475 !ZZ_ListIterDone(list, link); 476 link = link->next) { 477 JAR_Item *it = link->thing; 478 479 if (it->type == jarTypePhy && 480 it->pathname && !PORT_Strcmp(it->pathname, pathname)) { 481 JAR_Physical *phy = (JAR_Physical *)it->data; 482 return phy; 483 } 484 } 485 return NULL; 486 } 487 488 /* 489 * j a r _ e x t r a c t _ m a n i f e s t s 490 * 491 * Extract the manifest files and parse them, 492 * from an open archive file whose contents are known. 493 * 494 */ 495 static int 496 jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp) 497 { 498 int status, signatures; 499 500 if (format != jarArchZip && format != jarArchTar) 501 return JAR_ERR_CORRUPT; 502 503 if ((status = jar_extract_mf(jar, format, fp, "mf")) < 0) 504 return status; 505 if (!status) 506 return JAR_ERR_ORDER; 507 if ((status = jar_extract_mf(jar, format, fp, "sf")) < 0) 508 return status; 509 if (!status) 510 return JAR_ERR_ORDER; 511 if ((status = jar_extract_mf(jar, format, fp, "rsa")) < 0) 512 return status; 513 signatures = status; 514 if ((status = jar_extract_mf(jar, format, fp, "dsa")) < 0) 515 return status; 516 if (!(signatures += status)) 517 return JAR_ERR_SIG; 518 return 0; 519 } 520 521 /* 522 * j a r _ e x t r a c t _ m f 523 * 524 * Extracts manifest files based on an extension, which 525 * should be .MF, .SF, .RSA, etc. Order of the files is now no 526 * longer important when zipping jar files. 527 * 528 */ 529 static int 530 jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext) 531 { 532 ZZLink *link; 533 ZZList *list = jar->phy; 534 int ret = 0; 535 536 if (ZZ_ListEmpty(list)) 537 return JAR_ERR_PNF; 538 539 for (link = ZZ_ListHead(list); 540 ret >= 0 && !ZZ_ListIterDone(list, link); 541 link = link->next) { 542 JAR_Item *it = link->thing; 543 544 if (it->type == jarTypePhy && 545 !PORT_Strncmp(it->pathname, "META-INF", 8)) { 546 JAR_Physical *phy = (JAR_Physical *)it->data; 547 char *fn = it->pathname + 8; 548 char *e; 549 char *manifest; 550 long length; 551 int num, status; 552 553 if (PORT_Strlen(it->pathname) < 8) 554 continue; 555 556 if (*fn == '/' || *fn == '\\') 557 fn++; 558 if (*fn == 0) { 559 /* just a directory entry */ 560 continue; 561 } 562 563 /* skip to extension */ 564 for (e = fn; *e && *e != '.'; e++) 565 /* yip */; 566 567 /* and skip dot */ 568 if (*e == '.') 569 e++; 570 if (PORT_Strcasecmp(ext, e)) { 571 /* not the right extension */ 572 continue; 573 } 574 if (phy->length == 0 || phy->length > 0xFFFF) { 575 /* manifest files cannot be zero length or too big! */ 576 /* the 0xFFFF limit is per J2SE SDK */ 577 return JAR_ERR_CORRUPT; 578 } 579 580 /* Read in the manifest and parse it */ 581 /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ 582 manifest = (char *)PORT_ZAlloc(phy->length + 1); 583 if (!manifest) 584 return JAR_ERR_MEMORY; 585 586 JAR_FSEEK(fp, phy->offset, (PRSeekWhence)0); 587 num = JAR_FREAD(fp, manifest, phy->length); 588 if (num != phy->length) { 589 /* corrupt archive file */ 590 PORT_Free(manifest); 591 return JAR_ERR_CORRUPT; 592 } 593 594 if (phy->compression == 8) { 595 length = phy->length; 596 /* add an extra dummy byte at the end */ 597 manifest[length++] = 0xDD; 598 status = jar_inflate_memory((unsigned int)phy->compression, 599 &length, 600 phy->uncompressed_length, 601 &manifest); 602 if (status < 0) { 603 PORT_Free(manifest); 604 return status; 605 } 606 } else if (phy->compression) { 607 /* unsupported compression method */ 608 PORT_Free(manifest); 609 return JAR_ERR_CORRUPT; 610 } else 611 length = phy->length; 612 613 status = JAR_parse_manifest(jar, manifest, length, 614 it->pathname, "url"); 615 PORT_Free(manifest); 616 if (status < 0) 617 ret = status; 618 else 619 ++ret; 620 } else if (it->type == jarTypePhy) { 621 /* ordinary file */ 622 } 623 } 624 return ret; 625 } 626 627 /* 628 * j a r _ g e n _ i n d e x 629 * 630 * Generate an index for the various types of 631 * known archive files. Right now .ZIP and .TAR 632 * 633 */ 634 static int 635 jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp) 636 { 637 int result = JAR_ERR_CORRUPT; 638 639 JAR_FSEEK(fp, 0, (PRSeekWhence)0); 640 switch (format) { 641 case jarArchZip: 642 result = jar_listzip(jar, fp); 643 break; 644 645 case jarArchTar: 646 result = jar_listtar(jar, fp); 647 break; 648 649 case jarArchGuess: 650 case jarArchNone: 651 return JAR_ERR_GENERAL; 652 } 653 JAR_FSEEK(fp, 0, (PRSeekWhence)0); 654 return result; 655 } 656 657 /* 658 * j a r _ l i s t z i p 659 * 660 * List the physical contents of a Phil Katz 661 * style .ZIP file into the JAR linked list. 662 * 663 */ 664 static int 665 jar_listzip(JAR *jar, JAR_FILE fp) 666 { 667 ZZLink *ent; 668 JAR_Item *it = NULL; 669 JAR_Physical *phy = NULL; 670 struct ZipLocal *Local = PORT_ZNew(struct ZipLocal); 671 struct ZipCentral *Central = PORT_ZNew(struct ZipCentral); 672 struct ZipEnd *End = PORT_ZNew(struct ZipEnd); 673 674 int err = 0; 675 long pos = 0L; 676 unsigned int compression; 677 unsigned int filename_len, extra_len; 678 679 char filename[JAR_SIZE]; 680 char date[9], time[9]; 681 char sig[4]; 682 683 if (!Local || !Central || !End) { 684 /* out of memory */ 685 err = JAR_ERR_MEMORY; 686 goto loser; 687 } 688 689 while (1) { 690 PRUint32 sigVal; 691 JAR_FSEEK(fp, pos, (PRSeekWhence)0); 692 693 if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) { 694 /* zip file ends prematurely */ 695 err = JAR_ERR_CORRUPT; 696 goto loser; 697 } 698 699 JAR_FSEEK(fp, pos, (PRSeekWhence)0); 700 sigVal = x86LongToUint32(sig); 701 if (sigVal == LSIG) { 702 JAR_FREAD(fp, Local, sizeof *Local); 703 704 filename_len = x86ShortToUint32(Local->filename_len); 705 extra_len = x86ShortToUint32(Local->extrafield_len); 706 if (filename_len >= JAR_SIZE) { 707 /* corrupt zip file */ 708 err = JAR_ERR_CORRUPT; 709 goto loser; 710 } 711 712 if (JAR_FREAD(fp, filename, filename_len) != filename_len) { 713 /* truncated archive file */ 714 err = JAR_ERR_CORRUPT; 715 goto loser; 716 } 717 filename[filename_len] = 0; 718 /* Add this to our jar chain */ 719 phy = PORT_ZNew(JAR_Physical); 720 if (phy == NULL) { 721 err = JAR_ERR_MEMORY; 722 goto loser; 723 } 724 725 /* We will index any file that comes our way, but when it comes 726 to actually extraction, compression must be 0 or 8 */ 727 compression = x86ShortToUint32(Local->method); 728 phy->compression = (compression <= 255) ? compression : 222; 729 /* XXX 222 is bad magic. */ 730 731 phy->offset = pos + (sizeof *Local) + filename_len + extra_len; 732 phy->length = x86LongToUint32(Local->size); 733 phy->uncompressed_length = x86LongToUint32(Local->orglen); 734 735 dosdate(date, Local->date); 736 dostime(time, Local->time); 737 738 it = PORT_ZNew(JAR_Item); 739 if (it == NULL) { 740 err = JAR_ERR_MEMORY; 741 goto loser; 742 } 743 744 it->pathname = PORT_Strdup(filename); 745 it->type = jarTypePhy; 746 it->data = (unsigned char *)phy; 747 it->size = sizeof(JAR_Physical); 748 749 ent = ZZ_NewLink(it); 750 if (ent == NULL) { 751 err = JAR_ERR_MEMORY; 752 goto loser; 753 } 754 755 ZZ_AppendLink(jar->phy, ent); 756 pos = phy->offset + phy->length; 757 } else if (sigVal == CSIG) { 758 #if defined(XP_UNIX) 759 unsigned int attr = 0; 760 #endif 761 if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) { 762 /* apparently truncated archive */ 763 err = JAR_ERR_CORRUPT; 764 goto loser; 765 } 766 767 #if defined(XP_UNIX) 768 /* with unix we need to locate any bits from 769 the protection mask in the external attributes. */ 770 attr = Central->external_attributes[2]; /* magic */ 771 if (attr) { 772 /* we have to read the filename, again */ 773 filename_len = x86ShortToUint32(Central->filename_len); 774 if (filename_len >= JAR_SIZE) { 775 /* corrupt in central directory */ 776 err = JAR_ERR_CORRUPT; 777 goto loser; 778 } 779 780 if (JAR_FREAD(fp, filename, filename_len) != filename_len) { 781 /* truncated in central directory */ 782 err = JAR_ERR_CORRUPT; 783 goto loser; 784 } 785 filename[filename_len] = 0; 786 787 /* look up this name again */ 788 phy = jar_get_physical(jar, filename); 789 if (phy) { 790 /* always allow access by self */ 791 phy->mode = 0400 | attr; 792 } 793 } 794 #endif 795 pos += sizeof(struct ZipCentral) + 796 x86ShortToUint32(Central->filename_len) + 797 x86ShortToUint32(Central->commentfield_len) + 798 x86ShortToUint32(Central->extrafield_len); 799 } else if (sigVal == ESIG) { 800 if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) { 801 err = JAR_ERR_CORRUPT; 802 goto loser; 803 } 804 break; 805 } else { 806 /* garbage in archive */ 807 err = JAR_ERR_CORRUPT; 808 goto loser; 809 } 810 } 811 812 loser: 813 if (Local) 814 PORT_Free(Local); 815 if (phy && it == NULL) 816 PORT_Free(phy); 817 if (Central) 818 PORT_Free(Central); 819 if (End) 820 PORT_Free(End); 821 return err; 822 } 823 824 /* 825 * j a r _ l i s t t a r 826 * 827 * List the physical contents of a Unix 828 * .tar file into the JAR linked list. 829 * 830 */ 831 static int 832 jar_listtar(JAR *jar, JAR_FILE fp) 833 { 834 char *s; 835 JAR_Physical *phy; 836 long pos = 0L; 837 long sz; 838 union TarEntry tarball; 839 840 while (1) { 841 JAR_FSEEK(fp, pos, (PRSeekWhence)0); 842 843 if (JAR_FREAD(fp, &tarball, sizeof tarball) < sizeof tarball) 844 break; 845 846 if (!*tarball.val.filename) 847 break; 848 849 sz = octalToLong(tarball.val.size); 850 851 /* Tag the end of filename */ 852 s = tarball.val.filename; 853 while (*s && *s != ' ') 854 s++; 855 *s = 0; 856 857 /* Add to our linked list */ 858 phy = PORT_ZNew(JAR_Physical); 859 if (phy == NULL) 860 return JAR_ERR_MEMORY; 861 862 phy->compression = 0; 863 phy->offset = pos + sizeof tarball; 864 phy->length = sz; 865 866 ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy, 867 sizeof *phy); 868 869 /* Advance to next file entry */ 870 sz = PR_ROUNDUP(sz, sizeof tarball); 871 pos += sz + sizeof tarball; 872 } 873 874 return 0; 875 } 876 877 /* 878 * d o s d a t e 879 * 880 * Not used right now, but keep it in here because 881 * it will be needed. 882 * 883 */ 884 static int 885 dosdate(char *date, const char *s) 886 { 887 PRUint32 num = x86ShortToUint32(s); 888 889 PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F), 890 ((num >> 9) + 80)); 891 return 0; 892 } 893 894 /* 895 * d o s t i m e 896 * 897 * Not used right now, but keep it in here because 898 * it will be needed. 899 * 900 */ 901 static int 902 dostime(char *time, const char *s) 903 { 904 PRUint32 num = x86ShortToUint32(s); 905 906 PR_snprintf(time, 6, "%02d:%02d", ((num >> 11) & 0x1F), 907 ((num >> 5) & 0x3F)); 908 return 0; 909 } 910 911 #ifndef NSS_X86_OR_X64 912 /* 913 * Simulates an x86 (little endian, unaligned) ushort fetch from any address. 914 */ 915 static PRUint32 916 x86ShortToUint32(const void *v) 917 { 918 const unsigned char *ii = (const unsigned char *)v; 919 PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8); 920 return ret; 921 } 922 923 /* 924 * Simulates an x86 (little endian, unaligned) uint fetch from any address. 925 */ 926 static PRUint32 927 x86LongToUint32(const void *v) 928 { 929 const unsigned char *ll = (const unsigned char *)v; 930 PRUint32 ret; 931 932 ret = ((((PRUint32)(ll[0])) << 0) | 933 (((PRUint32)(ll[1])) << 8) | 934 (((PRUint32)(ll[2])) << 16) | 935 (((PRUint32)(ll[3])) << 24)); 936 return ret; 937 } 938 #endif 939 940 /* 941 * ASCII octal to binary long. 942 * Used for integer encoding inside tar files. 943 * 944 */ 945 static long 946 octalToLong(const char *s) 947 { 948 long num = 0L; 949 950 while (*s == ' ') 951 s++; 952 while (*s >= '0' && *s <= '7') { 953 num <<= 3; 954 num += *s++ - '0'; 955 } 956 return num; 957 } 958 959 /* 960 * g u e s s _ j a r 961 * 962 * Try to guess what kind of JAR file this is. 963 * Maybe tar, maybe zip. Look in the file for magic 964 * or at its filename. 965 * 966 */ 967 static int 968 jar_guess_jar(const char *filename, JAR_FILE fp) 969 { 970 PRInt32 len = PORT_Strlen(filename); 971 const char *ext = filename + len - 4; /* 4 for ".tar" */ 972 973 if (len >= 4 && !PL_strcasecmp(ext, ".tar")) 974 return jarArchTar; 975 return jarArchZip; 976 }