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 */ 019 package org.apache.commons.compress.archivers.zip; 020 021 import java.io.Serializable; 022 import java.util.Date; 023 import java.util.zip.ZipException; 024 025 /** 026 * <p>An extra field that stores additional file and directory timestamp data 027 * for zip entries. Each zip entry can include up to three timestamps 028 * (modify, access, create*). The timestamps are stored as 32 bit unsigned 029 * integers representing seconds since UNIX epoch (Jan 1st, 1970, UTC). 030 * This field improves on zip's default timestamp granularity, since it 031 * allows one to store additional timestamps, and, in addition, the timestamps 032 * are stored using per-second granularity (zip's default behaviour can only store 033 * timestamps to the nearest <em>even</em> second). 034 * <p/> 035 * </p><p> 036 * Unfortunately, 32 (unsigned) bits can only store dates up to the year 2106, 037 * and so this extra field will eventually be obsolete. Enjoy it while it lasts! 038 * </p> 039 * <ul> 040 * <li><b>modifyTime:</b> 041 * most recent time of file/directory modification 042 * (or file/dir creation if the entry has not been 043 * modified since it was created). 044 * </li> 045 * <li><b>accessTime:</b> 046 * most recent time file/directory was opened 047 * (e.g., read from disk). Many people disable 048 * their operating systems from updating this value 049 * using the NOATIME mount option to optimize disk behaviour, 050 * and thus it's not always reliable. In those cases 051 * it's always equal to modifyTime. 052 * </li> 053 * <li><b>*createTime:</b> 054 * modern linux file systems (e.g., ext2 and newer) 055 * do not appear to store a value like this, and so 056 * it's usually omitted altogether in the zip extra 057 * field. Perhaps other unix systems track this. 058 * </li></ul> 059 * <p> 060 * We're using the field definition given in Info-Zip's source archive: 061 * zip-3.0.tar.gz/proginfo/extrafld.txt 062 * </p> 063 * <pre> 064 * Value Size Description 065 * ----- ---- ----------- 066 * 0x5455 Short tag for this extra block type ("UT") 067 * TSize Short total data size for this block 068 * Flags Byte info bits 069 * (ModTime) Long time of last modification (UTC/GMT) 070 * (AcTime) Long time of last access (UTC/GMT) 071 * (CrTime) Long time of original creation (UTC/GMT) 072 * 073 * Central-header version: 074 * 075 * Value Size Description 076 * ----- ---- ----------- 077 * 0x5455 Short tag for this extra block type ("UT") 078 * TSize Short total data size for this block 079 * Flags Byte info bits (refers to local header!) 080 * (ModTime) Long time of last modification (UTC/GMT) 081 * </pre> 082 * @since 1.5 083 */ 084 public class X5455_ExtendedTimestamp implements ZipExtraField, Cloneable, Serializable { 085 private static final ZipShort HEADER_ID = new ZipShort(0x5455); 086 private static final long serialVersionUID = 1L; 087 088 static final byte MODIFY_TIME_BIT = 1; 089 static final byte ACCESS_TIME_BIT = 2; 090 static final byte CREATE_TIME_BIT = 4; 091 092 // The 3 boolean fields (below) come from this flags byte. The remaining 5 bits 093 // are ignored according to the current version of the spec (December 2012). 094 private byte flags; 095 096 // Note: even if bit1 and bit2 are set, the Central data will still not contain 097 // access/create fields: only local data ever holds those! This causes 098 // some of our implementation to look a little odd, with seemingly spurious 099 // != null and length checks. 100 private boolean bit0_modifyTimePresent; 101 private boolean bit1_accessTimePresent; 102 private boolean bit2_createTimePresent; 103 104 private ZipLong modifyTime; 105 private ZipLong accessTime; 106 private ZipLong createTime; 107 108 /** 109 * Constructor for X5455_ExtendedTimestamp. 110 */ 111 public X5455_ExtendedTimestamp() {} 112 113 /** 114 * The Header-ID. 115 * 116 * @return the value for the header id for this extrafield 117 */ 118 public ZipShort getHeaderId() { 119 return HEADER_ID; 120 } 121 122 /** 123 * Length of the extra field in the local file data - without 124 * Header-ID or length specifier. 125 * 126 * @return a <code>ZipShort</code> for the length of the data of this extra field 127 */ 128 public ZipShort getLocalFileDataLength() { 129 return new ZipShort(1 + 130 (bit0_modifyTimePresent ? 4 : 0) + 131 (bit1_accessTimePresent && accessTime != null ? 4 : 0) + 132 (bit2_createTimePresent && createTime != null ? 4 : 0) 133 ); 134 } 135 136 /** 137 * Length of the extra field in the local file data - without 138 * Header-ID or length specifier. 139 * <p/> 140 * For X5455 the central length is often smaller than the 141 * local length, because central cannot contain access or create 142 * timestamps. 143 * 144 * @return a <code>ZipShort</code> for the length of the data of this extra field 145 */ 146 public ZipShort getCentralDirectoryLength() { 147 return new ZipShort(1 + 148 (bit0_modifyTimePresent ? 4 : 0) 149 ); 150 } 151 152 /** 153 * The actual data to put into local file data - without Header-ID 154 * or length specifier. 155 * 156 * @return get the data 157 */ 158 public byte[] getLocalFileDataData() { 159 byte[] data = new byte[getLocalFileDataLength().getValue()]; 160 int pos = 0; 161 data[pos++] = 0; 162 if (bit0_modifyTimePresent) { 163 data[0] |= MODIFY_TIME_BIT; 164 System.arraycopy(modifyTime.getBytes(), 0, data, pos, 4); 165 pos += 4; 166 } 167 if (bit1_accessTimePresent && accessTime != null) { 168 data[0] |= ACCESS_TIME_BIT; 169 System.arraycopy(accessTime.getBytes(), 0, data, pos, 4); 170 pos += 4; 171 } 172 if (bit2_createTimePresent && createTime != null) { 173 data[0] |= CREATE_TIME_BIT; 174 System.arraycopy(createTime.getBytes(), 0, data, pos, 4); 175 pos += 4; 176 } 177 return data; 178 } 179 180 /** 181 * The actual data to put into central directory data - without Header-ID 182 * or length specifier. 183 * 184 * @return the central directory data 185 */ 186 public byte[] getCentralDirectoryData() { 187 byte[] centralData = new byte[getCentralDirectoryLength().getValue()]; 188 byte[] localData = getLocalFileDataData(); 189 190 // Truncate out create & access time (last 8 bytes) from 191 // the copy of the local data we obtained: 192 System.arraycopy(localData, 0, centralData, 0, centralData.length); 193 return centralData; 194 } 195 196 /** 197 * Populate data from this array as if it was in local file data. 198 * 199 * @param data an array of bytes 200 * @param offset the start offset 201 * @param length the number of bytes in the array from offset 202 * @throws java.util.zip.ZipException on error 203 */ 204 public void parseFromLocalFileData( 205 byte[] data, int offset, int length 206 ) throws ZipException { 207 reset(); 208 final int len = offset + length; 209 setFlags(data[offset++]); 210 if (bit0_modifyTimePresent) { 211 modifyTime = new ZipLong(data, offset); 212 offset += 4; 213 } 214 215 // Notice the extra length check in case we are parsing the shorter 216 // central data field (for both access and create timestamps). 217 if (bit1_accessTimePresent && offset + 4 <= len) { 218 accessTime = new ZipLong(data, offset); 219 offset += 4; 220 } 221 if (bit2_createTimePresent && offset + 4 <= len) { 222 createTime = new ZipLong(data, offset); 223 offset += 4; 224 } 225 } 226 227 /** 228 * Doesn't do anything special since this class always uses the 229 * same parsing logic for both central directory and local file data. 230 */ 231 public void parseFromCentralDirectoryData( 232 byte[] buffer, int offset, int length 233 ) throws ZipException { 234 reset(); 235 parseFromLocalFileData(buffer, offset, length); 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 setFlags((byte) 0); 244 this.modifyTime = null; 245 this.accessTime = null; 246 this.createTime = null; 247 } 248 249 /** 250 * Sets flags byte. The flags byte tells us which of the 251 * three datestamp fields are present in the data: 252 * <pre> 253 * bit0 - modify time 254 * bit1 - access time 255 * bit2 - create time 256 * </pre> 257 * Only first 3 bits of flags are used according to the 258 * latest version of the spec (December 2012). 259 * 260 * @param flags flags byte indicating which of the 261 * three datestamp fields are present. 262 */ 263 public void setFlags(byte flags) { 264 this.flags = flags; 265 this.bit0_modifyTimePresent = (flags & MODIFY_TIME_BIT) == MODIFY_TIME_BIT; 266 this.bit1_accessTimePresent = (flags & ACCESS_TIME_BIT) == ACCESS_TIME_BIT; 267 this.bit2_createTimePresent = (flags & CREATE_TIME_BIT) == CREATE_TIME_BIT; 268 } 269 270 /** 271 * Gets flags byte. The flags byte tells us which of the 272 * three datestamp fields are present in the data: 273 * <pre> 274 * bit0 - modify time 275 * bit1 - access time 276 * bit2 - create time 277 * </pre> 278 * Only first 3 bits of flags are used according to the 279 * latest version of the spec (December 2012). 280 * 281 * @return flags byte indicating which of the 282 * three datestamp fields are present. 283 */ 284 public byte getFlags() { return flags; } 285 286 /** 287 * Returns whether bit0 of the flags byte is set or not, 288 * which should correspond to the presence or absence of 289 * a modify timestamp in this particular zip entry. 290 * 291 * @return true if bit0 of the flags byte is set. 292 */ 293 public boolean isBit0_modifyTimePresent() { return bit0_modifyTimePresent; } 294 295 /** 296 * Returns whether bit1 of the flags byte is set or not, 297 * which should correspond to the presence or absence of 298 * a "last access" timestamp in this particular zip entry. 299 * 300 * @return true if bit1 of the flags byte is set. 301 */ 302 public boolean isBit1_accessTimePresent() { return bit1_accessTimePresent; } 303 304 /** 305 * Returns whether bit2 of the flags byte is set or not, 306 * which should correspond to the presence or absence of 307 * a create timestamp in this particular zip entry. 308 * 309 * @return true if bit2 of the flags byte is set. 310 */ 311 public boolean isBit2_createTimePresent() { return bit2_createTimePresent; } 312 313 /** 314 * Returns the modify time (seconds since epoch) of this zip entry 315 * as a ZipLong object, or null if no such timestamp exists in the 316 * zip entry. 317 * 318 * @return modify time (seconds since epoch) or null. 319 */ 320 public ZipLong getModifyTime() { return modifyTime; } 321 322 /** 323 * Returns the access time (seconds since epoch) of this zip entry 324 * as a ZipLong object, or null if no such timestamp exists in the 325 * zip entry. 326 * 327 * @return access time (seconds since epoch) or null. 328 */ 329 public ZipLong getAccessTime() { return accessTime; } 330 331 /** 332 * <p> 333 * Returns the create time (seconds since epoch) of this zip entry 334 * as a ZipLong object, or null if no such timestamp exists in the 335 * zip entry. 336 * </p><p> 337 * Note: modern linux file systems (e.g., ext2) 338 * do not appear to store a "create time" value, and so 339 * it's usually omitted altogether in the zip extra 340 * field. Perhaps other unix systems track this. 341 * 342 * @return create time (seconds since epoch) or null. 343 */ 344 public ZipLong getCreateTime() { return createTime; } 345 346 /** 347 * Returns the modify time as a java.util.Date 348 * of this zip entry, or null if no such timestamp exists in the zip entry. 349 * The milliseconds are always zeroed out, since the underlying data 350 * offers only per-second precision. 351 * 352 * @return modify time as java.util.Date or null. 353 */ 354 public Date getModifyJavaTime() { 355 return modifyTime != null ? new Date(modifyTime.getValue() * 1000) : null; 356 } 357 358 /** 359 * Returns the access time as a java.util.Date 360 * of this zip entry, or null if no such timestamp exists in the zip entry. 361 * The milliseconds are always zeroed out, since the underlying data 362 * offers only per-second precision. 363 * 364 * @return access time as java.util.Date or null. 365 */ 366 public Date getAccessJavaTime() { 367 return accessTime != null ? new Date(accessTime.getValue() * 1000) : null; 368 } 369 370 /** 371 * <p> 372 * Returns the create time as a a java.util.Date 373 * of this zip entry, or null if no such timestamp exists in the zip entry. 374 * The milliseconds are always zeroed out, since the underlying data 375 * offers only per-second precision. 376 * </p><p> 377 * Note: modern linux file systems (e.g., ext2) 378 * do not appear to store a "create time" value, and so 379 * it's usually omitted altogether in the zip extra 380 * field. Perhaps other unix systems track this. 381 * 382 * @return create time as java.util.Date or null. 383 */ 384 public Date getCreateJavaTime() { 385 return createTime != null ? new Date(createTime.getValue() * 1000) : null; 386 } 387 388 /** 389 * <p> 390 * Sets the modify time (seconds since epoch) of this zip entry 391 * using a ZipLong object. 392 * </p><p> 393 * Note: the setters for flags and timestamps are decoupled. 394 * Even if the timestamp is not-null, it will only be written 395 * out if the corresponding bit in the flags is also set. 396 * </p> 397 * 398 * @param l ZipLong of the modify time (seconds per epoch) 399 */ 400 public void setModifyTime(ZipLong l) { this.modifyTime = l; } 401 402 /** 403 * <p> 404 * Sets the access time (seconds since epoch) of this zip entry 405 * using a ZipLong object 406 * </p><p> 407 * Note: the setters for flags and timestamps are decoupled. 408 * Even if the timestamp is not-null, it will only be written 409 * out if the corresponding bit in the flags is also set. 410 * </p> 411 * 412 * @param l ZipLong of the access time (seconds per epoch) 413 */ 414 public void setAccessTime(ZipLong l) { this.accessTime = l; } 415 416 /** 417 * <p> 418 * Sets the create time (seconds since epoch) of this zip entry 419 * using a ZipLong object 420 * </p><p> 421 * Note: the setters for flags and timestamps are decoupled. 422 * Even if the timestamp is not-null, it will only be written 423 * out if the corresponding bit in the flags is also set. 424 * </p> 425 * 426 * @param l ZipLong of the create time (seconds per epoch) 427 */ 428 public void setCreateTime(ZipLong l) { this.createTime = l; } 429 430 /** 431 * <p> 432 * Sets the modify time as a java.util.Date 433 * of this zip entry. Supplied value is truncated to per-second 434 * precision (milliseconds zeroed-out). 435 * </p><p> 436 * Note: the setters for flags and timestamps are decoupled. 437 * Even if the timestamp is not-null, it will only be written 438 * out if the corresponding bit in the flags is also set. 439 * </p> 440 * 441 * @param d modify time as java.util.Date 442 */ 443 public void setModifyJavaTime(Date d) { setModifyTime(dateToZipLong(d)); } 444 445 /** 446 * <p> 447 * Sets the access time as a java.util.Date 448 * of this zip entry. Supplied value is truncated to per-second 449 * precision (milliseconds zeroed-out). 450 * </p><p> 451 * Note: the setters for flags and timestamps are decoupled. 452 * Even if the timestamp is not-null, it will only be written 453 * out if the corresponding bit in the flags is also set. 454 * </p> 455 * 456 * @param d access time as java.util.Date 457 */ 458 public void setAccessJavaTime(Date d) { setAccessTime(dateToZipLong(d)); } 459 460 /** 461 * <p> 462 * Sets the create time as a java.util.Date 463 * of this zip entry. Supplied value is truncated to per-second 464 * precision (milliseconds zeroed-out). 465 * </p><p> 466 * Note: the setters for flags and timestamps are decoupled. 467 * Even if the timestamp is not-null, it will only be written 468 * out if the corresponding bit in the flags is also set. 469 * </p> 470 * 471 * @param d create time as java.util.Date 472 */ 473 public void setCreateJavaTime(Date d) { setCreateTime(dateToZipLong(d)); } 474 475 /** 476 * Utility method converts java.util.Date (milliseconds since epoch) 477 * into a ZipLong (seconds since epoch). 478 * <p/> 479 * Also makes sure the converted ZipLong is not too big to fit 480 * in 32 unsigned bits. 481 * 482 * @param d java.util.Date to convert to ZipLong 483 * @return ZipLong 484 */ 485 private static ZipLong dateToZipLong(final Date d) { 486 if (d == null) { return null; } 487 488 final long TWO_TO_32 = 0x100000000L; 489 final long l = d.getTime() / 1000; 490 if (l >= TWO_TO_32) { 491 throw new IllegalArgumentException("Cannot set an X5455 timestamp larger than 2^32: " + l); 492 } 493 return new ZipLong(l); 494 } 495 496 /** 497 * Returns a String representation of this class useful for 498 * debugging purposes. 499 * 500 * @return A String representation of this class useful for 501 * debugging purposes. 502 */ 503 @Override 504 public String toString() { 505 StringBuilder buf = new StringBuilder(); 506 buf.append("0x5455 Zip Extra Field: Flags="); 507 buf.append(Integer.toBinaryString(ZipUtil.unsignedIntToSignedByte(flags))).append(" "); 508 if (bit0_modifyTimePresent && modifyTime != null) { 509 Date m = getModifyJavaTime(); 510 buf.append(" Modify:[").append(m).append("] "); 511 } 512 if (bit1_accessTimePresent && accessTime != null) { 513 Date a = getAccessJavaTime(); 514 buf.append(" Access:[").append(a).append("] "); 515 } 516 if (bit2_createTimePresent && createTime != null) { 517 Date c = getCreateJavaTime(); 518 buf.append(" Create:[").append(c).append("] "); 519 } 520 return buf.toString(); 521 } 522 523 @Override 524 public Object clone() throws CloneNotSupportedException { 525 return super.clone(); 526 } 527 528 @Override 529 public boolean equals(Object o) { 530 if (o instanceof X5455_ExtendedTimestamp) { 531 X5455_ExtendedTimestamp xf = (X5455_ExtendedTimestamp) o; 532 533 // The ZipLong==ZipLong clauses handle the cases where both are null. 534 // and only last 3 bits of flags matter. 535 return ((flags & 0x07) == (xf.flags & 0x07)) && 536 (modifyTime == xf.modifyTime || (modifyTime != null && modifyTime.equals(xf.modifyTime))) && 537 (accessTime == xf.accessTime || (accessTime != null && accessTime.equals(xf.accessTime))) && 538 (createTime == xf.createTime || (createTime != null && createTime.equals(xf.createTime))); 539 } else { 540 return false; 541 } 542 } 543 544 @Override 545 public int hashCode() { 546 int hc = (-123 * (flags & 0x07)); // only last 3 bits of flags matter 547 if (modifyTime != null) { 548 hc ^= modifyTime.hashCode(); 549 } 550 if (accessTime != null) { 551 // Since accessTime is often same as modifyTime, 552 // this prevents them from XOR negating each other. 553 hc ^= Integer.rotateLeft(accessTime.hashCode(), 11); 554 } 555 if (createTime != null) { 556 hc ^= Integer.rotateLeft(createTime.hashCode(), 22); 557 } 558 return hc; 559 } 560 561 }