tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }