/* * Return a new initialized jzentry corresponding to a given hash cell. * In case of error, returns NULL. * We already sanity-checked all the CEN headers for ZIP format errors * in readCEN(), so we don't check them again here. * The ZIP lock should be held here. */ static jzentry * newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint) { jlong locoff; jint nlen, elen, clen; jzentry *ze; char *cen; if ((ze = (jzentry *) malloc(sizeof(jzentry))) == NULL) return NULL; ze->name = NULL; ze->extra = NULL; ze->comment = NULL; #ifdef USE_MMAP if (zip->usemmap) { cen = (char*) zip->maddr + zc->cenpos - zip->offset; } else #endif { if (accessHint == ACCESS_RANDOM) cen = readCENHeader(zip, zc->cenpos, AMPLE_CEN_HEADER_SIZE); else cen = sequentialAccessReadCENHeader(zip, zc->cenpos); if (cen == NULL) goto Catch; } nlen = CENNAM(cen); elen = CENEXT(cen); clen = CENCOM(cen); ze->time = CENTIM(cen); ze->size = CENLEN(cen); ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen); ze->crc = CENCRC(cen); locoff = CENOFF(cen); ze->pos = -(zip->locpos + locoff); ze->flag = CENFLG(cen); if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch; memcpy(ze->name, cen + CENHDR, nlen); ze->name[nlen] = '\0'; if (elen > 0) { char *extra = cen + CENHDR + nlen; /* This entry has "extra" data */ if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch; ze->extra[0] = (unsigned char) elen; ze->extra[1] = (unsigned char) (elen >> 8); memcpy(ze->extra+2, extra, elen); if (ze->csize == ZIP64_MAGICVAL || ze->size == ZIP64_MAGICVAL || locoff == ZIP64_MAGICVAL) { jint off = 0; while ((off + 4) < elen) { // spec: HeaderID+DataSize+Data jint sz = SH(extra, off + 2); if (SH(extra, off) == ZIP64_EXTID) { off += 4; if (ze->size == ZIP64_MAGICVAL) { // if invalid zip64 extra fields, just skip if (sz < 8 || (off + 8) > elen) break; ze->size = LL(extra, off); sz -= 8; off += 8; } if (ze->csize == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > elen) break; ze->csize = LL(extra, off); sz -= 8; off += 8; } if (locoff == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > elen) break; ze->pos = -(zip->locpos + LL(extra, off)); sz -= 8; off += 8; } break; } off += (sz + 4); } } }
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 */ }
int JarFileParser::filtered_do_next_entries(JvmNameFilterProc entry_filter_proc, JvmDoJarEntryProc do_jar_entry_proc, void* caller_data, int entry_id, int max_size JVM_TRAPS) { GUARANTEE(entry_id >= 0 && max_size > 0, "Sanity"); DECLARE_STATIC_BUFFER(char, entry_name, MAX_ENTRY_NAME); unsigned int name_length = 0; int total_size = 0; if (max_size <= 0) { max_size = max_jint; } if (entry_id > 0) { raw_current_entry()->nextCenOffset = entry_id; } // NOTE: max_entries can be -1 that indicates no limitation on the number of // entries processed. for (total_size = 0; total_size < max_size;) { bool found = find_entry(NULL JVM_MUST_SUCCEED); if (!found) { break; } // 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) { return -1; } // Save the next offset as entry_filter_proc may move raw_current_entry() juint nextCenOffset = raw_current_entry()->nextCenOffset + CENHDRSIZ + name_length + CENEXT(cenp) + CENCOM(cenp); { UsingFastOops fast_oops; BufferedFile::Fast bf = buffered_file(); if (bf().get_bytes((address)entry_name, name_length) != name_length) { return -1; } } entry_name[name_length] = '\0'; if (((entry_filter_proc == NULL) || (entry_filter_proc(entry_name, caller_data) == KNI_TRUE)) && (do_jar_entry_proc != NULL)) { UsingFastOops fast_oops; Buffer::Fast buffer = load_entry(JVM_SINGLE_ARG_CHECK_0); if (buffer.is_null()) { return -1; } if (do_jar_entry_proc((char*)entry_name, buffer().data(), buffer().length(), caller_data) == KNI_FALSE) { return 0; } } total_size += raw_current_entry()->length; if (CURRENT_HAS_PENDING_EXCEPTION) { // if entry_filter_proc returned NULL, check for exception return -1; } raw_current_entry()->nextCenOffset = nextCenOffset; } // If we reached the end of JAR return 0. // Otherwise return nextCenOffset, it is used as a JAR entry id. return (total_size < max_size) ? 0 : raw_current_entry()->nextCenOffset; }
/** * 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 */ }