• 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.io.Serializable;
22 import java.math.BigInteger;
23 import java.util.zip.ZipException;
24 
25 import static org.apache.commons.compress.archivers.zip.ZipUtil.reverse;
26 import static org.apache.commons.compress.archivers.zip.ZipUtil.signedByteToUnsignedInt;
27 import static org.apache.commons.compress.archivers.zip.ZipUtil.unsignedIntToSignedByte;
28 
29 /**
30  * An extra field that stores UNIX UID/GID data (owner & group ownership) for a given
31  * zip entry.  We're using the field definition given in Info-Zip's source archive:
32  * zip-3.0.tar.gz/proginfo/extrafld.txt
33  *
34  * <pre>
35  * Local-header version:
36  *
37  * Value         Size        Description
38  * -----         ----        -----------
39  * 0x7875        Short       tag for this extra block type ("ux")
40  * TSize         Short       total data size for this block
41  * Version       1 byte      version of this extra field, currently 1
42  * UIDSize       1 byte      Size of UID field
43  * UID           Variable    UID for this entry (little endian)
44  * GIDSize       1 byte      Size of GID field
45  * GID           Variable    GID for this entry (little endian)
46  *
47  * Central-header version:
48  *
49  * Value         Size        Description
50  * -----         ----        -----------
51  * 0x7855        Short       tag for this extra block type ("Ux")
52  * TSize         Short       total data size for this block (0)
53  * </pre>
54  * @since 1.5
55  */
56 public class X7875_NewUnix implements ZipExtraField, Cloneable, Serializable {
57     private static final ZipShort HEADER_ID = new ZipShort(0x7875);
58     private static final ZipShort ZERO = new ZipShort(0);
59     private static final BigInteger ONE_THOUSAND = BigInteger.valueOf(1000);
60     private static final long serialVersionUID = 1L;
61 
62     private int version = 1; // always '1' according to current info-zip spec.
63 
64     // BigInteger helps us with little-endian / big-endian conversions.
65     // (thanks to BigInteger.toByteArray() and a reverse() method we created).
66     // Also, the spec theoretically allows UID/GID up to 255 bytes long!
67     //
68     // NOTE:  equals() and hashCode() currently assume these can never be null.
69     private BigInteger uid;
70     private BigInteger gid;
71 
72     /**
73      * Constructor for X7875_NewUnix.
74      */
X7875_NewUnix()75     public X7875_NewUnix() {
76         reset();
77     }
78 
79     /**
80      * The Header-ID.
81      *
82      * @return the value for the header id for this extrafield
83      */
84     @Override
getHeaderId()85     public ZipShort getHeaderId() {
86         return HEADER_ID;
87     }
88 
89     /**
90      * Gets the UID as a long.  UID is typically a 32 bit unsigned
91      * value on most UNIX systems, so we return a long to avoid
92      * integer overflow into the negatives in case values above
93      * and including 2^31 are being used.
94      *
95      * @return the UID value.
96      */
getUID()97     public long getUID() { return ZipUtil.bigToLong(uid); }
98 
99     /**
100      * Gets the GID as a long.  GID is typically a 32 bit unsigned
101      * value on most UNIX systems, so we return a long to avoid
102      * integer overflow into the negatives in case values above
103      * and including 2^31 are being used.
104      *
105      * @return the GID value.
106      */
getGID()107     public long getGID() { return ZipUtil.bigToLong(gid); }
108 
109     /**
110      * Sets the UID.
111      *
112      * @param l UID value to set on this extra field.
113      */
setUID(final long l)114     public void setUID(final long l) {
115         this.uid = ZipUtil.longToBig(l);
116     }
117 
118     /**
119      * Sets the GID.
120      *
121      * @param l GID value to set on this extra field.
122      */
setGID(final long l)123     public void setGID(final long l) {
124         this.gid = ZipUtil.longToBig(l);
125     }
126 
127     /**
128      * Length of the extra field in the local file data - without
129      * Header-ID or length specifier.
130      *
131      * @return a <code>ZipShort</code> for the length of the data of this extra field
132      */
133     @Override
getLocalFileDataLength()134     public ZipShort getLocalFileDataLength() {
135         byte[] b = trimLeadingZeroesForceMinLength(uid.toByteArray());
136         final int uidSize = b == null ? 0 : b.length;
137         b = trimLeadingZeroesForceMinLength(gid.toByteArray());
138         final int gidSize = b == null ? 0 : b.length;
139 
140         // The 3 comes from:  version=1 + uidsize=1 + gidsize=1
141         return new ZipShort(3 + uidSize + gidSize);
142     }
143 
144     /**
145      * Length of the extra field in the central directory data - without
146      * Header-ID or length specifier.
147      *
148      * @return a <code>ZipShort</code> for the length of the data of this extra field
149      */
150     @Override
getCentralDirectoryLength()151     public ZipShort getCentralDirectoryLength() {
152         return ZERO;
153     }
154 
155     /**
156      * The actual data to put into local file data - without Header-ID
157      * or length specifier.
158      *
159      * @return get the data
160      */
161     @Override
getLocalFileDataData()162     public byte[] getLocalFileDataData() {
163         byte[] uidBytes = uid.toByteArray();
164         byte[] gidBytes = gid.toByteArray();
165 
166         // BigInteger might prepend a leading-zero to force a positive representation
167         // (e.g., so that the sign-bit is set to zero).  We need to remove that
168         // before sending the number over the wire.
169         uidBytes = trimLeadingZeroesForceMinLength(uidBytes);
170         int uidBytesLen = uidBytes != null ? uidBytes.length : 0;
171         gidBytes = trimLeadingZeroesForceMinLength(gidBytes);
172         int gidBytesLen = gidBytes != null ? gidBytes.length : 0;
173 
174         // Couldn't bring myself to just call getLocalFileDataLength() when we've
175         // already got the arrays right here.  Yeah, yeah, I know, premature
176         // optimization is the root of all...
177         //
178         // The 3 comes from:  version=1 + uidsize=1 + gidsize=1
179         final byte[] data = new byte[3 + uidBytesLen + gidBytesLen];
180 
181         // reverse() switches byte array from big-endian to little-endian.
182         if (uidBytes != null) {
183             reverse(uidBytes);
184         }
185         if (gidBytes != null) {
186             reverse(gidBytes);
187         }
188 
189         int pos = 0;
190         data[pos++] = unsignedIntToSignedByte(version);
191         data[pos++] = unsignedIntToSignedByte(uidBytesLen);
192         if (uidBytes != null) {
193             System.arraycopy(uidBytes, 0, data, pos, uidBytesLen);
194         }
195         pos += uidBytesLen;
196         data[pos++] = unsignedIntToSignedByte(gidBytesLen);
197         if (gidBytes != null) {
198             System.arraycopy(gidBytes, 0, data, pos, gidBytesLen);
199         }
200         return data;
201     }
202 
203     /**
204      * The actual data to put into central directory data - without Header-ID
205      * or length specifier.
206      *
207      * @return get the data
208      */
209     @Override
getCentralDirectoryData()210     public byte[] getCentralDirectoryData() {
211         return new byte[0];
212     }
213 
214     /**
215      * Populate data from this array as if it was in local file data.
216      *
217      * @param data   an array of bytes
218      * @param offset the start offset
219      * @param length the number of bytes in the array from offset
220      * @throws java.util.zip.ZipException on error
221      */
222     @Override
parseFromLocalFileData( final byte[] data, int offset, final int length )223     public void parseFromLocalFileData(
224             final byte[] data, int offset, final int length
225     ) throws ZipException {
226         reset();
227         this.version = signedByteToUnsignedInt(data[offset++]);
228         final int uidSize = signedByteToUnsignedInt(data[offset++]);
229         final byte[] uidBytes = new byte[uidSize];
230         System.arraycopy(data, offset, uidBytes, 0, uidSize);
231         offset += uidSize;
232         this.uid = new BigInteger(1, reverse(uidBytes)); // sign-bit forced positive
233 
234         final int gidSize = signedByteToUnsignedInt(data[offset++]);
235         final byte[] gidBytes = new byte[gidSize];
236         System.arraycopy(data, offset, gidBytes, 0, gidSize);
237         this.gid = new BigInteger(1, reverse(gidBytes)); // sign-bit forced positive
238     }
239 
240     /**
241      * Doesn't do anything since this class doesn't store anything
242      * inside the central directory.
243      */
244     @Override
parseFromCentralDirectoryData( final byte[] buffer, final int offset, final int length )245     public void parseFromCentralDirectoryData(
246             final byte[] buffer, final int offset, final int length
247     ) throws ZipException {
248     }
249 
250     /**
251      * Reset state back to newly constructed state.  Helps us make sure
252      * parse() calls always generate clean results.
253      */
reset()254     private void reset() {
255         // Typical UID/GID of the first non-root user created on a unix system.
256         uid = ONE_THOUSAND;
257         gid = ONE_THOUSAND;
258     }
259 
260     /**
261      * Returns a String representation of this class useful for
262      * debugging purposes.
263      *
264      * @return A String representation of this class useful for
265      *         debugging purposes.
266      */
267     @Override
toString()268     public String toString() {
269         return "0x7875 Zip Extra Field: UID=" + uid + " GID=" + gid;
270     }
271 
272     @Override
clone()273     public Object clone() throws CloneNotSupportedException {
274         return super.clone();
275     }
276 
277     @Override
equals(final Object o)278     public boolean equals(final Object o) {
279         if (o instanceof X7875_NewUnix) {
280             final X7875_NewUnix xf = (X7875_NewUnix) o;
281             // We assume uid and gid can never be null.
282             return version == xf.version && uid.equals(xf.uid) && gid.equals(xf.gid);
283         }
284         return false;
285     }
286 
287     @Override
hashCode()288     public int hashCode() {
289         int hc = -1234567 * version;
290         // Since most UID's and GID's are below 65,536, this is (hopefully!)
291         // a nice way to make sure typical UID and GID values impact the hash
292         // as much as possible.
293         hc ^= Integer.rotateLeft(uid.hashCode(), 16);
294         hc ^= gid.hashCode();
295         return hc;
296     }
297 
298     /**
299      * Not really for external usage, but marked "package" visibility
300      * to help us JUnit it.   Trims a byte array of leading zeroes while
301      * also enforcing a minimum length, and thus it really trims AND pads
302      * at the same time.
303      *
304      * @param array byte[] array to trim & pad.
305      * @return trimmed & padded byte[] array.
306      */
trimLeadingZeroesForceMinLength(final byte[] array)307     static byte[] trimLeadingZeroesForceMinLength(final byte[] array) {
308         if (array == null) {
309             return array;
310         }
311 
312         int pos = 0;
313         for (final byte b : array) {
314             if (b == 0) {
315                 pos++;
316             } else {
317                 break;
318             }
319         }
320 
321         /*
322 
323         I agonized over my choice of MIN_LENGTH=1.  Here's the situation:
324         InfoZip (the tool I am using to test interop) always sets these
325         to length=4.  And so a UID of 0 (typically root) for example is
326         encoded as {4,0,0,0,0} (len=4, 32 bits of zero), when it could just
327         as easily be encoded as {1,0} (len=1, 8 bits of zero) according to
328         the spec.
329 
330         In the end I decided on MIN_LENGTH=1 for four reasons:
331 
332         1.)  We are adhering to the spec as far as I can tell, and so
333              a consumer that cannot parse this is broken.
334 
335         2.)  Fundamentally, zip files are about shrinking things, so
336              let's save a few bytes per entry while we can.
337 
338         3.)  Of all the people creating zip files using commons-
339              compress, how many care about UNIX UID/GID attributes
340              of the files they store?   (e.g., I am probably thinking
341              way too hard about this and no one cares!)
342 
343         4.)  InfoZip's tool, even though it carefully stores every UID/GID
344              for every file zipped on a unix machine (by default) currently
345              appears unable to ever restore UID/GID.
346              unzip -X has no effect on my machine, even when run as root!!!!
347 
348         And thus it is decided:  MIN_LENGTH=1.
349 
350         If anyone runs into interop problems from this, feel free to set
351         it to MIN_LENGTH=4 at some future time, and then we will behave
352         exactly like InfoZip (requires changes to unit tests, though).
353 
354         And I am sorry that the time you spent reading this comment is now
355         gone and you can never have it back.
356 
357         */
358         final int MIN_LENGTH = 1;
359 
360         final byte[] trimmedArray = new byte[Math.max(MIN_LENGTH, array.length - pos)];
361         final int startPos = trimmedArray.length - (array.length - pos);
362         System.arraycopy(array, pos, trimmedArray, startPos, trimmedArray.length - startPos);
363         return trimmedArray;
364     }
365 }
366