1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.commons.compress.archivers.zip; 20 21 22 import java.util.zip.ZipException; 23 24 /** 25 * An extra field who's sole purpose is to align and pad the local file header 26 * so that the entry's data starts at a certain position. 27 * 28 * <p>The padding content of the padding is ignored and not retained 29 * when reading a padding field.</p> 30 * 31 * <p>This enables Commons Compress to create "aligned" archives 32 * similar to Android's zipalign command line tool.</p> 33 * 34 * @since 1.14 35 * @see "https://developer.android.com/studio/command-line/zipalign.html" 36 * @see ZipArchiveEntry#setAlignment 37 */ 38 public class ResourceAlignmentExtraField implements ZipExtraField { 39 40 /** 41 * Extra field id used for storing alignment and padding. 42 */ 43 public static final ZipShort ID = new ZipShort(0xa11e); 44 45 public static final int BASE_SIZE = 2; 46 47 private static final int ALLOW_METHOD_MESSAGE_CHANGE_FLAG = 0x8000; 48 49 private short alignment; 50 51 private boolean allowMethodChange; 52 53 private int padding = 0; 54 ResourceAlignmentExtraField()55 public ResourceAlignmentExtraField() { 56 } 57 ResourceAlignmentExtraField(int alignment)58 public ResourceAlignmentExtraField(int alignment) { 59 this(alignment, false); 60 } 61 ResourceAlignmentExtraField(int alignment, boolean allowMethodChange)62 public ResourceAlignmentExtraField(int alignment, boolean allowMethodChange) { 63 this(alignment, allowMethodChange, 0); 64 } 65 ResourceAlignmentExtraField(int alignment, boolean allowMethodChange, int padding)66 public ResourceAlignmentExtraField(int alignment, boolean allowMethodChange, int padding) { 67 if (alignment < 0 || alignment > 0x7fff) { 68 throw new IllegalArgumentException("Alignment must be between 0 and 0x7fff, was: " + alignment); 69 } 70 this.alignment = (short) alignment; 71 this.allowMethodChange = allowMethodChange; 72 this.padding = padding; 73 } 74 75 /** 76 * Gets requested alignment. 77 * 78 * @return 79 * requested alignment. 80 */ getAlignment()81 public short getAlignment() { 82 return alignment; 83 } 84 85 /** 86 * Indicates whether method change is allowed when re-compressing the zip file. 87 * 88 * @return 89 * true if method change is allowed, false otherwise. 90 */ allowMethodChange()91 public boolean allowMethodChange() { 92 return allowMethodChange; 93 } 94 95 @Override getHeaderId()96 public ZipShort getHeaderId() { 97 return ID; 98 } 99 100 @Override getLocalFileDataLength()101 public ZipShort getLocalFileDataLength() { 102 return new ZipShort(BASE_SIZE + padding); 103 } 104 105 @Override getCentralDirectoryLength()106 public ZipShort getCentralDirectoryLength() { 107 return new ZipShort(BASE_SIZE); 108 } 109 110 @Override getLocalFileDataData()111 public byte[] getLocalFileDataData() { 112 byte[] content = new byte[BASE_SIZE + padding]; 113 ZipShort.putShort(alignment | (allowMethodChange ? ALLOW_METHOD_MESSAGE_CHANGE_FLAG : 0), 114 content, 0); 115 return content; 116 } 117 118 @Override getCentralDirectoryData()119 public byte[] getCentralDirectoryData() { 120 return ZipShort.getBytes(alignment | (allowMethodChange ? ALLOW_METHOD_MESSAGE_CHANGE_FLAG : 0)); 121 } 122 123 @Override parseFromLocalFileData(byte[] buffer, int offset, int length)124 public void parseFromLocalFileData(byte[] buffer, int offset, int length) throws ZipException { 125 parseFromCentralDirectoryData(buffer, offset, length); 126 this.padding = length - BASE_SIZE; 127 } 128 129 @Override parseFromCentralDirectoryData(byte[] buffer, int offset, int length)130 public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException { 131 if (length < BASE_SIZE) { 132 throw new ZipException("Too short content for ResourceAlignmentExtraField (0xa11e): " + length); 133 } 134 int alignmentValue = ZipShort.getValue(buffer, offset); 135 this.alignment = (short) (alignmentValue & (ALLOW_METHOD_MESSAGE_CHANGE_FLAG - 1)); 136 this.allowMethodChange = (alignmentValue & ALLOW_METHOD_MESSAGE_CHANGE_FLAG) != 0; 137 } 138 } 139