1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.util.zip;
18 
19 import com.android.tradefed.util.ByteArrayUtil;
20 
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.util.Arrays;
25 
26 /**
27  * LocalFileHeader is a class containing the information of a file/folder inside a zip file. The
28  * block of data is at the beginning part of each file entry.
29  *
30  * <p>Overall zipfile format: [Local file header + Compressed data [+ Extended local header]?]*
31  * [Central directory]* [End of central directory record]
32  *
33  * <p>Refer to following link for more details: https://en.wikipedia.org/wiki/Zip_(file_format)
34  */
35 public final class LocalFileHeader {
36 
37     public static final int LOCAL_FILE_HEADER_SIZE = 30;
38     private static final byte[] LOCAL_FILE_HEADER_SIGNATURE = {0x50, 0x4b, 0x03, 0x04};
39 
40     private int mCompressionMethod;
41     private long mCrc;
42     private long mCompressedSize;
43     private long mUncompressedSize;
44     private int mFileNameLength;
45     private int mExtraFieldLength;
46 
getCompressionMethod()47     public int getCompressionMethod() {
48         return mCompressionMethod;
49     }
50 
getCrc()51     public long getCrc() {
52         return mCrc;
53     }
54 
getCompressedSize()55     public long getCompressedSize() {
56         return mCompressedSize;
57     }
58 
getUncompressedSize()59     public long getUncompressedSize() {
60         return mUncompressedSize;
61     }
62 
getFileNameLength()63     public int getFileNameLength() {
64         return mFileNameLength;
65     }
66 
getExtraFieldLength()67     public int getExtraFieldLength() {
68         return mExtraFieldLength;
69     }
70 
getHeaderSize()71     public int getHeaderSize() {
72         return LOCAL_FILE_HEADER_SIZE + mFileNameLength + mExtraFieldLength;
73     }
74 
LocalFileHeader(File partialZipFile)75     public LocalFileHeader(File partialZipFile) throws IOException {
76         this(partialZipFile, 0);
77     }
78 
79     /**
80      * Constructor to collect local file header information of a file entry in a zip file.
81      *
82      * @param partialZipFile a {@link File} contains the local file header information.
83      * @param startOffset the start offset of the block of data for a local file header.
84      * @throws IOException
85      */
LocalFileHeader(File partialZipFile, int startOffset)86     public LocalFileHeader(File partialZipFile, int startOffset) throws IOException {
87         // Local file header:
88         //    Offset   Length   Contents
89         //      0      4 bytes  Local file header signature (0x04034b50)
90         //      4      2 bytes  Version needed to extract
91         //      6      2 bytes  General purpose bit flag
92         //      8      2 bytes  Compression method
93         //     10      2 bytes  Last mod file time
94         //     12      2 bytes  Last mod file date
95         //     14      4 bytes  CRC-32
96         //     18      4 bytes  Compressed size (n)
97         //     22      4 bytes  Uncompressed size
98         //     26      2 bytes  Filename length (f)
99         //     28      2 bytes  Extra field length (e)
100         //            (f)bytes  Filename
101         //            (e)bytes  Extra field
102         //            (n)bytes  Compressed data
103         byte[] data;
104         try (FileInputStream stream = new FileInputStream(partialZipFile)) {
105             stream.skip(startOffset);
106             data = new byte[LOCAL_FILE_HEADER_SIZE];
107             stream.read(data);
108         }
109 
110         // Check signature
111         if (!Arrays.equals(LOCAL_FILE_HEADER_SIGNATURE, Arrays.copyOfRange(data, 0, 4))) {
112             throw new IOException("Invalid local file header for zip file is found.");
113         }
114         mCompressionMethod = ByteArrayUtil.getInt(data, 8, 2);
115         mCrc = ByteArrayUtil.getLong(data, 14, 4);
116         mCompressedSize = ByteArrayUtil.getLong(data, 18, 2);
117         mUncompressedSize = ByteArrayUtil.getLong(data, 22, 2);
118         mFileNameLength = ByteArrayUtil.getInt(data, 26, 2);
119         mExtraFieldLength = ByteArrayUtil.getInt(data, 28, 2);
120     }
121 }
122