예제 #1
0
  /**
   * Appends a zip64 extended info record to the extras contained in {@code ze}. If {@code ze}
   * contains no extras, a new extras array is created.
   */
  public static void insertZip64ExtendedInfoToExtras(ZipEntry ze) throws ZipException {
    final byte[] output;
    // We always write the size, uncompressed size and local rel header offset in all our
    // Zip64 extended info headers (in both the local file header as well as the central
    // directory). We always omit the disk number because we don't support spanned
    // archives anyway.
    //
    //  2 bytes : Zip64 Extended Info Header ID
    //  2 bytes : Zip64 Extended Info Field Size.
    //  8 bytes : Uncompressed size
    //  8 bytes : Compressed size
    //  8 bytes : Local header rel offset.
    // ----------
    // 28 bytes : total
    final int extendedInfoSize = 28;

    if (ze.extra == null) {
      output = new byte[extendedInfoSize];
    } else {
      // If the existing extras are already too big, we have no choice but to throw
      // an error.
      if (ze.extra.length + extendedInfoSize > 65535) {
        throw new ZipException("No space in extras for zip64 extended entry info");
      }

      // We copy existing extras over and put the zip64 extended info at the beginning. This
      // is to avoid breakages in the presence of "old style" extras which don't contain
      // headers and lengths. The spec is again silent about these inconsistencies.
      //
      // This means that people that for ZipOutputStream users, the value ZipEntry.getExtra
      // after an entry is written will be different from before. This shouldn't be an issue
      // in practice.
      output = new byte[ze.extra.length + extendedInfoSize];
      System.arraycopy(ze.extra, 0, output, extendedInfoSize, ze.extra.length);
    }

    ByteBuffer bb = ByteBuffer.wrap(output).order(ByteOrder.LITTLE_ENDIAN);
    bb.putShort(ZIP64_EXTENDED_INFO_HEADER_ID);
    // We subtract four because extendedInfoSize includes the ID and field
    // size itself.
    bb.putShort((short) (extendedInfoSize - 4));

    if (ze.getMethod() == ZipEntry.STORED) {
      bb.putLong(ze.size);
      bb.putLong(ze.compressedSize);
    } else {
      // Store these fields in the data descriptor instead.
      bb.putLong(0); // size.
      bb.putLong(0); // compressed size.
    }

    // The offset is only relevant in the central directory entry, but we write it out here
    // anyway, since we know what it is.
    bb.putLong(ze.localHeaderRelOffset);

    ze.extra = output;
  }
 /** Returns a deep copy of this zip entry. */
 @Override
 public Object clone() {
   try {
     ZipEntry result = (ZipEntry) super.clone();
     result.extra = extra != null ? extra.clone() : null;
     return result;
   } catch (CloneNotSupportedException e) {
     throw new AssertionError(e);
   }
 }
예제 #3
0
 /** Returns a copy of this entry. */
 public Object clone() {
   try {
     ZipEntry e = (ZipEntry) super.clone();
     e.extra = (extra == null) ? null : extra.clone();
     return e;
   } catch (CloneNotSupportedException e) {
     // This should never happen, since we are Cloneable
     throw new InternalError();
   }
 }
예제 #4
0
  /**
   * Parse the zip64 extended info record from the extras present in {@code ze}.
   *
   * <p>If {@code fromCentralDirectory} is true, we assume we're parsing a central directory record.
   * We assume a local file header otherwise. The difference between the two is that a central
   * directory entry is required to be complete, whereas a local file header isn't. This is due to
   * the presence of an optional data descriptor after the file content.
   *
   * @return {@code} true iff. a zip64 extended info record was found.
   */
  public static boolean parseZip64ExtendedInfo(ZipEntry ze, boolean fromCentralDirectory)
      throws ZipException {
    int extendedInfoSize = -1;
    int extendedInfoStart = -1;
    // If this file contains a zip64 central directory locator, entries might
    // optionally contain a zip64 extended information extra entry.
    if (ze.extra != null && ze.extra.length > 0) {
      // Extensible data fields are of the form header1+data1 + header2+data2 and so
      // on, where each header consists of a 2 byte header ID followed by a 2 byte size.
      // We need to iterate through the entire list of headers to find the header ID
      // for the zip64 extended information extra field (0x0001).
      final ByteBuffer buf = ByteBuffer.wrap(ze.extra).order(ByteOrder.LITTLE_ENDIAN);
      extendedInfoSize = getZip64ExtendedInfoSize(buf);
      if (extendedInfoSize != -1) {
        extendedInfoStart = buf.position();
        try {
          // The size & compressed size only make sense in the central directory *or* if
          // we know them beforehand. If we don't know them beforehand, they're stored in
          // the data descriptor and should be read from there.
          //
          // Note that the spec says that the local file header "MUST" contain the
          // original and compressed size fields. We don't care too much about that.
          // The spec claims that the order of fields is fixed anyway.
          if (fromCentralDirectory || (ze.getMethod() == ZipEntry.STORED)) {
            if (ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
              ze.size = buf.getLong();
            }

            if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
              ze.compressedSize = buf.getLong();
            }
          }

          // The local header offset is significant only in the central directory. It makes no
          // sense within the local header itself.
          if (fromCentralDirectory) {
            if (ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
              ze.localHeaderRelOffset = buf.getLong();
            }
          }
        } catch (BufferUnderflowException bue) {
          ZipException zipException = new ZipException("Error parsing extended info");
          zipException.initCause(bue);
          throw zipException;
        }
      }
    }

    // This entry doesn't contain a zip64 extended information data entry header.
    // We have to check that the compressedSize / size / localHeaderRelOffset values
    // are valid and don't require the presence of the extended header.
    if (extendedInfoSize == -1) {
      if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE
          || ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE
          || ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
        throw new ZipException(
            "File contains no zip64 extended information: "
                + "name="
                + ze.name
                + "compressedSize="
                + ze.compressedSize
                + ", size="
                + ze.size
                + ", localHeader="
                + ze.localHeaderRelOffset);
      }

      return false;
    } else {
      // If we're parsed the zip64 extended info header, we remove it from the extras
      // so that applications that set their own extras will see the data they set.

      // This is an unfortunate workaround needed due to a gap in the spec. The spec demands
      // that extras are present in the "extensible" format, which means that each extra field
      // must be prefixed with a header ID and a length. However, earlier versions of the spec
      // made no mention of this, nor did any existing API enforce it. This means users could
      // set "free form" extras without caring very much whether the implementation wanted to
      // extend or add to them.

      // The start of the extended info header.
      final int extendedInfoHeaderStart = extendedInfoStart - 4;
      // The total size of the extended info, including the header.
      final int extendedInfoTotalSize = extendedInfoSize + 4;

      final int extrasLen = ze.extra.length - extendedInfoTotalSize;
      byte[] extrasWithoutZip64 = new byte[extrasLen];

      System.arraycopy(ze.extra, 0, extrasWithoutZip64, 0, extendedInfoHeaderStart);
      System.arraycopy(
          ze.extra,
          extendedInfoHeaderStart + extendedInfoTotalSize,
          extrasWithoutZip64,
          extendedInfoHeaderStart,
          (extrasLen - extendedInfoHeaderStart));

      ze.extra = extrasWithoutZip64;
      return true;
    }
  }