001: /*
002: * Copyright 2001-2002,2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: */
017:
018: package org.apache.tools.zip;
019:
020: import java.util.zip.CRC32;
021: import java.util.zip.ZipException;
022:
023: /**
024: * Adds Unix file permission and UID/GID fields as well as symbolic
025: * link handling.
026: *
027: * <p>This class uses the ASi extra field in the format:
028: * <pre>
029: * Value Size Description
030: * ----- ---- -----------
031: * (Unix3) 0x756e Short tag for this extra block type
032: * TSize Short total data size for this block
033: * CRC Long CRC-32 of the remaining data
034: * Mode Short file permissions
035: * SizDev Long symlink'd size OR major/minor dev num
036: * UID Short user ID
037: * GID Short group ID
038: * (var.) variable symbolic link filename
039: * </pre>
040: * taken from appnote.iz (Info-ZIP note, 981119) found at <a
041: * href="ftp://ftp.uu.net/pub/archiving/zip/doc/">ftp://ftp.uu.net/pub/archiving/zip/doc/</a></p>
042:
043: *
044: * <p>Short is two bytes and Long is four bytes in big endian byte and
045: * word order, device numbers are currently not supported.</p>
046: *
047: * @author Stefan Bodewig
048: * @version $Revision: 1.6.2.3 $
049: */
050: public class AsiExtraField implements ZipExtraField, UnixStat,
051: Cloneable {
052:
053: private static final ZipShort HEADER_ID = new ZipShort(0x756E);
054:
055: /**
056: * Standard Unix stat(2) file mode.
057: *
058: * @since 1.1
059: */
060: private int mode = 0;
061: /**
062: * User ID.
063: *
064: * @since 1.1
065: */
066: private int uid = 0;
067: /**
068: * Group ID.
069: *
070: * @since 1.1
071: */
072: private int gid = 0;
073: /**
074: * File this entry points to, if it is a symbolic link.
075: *
076: * <p>empty string - if entry is not a symbolic link.</p>
077: *
078: * @since 1.1
079: */
080: private String link = "";
081: /**
082: * Is this an entry for a directory?
083: *
084: * @since 1.1
085: */
086: private boolean dirFlag = false;
087:
088: /**
089: * Instance used to calculate checksums.
090: *
091: * @since 1.1
092: */
093: private CRC32 crc = new CRC32();
094:
095: public AsiExtraField() {
096: }
097:
098: /**
099: * The Header-ID.
100: *
101: * @since 1.1
102: */
103: public ZipShort getHeaderId() {
104: return HEADER_ID;
105: }
106:
107: /**
108: * Length of the extra field in the local file data - without
109: * Header-ID or length specifier.
110: *
111: * @since 1.1
112: */
113: public ZipShort getLocalFileDataLength() {
114: return new ZipShort(4 // CRC
115: + 2 // Mode
116: + 4 // SizDev
117: + 2 // UID
118: + 2 // GID
119: + getLinkedFile().getBytes().length);
120: }
121:
122: /**
123: * Delegate to local file data.
124: *
125: * @since 1.1
126: */
127: public ZipShort getCentralDirectoryLength() {
128: return getLocalFileDataLength();
129: }
130:
131: /**
132: * The actual data to put into local file data - without Header-ID
133: * or length specifier.
134: *
135: * @since 1.1
136: */
137: public byte[] getLocalFileDataData() {
138: // CRC will be added later
139: byte[] data = new byte[getLocalFileDataLength().getValue() - 4];
140: System.arraycopy((new ZipShort(getMode())).getBytes(), 0, data,
141: 0, 2);
142:
143: byte[] linkArray = getLinkedFile().getBytes();
144: System.arraycopy((new ZipLong(linkArray.length)).getBytes(), 0,
145: data, 2, 4);
146:
147: System.arraycopy((new ZipShort(getUserId())).getBytes(), 0,
148: data, 6, 2);
149: System.arraycopy((new ZipShort(getGroupId())).getBytes(), 0,
150: data, 8, 2);
151:
152: System.arraycopy(linkArray, 0, data, 10, linkArray.length);
153:
154: crc.reset();
155: crc.update(data);
156: long checksum = crc.getValue();
157:
158: byte[] result = new byte[data.length + 4];
159: System.arraycopy((new ZipLong(checksum)).getBytes(), 0, result,
160: 0, 4);
161: System.arraycopy(data, 0, result, 4, data.length);
162: return result;
163: }
164:
165: /**
166: * Delegate to local file data.
167: *
168: * @since 1.1
169: */
170: public byte[] getCentralDirectoryData() {
171: return getLocalFileDataData();
172: }
173:
174: /**
175: * Set the user id.
176: *
177: * @since 1.1
178: */
179: public void setUserId(int uid) {
180: this .uid = uid;
181: }
182:
183: /**
184: * Get the user id.
185: *
186: * @since 1.1
187: */
188: public int getUserId() {
189: return uid;
190: }
191:
192: /**
193: * Set the group id.
194: *
195: * @since 1.1
196: */
197: public void setGroupId(int gid) {
198: this .gid = gid;
199: }
200:
201: /**
202: * Get the group id.
203: *
204: * @since 1.1
205: */
206: public int getGroupId() {
207: return gid;
208: }
209:
210: /**
211: * Indicate that this entry is a symbolic link to the given filename.
212: *
213: * @param name Name of the file this entry links to, empty String
214: * if it is not a symbolic link.
215: *
216: * @since 1.1
217: */
218: public void setLinkedFile(String name) {
219: link = name;
220: mode = getMode(mode);
221: }
222:
223: /**
224: * Name of linked file
225: *
226: * @return name of the file this entry links to if it is a
227: * symbolic link, the empty string otherwise.
228: *
229: * @since 1.1
230: */
231: public String getLinkedFile() {
232: return link;
233: }
234:
235: /**
236: * Is this entry a symbolic link?
237: *
238: * @since 1.1
239: */
240: public boolean isLink() {
241: return getLinkedFile().length() != 0;
242: }
243:
244: /**
245: * File mode of this file.
246: *
247: * @since 1.1
248: */
249: public void setMode(int mode) {
250: this .mode = getMode(mode);
251: }
252:
253: /**
254: * File mode of this file.
255: *
256: * @since 1.1
257: */
258: public int getMode() {
259: return mode;
260: }
261:
262: /**
263: * Indicate whether this entry is a directory.
264: *
265: * @since 1.1
266: */
267: public void setDirectory(boolean dirFlag) {
268: this .dirFlag = dirFlag;
269: mode = getMode(mode);
270: }
271:
272: /**
273: * Is this entry a directory?
274: *
275: * @since 1.1
276: */
277: public boolean isDirectory() {
278: return dirFlag && !isLink();
279: }
280:
281: /**
282: * Populate data from this array as if it was in local file data.
283: *
284: * @since 1.1
285: */
286: public void parseFromLocalFileData(byte[] data, int offset,
287: int length) throws ZipException {
288:
289: long givenChecksum = (new ZipLong(data, offset)).getValue();
290: byte[] tmp = new byte[length - 4];
291: System.arraycopy(data, offset + 4, tmp, 0, length - 4);
292: crc.reset();
293: crc.update(tmp);
294: long realChecksum = crc.getValue();
295: if (givenChecksum != realChecksum) {
296: throw new ZipException("bad CRC checksum "
297: + Long.toHexString(givenChecksum) + " instead of "
298: + Long.toHexString(realChecksum));
299: }
300:
301: int newMode = (new ZipShort(tmp, 0)).getValue();
302: byte[] linkArray = new byte[(int) (new ZipLong(tmp, 2))
303: .getValue()];
304: uid = (new ZipShort(tmp, 6)).getValue();
305: gid = (new ZipShort(tmp, 8)).getValue();
306:
307: if (linkArray.length == 0) {
308: link = "";
309: } else {
310: System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
311: link = new String(linkArray);
312: }
313: setDirectory((newMode & DIR_FLAG) != 0);
314: setMode(newMode);
315: }
316:
317: /**
318: * Get the file mode for given permissions with the correct file type.
319: *
320: * @since 1.1
321: */
322: protected int getMode(int mode) {
323: int type = FILE_FLAG;
324: if (isLink()) {
325: type = LINK_FLAG;
326: } else if (isDirectory()) {
327: type = DIR_FLAG;
328: }
329: return type | (mode & PERM_MASK);
330: }
331:
332: }
|