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