Exemple #1
0
/*
 * Searches for end of central directory (END) header. The contents of
 * the END header will be read and placed in endbuf. Returns the file
 * position of the END header, otherwise returns -1 if the END header
 * was not found or an error occurred.
 */
static jlong
findEND(jzfile *zip, void *endbuf)
{
    char buf[READBLOCKSZ];
    jlong pos;
    const jlong len = zip->len;
    const ZFILE zfd = zip->zfd;
    const jlong minHDR = len - END_MAXLEN > 0 ? len - END_MAXLEN : 0;
    const jlong minPos = minHDR - (sizeof(buf)-ENDHDR);
    jint clen;

    for (pos = len - sizeof(buf); pos >= minPos; pos -= (sizeof(buf)-ENDHDR)) {

        int i;
        jlong off = 0;
        if (pos < 0) {
            /* Pretend there are some NUL bytes before start of file */
            off = -pos;
            memset(buf, '\0', (size_t)off);
        }

        if (readFullyAt(zfd, buf + off, sizeof(buf) - off,
                        pos + off) == -1) {
            return -1;  /* System error */
        }

        /* Now scan the block backwards for END header signature */
        for (i = sizeof(buf) - ENDHDR; i >= 0; i--) {
            if (buf[i+0] == 'P'    &&
                buf[i+1] == 'K'    &&
                buf[i+2] == '\005' &&
                buf[i+3] == '\006' &&
                ((pos + i + ENDHDR + ENDCOM(buf + i) == len)
                 || verifyEND(zip, pos + i, buf + i))) {
                /* Found END header */
                memcpy(endbuf, buf + i, ENDHDR);

                clen = ENDCOM(endbuf);
                if (clen != 0) {
                    zip->comment = malloc(clen + 1);
                    if (zip->comment == NULL) {
                        return -1;
                    }
                    if (readFullyAt(zfd, zip->comment, clen, pos + i + ENDHDR)
                        == -1) {
                        free(zip->comment);
                        zip->comment = NULL;
                        return -1;
                    }
                    zip->comment[clen] = '\0';
                    zip->clen = clen;
                }
                return pos + i;
            }
        }
    }

    return -1; /* END header not found */
}
/*
 * 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);
}
Exemple #3
0
static long
fixEND(FILE *zfd, long len)
{
    char buf[READBLOCKSZ];
    long pos;
    const long minHDR = len - END_MAXLEN > 0 ? len - END_MAXLEN : 0;
    const long minPos = minHDR - (sizeof(buf)-ENDHDR);

    for (pos = len - sizeof(buf); pos >= minPos; pos -= (sizeof(buf)-ENDHDR)) {

        int i;
        long off = 0;
        if (pos < 0) {
            /* Pretend there are some NUL bytes before start of file */
            off = -pos;
            memset(buf, '\0', off);
        }

        if (readFullyAt(zfd, buf + off, sizeof(buf) - off,
                        pos + off) == -1) {
            return -1;  /* System error */
        }

        /* Now scan the block backwards for END header signature */
        for (i = sizeof(buf) - ENDHDR; i >= 0; i--) {
            if (buf[i+0] == 'P'    &&
                buf[i+1] == 'K'    &&
                buf[i+2] == '\005' &&
                buf[i+3] == '\006') {
                    unsigned int virSize = pos + i + ENDHDR + ENDCOM(buf + i);
                    if (virSize != len) {
                        setFileLength(zfd, virSize);
                    }
                    return pos + i;
            }
        }
    }
    return 0; /* END header not found */
}
/**
 * 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;
}
bool JarFileParser::find_end_of_central_header() {
  DECLARE_STATIC_BUFFER(unsigned char, buffer, TMPBUFFERSIZE);
  BufferedFile::Raw bf = buffered_file();

  /* Get the length of the file */
  const jint length = (int) bf().file_size();

  /* Calculate the smallest possible offset for the end header.  It
   * can be at most 0xFFFF + ENDHDRSIZ bytes from the end of the file, but
   * the file must also have a local header and a central header
   */
  jint minOffset = length - (0xFFFF + ENDHDRSIZ);
  if (minOffset < LOCHDRSIZ + CENHDRSIZ) {
    minOffset = LOCHDRSIZ + CENHDRSIZ;
  }

  /* We assume that "buffer" contains the contents
   * of part of the file. currentOffset contains the offset of buffer[0].
   */

  /* Read in the last ENDHDRSIZ bytes into the buffer.  99% of the time,
   * the file won't have a comment, and this is the only read we'll need */
  if ( (bf().seek(-ENDHDRSIZ, SEEK_END) < 0)
    || (bf().get_bytes(buffer, ENDHDRSIZ) != ENDHDRSIZ)) {
    return false;
  }
  /* Set currentOffset to be the offset of buffer[0] */
  jint currentOffset = length - ENDHDRSIZ;
  /* Set bp to be the location at which to start looking */
  unsigned const char* bp = buffer;

  for (;;) {
    /* "buffer" contains a block of data from the file, starting at
     * currentOffset "position" in the file.
     * We investigate whether   currentOffset + (bp - buffer)  is the start
     * of the end header in the zip file.
     *
     * We use a simplified version of Knuth-Morris-Pratt search algorithm.
     * The header we're looking for is 'P' 'K' 5  6
     */
    switch(bp[0]) {
    case '\006':   /* The header must start at least 3 bytes back */
      bp -= 3; break;
    case '\005':   /* The header must start at least 2 bytes back  */
      bp -= 2; break;
    case 'K':      /* The header must start at least 1 byte back  */
      bp -= 1; break;
    case 'P':      /* Either this is the header, or the header must
                    * start at least 4  back */
      if (bp[1] == 'K' && bp[2] == 5 && bp[3] == 6) {
        /* We have what may be a header.  Let's make sure the
         * implied length of the jar file matches the actual
         * length.
         */
        int endpos = (int) currentOffset + (bp - buffer);
        if (endpos + ENDHDRSIZ + ENDCOM(bp) == length) {
          juint cenOffset = endpos - ENDSIZ(bp);
          juint locOffset = cenOffset - ENDOFF(bp);
          unsigned char sig[4];

          if (bf().seek(locOffset, SEEK_SET) >= 0 &&
              bf().get_bytes(sig, 4) == 4 &&
              sig[0] == (unsigned char)'P' && 
              sig[1] == (unsigned char)'K' && 
              sig[2] == (unsigned char) 3  && 
              sig[3] == (unsigned char) 4) {

            raw_current_entry()->cenOffset = cenOffset;
            raw_current_entry()->nextCenOffset = cenOffset;
            raw_current_entry()->locOffset = locOffset;
#if ENABLE_ROM_GENERATOR
            raw_current_entry()->totalEntryCount = ENDTOT(bp);
#endif
          }
          return true; // Found central header
        }
      }
      /* FALL THROUGH */
    default:
      /* The header must start at least four characters back, since
       * the current character isn't in the header */
      bp -= 4;
    }
    if (bp < buffer) {
      /* We've moved outside our window into the file.  We must
       * move the window backwards */
      size_t count = (size_t) (currentOffset - minOffset); /* Bytes left in file */
      if (((jint)count) <= 0) {
        /* Nothing left to read.  Time to give up */
        return false;
      } else {
        /* up to ((bp - buffer) + ENDHDRSIZ) bytes in the buffer might
         * still be part of the end header, so the most bytes we can
         * actually read are
         *      TMPBUFFERSIZE - ((bp - buffer) + ENDHDRSIZE).
         */
        size_t available = (TMPBUFFERSIZE - ENDHDRSIZ) + (buffer - bp);
        if (count > available) {
          count = available;
        }
      }
      /* Back up, while keeping our virtual currentOffset the same */
      currentOffset -= count;
      bp += count;
      jvm_memmove(buffer + count, buffer, TMPBUFFERSIZE - count);
      if ( bf().seek(currentOffset, SEEK_SET) < 0 ||
           bf().get_bytes(buffer, count) != size_t(count) ) {
        return false;
      }
    }
  } /* end of for loop */
}