// Copyright 2015 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.zip; import com.google.devtools.build.zip.ZipFileEntry.Feature; import java.io.IOException; import java.util.EnumSet; import java.util.zip.ZipException; class LocalFileHeader { static final int SIGNATURE = 0x04034b50; static final int FIXED_DATA_SIZE = 30; static final int SIGNATURE_OFFSET = 0; static final int VERSION_OFFSET = 4; static final int FLAGS_OFFSET = 6; static final int METHOD_OFFSET = 8; static final int MOD_TIME_OFFSET = 10; static final int CRC_OFFSET = 14; static final int COMPRESSED_SIZE_OFFSET = 18; static final int UNCOMPRESSED_SIZE_OFFSET = 22; static final int FILENAME_LENGTH_OFFSET = 26; static final int EXTRA_FIELD_LENGTH_OFFSET = 28; static final int VARIABLE_DATA_OFFSET = 30; /** * Generates the raw byte data of the local file header for the {@link ZipFileEntry}. Uses the * specified {@link ZipFileData} to encode the file name and comment. * @throws IOException */ static byte[] create(ZipFileEntry entry, ZipFileData file, boolean allowZip64) throws IOException { byte[] name = entry.getName().getBytes(file.getCharset()); // We don't do a defensive copy here so that later, when we write the central directory entry, // the changes we make here take effect. // TODO(bazel-team): This seems like a bug. Investigate. ExtraDataList extra = entry.getExtra(); EnumSet<Feature> features = entry.getFeatureSet(); int size = (int) entry.getSize(); int csize = (int) entry.getCompressedSize(); if (features.contains(Feature.ZIP64_SIZE) || features.contains(Feature.ZIP64_CSIZE)) { if (!allowZip64) { throw new ZipException(String.format("Writing an entry of size %d(%d) without Zip64" + " extensions is not supported.", entry.getSize(), entry.getCompressedSize())); } extra.remove((short) 0x0001); int extraSize = 0; if (features.contains(Feature.ZIP64_SIZE)) { size = -1; extraSize += 8; } if (features.contains(Feature.ZIP64_CSIZE)) { csize = -1; extraSize += 8; } byte[] zip64Extra = new byte[ExtraData.FIXED_DATA_SIZE + extraSize]; ZipUtil.shortToLittleEndian(zip64Extra, ExtraData.ID_OFFSET, (short) 0x0001); ZipUtil.shortToLittleEndian(zip64Extra, ExtraData.LENGTH_OFFSET, (short) extraSize); int offset = ExtraData.FIXED_DATA_SIZE; if (features.contains(Feature.ZIP64_SIZE)) { ZipUtil.longToLittleEndian(zip64Extra, offset, entry.getSize()); offset += 8; } if (features.contains(Feature.ZIP64_CSIZE)) { ZipUtil.longToLittleEndian(zip64Extra, offset, entry.getCompressedSize()); offset += 8; } extra.add(new ExtraData(zip64Extra, 0)); } else { extra.remove((short) 0x0001); } extra.remove(ExtraDataList.EXTENDED_TIMESTAMP); extra.remove(ExtraDataList.INFOZIP_UNIX_NEW); byte[] buf = new byte[FIXED_DATA_SIZE + name.length + extra.getLength()]; ZipUtil.intToLittleEndian(buf, SIGNATURE_OFFSET, SIGNATURE); ZipUtil.shortToLittleEndian(buf, VERSION_OFFSET, entry.getVersionNeeded()); ZipUtil.shortToLittleEndian(buf, FLAGS_OFFSET, entry.getFlags()); ZipUtil.shortToLittleEndian(buf, METHOD_OFFSET, entry.getMethod().getValue()); ZipUtil.intToLittleEndian(buf, MOD_TIME_OFFSET, ZipUtil.unixToDosTime(entry.getTime())); ZipUtil.intToLittleEndian(buf, CRC_OFFSET, (int) (entry.getCrc() & 0xffffffff)); ZipUtil.intToLittleEndian(buf, COMPRESSED_SIZE_OFFSET, csize); ZipUtil.intToLittleEndian(buf, UNCOMPRESSED_SIZE_OFFSET, size); ZipUtil.shortToLittleEndian(buf, FILENAME_LENGTH_OFFSET, (short) name.length); ZipUtil.shortToLittleEndian(buf, EXTRA_FIELD_LENGTH_OFFSET, (short) extra.getLength()); System.arraycopy(name, 0, buf, FIXED_DATA_SIZE, name.length); ZipUtil.readFully(extra.getByteStream(), buf, FIXED_DATA_SIZE + name.length, extra.getLength()); return buf; } }