blob: 40968c3afa493a84d4d9c3bf02b129f467a4e5a8 [file] [log] [blame]
// 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;
}
}