applied LGPLv3 and source code formatting.
[vamsas.git] / src / org / apache / tools / zip / AsiExtraField.java
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18
19 package org.apache.tools.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 link
26  * handling.
27  * 
28  * <p>
29  * This class uses the ASi extra field in the format:
30  * 
31  * <pre>
32  *         Value         Size            Description
33  *         -----         ----            -----------
34  * (Unix3) 0x756e        Short           tag for this extra block type
35  *         TSize         Short           total data size for this block
36  *         CRC           Long            CRC-32 of the remaining data
37  *         Mode          Short           file permissions
38  *         SizDev        Long            symlink'd size OR major/minor dev num
39  *         UID           Short           user ID
40  *         GID           Short           group ID
41  *         (var.)        variable        symbolic link filename
42  * </pre>
43  * 
44  * taken from appnote.iz (Info-ZIP note, 981119) found at <a
45  * href="ftp://ftp.uu.net/pub/archiving/zip/doc/"
46  * >ftp://ftp.uu.net/pub/archiving/zip/doc/</a>
47  * </p>
48  * 
49  * 
50  * <p>
51  * Short is two bytes and Long is four bytes in big endian byte and word order,
52  * device numbers are currently not supported.
53  * </p>
54  * 
55  */
56 public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable {
57
58   private static final ZipShort HEADER_ID = new ZipShort(0x756E);
59
60   /**
61    * Standard Unix stat(2) file mode.
62    * 
63    * @since 1.1
64    */
65   private int mode = 0;
66
67   /**
68    * User ID.
69    * 
70    * @since 1.1
71    */
72   private int uid = 0;
73
74   /**
75    * Group ID.
76    * 
77    * @since 1.1
78    */
79   private int gid = 0;
80
81   /**
82    * File this entry points to, if it is a symbolic link.
83    * 
84    * <p>
85    * empty string - if entry is not a symbolic link.
86    * </p>
87    * 
88    * @since 1.1
89    */
90   private String link = "";
91
92   /**
93    * Is this an entry for a directory?
94    * 
95    * @since 1.1
96    */
97   private boolean dirFlag = false;
98
99   /**
100    * Instance used to calculate checksums.
101    * 
102    * @since 1.1
103    */
104   private CRC32 crc = new CRC32();
105
106   /** Constructor for AsiExtraField. */
107   public AsiExtraField() {
108   }
109
110   /**
111    * The Header-ID.
112    * 
113    * @return the value for the header id for this extrafield
114    * @since 1.1
115    */
116   public ZipShort getHeaderId() {
117     return HEADER_ID;
118   }
119
120   /**
121    * Length of the extra field in the local file data - without Header-ID or
122    * length specifier.
123    * 
124    * @return a <code>ZipShort</code> for the length of the data of this extra
125    *         field
126    * @since 1.1
127    */
128   public ZipShort getLocalFileDataLength() {
129     return new ZipShort(4 // CRC
130         + 2 // Mode
131         + 4 // SizDev
132         + 2 // UID
133         + 2 // GID
134         + getLinkedFile().getBytes().length);
135   }
136
137   /**
138    * Delegate to local file data.
139    * 
140    * @return the centralDirectory length
141    * @since 1.1
142    */
143   public ZipShort getCentralDirectoryLength() {
144     return getLocalFileDataLength();
145   }
146
147   /**
148    * The actual data to put into local file data - without Header-ID or length
149    * specifier.
150    * 
151    * @return get the data
152    * @since 1.1
153    */
154   public byte[] getLocalFileDataData() {
155     // CRC will be added later
156     byte[] data = new byte[getLocalFileDataLength().getValue() - 4];
157     System.arraycopy(ZipShort.getBytes(getMode()), 0, data, 0, 2);
158
159     byte[] linkArray = getLinkedFile().getBytes();
160     System.arraycopy(ZipLong.getBytes(linkArray.length), 0, data, 2, 4);
161
162     System.arraycopy(ZipShort.getBytes(getUserId()), 0, data, 6, 2);
163     System.arraycopy(ZipShort.getBytes(getGroupId()), 0, data, 8, 2);
164
165     System.arraycopy(linkArray, 0, data, 10, linkArray.length);
166
167     crc.reset();
168     crc.update(data);
169     long checksum = crc.getValue();
170
171     byte[] result = new byte[data.length + 4];
172     System.arraycopy(ZipLong.getBytes(checksum), 0, result, 0, 4);
173     System.arraycopy(data, 0, result, 4, data.length);
174     return result;
175   }
176
177   /**
178    * Delegate to local file data.
179    * 
180    * @return the local file data
181    * @since 1.1
182    */
183   public byte[] getCentralDirectoryData() {
184     return getLocalFileDataData();
185   }
186
187   /**
188    * Set the user id.
189    * 
190    * @param uid
191    *          the user id
192    * @since 1.1
193    */
194   public void setUserId(int uid) {
195     this.uid = uid;
196   }
197
198   /**
199    * Get the user id.
200    * 
201    * @return the user id
202    * @since 1.1
203    */
204   public int getUserId() {
205     return uid;
206   }
207
208   /**
209    * Set the group id.
210    * 
211    * @param gid
212    *          the group id
213    * @since 1.1
214    */
215   public void setGroupId(int gid) {
216     this.gid = gid;
217   }
218
219   /**
220    * Get the group id.
221    * 
222    * @return the group id
223    * @since 1.1
224    */
225   public int getGroupId() {
226     return gid;
227   }
228
229   /**
230    * Indicate that this entry is a symbolic link to the given filename.
231    * 
232    * @param name
233    *          Name of the file this entry links to, empty String if it is not a
234    *          symbolic link.
235    * 
236    * @since 1.1
237    */
238   public void setLinkedFile(String name) {
239     link = name;
240     mode = getMode(mode);
241   }
242
243   /**
244    * Name of linked file
245    * 
246    * @return name of the file this entry links to if it is a symbolic link, the
247    *         empty string otherwise.
248    * 
249    * @since 1.1
250    */
251   public String getLinkedFile() {
252     return link;
253   }
254
255   /**
256    * Is this entry a symbolic link?
257    * 
258    * @return true if this is a symbolic link
259    * @since 1.1
260    */
261   public boolean isLink() {
262     return getLinkedFile().length() != 0;
263   }
264
265   /**
266    * File mode of this file.
267    * 
268    * @param mode
269    *          the file mode
270    * @since 1.1
271    */
272   public void setMode(int mode) {
273     this.mode = getMode(mode);
274   }
275
276   /**
277    * File mode of this file.
278    * 
279    * @return the file mode
280    * @since 1.1
281    */
282   public int getMode() {
283     return mode;
284   }
285
286   /**
287    * Indicate whether this entry is a directory.
288    * 
289    * @param dirFlag
290    *          if true, this entry is a directory
291    * @since 1.1
292    */
293   public void setDirectory(boolean dirFlag) {
294     this.dirFlag = dirFlag;
295     mode = getMode(mode);
296   }
297
298   /**
299    * Is this entry a directory?
300    * 
301    * @return true if this entry is a directory
302    * @since 1.1
303    */
304   public boolean isDirectory() {
305     return dirFlag && !isLink();
306   }
307
308   /**
309    * Populate data from this array as if it was in local file data.
310    * 
311    * @param data
312    *          an array of bytes
313    * @param offset
314    *          the start offset
315    * @param length
316    *          the number of bytes in the array from offset
317    * @since 1.1
318    * @throws ZipException
319    *           on error
320    */
321   public void parseFromLocalFileData(byte[] data, int offset, int length)
322       throws ZipException {
323
324     long givenChecksum = ZipLong.getValue(data, offset);
325     byte[] tmp = new byte[length - 4];
326     System.arraycopy(data, offset + 4, tmp, 0, length - 4);
327     crc.reset();
328     crc.update(tmp);
329     long realChecksum = crc.getValue();
330     if (givenChecksum != realChecksum) {
331       throw new ZipException("bad CRC checksum "
332           + Long.toHexString(givenChecksum) + " instead of "
333           + Long.toHexString(realChecksum));
334     }
335
336     int newMode = ZipShort.getValue(tmp, 0);
337     byte[] linkArray = new byte[(int) ZipLong.getValue(tmp, 2)];
338     uid = ZipShort.getValue(tmp, 6);
339     gid = ZipShort.getValue(tmp, 8);
340
341     if (linkArray.length == 0) {
342       link = "";
343     } else {
344       System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
345       link = new String(linkArray);
346     }
347     setDirectory((newMode & DIR_FLAG) != 0);
348     setMode(newMode);
349   }
350
351   /**
352    * Get the file mode for given permissions with the correct file type.
353    * 
354    * @param mode
355    *          the mode
356    * @return the type with the mode
357    * @since 1.1
358    */
359   protected int getMode(int mode) {
360     int type = FILE_FLAG;
361     if (isLink()) {
362       type = LINK_FLAG;
363     } else if (isDirectory()) {
364       type = DIR_FLAG;
365     }
366     return type | (mode & PERM_MASK);
367   }
368
369 }