/* * 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); }
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 */ }