blob: b1ac3ee431bcce8f6d357c4bc1f5cdcc056993df [file] [log] [blame]
Julius Davies0e1d5a42013-01-08 21:54:15 +00001/*
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 */
19package org.apache.commons.compress.archivers.zip;
20
21import java.io.Serializable;
22import java.util.Date;
23import java.util.zip.ZipException;
24
25/**
26 * <p>An extra field that stores additional file and directory timestamp data
27 * for zip entries. Each zip entry can include up to three timestamps
28 * (modify, access, create*). The timestamps are stored as 32 bit unsigned
29 * integers representing seconds since UNIX epoch (Jan 1st, 1970, UTC).
30 * This field improves on zip's default timestamp granularity, since it
31 * allows one to store additional timestamps, and, in addition, the timestamps
32 * are stored using per-second granularity (zip's default behaviour can only store
33 * timestamps to the nearest <em>even</em> second).
Julius Davies0e1d5a42013-01-08 21:54:15 +000034 * </p><p>
35 * Unfortunately, 32 (unsigned) bits can only store dates up to the year 2106,
36 * and so this extra field will eventually be obsolete. Enjoy it while it lasts!
37 * </p>
38 * <ul>
39 * <li><b>modifyTime:</b>
40 * most recent time of file/directory modification
41 * (or file/dir creation if the entry has not been
42 * modified since it was created).
43 * </li>
44 * <li><b>accessTime:</b>
45 * most recent time file/directory was opened
46 * (e.g., read from disk). Many people disable
47 * their operating systems from updating this value
48 * using the NOATIME mount option to optimize disk behaviour,
49 * and thus it's not always reliable. In those cases
50 * it's always equal to modifyTime.
51 * </li>
52 * <li><b>*createTime:</b>
53 * modern linux file systems (e.g., ext2 and newer)
54 * do not appear to store a value like this, and so
55 * it's usually omitted altogether in the zip extra
56 * field. Perhaps other unix systems track this.
57 * </li></ul>
58 * <p>
59 * We're using the field definition given in Info-Zip's source archive:
60 * zip-3.0.tar.gz/proginfo/extrafld.txt
61 * </p>
62 * <pre>
63 * Value Size Description
64 * ----- ---- -----------
65 * 0x5455 Short tag for this extra block type ("UT")
66 * TSize Short total data size for this block
67 * Flags Byte info bits
68 * (ModTime) Long time of last modification (UTC/GMT)
69 * (AcTime) Long time of last access (UTC/GMT)
70 * (CrTime) Long time of original creation (UTC/GMT)
71 *
72 * Central-header version:
73 *
74 * Value Size Description
75 * ----- ---- -----------
76 * 0x5455 Short tag for this extra block type ("UT")
77 * TSize Short total data size for this block
78 * Flags Byte info bits (refers to local header!)
79 * (ModTime) Long time of last modification (UTC/GMT)
80 * </pre>
Stefan Bodewigc1fd3172013-03-10 16:37:16 +000081 * @since 1.5
Julius Davies0e1d5a42013-01-08 21:54:15 +000082 */
83public class X5455_ExtendedTimestamp implements ZipExtraField, Cloneable, Serializable {
84 private static final ZipShort HEADER_ID = new ZipShort(0x5455);
85 private static final long serialVersionUID = 1L;
86
Stefan Bodewig09a41992013-12-08 19:14:25 +000087 /**
88 * The bit set inside the flags by when the last modification time
89 * is present in this extra field.
90 */
91 public static final byte MODIFY_TIME_BIT = 1;
92 /**
93 * The bit set inside the flags by when the lasr access time is
94 * present in this extra field.
95 */
96 public static final byte ACCESS_TIME_BIT = 2;
97 /**
98 * The bit set inside the flags by when the original creation time
99 * is present in this extra field.
100 */
101 public static final byte CREATE_TIME_BIT = 4;
Julius Davies0e1d5a42013-01-08 21:54:15 +0000102
103 // The 3 boolean fields (below) come from this flags byte. The remaining 5 bits
104 // are ignored according to the current version of the spec (December 2012).
105 private byte flags;
106
107 // Note: even if bit1 and bit2 are set, the Central data will still not contain
108 // access/create fields: only local data ever holds those! This causes
109 // some of our implementation to look a little odd, with seemingly spurious
110 // != null and length checks.
111 private boolean bit0_modifyTimePresent;
112 private boolean bit1_accessTimePresent;
113 private boolean bit2_createTimePresent;
114
115 private ZipLong modifyTime;
116 private ZipLong accessTime;
117 private ZipLong createTime;
118
119 /**
120 * Constructor for X5455_ExtendedTimestamp.
121 */
122 public X5455_ExtendedTimestamp() {}
123
124 /**
125 * The Header-ID.
126 *
127 * @return the value for the header id for this extrafield
128 */
129 public ZipShort getHeaderId() {
130 return HEADER_ID;
131 }
132
133 /**
134 * Length of the extra field in the local file data - without
135 * Header-ID or length specifier.
136 *
137 * @return a <code>ZipShort</code> for the length of the data of this extra field
138 */
139 public ZipShort getLocalFileDataLength() {
140 return new ZipShort(1 +
141 (bit0_modifyTimePresent ? 4 : 0) +
142 (bit1_accessTimePresent && accessTime != null ? 4 : 0) +
143 (bit2_createTimePresent && createTime != null ? 4 : 0)
144 );
145 }
146
147 /**
148 * Length of the extra field in the local file data - without
149 * Header-ID or length specifier.
Stefan Bodewig45e51c22013-12-22 07:03:43 +0000150 *
151 * <p>For X5455 the central length is often smaller than the
Julius Davies0e1d5a42013-01-08 21:54:15 +0000152 * local length, because central cannot contain access or create
Stefan Bodewig45e51c22013-12-22 07:03:43 +0000153 * timestamps.</p>
Julius Davies0e1d5a42013-01-08 21:54:15 +0000154 *
155 * @return a <code>ZipShort</code> for the length of the data of this extra field
156 */
157 public ZipShort getCentralDirectoryLength() {
158 return new ZipShort(1 +
159 (bit0_modifyTimePresent ? 4 : 0)
160 );
161 }
162
163 /**
164 * The actual data to put into local file data - without Header-ID
165 * or length specifier.
166 *
167 * @return get the data
168 */
169 public byte[] getLocalFileDataData() {
170 byte[] data = new byte[getLocalFileDataLength().getValue()];
171 int pos = 0;
172 data[pos++] = 0;
173 if (bit0_modifyTimePresent) {
174 data[0] |= MODIFY_TIME_BIT;
175 System.arraycopy(modifyTime.getBytes(), 0, data, pos, 4);
176 pos += 4;
177 }
178 if (bit1_accessTimePresent && accessTime != null) {
179 data[0] |= ACCESS_TIME_BIT;
180 System.arraycopy(accessTime.getBytes(), 0, data, pos, 4);
181 pos += 4;
182 }
183 if (bit2_createTimePresent && createTime != null) {
184 data[0] |= CREATE_TIME_BIT;
185 System.arraycopy(createTime.getBytes(), 0, data, pos, 4);
186 pos += 4;
187 }
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 the central directory data
196 */
197 public byte[] getCentralDirectoryData() {
198 byte[] centralData = new byte[getCentralDirectoryLength().getValue()];
199 byte[] localData = getLocalFileDataData();
200
201 // Truncate out create & access time (last 8 bytes) from
202 // the copy of the local data we obtained:
203 System.arraycopy(localData, 0, centralData, 0, centralData.length);
204 return centralData;
205 }
206
207 /**
208 * Populate data from this array as if it was in local file data.
209 *
210 * @param data an array of bytes
211 * @param offset the start offset
212 * @param length the number of bytes in the array from offset
213 * @throws java.util.zip.ZipException on error
214 */
215 public void parseFromLocalFileData(
216 byte[] data, int offset, int length
217 ) throws ZipException {
218 reset();
219 final int len = offset + length;
220 setFlags(data[offset++]);
221 if (bit0_modifyTimePresent) {
222 modifyTime = new ZipLong(data, offset);
223 offset += 4;
224 }
225
226 // Notice the extra length check in case we are parsing the shorter
227 // central data field (for both access and create timestamps).
228 if (bit1_accessTimePresent && offset + 4 <= len) {
229 accessTime = new ZipLong(data, offset);
230 offset += 4;
231 }
232 if (bit2_createTimePresent && offset + 4 <= len) {
233 createTime = new ZipLong(data, offset);
234 offset += 4;
235 }
236 }
237
238 /**
239 * Doesn't do anything special since this class always uses the
240 * same parsing logic for both central directory and local file data.
241 */
242 public void parseFromCentralDirectoryData(
243 byte[] buffer, int offset, int length
244 ) throws ZipException {
245 reset();
246 parseFromLocalFileData(buffer, offset, length);
247 }
248
249 /**
250 * Reset state back to newly constructed state. Helps us make sure
251 * parse() calls always generate clean results.
252 */
253 private void reset() {
254 setFlags((byte) 0);
255 this.modifyTime = null;
256 this.accessTime = null;
257 this.createTime = null;
258 }
259
260 /**
261 * Sets flags byte. The flags byte tells us which of the
262 * three datestamp fields are present in the data:
263 * <pre>
264 * bit0 - modify time
265 * bit1 - access time
266 * bit2 - create time
267 * </pre>
268 * Only first 3 bits of flags are used according to the
269 * latest version of the spec (December 2012).
270 *
271 * @param flags flags byte indicating which of the
272 * three datestamp fields are present.
273 */
274 public void setFlags(byte flags) {
275 this.flags = flags;
276 this.bit0_modifyTimePresent = (flags & MODIFY_TIME_BIT) == MODIFY_TIME_BIT;
277 this.bit1_accessTimePresent = (flags & ACCESS_TIME_BIT) == ACCESS_TIME_BIT;
278 this.bit2_createTimePresent = (flags & CREATE_TIME_BIT) == CREATE_TIME_BIT;
279 }
280
281 /**
282 * Gets flags byte. The flags byte tells us which of the
283 * three datestamp fields are present in the data:
284 * <pre>
285 * bit0 - modify time
286 * bit1 - access time
287 * bit2 - create time
288 * </pre>
289 * Only first 3 bits of flags are used according to the
290 * latest version of the spec (December 2012).
291 *
292 * @return flags byte indicating which of the
293 * three datestamp fields are present.
294 */
295 public byte getFlags() { return flags; }
296
297 /**
298 * Returns whether bit0 of the flags byte is set or not,
299 * which should correspond to the presence or absence of
300 * a modify timestamp in this particular zip entry.
301 *
302 * @return true if bit0 of the flags byte is set.
303 */
304 public boolean isBit0_modifyTimePresent() { return bit0_modifyTimePresent; }
305
306 /**
307 * Returns whether bit1 of the flags byte is set or not,
308 * which should correspond to the presence or absence of
309 * a "last access" timestamp in this particular zip entry.
310 *
311 * @return true if bit1 of the flags byte is set.
312 */
313 public boolean isBit1_accessTimePresent() { return bit1_accessTimePresent; }
314
315 /**
316 * Returns whether bit2 of the flags byte is set or not,
317 * which should correspond to the presence or absence of
318 * a create timestamp in this particular zip entry.
319 *
320 * @return true if bit2 of the flags byte is set.
321 */
322 public boolean isBit2_createTimePresent() { return bit2_createTimePresent; }
323
324 /**
325 * Returns the modify time (seconds since epoch) of this zip entry
326 * as a ZipLong object, or null if no such timestamp exists in the
327 * zip entry.
328 *
329 * @return modify time (seconds since epoch) or null.
330 */
331 public ZipLong getModifyTime() { return modifyTime; }
332
333 /**
334 * Returns the access time (seconds since epoch) of this zip entry
335 * as a ZipLong object, or null if no such timestamp exists in the
336 * zip entry.
337 *
338 * @return access time (seconds since epoch) or null.
339 */
340 public ZipLong getAccessTime() { return accessTime; }
341
342 /**
343 * <p>
344 * Returns the create time (seconds since epoch) of this zip entry
345 * as a ZipLong object, or null if no such timestamp exists in the
346 * zip entry.
347 * </p><p>
348 * Note: modern linux file systems (e.g., ext2)
349 * do not appear to store a "create time" value, and so
350 * it's usually omitted altogether in the zip extra
351 * field. Perhaps other unix systems track this.
352 *
353 * @return create time (seconds since epoch) or null.
354 */
355 public ZipLong getCreateTime() { return createTime; }
356
357 /**
358 * Returns the modify time as a java.util.Date
359 * of this zip entry, or null if no such timestamp exists in the zip entry.
360 * The milliseconds are always zeroed out, since the underlying data
361 * offers only per-second precision.
362 *
363 * @return modify time as java.util.Date or null.
364 */
365 public Date getModifyJavaTime() {
366 return modifyTime != null ? new Date(modifyTime.getValue() * 1000) : null;
367 }
368
369 /**
370 * Returns the access time as a java.util.Date
371 * of this zip entry, or null if no such timestamp exists in the zip entry.
372 * The milliseconds are always zeroed out, since the underlying data
373 * offers only per-second precision.
374 *
375 * @return access time as java.util.Date or null.
376 */
377 public Date getAccessJavaTime() {
378 return accessTime != null ? new Date(accessTime.getValue() * 1000) : null;
379 }
380
381 /**
382 * <p>
383 * Returns the create time as a a java.util.Date
384 * of this zip entry, or null if no such timestamp exists in the zip entry.
385 * The milliseconds are always zeroed out, since the underlying data
386 * offers only per-second precision.
387 * </p><p>
388 * Note: modern linux file systems (e.g., ext2)
389 * do not appear to store a "create time" value, and so
390 * it's usually omitted altogether in the zip extra
391 * field. Perhaps other unix systems track this.
392 *
393 * @return create time as java.util.Date or null.
394 */
395 public Date getCreateJavaTime() {
396 return createTime != null ? new Date(createTime.getValue() * 1000) : null;
397 }
398
399 /**
400 * <p>
401 * Sets the modify time (seconds since epoch) of this zip entry
402 * using a ZipLong object.
403 * </p><p>
404 * Note: the setters for flags and timestamps are decoupled.
405 * Even if the timestamp is not-null, it will only be written
406 * out if the corresponding bit in the flags is also set.
407 * </p>
408 *
409 * @param l ZipLong of the modify time (seconds per epoch)
410 */
Stefan Bodewig09a41992013-12-08 19:14:25 +0000411 public void setModifyTime(ZipLong l) {
412 bit0_modifyTimePresent = l != null;
413 flags = (byte) (l != null ? (flags | MODIFY_TIME_BIT)
414 : (flags & ~MODIFY_TIME_BIT));
415 this.modifyTime = l;
416 }
Julius Davies0e1d5a42013-01-08 21:54:15 +0000417
418 /**
419 * <p>
420 * Sets the access time (seconds since epoch) of this zip entry
421 * using a ZipLong object
422 * </p><p>
423 * Note: the setters for flags and timestamps are decoupled.
424 * Even if the timestamp is not-null, it will only be written
425 * out if the corresponding bit in the flags is also set.
426 * </p>
427 *
428 * @param l ZipLong of the access time (seconds per epoch)
429 */
Stefan Bodewig09a41992013-12-08 19:14:25 +0000430 public void setAccessTime(ZipLong l) {
431 bit1_accessTimePresent = l != null;
432 flags = (byte) (l != null ? (flags | ACCESS_TIME_BIT)
433 : (flags & ~ACCESS_TIME_BIT));
434 this.accessTime = l;
435 }
Julius Davies0e1d5a42013-01-08 21:54:15 +0000436
437 /**
438 * <p>
439 * Sets the create time (seconds since epoch) of this zip entry
440 * using a ZipLong object
441 * </p><p>
442 * Note: the setters for flags and timestamps are decoupled.
443 * Even if the timestamp is not-null, it will only be written
444 * out if the corresponding bit in the flags is also set.
445 * </p>
446 *
447 * @param l ZipLong of the create time (seconds per epoch)
448 */
Stefan Bodewig09a41992013-12-08 19:14:25 +0000449 public void setCreateTime(ZipLong l) {
450 bit2_createTimePresent = l != null;
451 flags = (byte) (l != null ? (flags | CREATE_TIME_BIT)
452 : (flags & ~CREATE_TIME_BIT));
453 this.createTime = l;
454 }
Julius Davies0e1d5a42013-01-08 21:54:15 +0000455
456 /**
457 * <p>
458 * Sets the modify time as a java.util.Date
459 * of this zip entry. Supplied value is truncated to per-second
460 * precision (milliseconds zeroed-out).
461 * </p><p>
462 * Note: the setters for flags and timestamps are decoupled.
463 * Even if the timestamp is not-null, it will only be written
464 * out if the corresponding bit in the flags is also set.
465 * </p>
466 *
467 * @param d modify time as java.util.Date
468 */
469 public void setModifyJavaTime(Date d) { setModifyTime(dateToZipLong(d)); }
470
471 /**
472 * <p>
473 * Sets the access time as a java.util.Date
474 * of this zip entry. Supplied value is truncated to per-second
475 * precision (milliseconds zeroed-out).
476 * </p><p>
477 * Note: the setters for flags and timestamps are decoupled.
478 * Even if the timestamp is not-null, it will only be written
479 * out if the corresponding bit in the flags is also set.
480 * </p>
481 *
482 * @param d access time as java.util.Date
483 */
484 public void setAccessJavaTime(Date d) { setAccessTime(dateToZipLong(d)); }
485
486 /**
487 * <p>
488 * Sets the create time as a java.util.Date
489 * of this zip entry. Supplied value is truncated to per-second
490 * precision (milliseconds zeroed-out).
491 * </p><p>
492 * Note: the setters for flags and timestamps are decoupled.
493 * Even if the timestamp is not-null, it will only be written
494 * out if the corresponding bit in the flags is also set.
495 * </p>
496 *
497 * @param d create time as java.util.Date
498 */
499 public void setCreateJavaTime(Date d) { setCreateTime(dateToZipLong(d)); }
500
501 /**
502 * Utility method converts java.util.Date (milliseconds since epoch)
503 * into a ZipLong (seconds since epoch).
504 * <p/>
505 * Also makes sure the converted ZipLong is not too big to fit
506 * in 32 unsigned bits.
507 *
508 * @param d java.util.Date to convert to ZipLong
509 * @return ZipLong
510 */
511 private static ZipLong dateToZipLong(final Date d) {
512 if (d == null) { return null; }
513
514 final long TWO_TO_32 = 0x100000000L;
515 final long l = d.getTime() / 1000;
516 if (l >= TWO_TO_32) {
517 throw new IllegalArgumentException("Cannot set an X5455 timestamp larger than 2^32: " + l);
518 }
519 return new ZipLong(l);
520 }
521
522 /**
523 * Returns a String representation of this class useful for
524 * debugging purposes.
525 *
526 * @return A String representation of this class useful for
527 * debugging purposes.
528 */
529 @Override
530 public String toString() {
531 StringBuilder buf = new StringBuilder();
532 buf.append("0x5455 Zip Extra Field: Flags=");
533 buf.append(Integer.toBinaryString(ZipUtil.unsignedIntToSignedByte(flags))).append(" ");
534 if (bit0_modifyTimePresent && modifyTime != null) {
535 Date m = getModifyJavaTime();
536 buf.append(" Modify:[").append(m).append("] ");
537 }
538 if (bit1_accessTimePresent && accessTime != null) {
539 Date a = getAccessJavaTime();
540 buf.append(" Access:[").append(a).append("] ");
541 }
542 if (bit2_createTimePresent && createTime != null) {
543 Date c = getCreateJavaTime();
544 buf.append(" Create:[").append(c).append("] ");
545 }
546 return buf.toString();
547 }
548
549 @Override
550 public Object clone() throws CloneNotSupportedException {
551 return super.clone();
552 }
553
554 @Override
555 public boolean equals(Object o) {
556 if (o instanceof X5455_ExtendedTimestamp) {
557 X5455_ExtendedTimestamp xf = (X5455_ExtendedTimestamp) o;
558
559 // The ZipLong==ZipLong clauses handle the cases where both are null.
560 // and only last 3 bits of flags matter.
561 return ((flags & 0x07) == (xf.flags & 0x07)) &&
562 (modifyTime == xf.modifyTime || (modifyTime != null && modifyTime.equals(xf.modifyTime))) &&
563 (accessTime == xf.accessTime || (accessTime != null && accessTime.equals(xf.accessTime))) &&
564 (createTime == xf.createTime || (createTime != null && createTime.equals(xf.createTime)));
565 } else {
566 return false;
567 }
568 }
569
570 @Override
571 public int hashCode() {
572 int hc = (-123 * (flags & 0x07)); // only last 3 bits of flags matter
573 if (modifyTime != null) {
574 hc ^= modifyTime.hashCode();
575 }
576 if (accessTime != null) {
577 // Since accessTime is often same as modifyTime,
578 // this prevents them from XOR negating each other.
579 hc ^= Integer.rotateLeft(accessTime.hashCode(), 11);
580 }
581 if (createTime != null) {
582 hc ^= Integer.rotateLeft(createTime.hashCode(), 22);
583 }
584 return hc;
585 }
586
Stefan Bodewigc1fd3172013-03-10 16:37:16 +0000587}