• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import java.util.zip.CRC32;
22 import java.util.zip.ZipException;
23 
24 /**
25  * Adds Unix file permission and UID/GID fields as well as symbolic
26  * link handling.
27  *
28  * <p>This class uses the ASi extra field in the format:</p>
29  * <pre>
30  *         Value         Size            Description
31  *         -----         ----            -----------
32  * (Unix3) 0x756e        Short           tag for this extra block type
33  *         TSize         Short           total data size for this block
34  *         CRC           Long            CRC-32 of the remaining data
35  *         Mode          Short           file permissions
36  *         SizDev        Long            symlink'd size OR major/minor dev num
37  *         UID           Short           user ID
38  *         GID           Short           group ID
39  *         (var.)        variable        symbolic link filename
40  * </pre>
41  * <p>taken from appnote.iz (Info-ZIP note, 981119) found at <a
42  * href="ftp://ftp.uu.net/pub/archiving/zip/doc/">ftp://ftp.uu.net/pub/archiving/zip/doc/</a></p>
43  *
44  * <p>Short is two bytes and Long is four bytes in big endian byte and
45  * word order, device numbers are currently not supported.</p>
46  * @NotThreadSafe
47  *
48  * <p>Since the documentation this class is based upon doesn't mention
49  * the character encoding of the file name at all, it is assumed that
50  * it uses the current platform's default encoding.</p>
51  */
52 public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable {
53 
54     private static final ZipShort HEADER_ID = new ZipShort(0x756E);
55     private static final int      WORD = 4;
56     /**
57      * Standard Unix stat(2) file mode.
58      */
59     private int mode = 0;
60     /**
61      * User ID.
62      */
63     private int uid = 0;
64     /**
65      * Group ID.
66      */
67     private int gid = 0;
68     /**
69      * File this entry points to, if it is a symbolic link.
70      *
71      * <p>empty string - if entry is not a symbolic link.</p>
72      */
73     private String link = "";
74     /**
75      * Is this an entry for a directory?
76      */
77     private boolean dirFlag = false;
78 
79     /**
80      * Instance used to calculate checksums.
81      */
82     private CRC32 crc = new CRC32();
83 
84     /** Constructor for AsiExtraField. */
AsiExtraField()85     public AsiExtraField() {
86     }
87 
88     /**
89      * The Header-ID.
90      * @return the value for the header id for this extrafield
91      */
92     @Override
getHeaderId()93     public ZipShort getHeaderId() {
94         return HEADER_ID;
95     }
96 
97     /**
98      * Length of the extra field in the local file data - without
99      * Header-ID or length specifier.
100      * @return a <code>ZipShort</code> for the length of the data of this extra field
101      */
102     @Override
getLocalFileDataLength()103     public ZipShort getLocalFileDataLength() {
104         return new ZipShort(WORD         // CRC
105                           + 2         // Mode
106                           + WORD         // SizDev
107                           + 2         // UID
108                           + 2         // GID
109                           + getLinkedFile().getBytes().length);
110                           // Uses default charset - see class Javadoc
111     }
112 
113     /**
114      * Delegate to local file data.
115      * @return the centralDirectory length
116      */
117     @Override
getCentralDirectoryLength()118     public ZipShort getCentralDirectoryLength() {
119         return getLocalFileDataLength();
120     }
121 
122     /**
123      * The actual data to put into local file data - without Header-ID
124      * or length specifier.
125      * @return get the data
126      */
127     @Override
getLocalFileDataData()128     public byte[] getLocalFileDataData() {
129         // CRC will be added later
130         final byte[] data = new byte[getLocalFileDataLength().getValue() - WORD];
131         System.arraycopy(ZipShort.getBytes(getMode()), 0, data, 0, 2);
132 
133         final byte[] linkArray = getLinkedFile().getBytes(); // Uses default charset - see class Javadoc
134         // CheckStyle:MagicNumber OFF
135         System.arraycopy(ZipLong.getBytes(linkArray.length),
136                          0, data, 2, WORD);
137 
138         System.arraycopy(ZipShort.getBytes(getUserId()),
139                          0, data, 6, 2);
140         System.arraycopy(ZipShort.getBytes(getGroupId()),
141                          0, data, 8, 2);
142 
143         System.arraycopy(linkArray, 0, data, 10, linkArray.length);
144         // CheckStyle:MagicNumber ON
145 
146         crc.reset();
147         crc.update(data);
148         final long checksum = crc.getValue();
149 
150         final byte[] result = new byte[data.length + WORD];
151         System.arraycopy(ZipLong.getBytes(checksum), 0, result, 0, WORD);
152         System.arraycopy(data, 0, result, WORD, data.length);
153         return result;
154     }
155 
156     /**
157      * Delegate to local file data.
158      * @return the local file data
159      */
160     @Override
getCentralDirectoryData()161     public byte[] getCentralDirectoryData() {
162         return getLocalFileDataData();
163     }
164 
165     /**
166      * Set the user id.
167      * @param uid the user id
168      */
setUserId(final int uid)169     public void setUserId(final int uid) {
170         this.uid = uid;
171     }
172 
173     /**
174      * Get the user id.
175      * @return the user id
176      */
getUserId()177     public int getUserId() {
178         return uid;
179     }
180 
181     /**
182      * Set the group id.
183      * @param gid the group id
184      */
setGroupId(final int gid)185     public void setGroupId(final int gid) {
186         this.gid = gid;
187     }
188 
189     /**
190      * Get the group id.
191      * @return the group id
192      */
getGroupId()193     public int getGroupId() {
194         return gid;
195     }
196 
197     /**
198      * Indicate that this entry is a symbolic link to the given filename.
199      *
200      * @param name Name of the file this entry links to, empty String
201      *             if it is not a symbolic link.
202      */
setLinkedFile(final String name)203     public void setLinkedFile(final String name) {
204         link = name;
205         mode = getMode(mode);
206     }
207 
208     /**
209      * Name of linked file
210      *
211      * @return name of the file this entry links to if it is a
212      *         symbolic link, the empty string otherwise.
213      */
getLinkedFile()214     public String getLinkedFile() {
215         return link;
216     }
217 
218     /**
219      * Is this entry a symbolic link?
220      * @return true if this is a symbolic link
221      */
isLink()222     public boolean isLink() {
223         return getLinkedFile().length() != 0;
224     }
225 
226     /**
227      * File mode of this file.
228      * @param mode the file mode
229      */
setMode(final int mode)230     public void setMode(final int mode) {
231         this.mode = getMode(mode);
232     }
233 
234     /**
235      * File mode of this file.
236      * @return the file mode
237      */
getMode()238     public int getMode() {
239         return mode;
240     }
241 
242     /**
243      * Indicate whether this entry is a directory.
244      * @param dirFlag if true, this entry is a directory
245      */
setDirectory(final boolean dirFlag)246     public void setDirectory(final boolean dirFlag) {
247         this.dirFlag = dirFlag;
248         mode = getMode(mode);
249     }
250 
251     /**
252      * Is this entry a directory?
253      * @return true if this entry is a directory
254      */
isDirectory()255     public boolean isDirectory() {
256         return dirFlag && !isLink();
257     }
258 
259     /**
260      * Populate data from this array as if it was in local file data.
261      * @param data an array of bytes
262      * @param offset the start offset
263      * @param length the number of bytes in the array from offset
264      * @throws ZipException on error
265      */
266     @Override
parseFromLocalFileData(final byte[] data, final int offset, final int length)267     public void parseFromLocalFileData(final byte[] data, final int offset, final int length)
268         throws ZipException {
269 
270         final long givenChecksum = ZipLong.getValue(data, offset);
271         final byte[] tmp = new byte[length - WORD];
272         System.arraycopy(data, offset + WORD, tmp, 0, length - WORD);
273         crc.reset();
274         crc.update(tmp);
275         final long realChecksum = crc.getValue();
276         if (givenChecksum != realChecksum) {
277             throw new ZipException("bad CRC checksum "
278                                    + Long.toHexString(givenChecksum)
279                                    + " instead of "
280                                    + Long.toHexString(realChecksum));
281         }
282 
283         final int newMode = ZipShort.getValue(tmp, 0);
284         // CheckStyle:MagicNumber OFF
285         final byte[] linkArray = new byte[(int) ZipLong.getValue(tmp, 2)];
286         uid = ZipShort.getValue(tmp, 6);
287         gid = ZipShort.getValue(tmp, 8);
288 
289         if (linkArray.length == 0) {
290             link = "";
291         } else {
292             System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
293             link = new String(linkArray); // Uses default charset - see class Javadoc
294         }
295         // CheckStyle:MagicNumber ON
296         setDirectory((newMode & DIR_FLAG) != 0);
297         setMode(newMode);
298     }
299 
300     /**
301      * Doesn't do anything special since this class always uses the
302      * same data in central directory and local file data.
303      */
304     @Override
parseFromCentralDirectoryData(final byte[] buffer, final int offset, final int length)305     public void parseFromCentralDirectoryData(final byte[] buffer, final int offset,
306                                               final int length)
307         throws ZipException {
308         parseFromLocalFileData(buffer, offset, length);
309     }
310 
311     /**
312      * Get the file mode for given permissions with the correct file type.
313      * @param mode the mode
314      * @return the type with the mode
315      */
getMode(final int mode)316     protected int getMode(final int mode) {
317         int type = FILE_FLAG;
318         if (isLink()) {
319             type = LINK_FLAG;
320         } else if (isDirectory()) {
321             type = DIR_FLAG;
322         }
323         return type | (mode & PERM_MASK);
324     }
325 
326     @Override
clone()327     public Object clone() {
328         try {
329             final AsiExtraField cloned = (AsiExtraField) super.clone();
330             cloned.crc = new CRC32();
331             return cloned;
332         } catch (final CloneNotSupportedException cnfe) {
333             // impossible
334             throw new RuntimeException(cnfe); //NOSONAR
335         }
336     }
337 }
338