/*
 * A very little used routine to handle the case that zip file has
 * a comment at the end. Believe it or not, the only way to find the
 * END record is to walk backwards, byte by bloody byte looking for
 * the END record signature.
 *
 *	fd:	File descriptor of the jar file.
 *	eb:	Pointer to a buffer to receive a copy of the END header.
 *
 * Returns the offset of the END record in the file on success,
 * -1 on failure.
 */
static off_t
find_end(int fd, Byte *eb)
{
    off_t   len;
    off_t   pos;
    off_t   flen;
    int	    bytes;
    Byte    *cp;
    Byte    *endpos;
    Byte    *buffer;

    /*
     * 99.44% (or more) of the time, there will be no comment at the
     * end of the zip file.  Try reading just enough to read the END
     * record from the end of the file.
     */
    if ((pos = lseek(fd, -ENDHDR, SEEK_END)) < (off_t)0)
	return (-1);
    if ((bytes = read(fd, eb, ENDHDR)) < 0)
	return (-1);
    if (GETSIG(eb) == ENDSIG)
	return (pos);

    /*
     * Shucky-Darn,... There is a comment at the end of the zip file.
     *
     * Allocate and fill a buffer with enough of the zip file
     * to meet the specification for a maximal comment length.
     */
    if ((flen = lseek(fd, 0, SEEK_END)) < (off_t)0)
	return (-1);
    len = (flen < END_MAXLEN) ? flen : END_MAXLEN;
    if (lseek(fd, -len, SEEK_END) < (off_t)0)
	return (-1);
    if ((buffer = malloc(END_MAXLEN)) == NULL)
	return (-1);
    if ((bytes = read(fd, buffer, len)) < 0) {
	free(buffer);
	return (-1);
    }

    /*
     * Search backwards from the end of file stopping when the END header
     * signature is found. (The first condition of the "if" is just a
     * fast fail, because the GETSIG macro isn't always cheap.  The
     * final condition protects against false positives.)
     */
    endpos = &buffer[bytes];
    for (cp = &buffer[bytes - ENDHDR]; cp >= &buffer[0]; cp--)
	if ((*cp == (ENDSIG & 0xFF)) && (GETSIG(cp) == ENDSIG) &&
	  (cp + ENDHDR + ENDCOM(cp) == endpos)) {
	    (void) memcpy(eb, cp, ENDHDR);
	    free(buffer);
	    return (flen - (endpos - cp));
	}
    free(buffer);
    return (-1);
}
Beispiel #2
0
static jboolean verifyEND(jzfile *zip, jlong endpos, char *endbuf) {
    /* ENDSIG matched, however the size of file comment in it does not
       match the real size. One "common" cause for this problem is some
       "extra" bytes are padded at the end of the zipfile.
       Let's do some extra verification, we don't care about the performance
       in this situation.
     */
    jlong cenpos = endpos - ENDSIZ(endbuf);
    jlong locpos = cenpos - ENDOFF(endbuf);
    char buf[4];
    return (cenpos >= 0 &&
            locpos >= 0 &&
            readFullyAt(zip->zfd, buf, sizeof(buf), cenpos) != -1 &&
            GETSIG(buf) == CENSIG &&
            readFullyAt(zip->zfd, buf, sizeof(buf), locpos) != -1 &&
            GETSIG(buf) == LOCSIG);
}
static jlong
find_end64(int fd, Byte *ep, jlong pos)
{
    jlong end64pos;
    jlong bytes;
    if ((end64pos = JLI_Lseek(fd, pos - ZIP64_LOCHDR, SEEK_SET)) < (jlong)0)
        return -1;
    if ((bytes = read(fd, ep, ZIP64_LOCHDR)) < 0)
        return -1;
    if (GETSIG(ep) == ZIP64_LOCSIG)
       return end64pos;
    return -1;
}
Beispiel #4
0
/*
 * Check if the given file is a JAR file.
 *
 * Parameters:
 *     path  - the path to the file to check for JAR magic.
 *
 * Returns:
 *     This function return NULL on success.  Otherwise, errno is set, and it
 *     returns a message that indicates what caused the failure.
 */
const char * isJar(const char * path) {
    const char * result = BAD_FILE_MSG;

    int fd = open(path, O_RDONLY);
    if (fd != -1) {
        unsigned char buf[CHUNK_SIZE];

        ssize_t count = read(fd, buf, CHUNK_SIZE);
        if (count >= MIN_SIZE) {
            result = BAD_MAGIC_MSG;

            // be sure the file is at least a ZIP file
            if (GETSIG(buf) == LOCSIG) {

                off_t flen  = LOCNAM(buf);
                off_t xlen  = LOCEXT(buf);
                off_t start = LOCHDR + flen;
                off_t end   = start  + xlen;

                if (end <= count) {
                    while (start < end) {
                        off_t xhid  = SH(buf, start);
                        off_t xdlen = SH(buf, start + 2);

                        start += 4 + xdlen;
                        if (xhid == 0xcafe) {
                            // found the JAR magic
                            result = NULL;
                            break;
                        }
                    }
                }
            }
        }

        if (result != NULL) {
            errno = BAD_MAGIC;
        }

        close (fd);
    }

    return (result);
}
Beispiel #5
0
/*
 * Reads zip file central directory. Returns the file position of first
 * CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL
 * then the error was a zip format error and zip->msg has the error text.
 * Always pass in -1 for knownTotal; it's used for a recursive call.
 */
static jlong
readCEN(jzfile *zip, jint knownTotal)
{
    /* Following are unsigned 32-bit */
    jlong endpos, end64pos, cenpos, cenlen, cenoff;
    /* Following are unsigned 16-bit */
    jint total, tablelen, i, j;
    unsigned char *cenbuf = NULL;
    unsigned char *cenend;
    unsigned char *cp;
#ifdef USE_MMAP
    static jlong pagesize;
    jlong offset;
#endif
    unsigned char endbuf[ENDHDR];
    jint endhdrlen = ENDHDR;
    jzcell *entries;
    jint *table;

    /* Clear previous zip error */
    zip->msg = NULL;
    /* Get position of END header */
    if ((endpos = findEND(zip, endbuf)) == -1)
        return -1; /* no END header or system error */

    if (endpos == 0) return 0;  /* only END header present */

    freeCEN(zip);
   /* Get position and length of central directory */
    cenlen = ENDSIZ(endbuf);
    cenoff = ENDOFF(endbuf);
    total  = ENDTOT(endbuf);
    if (cenlen == ZIP64_MAGICVAL || cenoff == ZIP64_MAGICVAL ||
        total == ZIP64_MAGICCOUNT) {
        unsigned char end64buf[ZIP64_ENDHDR];
        if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) {
            cenlen = ZIP64_ENDSIZ(end64buf);
            cenoff = ZIP64_ENDOFF(end64buf);
            total = (jint)ZIP64_ENDTOT(end64buf);
            endpos = end64pos;
            endhdrlen = ZIP64_ENDHDR;
        }
    }

    if (cenlen > endpos)
        ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
    cenpos = endpos - cenlen;

    /* Get position of first local file (LOC) header, taking into
     * account that there may be a stub prefixed to the zip file. */
    zip->locpos = cenpos - cenoff;
    if (zip->locpos < 0)
        ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)");

#ifdef USE_MMAP
    if (zip->usemmap) {
      /* On Solaris & Linux prior to JDK 6, we used to mmap the whole jar file to
       * read the jar file contents. However, this greatly increased the perceived
       * footprint numbers because the mmap'ed pages were adding into the totals shown
       * by 'ps' and 'top'. We switched to mmaping only the central directory of jar
       * file while calling 'read' to read the rest of jar file. Here are a list of
       * reasons apart from above of why we are doing so:
       * 1. Greatly reduces mmap overhead after startup complete;
       * 2. Avoids dual path code maintainance;
       * 3. Greatly reduces risk of address space (not virtual memory) exhaustion.
       */
        if (pagesize == 0) {
            pagesize = (jlong)sysconf(_SC_PAGESIZE);
            if (pagesize == 0) goto Catch;
        }
        if (cenpos > pagesize) {
            offset = cenpos & ~(pagesize - 1);
        } else {
            offset = 0;
        }
        /* When we are not calling recursively, knownTotal is -1. */
        if (knownTotal == -1) {
            void* mappedAddr;
            /* Mmap the CEN and END part only. We have to figure
               out the page size in order to make offset to be multiples of
               page size.
            */
            zip->mlen = cenpos - offset + cenlen + endhdrlen;
            zip->offset = offset;
            mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
            zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
                (unsigned char*)mappedAddr;

            if (zip->maddr == NULL) {
                jio_fprintf(stderr, "mmap failed for CEN and END part of zip file\n");
                goto Catch;
            }
        }
        cenbuf = zip->maddr + cenpos - offset;
    } else
#endif
    {
        if ((cenbuf = malloc((size_t) cenlen)) == NULL ||
            (readFullyAt(zip->zfd, cenbuf, cenlen, cenpos) == -1))
        goto Catch;
    }

    cenend = cenbuf + cenlen;

    /* Initialize zip file data structures based on the total number
     * of central directory entries as stored in ENDTOT.  Since this
     * is a 2-byte field, but we (and other zip implementations)
     * support approx. 2**31 entries, we do not trust ENDTOT, but
     * treat it only as a strong hint.  When we call ourselves
     * recursively, knownTotal will have the "true" value.
     *
     * Keep this path alive even with the Zip64 END support added, just
     * for zip files that have more than 0xffff entries but don't have
     * the Zip64 enabled.
     */
    total = (knownTotal != -1) ? knownTotal : total;
    entries  = zip->entries  = calloc(total, sizeof(entries[0]));
    tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions
    table    = zip->table    = malloc(tablelen * sizeof(table[0]));
    if (entries == NULL || table == NULL) goto Catch;
    for (j = 0; j < tablelen; j++)
        table[j] = ZIP_ENDCHAIN;

    /* Iterate through the entries in the central directory */
    for (i = 0, cp = cenbuf; cp <= cenend - CENHDR; i++, cp += CENSIZE(cp)) {
        /* Following are unsigned 16-bit */
        jint method, nlen;
        unsigned int hsh;

        if (i >= total) {
            /* This will only happen if the zip file has an incorrect
             * ENDTOT field, which usually means it contains more than
             * 65535 entries. */
            cenpos = readCEN(zip, countCENHeaders(cenbuf, cenend));
            goto Finally;
        }

        method = CENHOW(cp);
        nlen   = CENNAM(cp);

        if (GETSIG(cp) != CENSIG)
            ZIP_FORMAT_ERROR("invalid CEN header (bad signature)");
        if (CENFLG(cp) & 1)
            ZIP_FORMAT_ERROR("invalid CEN header (encrypted entry)");
        if (method != STORED && method != DEFLATED)
            ZIP_FORMAT_ERROR("invalid CEN header (bad compression method)");
        if (cp + CENHDR + nlen > cenend)
            ZIP_FORMAT_ERROR("invalid CEN header (bad header size)");

        /* if the entry is metadata add it to our metadata names */
        if (isMetaName((char *)cp+CENHDR, nlen))
            if (addMetaName(zip, (char *)cp+CENHDR, nlen) != 0)
                goto Catch;

        /* Record the CEN offset and the name hash in our hash cell. */
        entries[i].cenpos = cenpos + (cp - cenbuf);
        entries[i].hash = hashN((char *)cp+CENHDR, nlen);

        /* Add the entry to the hash table */
        hsh = entries[i].hash % tablelen;
        entries[i].next = table[hsh];
        table[hsh] = i;
    }
    if (cp != cenend)
        ZIP_FORMAT_ERROR("invalid CEN header (bad header size)");

    zip->total = i;
    goto Finally;

 Catch:
    freeCEN(zip);
    cenpos = -1;

 Finally:
#ifdef USE_MMAP
    if (!zip->usemmap)
#endif
        free(cenbuf);

    return cenpos;
}
static int
find_file(int fd, zentry *entry, const char *file_name)
{
    int	    bytes;
    int	    res;
    int	    entry_size;
    int	    read_size;
    int	    base_offset;
    Byte    *p;
    Byte    *bp;
    Byte    *buffer;
    Byte    locbuf[LOCHDR];

    if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) {
	return(-1);
    }

    p = buffer;
    bp = buffer;

    /*
     * Read the END Header, which is the starting point for ZIP files.
     * (Clearly designed to make writing a zip file easier than reading
     * one. Now isn't that precious...)
     */
    if ((base_offset = find_end(fd, bp)) == -1) {
        free(buffer);
	return (-1);
    }

    /*
     * There is a historical, but undocumented, ability to allow for
     * additional "stuff" to be prepended to the zip/jar file. It seems
     * that this has been used to prepend an actual java launcher
     * executable to the jar on Windows.  Although this is just another
     * form of statically linking a small piece of the JVM to the
     * application, we choose to continue to support it.  Note that no
     * guarantees have been made (or should be made) to the customer that
     * this will continue to work.
     *
     * Therefore, calculate the base offset of the zip file (within the
     * expanded file) by assuming that the central directory is followed
     * immediately by the end record.
     */
    base_offset = base_offset - ENDSIZ(p) - ENDOFF(p);

    /*
     * The END Header indicates the start of the Central Directory
     * Headers. Remember that the desired Central Directory Header (CEN)
     * will almost always be the second one and the first one is a small
     * directory entry ("META-INF/"). Keep the code optimized for
     * that case.
     *
     * Begin by seeking to the beginning of the Central Directory and
     * reading in the first buffer full of bits.
     */
    if (lseek(fd, base_offset + ENDOFF(p), SEEK_SET) < (off_t)0) {
        free(buffer);
	return (-1);
    }
    if ((bytes = read(fd, bp, MINREAD)) < 0) {
        free(buffer);
	return (-1);
    }

    /*
     * Loop through the Central Directory Headers. Note that a valid zip/jar
     * must have an ENDHDR (with ENDSIG) after the Central Directory.
     */
    while (GETSIG(p) == CENSIG) {

	/*
	 * If a complete header isn't in the buffer, shift the contents
	 * of the buffer down and refill the buffer.  Note that the check
	 * for "bytes < CENHDR" must be made before the test for the entire
	 * size of the header, because if bytes is less than CENHDR, the
	 * actual size of the header can't be determined. The addition of
	 * SIGSIZ guarantees that the next signature is also in the buffer
	 * for proper loop termination.
	 */
	if (bytes < CENHDR) {
	    p = memmove(bp, p, bytes);
	    if ((res = read(fd, bp + bytes, MINREAD)) <= 0) {
	      	free(buffer);
		return (-1);
	    }
	    bytes += res;
	}
	entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p);
	if (bytes < entry_size + SIGSIZ) {
	    if (p != bp)
		p = memmove(bp, p, bytes);
	    read_size = entry_size - bytes + SIGSIZ;
	    read_size = (read_size < MINREAD) ? MINREAD : read_size;
	    if ((res = read(fd, bp + bytes,  read_size)) <= 0) {
	      	free(buffer);
		return (-1);
 	    }
	    bytes += res;
	}

	/*
	 * Check if the name is the droid we are looking for; the jar file
	 * manifest.  If so, build the entry record from the data found in
	 * the header located and return success.
	 */
        if (CENNAM(p) == strlen(file_name) &&
          memcmp((p + CENHDR), file_name, strlen(file_name)) == 0) {
	    if (lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (off_t)0) {
    		free(buffer);
		return (-1);
	    }
	    if (read(fd, locbuf, LOCHDR) < 0) {
    		free(buffer);
		return (-1);
	    }
	    if (GETSIG(locbuf) != LOCSIG) {
    		free(buffer);
		return (-1);
	    }
	    entry->isize = CENLEN(p);
	    entry->csize = CENSIZ(p);
	    entry->offset = base_offset + CENOFF(p) + LOCHDR +
		LOCNAM(locbuf) + LOCEXT(locbuf);
	    entry->how = CENHOW(p);
	    free(buffer);
	    return (0);
	}

	/*
	 * Point to the next entry and decrement the count of valid remaining
	 * bytes.
	 */
	bytes -= entry_size;
	p += entry_size;
    }
    free(buffer);
    return (-1);	/* Fell off the end the loop without a Manifest */
}
/**
 * Writes all entries except ".class" to the data_file_handle,
 * writes corresponding central directory records and the end of central
 * directory record to the directory_file_handle.
 * Treats .ZIP file format as documented in
 * ftp://ftp.uu.net/pub/archiving/zip/doc/appnote-970311-iz.zip.
 * Assumes that the .ZIP file doesn't span across multiple disks.
 * Assumes that raw_current_entry()->nextCenOffset contains the offset 
 * of the first entry of the central directory (as set in the constructor).
 * Returns true if completes successfully, false otherwise.
 */
bool
JarFileParser::copy_non_class_entries_to(OsFile_Handle data_file_handle,
                                         OsFile_Handle directory_file_handle 
                                         JVM_TRAPS) {
  UsingFastOops fast_oops;
  BufferedFile::Fast jar_buffer = buffered_file();

  const char class_suffix[] = {'.','c','l','a','s','s','\0'};
  juint locOffset = raw_current_entry()->locOffset;
  const juint buffer_size = MAX_ENTRY_NAME;
  DECLARE_STATIC_BUFFER(unsigned char, buffer,  buffer_size);
  unsigned int name_length = 0;
  juint loc_header_offset = 0;
  juint cen_header_offset = 0;
  size_t bytes_to_copy = 0;

  unsigned int written_entry_count = 0;
  unsigned int read_entry_count = 0;
  unsigned int total_entry_count = raw_current_entry()->totalEntryCount;

  // No OsFile_ routine to get the current position, 
  // so keep this information by ourselves.
  juint data_file_position = 0;
  juint directory_file_position = 0;

  GUARANTEE(buffer_size >= LOCHDRSIZ && 
            buffer_size >= EXTHDRSIZ && 
            buffer_size >= ENDHDRSIZ && 
            buffer_size >= MAX_ENTRY_NAME, "Buffer is too small");

  if ((OsFile_seek(data_file_handle, 0L, SEEK_SET) < 0) 
      || (OsFile_seek(directory_file_handle, 0L, SEEK_SET) < 0)) {
    return false;
  }

  for (read_entry_count = 0; read_entry_count < total_entry_count;
       read_entry_count++) {

    // Find the next entry in the JAR.
    bool found = find_entry(NULL JVM_MUST_SUCCEED);
    if (!found) {
      return false;
    }

    cen_header_offset = raw_current_entry()->nextCenOffset;

    // If find_entry succeeded, centralHeader contains the central directory  
    // header for the found entry.
    unsigned char *cenp = (unsigned char *)raw_current_entry()->centralHeader;

    name_length = CENNAM(cenp);

    if (name_length >= MAX_ENTRY_NAME) {
      Throw::error(jarfile_error JVM_THROW_0);
    } else {
      jar_buffer().get_bytes(buffer, name_length);
      if (!JarFileParser::match(class_suffix, (char*)buffer, name_length)) {
        loc_header_offset = locOffset + CENOFF(cenp);

        // Update the relative offset of local header 
        // in the central directory entry.
        // Use little-endian order according to the .ZIP format.
        Bytes::put_Java_u4(cenp + CENOFF_OFFSET,
                           Bytes::swap_u4(data_file_position));

        // Write the central directory entry to the directory file.
        if (OsFile_write(directory_file_handle, 
                         cenp, 1, CENHDRSIZ) != CENHDRSIZ) {
          return false;
        }

        directory_file_position += CENHDRSIZ;

        // Write the file name to the direcory file.
        if (OsFile_write(directory_file_handle, 
                         buffer, 1, name_length) != name_length) {
          return false;
        }

        directory_file_position += name_length;

        // Copy the extra field and file comment to the directory file.
        bytes_to_copy = CENEXT(cenp) + CENCOM(cenp);
        if (block_copy(&jar_buffer, directory_file_handle,
                       bytes_to_copy) != bytes_to_copy) {
          return false;
        }

        directory_file_position += bytes_to_copy;

        // Copy the local file header to the data file.
        if ((jar_buffer().seek(loc_header_offset, SEEK_SET) < 0) 
            || (jar_buffer().get_bytes(buffer, LOCHDRSIZ) != LOCHDRSIZ)
            || (OsFile_write(data_file_handle, 
                             buffer, 1, LOCHDRSIZ) != LOCHDRSIZ)) {
          return false;
        }

        data_file_position += LOCHDRSIZ;

        {
          unsigned char *locp = (unsigned char *)buffer;

          // Copy the file name, extra field and the compressed file data 
          // to the data file.
          bytes_to_copy = LOCNAM(locp) + LOCEXT(locp) + CENSIZ(cenp);
          if (block_copy(&jar_buffer, data_file_handle,
                         bytes_to_copy) != bytes_to_copy) {
            return false;
          }

          data_file_position += bytes_to_copy;

          // Check if the data descriptor exists.
          if ((LOCFLG(locp) & 8) == 8) {
            // Copy the data descriptor to the data file.
            if (jar_buffer().get_bytes(buffer, EXTHDRSIZ) != EXTHDRSIZ) {
              return false;
            }

            // The data descriptor may or may not start with the signature
            // depending on .ZIP file format revision used.
            if (GETSIG(buffer) == EXTSIG) {
              // According to the Info-ZIP Application Note 970311,
              // the data descriptor starts with the signature.
              if (OsFile_write(data_file_handle, 
                               buffer, 1, EXTHDRSIZ) != EXTHDRSIZ) {
                return false;
              }

              data_file_position += EXTHDRSIZ;
            } else {
              // According to the .ZIP format specification version 6.1.0,
              // the data descriptor doesn't start with the signature.
              if (OsFile_write(data_file_handle, 
                               buffer, 1, EXTHDRSIZ - 4) != EXTHDRSIZ - 4) {
                return false;
              }

              data_file_position += EXTHDRSIZ - 4;
            }
          }
        }

        written_entry_count++;
      }
    }

    cen_header_offset += CENHDRSIZ + name_length + CENEXT(cenp) + CENCOM(cenp);
    raw_current_entry()->nextCenOffset = cen_header_offset;
  }

  // Now that all central directory entries are processed, 
  // cen_header_offset points to the end of the central directory.
  if (jar_buffer().seek(cen_header_offset, SEEK_SET) < 0) {
    return false;
  }

  if (jar_buffer().get_bytes(buffer, ENDHDRSIZ) != ENDHDRSIZ) {
    return false;
  }

  {
    unsigned char *endp = (unsigned char *)buffer;

    if (GETSIG(endp) != ENDSIG) {
      return false;
    }

    // Update the record to match the new number of entries.
    // Use little-endian order according to the .ZIP format.

    Bytes::put_Java_u2(endp + ENDSUB_OFFSET,
                       Bytes::swap_u2(written_entry_count)); 
    Bytes::put_Java_u2(endp + ENDTOT_OFFSET,
                       Bytes::swap_u2(written_entry_count)); 

    // The size of the central directory is exactly 
    // the number of bytes written to the directory_file.
    Bytes::put_Java_u4(endp + ENDSIZ_OFFSET,
                       Bytes::swap_u4(directory_file_position)); 
    // The offset of the central directory is exactly 
    // the number of bytes written to the data_file.
    Bytes::put_Java_u4(endp + ENDOFF_OFFSET,
                       Bytes::swap_u4(data_file_position)); 

    if (OsFile_write(directory_file_handle, endp, 1, ENDHDRSIZ) != ENDHDRSIZ) {
      return false;
    }

    if (block_copy(&jar_buffer,
                   directory_file_handle, ENDCOM(endp)) != (size_t)ENDCOM(endp)) {
      return false;
    }
  }

  return true;
}
/*
 * Locate the manifest file with the zip/jar file.
 *
 *      fd:     File descriptor of the jar file.
 *      entry:  To be populated with the information necessary to perform
 *              the inflation (the compressed and uncompressed sizes and
 *              the offset in the file where the compressed data is located).
 *
 * Returns zero upon success. Returns a negative value upon failure.
 *
 * The buffer for reading the Central Directory if the zip/jar file needs
 * to be large enough to accommodate the largest possible single record
 * and the signature of the next record which is:
 *
 *      3*2**16 + CENHDR + SIGSIZ
 *
 * Each of the three variable sized fields (name, comment and extension)
 * has a maximum possible size of 64k.
 *
 * Typically, only a small bit of this buffer is used with bytes shuffled
 * down to the beginning of the buffer.  It is one thing to allocate such
 * a large buffer and another thing to actually start faulting it in.
 *
 * In most cases, all that needs to be read are the first two entries in
 * a typical jar file (META-INF and META-INF/MANIFEST.MF). Keep this factoid
 * in mind when optimizing this code.
 */
static int
find_file(int fd, zentry *entry, const char *file_name)
{
    int     bytes;
    int     res;
    int     entry_size;
    int     read_size;
    jlong   base_offset;
    Byte    *p;
    Byte    *bp;
    Byte    *buffer;
    Byte    locbuf[LOCHDR];

    if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) {
        return(-1);
    }

    bp = buffer;
    base_offset = compute_cen(fd, bp);
    if (base_offset == -1) {
        free(buffer);
        return -1;
    }

    if ((bytes = read(fd, bp, MINREAD)) < 0) {
        free(buffer);
        return (-1);
    }
    p = bp;
    /*
     * Loop through the Central Directory Headers. Note that a valid zip/jar
     * must have an ENDHDR (with ENDSIG) after the Central Directory.
     */
    while (GETSIG(p) == CENSIG) {

        /*
         * If a complete header isn't in the buffer, shift the contents
         * of the buffer down and refill the buffer.  Note that the check
         * for "bytes < CENHDR" must be made before the test for the entire
         * size of the header, because if bytes is less than CENHDR, the
         * actual size of the header can't be determined. The addition of
         * SIGSIZ guarantees that the next signature is also in the buffer
         * for proper loop termination.
         */
        if (bytes < CENHDR) {
            p = memmove(bp, p, bytes);
            if ((res = read(fd, bp + bytes, MINREAD)) <= 0) {
                free(buffer);
                return (-1);
            }
            bytes += res;
        }
        entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p);
        if (bytes < entry_size + SIGSIZ) {
            if (p != bp)
                p = memmove(bp, p, bytes);
            read_size = entry_size - bytes + SIGSIZ;
            read_size = (read_size < MINREAD) ? MINREAD : read_size;
            if ((res = read(fd, bp + bytes,  read_size)) <= 0) {
                free(buffer);
                return (-1);
            }
            bytes += res;
        }

        /*
         * Check if the name is the droid we are looking for; the jar file
         * manifest.  If so, build the entry record from the data found in
         * the header located and return success.
         */
        if ((size_t)CENNAM(p) == JLI_StrLen(file_name) &&
          memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) {
            if (JLI_Lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (jlong)0) {
                free(buffer);
                return (-1);
            }
            if (read(fd, locbuf, LOCHDR) < 0) {
                free(buffer);
                return (-1);
            }
            if (GETSIG(locbuf) != LOCSIG) {
                free(buffer);
                return (-1);
            }
            entry->isize = CENLEN(p);
            entry->csize = CENSIZ(p);
            entry->offset = base_offset + CENOFF(p) + LOCHDR +
                LOCNAM(locbuf) + LOCEXT(locbuf);
            entry->how = CENHOW(p);
            free(buffer);
            return (0);
        }

        /*
         * Point to the next entry and decrement the count of valid remaining
         * bytes.
         */
        bytes -= entry_size;
        p += entry_size;
    }
    free(buffer);
    return (-1);        /* Fell off the end the loop without a Manifest */
}
/*
 * Computes and positions at the start of the CEN header, ie. the central
 * directory, this will also return the offset if there is a zip file comment
 * at the end of the archive, for most cases this would be 0.
 */
static jlong
compute_cen(int fd, Byte *bp)
{
    int bytes;
    Byte *p;
    jlong base_offset;
    jlong offset;
    char buffer[MINREAD];
    p = buffer;
    /*
     * Read the END Header, which is the starting point for ZIP files.
     * (Clearly designed to make writing a zip file easier than reading
     * one. Now isn't that precious...)
     */
    if ((base_offset = find_end(fd, bp)) == -1) {
        return (-1);
    }
    p = bp;
    /*
     * There is a historical, but undocumented, ability to allow for
     * additional "stuff" to be prepended to the zip/jar file. It seems
     * that this has been used to prepend an actual java launcher
     * executable to the jar on Windows.  Although this is just another
     * form of statically linking a small piece of the JVM to the
     * application, we choose to continue to support it.  Note that no
     * guarantees have been made (or should be made) to the customer that
     * this will continue to work.
     *
     * Therefore, calculate the base offset of the zip file (within the
     * expanded file) by assuming that the central directory is followed
     * immediately by the end record.
     */
    if (zip64_present) {
        if ((offset = ZIP64_LOCOFF(p)) < (jlong)0) {
            return -1;
        }
        if (JLI_Lseek(fd, offset, SEEK_SET) < (jlong) 0) {
            return (-1);
        }
        if ((bytes = read(fd, buffer, MINREAD)) < 0) {
            return (-1);
        }
        if (GETSIG(buffer) != ZIP64_ENDSIG) {
            return -1;
        }
        if ((offset = ZIP64_ENDOFF(buffer)) < (jlong)0) {
            return -1;
        }
        if (JLI_Lseek(fd, offset, SEEK_SET) < (jlong)0) {
            return (-1);
        }
        p = buffer;
        base_offset = base_offset - ZIP64_ENDSIZ(p) - ZIP64_ENDOFF(p) - ZIP64_ENDHDR;
    } else {
        base_offset = base_offset - ENDSIZ(p) - ENDOFF(p);
        /*
         * The END Header indicates the start of the Central Directory
         * Headers. Remember that the desired Central Directory Header (CEN)
         * will almost always be the second one and the first one is a small
         * directory entry ("META-INF/"). Keep the code optimized for
         * that case.
         *
         * Seek to the beginning of the Central Directory.
         */
        if (JLI_Lseek(fd, base_offset + ENDOFF(p), SEEK_SET) < (jlong) 0) {
            return (-1);
        }
    }
    return base_offset;
}
jzfile *
ZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified,
                 jboolean usemmap)
{
    char errbuf[256];
    jlong len;
    jzfile *zip;

    if ((zip = allocZip(name)) == NULL) {
        return NULL;
    }

#ifdef USE_MMAP
    zip->usemmap = usemmap;
#endif
    zip->refs = 1;
    zip->lastModified = lastModified;

    if (zfd == -1) {
        if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0)
            *pmsg = strdup(errbuf);
        freeZip(zip);
        return NULL;
    }

    // Assumption, zfd refers to start of file. Trivially, reuse errbuf.
    if (readFully(zfd, errbuf, 4) != -1) {  // errors will be handled later
        if (GETSIG(errbuf) == LOCSIG)
            zip->locsig = JNI_TRUE;
        else
            zip->locsig = JNI_FALSE;
    }

    len = zip->len = IO_Lseek(zfd, 0, SEEK_END);
    if (len <= 0) {
        if (len == 0) { /* zip file is empty */
            if (pmsg) {
                *pmsg = strdup("zip file is empty");
            }
        } else { /* error */
            if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0)
                *pmsg = strdup(errbuf);
        }
        ZFILE_Close(zfd);
        freeZip(zip);
        return NULL;
    }

    zip->zfd = zfd;
    if (readCEN(zip, -1) < 0) {
        /* An error occurred while trying to read the zip file */
        if (pmsg != 0) {
            /* Set the zip error message */
            if (zip->msg != NULL)
                *pmsg = strdup(zip->msg);
        }
        freeZip(zip);
        return NULL;
    }
    MLOCK(zfiles_lock);
    zip->next = zfiles;
    zfiles = zip;
    MUNLOCK(zfiles_lock);

    return zip;
}