1 /* 2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.ohos.hapsigntool.zip; 17 18 import com.ohos.hapsigntool.error.ZipException; 19 20 import java.util.Arrays; 21 import java.util.Calendar; 22 import java.util.zip.CRC32; 23 24 /** 25 * ZipEntry and CentralDirectory data 26 * 27 * @since 2023/12/02 28 */ 29 public class ZipEntry { 30 private ZipEntryData zipEntryData; 31 32 private CentralDirectory fileEntryInCentralDirectory; 33 34 /** 35 * updateLength 36 */ updateLength()37 public void updateLength() { 38 zipEntryData.updateLength(); 39 fileEntryInCentralDirectory.updateLength(); 40 } 41 42 /** 43 * alignment one entry 44 * 45 * @param alignNum need align bytes length 46 * @return add bytes length 47 * @throws ZipException alignment exception 48 */ alignment(int alignNum)49 public int alignment(int alignNum) throws ZipException { 50 // if cd extra len bigger than entry extra len, make cd and entry extra length equals 51 int padding = calZeroPaddingLengthForEntryExtra(); 52 int remainder = (int) ((zipEntryData.getZipEntryHeader().getLength() 53 + fileEntryInCentralDirectory.getOffset()) % alignNum); 54 55 if (remainder == 0) { 56 return padding; 57 } 58 int add = alignNum - remainder; 59 int newExtraLength = zipEntryData.getZipEntryHeader().getExtraLength() + add; 60 if (newExtraLength > UnsignedDecimalUtil.MAX_UNSIGNED_SHORT_VALUE) { 61 throw new ZipException("can not align " + zipEntryData.getZipEntryHeader().getFileName()); 62 } 63 setEntryHeaderNewExtraLength(newExtraLength); 64 setCenterDirectoryNewExtraLength(newExtraLength); 65 66 return add; 67 } 68 calZeroPaddingLengthForEntryExtra()69 private int calZeroPaddingLengthForEntryExtra() throws ZipException { 70 int entryExtraLen = zipEntryData.getZipEntryHeader().getExtraLength(); 71 int cdExtraLen = fileEntryInCentralDirectory.getExtraLength(); 72 if (cdExtraLen > entryExtraLen) { 73 setEntryHeaderNewExtraLength(cdExtraLen); 74 return cdExtraLen - entryExtraLen; 75 } 76 if (cdExtraLen < entryExtraLen) { 77 setCenterDirectoryNewExtraLength(entryExtraLen); 78 return entryExtraLen - cdExtraLen; 79 } 80 return 0; 81 } 82 setCenterDirectoryNewExtraLength(int newLength)83 private void setCenterDirectoryNewExtraLength(int newLength) throws ZipException { 84 byte[] newCDExtra = getAlignmentNewExtra(newLength, fileEntryInCentralDirectory.getExtraData()); 85 fileEntryInCentralDirectory.setExtraData(newCDExtra); 86 fileEntryInCentralDirectory.setExtraLength(newLength); 87 fileEntryInCentralDirectory.setLength(CentralDirectory.CD_LENGTH 88 + fileEntryInCentralDirectory.getFileNameLength() 89 + fileEntryInCentralDirectory.getExtraLength() + fileEntryInCentralDirectory.getCommentLength()); 90 } 91 setEntryHeaderNewExtraLength(int newLength)92 private void setEntryHeaderNewExtraLength(int newLength) throws ZipException { 93 ZipEntryHeader zipEntryHeader = zipEntryData.getZipEntryHeader(); 94 byte[] newExtra = getAlignmentNewExtra(newLength, zipEntryHeader.getExtraData()); 95 zipEntryHeader.setExtraData(newExtra); 96 zipEntryHeader.setExtraLength(newLength); 97 zipEntryHeader.setLength(ZipEntryHeader.HEADER_LENGTH + zipEntryHeader.getExtraLength() 98 + zipEntryHeader.getFileNameLength()); 99 zipEntryData.setLength(zipEntryHeader.getLength() + zipEntryData.getFileSize() 100 + (zipEntryData.getDataDescriptor() == null ? 0 : DataDescriptor.DES_LENGTH)); 101 } 102 getAlignmentNewExtra(int newLength, byte[] old)103 private byte[] getAlignmentNewExtra(int newLength, byte[] old) throws ZipException { 104 if (old == null) { 105 return new byte[newLength]; 106 } 107 if (newLength < old.length) { 108 throw new ZipException("can not align " + zipEntryData.getZipEntryHeader().getFileName()); 109 } 110 return Arrays.copyOf(old, newLength); 111 } 112 getZipEntryData()113 public ZipEntryData getZipEntryData() { 114 return zipEntryData; 115 } 116 setZipEntryData(ZipEntryData zipEntryData)117 public void setZipEntryData(ZipEntryData zipEntryData) { 118 this.zipEntryData = zipEntryData; 119 } 120 getCentralDirectory()121 public CentralDirectory getCentralDirectory() { 122 return fileEntryInCentralDirectory; 123 } 124 setCentralDirectory(CentralDirectory centralDirectory)125 public void setCentralDirectory(CentralDirectory centralDirectory) { 126 this.fileEntryInCentralDirectory = centralDirectory; 127 } 128 129 /** 130 * zip entry builder 131 */ 132 public static class Builder { 133 private short version = 10; 134 135 private short flag = 2048; 136 137 private short method = 0; 138 139 private long compressedSize; 140 141 private long unCompressedSize; 142 143 private String fileName; 144 145 private byte[] extraData; 146 147 private byte[] comment; 148 149 private byte[] data; 150 setVersion(short version)151 public Builder setVersion(short version) { 152 this.version = version; 153 return this; 154 } 155 setFlag(short flag)156 public Builder setFlag(short flag) { 157 this.flag = flag; 158 return this; 159 } 160 setMethod(short method)161 public Builder setMethod(short method) { 162 this.method = method; 163 return this; 164 } 165 setCompressedSize(long compressedSize)166 public Builder setCompressedSize(long compressedSize) { 167 this.compressedSize = compressedSize; 168 return this; 169 } 170 setUncompressedSize(long unCompressedSize)171 public Builder setUncompressedSize(long unCompressedSize) { 172 this.unCompressedSize = unCompressedSize; 173 return this; 174 } 175 setFileName(String fileName)176 public Builder setFileName(String fileName) { 177 this.fileName = fileName; 178 return this; 179 } 180 setExtraData(byte[] extraData)181 public Builder setExtraData(byte[] extraData) { 182 this.extraData = extraData; 183 return this; 184 } 185 setComment(byte[] comment)186 public Builder setComment(byte[] comment) { 187 this.comment = comment; 188 return this; 189 } 190 setData(byte[] data)191 public Builder setData(byte[] data) { 192 this.data = data; 193 return this; 194 } 195 196 /** 197 * build zip entry 198 * 199 * @return Zip Entry 200 * @throws ZipException ZipException 201 */ build()202 public ZipEntry build() throws ZipException { 203 Calendar calendar = Calendar.getInstance(); 204 // java time stamp to dos timestamp, dos time start 1980 205 int time = (calendar.get(Calendar.YEAR) - 1980) << 25 | (calendar.get(Calendar.MONTH) + 1) << 21 206 | calendar.get(Calendar.DAY_OF_MONTH) << 16 | calendar.get(Calendar.HOUR_OF_DAY) << 11 207 | calendar.get(Calendar.MINUTE) << 5 | calendar.get(Calendar.SECOND) >> 1; 208 CentralDirectory cd = addCenterDirectory(time); 209 ZipEntryHeader zipEntryHeader = addZipEntryHeader(time); 210 if (data == null) { 211 throw new ZipException("can not find entry data"); 212 } 213 final CRC32 c = new CRC32(); 214 c.update(data); 215 final int crc32 = new Long(c.getValue()).intValue(); 216 cd.setCrc32(crc32); 217 zipEntryHeader.setCrc32(crc32); 218 219 ZipEntryData entryData = new ZipEntryData(); 220 entryData.setData(data); 221 entryData.setZipEntryHeader(zipEntryHeader); 222 ZipEntry entry = new ZipEntry(); 223 entry.setZipEntryData(entryData); 224 entryData.setType(EntryType.BIT_MAP); 225 entry.setCentralDirectory(cd); 226 return entry; 227 } 228 addCenterDirectory(int time)229 private CentralDirectory addCenterDirectory(int time) { 230 CentralDirectory cd = new CentralDirectory(); 231 cd.setVersion(version); 232 cd.setVersionExtra(version); 233 cd.setFlag(flag); 234 cd.setMethod(method); 235 cd.setLastTime((short) time); 236 cd.setLastDate((short) (time >> 16)); 237 cd.setCompressedSize(compressedSize); 238 cd.setUnCompressedSize(unCompressedSize); 239 cd.setFileName(fileName); 240 cd.setFileNameLength(fileName.length()); 241 if (extraData != null) { 242 cd.setExtraData(extraData); 243 cd.setExtraLength(extraData.length); 244 } else { 245 cd.setExtraLength(0); 246 } 247 if (comment != null) { 248 cd.setComment(comment); 249 cd.setCommentLength(comment.length); 250 } else { 251 cd.setCommentLength(0); 252 } 253 cd.setDiskNumStart(0); 254 cd.setExternalFile(0); 255 256 cd.updateLength(); 257 return cd; 258 } 259 addZipEntryHeader(int time)260 private ZipEntryHeader addZipEntryHeader(int time) { 261 ZipEntryHeader zipEntryHeader = new ZipEntryHeader(); 262 zipEntryHeader.setVersion(version); 263 zipEntryHeader.setFlag(flag); 264 zipEntryHeader.setMethod(method); 265 zipEntryHeader.setLastTime((short) time); 266 zipEntryHeader.setLastDate((short) (time >> 16)); 267 zipEntryHeader.setCompressedSize(compressedSize); 268 zipEntryHeader.setUnCompressedSize(unCompressedSize); 269 zipEntryHeader.setFileName(fileName); 270 zipEntryHeader.setFileNameLength(fileName.length()); 271 if (extraData != null) { 272 zipEntryHeader.setExtraData(extraData); 273 zipEntryHeader.setExtraLength(extraData.length); 274 } else { 275 zipEntryHeader.setExtraLength(0); 276 } 277 zipEntryHeader.updateLength(); 278 return zipEntryHeader; 279 } 280 } 281 }