001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.archivers.zip;
020
021import static org.apache.commons.compress.archivers.zip.ZipUtil.reverse;
022import static org.apache.commons.compress.archivers.zip.ZipUtil.signedByteToUnsignedInt;
023import static org.apache.commons.compress.archivers.zip.ZipUtil.unsignedIntToSignedByte;
024
025import java.io.Serializable;
026import java.math.BigInteger;
027import java.util.Arrays;
028import java.util.zip.ZipException;
029
030import org.apache.commons.compress.utils.ByteUtils;
031
032/**
033 * An extra field that stores UNIX UID/GID data (owner & group ownership) for a given
034 * ZIP entry.  We're using the field definition given in Info-Zip's source archive:
035 * zip-3.0.tar.gz/proginfo/extrafld.txt
036 *
037 * <pre>
038 * Local-header version:
039 *
040 * Value         Size        Description
041 * -----         ----        -----------
042 * 0x7875        Short       tag for this extra block type ("ux")
043 * TSize         Short       total data size for this block
044 * Version       1 byte      version of this extra field, currently 1
045 * UIDSize       1 byte      Size of UID field
046 * UID           Variable    UID for this entry (little endian)
047 * GIDSize       1 byte      Size of GID field
048 * GID           Variable    GID for this entry (little endian)
049 *
050 * Central-header version:
051 *
052 * Value         Size        Description
053 * -----         ----        -----------
054 * 0x7855        Short       tag for this extra block type ("Ux")
055 * TSize         Short       total data size for this block (0)
056 * </pre>
057 * @since 1.5
058 */
059public class X7875_NewUnix implements ZipExtraField, Cloneable, Serializable {
060    private static final ZipShort HEADER_ID = new ZipShort(0x7875);
061    private static final ZipShort ZERO = new ZipShort(0);
062    private static final BigInteger ONE_THOUSAND = BigInteger.valueOf(1000);
063    private static final long serialVersionUID = 1L;
064
065    /**
066     * Not really for external usage, but marked "package" visibility
067     * to help us JUnit it.   Trims a byte array of leading zeroes while
068     * also enforcing a minimum length, and thus it really trims AND pads
069     * at the same time.
070     *
071     * @param array byte[] array to trim & pad.
072     * @return trimmed & padded byte[] array.
073     */
074    static byte[] trimLeadingZeroesForceMinLength(final byte[] array) {
075        if (array == null) {
076            return null;
077        }
078
079        int pos = 0;
080        for (final byte b : array) {
081            if (b != 0) {
082                break;
083            }
084            pos++;
085        }
086
087        /*
088
089        I agonized over my choice of MIN_LENGTH=1.  Here's the situation:
090        InfoZip (the tool I am using to test interop) always sets these
091        to length=4.  And so a UID of 0 (typically root) for example is
092        encoded as {4,0,0,0,0} (len=4, 32 bits of zero), when it could just
093        as easily be encoded as {1,0} (len=1, 8 bits of zero) according to
094        the spec.
095
096        In the end I decided on MIN_LENGTH=1 for four reasons:
097
098        1.)  We are adhering to the spec as far as I can tell, and so
099             a consumer that cannot parse this is broken.
100
101        2.)  Fundamentally, ZIP files are about shrinking things, so
102             let's save a few bytes per entry while we can.
103
104        3.)  Of all the people creating ZIP files using commons-
105             compress, how many care about UNIX UID/GID attributes
106             of the files they store?   (e.g., I am probably thinking
107             way too hard about this and no one cares!)
108
109        4.)  InfoZip's tool, even though it carefully stores every UID/GID
110             for every file zipped on a Unix machine (by default) currently
111             appears unable to ever restore UID/GID.
112             unzip -X has no effect on my machine, even when run as root!!!!
113
114        And thus it is decided:  MIN_LENGTH=1.
115
116        If anyone runs into interop problems from this, feel free to set
117        it to MIN_LENGTH=4 at some future time, and then we will behave
118        exactly like InfoZip (requires changes to unit tests, though).
119
120        And I am sorry that the time you spent reading this comment is now
121        gone, and you can never have it back.
122
123        */
124        final int MIN_LENGTH = 1;
125
126        final byte[] trimmedArray = new byte[Math.max(MIN_LENGTH, array.length - pos)];
127        final int startPos = trimmedArray.length - (array.length - pos);
128        System.arraycopy(array, pos, trimmedArray, startPos, trimmedArray.length - startPos);
129        return trimmedArray;
130    }
131
132    private int version = 1; // always '1' according to current info-zip spec.
133    // BigInteger helps us with little-endian / big-endian conversions.
134    // (thanks to BigInteger.toByteArray() and a reverse() method we created).
135    // Also, the spec theoretically allows UID/GID up to 255 bytes long!
136    //
137    // NOTE:  equals() and hashCode() currently assume these can never be null.
138    private BigInteger uid;
139
140    private BigInteger gid;
141
142    /**
143     * Constructor for X7875_NewUnix.
144     */
145    public X7875_NewUnix() {
146        reset();
147    }
148
149    @Override
150    public Object clone() throws CloneNotSupportedException {
151        return super.clone();
152    }
153
154    @Override
155    public boolean equals(final Object o) {
156        if (o instanceof X7875_NewUnix) {
157            final X7875_NewUnix xf = (X7875_NewUnix) o;
158            // We assume uid and gid can never be null.
159            return version == xf.version && uid.equals(xf.uid) && gid.equals(xf.gid);
160        }
161        return false;
162    }
163
164    /**
165     * The actual data to put into central directory data - without Header-ID
166     * or length specifier.
167     *
168     * @return get the data
169     */
170    @Override
171    public byte[] getCentralDirectoryData() {
172        return ByteUtils.EMPTY_BYTE_ARRAY;
173    }
174
175    /**
176     * Length of the extra field in the central directory data - without
177     * Header-ID or length specifier.
178     *
179     * @return a {@code ZipShort} for the length of the data of this extra field
180     */
181    @Override
182    public ZipShort getCentralDirectoryLength() {
183        return ZERO;
184    }
185
186    /**
187     * Gets the GID as a long.  GID is typically a 32 bit unsigned
188     * value on most UNIX systems, so we return a long to avoid
189     * integer overflow into the negatives in case values above
190     * and including 2^31 are being used.
191     *
192     * @return the GID value.
193     */
194    public long getGID() { return ZipUtil.bigToLong(gid); }
195
196    /**
197     * The Header-ID.
198     *
199     * @return the value for the header id for this extrafield
200     */
201    @Override
202    public ZipShort getHeaderId() {
203        return HEADER_ID;
204    }
205
206    /**
207     * The actual data to put into local file data - without Header-ID
208     * or length specifier.
209     *
210     * @return get the data
211     */
212    @Override
213    public byte[] getLocalFileDataData() {
214        byte[] uidBytes = uid.toByteArray();
215        byte[] gidBytes = gid.toByteArray();
216
217        // BigInteger might prepend a leading-zero to force a positive representation
218        // (e.g., so that the sign-bit is set to zero).  We need to remove that
219        // before sending the number over the wire.
220        uidBytes = trimLeadingZeroesForceMinLength(uidBytes);
221        final int uidBytesLen = uidBytes != null ? uidBytes.length : 0;
222        gidBytes = trimLeadingZeroesForceMinLength(gidBytes);
223        final int gidBytesLen = gidBytes != null ? gidBytes.length : 0;
224
225        // Couldn't bring myself to just call getLocalFileDataLength() when we've
226        // already got the arrays right here.  Yeah, yeah, I know, premature
227        // optimization is the root of all...
228        //
229        // The 3 comes from:  version=1 + uidsize=1 + gidsize=1
230        final byte[] data = new byte[3 + uidBytesLen + gidBytesLen];
231
232        // reverse() switches byte array from big-endian to little-endian.
233        if (uidBytes != null) {
234            reverse(uidBytes);
235        }
236        if (gidBytes != null) {
237            reverse(gidBytes);
238        }
239
240        int pos = 0;
241        data[pos++] = unsignedIntToSignedByte(version);
242        data[pos++] = unsignedIntToSignedByte(uidBytesLen);
243        if (uidBytes != null) {
244            System.arraycopy(uidBytes, 0, data, pos, uidBytesLen);
245        }
246        pos += uidBytesLen;
247        data[pos++] = unsignedIntToSignedByte(gidBytesLen);
248        if (gidBytes != null) {
249            System.arraycopy(gidBytes, 0, data, pos, gidBytesLen);
250        }
251        return data;
252    }
253
254    /**
255     * Length of the extra field in the local file data - without
256     * Header-ID or length specifier.
257     *
258     * @return a {@code ZipShort} for the length of the data of this extra field
259     */
260    @Override
261    public ZipShort getLocalFileDataLength() {
262        byte[] b = trimLeadingZeroesForceMinLength(uid.toByteArray());
263        final int uidSize = b == null ? 0 : b.length;
264        b = trimLeadingZeroesForceMinLength(gid.toByteArray());
265        final int gidSize = b == null ? 0 : b.length;
266
267        // The 3 comes from:  version=1 + uidsize=1 + gidsize=1
268        return new ZipShort(3 + uidSize + gidSize);
269    }
270
271    /**
272     * Gets the UID as a long.  UID is typically a 32 bit unsigned
273     * value on most UNIX systems, so we return a long to avoid
274     * integer overflow into the negatives in case values above
275     * and including 2^31 are being used.
276     *
277     * @return the UID value.
278     */
279    public long getUID() { return ZipUtil.bigToLong(uid); }
280
281    @Override
282    public int hashCode() {
283        int hc = -1234567 * version;
284        // Since most UIDs and GIDs are below 65,536, this is (hopefully!)
285        // a nice way to make sure typical UID and GID values impact the hash
286        // as much as possible.
287        hc ^= Integer.rotateLeft(uid.hashCode(), 16);
288        hc ^= gid.hashCode();
289        return hc;
290    }
291
292    /**
293     * Doesn't do anything since this class doesn't store anything
294     * inside the central directory.
295     */
296    @Override
297    public void parseFromCentralDirectoryData(
298            final byte[] buffer, final int offset, final int length
299    ) throws ZipException {
300    }
301
302    /**
303     * Populate data from this array as if it was in local file data.
304     *
305     * @param data   an array of bytes
306     * @param offset the start offset
307     * @param length the number of bytes in the array from offset
308     * @throws java.util.zip.ZipException on error
309     */
310    @Override
311    public void parseFromLocalFileData(
312            final byte[] data, int offset, final int length
313    ) throws ZipException {
314        reset();
315        if (length < 3) {
316            throw new ZipException("X7875_NewUnix length is too short, only "
317                + length + " bytes");
318        }
319        this.version = signedByteToUnsignedInt(data[offset++]);
320        final int uidSize = signedByteToUnsignedInt(data[offset++]);
321        if (uidSize + 3 > length) {
322            throw new ZipException("X7875_NewUnix invalid: uidSize " + uidSize
323                + " doesn't fit into " + length + " bytes");
324        }
325        final byte[] uidBytes = Arrays.copyOfRange(data, offset, offset + uidSize);
326        offset += uidSize;
327        this.uid = new BigInteger(1, reverse(uidBytes)); // sign-bit forced positive
328
329        final int gidSize = signedByteToUnsignedInt(data[offset++]);
330        if (uidSize + 3 + gidSize > length) {
331            throw new ZipException("X7875_NewUnix invalid: gidSize " + gidSize
332                + " doesn't fit into " + length + " bytes");
333        }
334        final byte[] gidBytes = Arrays.copyOfRange(data, offset, offset + gidSize);
335        this.gid = new BigInteger(1, reverse(gidBytes)); // sign-bit forced positive
336    }
337
338    /**
339     * Reset state back to newly constructed state.  Helps us make sure
340     * parse() calls always generate clean results.
341     */
342    private void reset() {
343        // Typical UID/GID of the first non-root user created on a Unix system.
344        uid = ONE_THOUSAND;
345        gid = ONE_THOUSAND;
346    }
347
348    /**
349     * Sets the GID.
350     *
351     * @param l GID value to set on this extra field.
352     */
353    public void setGID(final long l) {
354        this.gid = ZipUtil.longToBig(l);
355    }
356
357    /**
358     * Sets the UID.
359     *
360     * @param l UID value to set on this extra field.
361     */
362    public void setUID(final long l) {
363        this.uid = ZipUtil.longToBig(l);
364    }
365
366    /**
367     * Returns a String representation of this class useful for
368     * debugging purposes.
369     *
370     * @return A String representation of this class useful for
371     *         debugging purposes.
372     */
373    @Override
374    public String toString() {
375        return "0x7875 Zip Extra Field: UID=" + uid + " GID=" + gid;
376    }
377}