~ubuntu-branches/ubuntu/utopic/libcommons-compress-java/utopic

« back to all changes in this revision

Viewing changes to src/main/java/org/apache/commons/compress/archivers/zip/X7875_NewUnix.java

  • Committer: Package Import Robot
  • Author(s): tony mancill
  • Date: 2013-05-17 07:53:06 UTC
  • mfrom: (7.1.1 experimental)
  • Revision ID: package-import@ubuntu.com-20130517075306-4yt0jyr782vasejl
Tags: 1.5-1
* Team upload.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

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