1 /* 2 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641 8075526 8130914 27 * 8161942 8206389 28 * @summary Test ZOS and ZIS timestamp in extra field correctly 29 */ 30 31 import java.io.*; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.Paths; 35 import java.nio.file.attribute.BasicFileAttributeView; 36 import java.nio.file.attribute.FileOwnerAttributeView; 37 import java.nio.file.attribute.FileTime; 38 import java.nio.file.attribute.PosixFilePermission; 39 import java.time.Instant; 40 import java.util.Arrays; 41 import java.util.Set; 42 import java.util.TimeZone; 43 import java.util.concurrent.TimeUnit; 44 import java.util.zip.ZipEntry; 45 import java.util.zip.ZipFile; 46 import java.util.zip.ZipInputStream; 47 import java.util.zip.ZipOutputStream; 48 49 50 51 public class TestExtraTime { 52 53 public static void main(String[] args) throws Throwable{ 54 55 File src = new File(System.getProperty("test.src", "."), "TestExtraTime.java"); 56 if (!src.exists()) { 57 return; 58 } 59 60 TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai"); 61 62 for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) { 63 64 // ms-dos 1980 epoch problem 65 test0(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra); 66 // negative epoch time 67 test0(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra); 68 69 long time = src.lastModified(); 70 test(FileTime.from(time, TimeUnit.MILLISECONDS), 71 FileTime.from(time + 300000, TimeUnit.MILLISECONDS), 72 FileTime.from(time - 300000, TimeUnit.MILLISECONDS), 73 tz, extra); 74 75 // now 76 time = Instant.now().toEpochMilli(); 77 test(FileTime.from(time, TimeUnit.MILLISECONDS), 78 FileTime.from(time + 300000, TimeUnit.MILLISECONDS), 79 FileTime.from(time - 300000, TimeUnit.MILLISECONDS), 80 tz, extra); 81 82 // unix 2038 83 time = 0x80000000L; 84 test(FileTime.from(time, TimeUnit.SECONDS), 85 FileTime.from(time, TimeUnit.SECONDS), 86 FileTime.from(time, TimeUnit.SECONDS), 87 tz, extra); 88 89 // mtime < unix 2038 90 time = 0x7fffffffL; 91 test(FileTime.from(time, TimeUnit.SECONDS), 92 FileTime.from(time + 30000, TimeUnit.SECONDS), 93 FileTime.from(time + 30000, TimeUnit.SECONDS), 94 tz, extra); 95 } 96 97 testNullHandling(); 98 testTagOnlyHandling(); 99 testTimeConversions(); 100 testNullMtime(); 101 } 102 103 static void test(FileTime mtime, FileTime atime, FileTime ctime, 104 TimeZone tz, byte[] extra) throws Throwable { 105 test0(mtime, null, null, null, extra); 106 test0(mtime, null, null, tz, extra); // non-default tz 107 test0(mtime, atime, null, null, extra); 108 test0(mtime, null, ctime, null, extra); 109 test0(mtime, atime, ctime, null, extra); 110 test0(mtime, atime, null, tz, extra); 111 test0(mtime, null, ctime, tz, extra); 112 test0(mtime, atime, ctime, tz, extra); 113 } 114 115 static void test0(FileTime mtime, FileTime atime, FileTime ctime, 116 TimeZone tz, byte[] extra) throws Throwable { 117 System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n", 118 mtime, atime, ctime); 119 TimeZone tz0 = TimeZone.getDefault(); 120 if (tz != null) { 121 TimeZone.setDefault(tz); 122 } 123 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 124 ZipOutputStream zos = new ZipOutputStream(baos); 125 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 126 ze.setExtra(extra); 127 ze.setLastModifiedTime(mtime); 128 if (atime != null) 129 ze.setLastAccessTime(atime); 130 if (ctime != null) 131 ze.setCreationTime(ctime); 132 zos.putNextEntry(ze); 133 zos.write(new byte[] { 1,2 ,3, 4}); 134 135 // append an extra entry to help check if the length and data 136 // of the extra field are being correctly written (in previous 137 // entry). 138 if (extra != null) { 139 ze = new ZipEntry("TestExtraEntry"); 140 zos.putNextEntry(ze); 141 } 142 zos.close(); 143 if (tz != null) { 144 TimeZone.setDefault(tz0); 145 } 146 // ZipInputStream 147 ZipInputStream zis = new ZipInputStream( 148 new ByteArrayInputStream(baos.toByteArray())); 149 ze = zis.getNextEntry(); 150 zis.close(); 151 check(mtime, atime, ctime, ze, extra); 152 153 // ZipFile 154 Path zpath = Paths.get(System.getProperty("test.dir", "."), 155 "TestExtraTime.zip"); 156 Path zparent = zpath.getParent(); 157 if (zparent != null && !Files.isWritable(zparent)) { 158 System.err.format("zpath %s parent %s is not writable%n", 159 zpath, zparent); 160 } 161 if (Files.exists(zpath)) { 162 System.err.format("zpath %s already exists%n", zpath); 163 if (Files.isDirectory(zpath)) { 164 System.err.format("%n%s contents:%n", zpath); 165 Files.list(zpath).forEach(System.err::println); 166 } 167 FileOwnerAttributeView foav = Files.getFileAttributeView(zpath, 168 FileOwnerAttributeView.class); 169 System.err.format("zpath %s owner: %s%n", zpath, foav.getOwner()); 170 System.err.format("zpath %s permissions:%n", zpath); 171 Set<PosixFilePermission> perms = 172 Files.getPosixFilePermissions(zpath); 173 perms.stream().forEach(System.err::println); 174 } 175 if (Files.isSymbolicLink(zpath)) { 176 System.err.format("zpath %s is a symbolic link%n", zpath); 177 } 178 Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath); 179 try (ZipFile zf = new ZipFile(zpath.toFile())) { 180 ze = zf.getEntry("TestExtraTime.java"); 181 // ZipFile read entry from cen, which does not have a/ctime, 182 // for now. 183 check(mtime, null, null, ze, extra); 184 } finally { 185 Files.delete(zpath); 186 } 187 } 188 189 static void check(FileTime mtime, FileTime atime, FileTime ctime, 190 ZipEntry ze, byte[] extra) { 191 /* 192 System.out.printf(" mtime [%tc]: [%tc]/[%tc]%n", 193 mtime.to(TimeUnit.MILLISECONDS), 194 ze.getTime(), 195 ze.getLastModifiedTime().to(TimeUnit.MILLISECONDS)); 196 */ 197 if (mtime != null && 198 mtime.to(TimeUnit.SECONDS) != 199 ze.getLastModifiedTime().to(TimeUnit.SECONDS)) { 200 throw new RuntimeException("Timestamp: storing mtime failed!"); 201 } 202 if (atime != null && 203 atime.to(TimeUnit.SECONDS) != 204 ze.getLastAccessTime().to(TimeUnit.SECONDS)) 205 throw new RuntimeException("Timestamp: storing atime failed!"); 206 if (ctime != null && 207 ctime.to(TimeUnit.SECONDS) != 208 ze.getCreationTime().to(TimeUnit.SECONDS)) 209 throw new RuntimeException("Timestamp: storing ctime failed!"); 210 if (extra != null) { 211 // if extra data exists, the current implementation put it at 212 // the end of the extra data array (implementation detail) 213 byte[] extra1 = ze.getExtra(); 214 if (extra1 == null || extra1.length < extra.length || 215 !Arrays.equals(Arrays.copyOfRange(extra1, 216 extra1.length - extra.length, 217 extra1.length), 218 extra)) { 219 throw new RuntimeException("Timestamp: storing extra field failed!"); 220 } 221 } 222 } 223 224 static void testNullHandling() { 225 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 226 try { 227 ze.setLastAccessTime(null); 228 throw new RuntimeException("setLastAccessTime(null) should throw NPE"); 229 } catch (NullPointerException ignored) { 230 // pass 231 } 232 try { 233 ze.setCreationTime(null); 234 throw new RuntimeException("setCreationTime(null) should throw NPE"); 235 } catch (NullPointerException ignored) { 236 // pass 237 } 238 try { 239 ze.setLastModifiedTime(null); 240 throw new RuntimeException("setLastModifiedTime(null) should throw NPE"); 241 } catch (NullPointerException ignored) { 242 // pass 243 } 244 } 245 246 // verify that setting and getting any time is possible as per the intent 247 // of 4759491 248 static void testTimeConversions() { 249 // Sample across the entire range 250 long step = Long.MAX_VALUE / 100L; 251 testTimeConversions(Long.MIN_VALUE, Long.MAX_VALUE - step, step); 252 253 // Samples through the near future 254 long currentTime = System.currentTimeMillis(); 255 testTimeConversions(currentTime, currentTime + 1_000_000, 10_000); 256 } 257 258 static void testTimeConversions(long from, long to, long step) { 259 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 260 for (long time = from; time <= to; time += step) { 261 ze.setTime(time); 262 FileTime lastModifiedTime = ze.getLastModifiedTime(); 263 if (lastModifiedTime.toMillis() != time) { 264 throw new RuntimeException("setTime should make getLastModifiedTime " + 265 "return the specified instant: " + time + 266 " got: " + lastModifiedTime.toMillis()); 267 } 268 if (ze.getTime() != time) { 269 throw new RuntimeException("getTime after setTime, expected: " + 270 time + " got: " + ze.getTime()); 271 } 272 } 273 } 274 275 static void check(ZipEntry ze, byte[] extra) { 276 if (extra != null) { 277 byte[] extra1 = ze.getExtra(); 278 if (extra1 == null || extra1.length < extra.length || 279 !Arrays.equals(Arrays.copyOfRange(extra1, 280 extra1.length - extra.length, 281 extra1.length), 282 extra)) { 283 throw new RuntimeException("Timestamp: storing extra field failed!"); 284 } 285 } 286 } 287 288 static void testTagOnlyHandling() throws Throwable { 289 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 290 byte[] extra = new byte[] { 0x0a, 0, 4, 0, 0, 0, 0, 0 }; 291 try (ZipOutputStream zos = new ZipOutputStream(baos)) { 292 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 293 ze.setExtra(extra); 294 zos.putNextEntry(ze); 295 zos.write(new byte[] { 1,2 ,3, 4}); 296 } 297 try (ZipInputStream zis = new ZipInputStream( 298 new ByteArrayInputStream(baos.toByteArray()))) { 299 ZipEntry ze = zis.getNextEntry(); 300 check(ze, extra); 301 } 302 Path zpath = Paths.get(System.getProperty("test.dir", "."), 303 "TestExtraTime.zip"); 304 Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath); 305 try (ZipFile zf = new ZipFile(zpath.toFile())) { 306 ZipEntry ze = zf.getEntry("TestExtraTime.java"); 307 check(ze, extra); 308 } finally { 309 Files.delete(zpath); 310 } 311 } 312 313 static void checkLastModifiedTimeDOS(FileTime mtime, ZipEntry ze) { 314 FileTime lmt = ze.getLastModifiedTime(); 315 if ((lmt.to(TimeUnit.SECONDS) >>> 1) != (mtime.to(TimeUnit.SECONDS) >>> 1) || 316 lmt.to(TimeUnit.MILLISECONDS) != ze.getTime() || 317 lmt.to(TimeUnit.MILLISECONDS) % 1000 != 0) { 318 throw new RuntimeException("Timestamp: storing mtime in dos format failed!"); 319 } 320 } 321 322 static void testNullMtime() throws Throwable { 323 Instant now = Instant.now(); 324 FileTime ctime = FileTime.from(now); 325 FileTime atime = FileTime.from(now.plusSeconds(7)); 326 FileTime mtime = FileTime.from(now.plusSeconds(13)); 327 System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n", 328 mtime, atime, ctime); 329 330 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 331 try (ZipOutputStream zos = new ZipOutputStream(baos)) { 332 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 333 ze.setCreationTime(ctime); 334 ze.setLastAccessTime(atime); 335 // ze.setLastModifiedTime(now); 336 ze.setTime(mtime.toMillis()); 337 zos.putNextEntry(ze); 338 zos.write(new byte[] { 1,2 ,3, 4}); 339 } 340 341 try (ZipInputStream zis = new ZipInputStream( 342 new ByteArrayInputStream(baos.toByteArray()))) { 343 ZipEntry ze = zis.getNextEntry(); 344 // check LOC 345 check(null, atime, ctime, ze, null); 346 checkLastModifiedTimeDOS(mtime, ze); 347 } 348 349 Path zpath = Paths.get(System.getProperty("test.dir", "."), 350 "TestExtraTime.zip"); 351 Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath); 352 try (ZipFile zf = new ZipFile(zpath.toFile())) { 353 ZipEntry ze = zf.getEntry("TestExtraTime.java"); 354 // check CEN 355 checkLastModifiedTimeDOS(mtime, ze); 356 } finally { 357 Files.delete(zpath); 358 } 359 } 360 }