security/nss/lib/jar/jarfile.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial