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 8161942 27 * @summary Test ZOS and ZIS timestamp in extra field correctly 28 */ 29 30 import java.io.*; 31 import java.nio.file.Files; 32 import java.nio.file.Path; 33 import java.nio.file.Paths; 34 import java.nio.file.attribute.BasicFileAttributeView; 35 import java.nio.file.attribute.FileOwnerAttributeView; 36 import java.nio.file.attribute.FileTime; 37 import java.nio.file.attribute.PosixFilePermission; 38 import java.time.Instant; 39 import java.util.Arrays; 40 import java.util.Set; 41 import java.util.TimeZone; 42 import java.util.concurrent.TimeUnit; 43 import java.util.zip.ZipEntry; 44 import java.util.zip.ZipFile; 45 import java.util.zip.ZipInputStream; 46 import java.util.zip.ZipOutputStream; 47 48 49 50 public class TestExtraTime { 51 52 public static void main(String[] args) throws Throwable{ 53 54 File src = new File(System.getProperty("test.src", "."), "TestExtraTime.java"); 55 if (!src.exists()) { 56 return; 57 } 58 59 TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai"); 60 61 for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) { 62 63 // ms-dos 1980 epoch problem 64 test0(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra); 65 // negative epoch time 66 test0(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra); 67 68 long time = src.lastModified(); 69 test(FileTime.from(time, TimeUnit.MILLISECONDS), 70 FileTime.from(time + 300000, TimeUnit.MILLISECONDS), 71 FileTime.from(time - 300000, TimeUnit.MILLISECONDS), 72 tz, extra); 73 74 // now 75 time = Instant.now().toEpochMilli(); 76 test(FileTime.from(time, TimeUnit.MILLISECONDS), 77 FileTime.from(time + 300000, TimeUnit.MILLISECONDS), 78 FileTime.from(time - 300000, TimeUnit.MILLISECONDS), 79 tz, extra); 80 81 // unix 2038 82 time = 0x80000000L; 83 test(FileTime.from(time, TimeUnit.SECONDS), 84 FileTime.from(time, TimeUnit.SECONDS), 85 FileTime.from(time, TimeUnit.SECONDS), 86 tz, extra); 87 88 // mtime < unix 2038 89 time = 0x7fffffffL; 90 test(FileTime.from(time, TimeUnit.SECONDS), 91 FileTime.from(time + 30000, TimeUnit.SECONDS), 92 FileTime.from(time + 30000, TimeUnit.SECONDS), 93 tz, extra); 94 } 95 96 testNullHandling(); 97 testTagOnlyHandling(); 98 testTimeConversions(); 99 } 100 101 static void test(FileTime mtime, FileTime atime, FileTime ctime, 102 TimeZone tz, byte[] extra) throws Throwable { 103 test0(mtime, null, null, null, extra); 104 test0(mtime, null, null, tz, extra); // non-default tz 105 test0(mtime, atime, null, null, extra); 106 test0(mtime, null, ctime, null, extra); 107 test0(mtime, atime, ctime, null, extra); 108 test0(mtime, atime, null, tz, extra); 109 test0(mtime, null, ctime, tz, extra); 110 test0(mtime, atime, ctime, tz, extra); 111 } 112 113 static void test0(FileTime mtime, FileTime atime, FileTime ctime, 114 TimeZone tz, byte[] extra) throws Throwable { 115 System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n", 116 mtime, atime, ctime); 117 TimeZone tz0 = TimeZone.getDefault(); 118 if (tz != null) { 119 TimeZone.setDefault(tz); 120 } 121 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 122 ZipOutputStream zos = new ZipOutputStream(baos); 123 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 124 ze.setExtra(extra); 125 ze.setLastModifiedTime(mtime); 126 if (atime != null) 127 ze.setLastAccessTime(atime); 128 if (ctime != null) 129 ze.setCreationTime(ctime); 130 zos.putNextEntry(ze); 131 zos.write(new byte[] { 1,2 ,3, 4}); 132 133 // append an extra entry to help check if the length and data 134 // of the extra field are being correctly written (in previous 135 // entry). 136 if (extra != null) { 137 ze = new ZipEntry("TestExtraEntry"); 138 zos.putNextEntry(ze); 139 } 140 zos.close(); 141 if (tz != null) { 142 TimeZone.setDefault(tz0); 143 } 144 // ZipInputStream 145 ZipInputStream zis = new ZipInputStream( 146 new ByteArrayInputStream(baos.toByteArray())); 147 ze = zis.getNextEntry(); 148 zis.close(); 149 check(mtime, atime, ctime, ze, extra); 150 151 // ZipFile 152 Path zpath = Paths.get(System.getProperty("test.dir", "."), 153 "TestExtraTime.zip"); 154 Path zparent = zpath.getParent(); 155 if (zparent != null && !Files.isWritable(zparent)) { 156 System.err.format("zpath %s parent %s is not writable%n", 157 zpath, zparent); 158 } 159 if (Files.exists(zpath)) { 160 System.err.format("zpath %s already exists%n", zpath); 161 if (Files.isDirectory(zpath)) { 162 System.err.format("%n%s contents:%n", zpath); 163 Files.list(zpath).forEach(System.err::println); 164 } 165 FileOwnerAttributeView foav = Files.getFileAttributeView(zpath, 166 FileOwnerAttributeView.class); 167 System.err.format("zpath %s owner: %s%n", zpath, foav.getOwner()); 168 System.err.format("zpath %s permissions:%n", zpath); 169 Set<PosixFilePermission> perms = 170 Files.getPosixFilePermissions(zpath); 171 perms.stream().forEach(System.err::println); 172 } 173 if (Files.isSymbolicLink(zpath)) { 174 System.err.format("zpath %s is a symbolic link%n", zpath); 175 } 176 Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath); 177 try (ZipFile zf = new ZipFile(zpath.toFile())) { 178 ze = zf.getEntry("TestExtraTime.java"); 179 // ZipFile read entry from cen, which does not have a/ctime, 180 // for now. 181 check(mtime, null, null, ze, extra); 182 } finally { 183 Files.delete(zpath); 184 } 185 } 186 187 static void check(FileTime mtime, FileTime atime, FileTime ctime, 188 ZipEntry ze, byte[] extra) { 189 /* 190 System.out.printf(" mtime [%tc]: [%tc]/[%tc]%n", 191 mtime.to(TimeUnit.MILLISECONDS), 192 ze.getTime(), 193 ze.getLastModifiedTime().to(TimeUnit.MILLISECONDS)); 194 */ 195 if (mtime.to(TimeUnit.SECONDS) != 196 ze.getLastModifiedTime().to(TimeUnit.SECONDS)) 197 throw new RuntimeException("Timestamp: storing mtime failed!"); 198 if (atime != null && 199 atime.to(TimeUnit.SECONDS) != 200 ze.getLastAccessTime().to(TimeUnit.SECONDS)) 201 throw new RuntimeException("Timestamp: storing atime failed!"); 202 if (ctime != null && 203 ctime.to(TimeUnit.SECONDS) != 204 ze.getCreationTime().to(TimeUnit.SECONDS)) 205 throw new RuntimeException("Timestamp: storing ctime failed!"); 206 if (extra != null) { 207 // if extra data exists, the current implementation put it at 208 // the end of the extra data array (implementation detail) 209 byte[] extra1 = ze.getExtra(); 210 if (extra1 == null || extra1.length < extra.length || 211 !Arrays.equals(Arrays.copyOfRange(extra1, 212 extra1.length - extra.length, 213 extra1.length), 214 extra)) { 215 throw new RuntimeException("Timestamp: storing extra field failed!"); 216 } 217 } 218 } 219 220 static void testNullHandling() { 221 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 222 try { 223 ze.setLastAccessTime(null); 224 throw new RuntimeException("setLastAccessTime(null) should throw NPE"); 225 } catch (NullPointerException ignored) { 226 // pass 227 } 228 try { 229 ze.setCreationTime(null); 230 throw new RuntimeException("setCreationTime(null) should throw NPE"); 231 } catch (NullPointerException ignored) { 232 // pass 233 } 234 try { 235 ze.setLastModifiedTime(null); 236 throw new RuntimeException("setLastModifiedTime(null) should throw NPE"); 237 } catch (NullPointerException ignored) { 238 // pass 239 } 240 } 241 242 // verify that setting and getting any time is possible as per the intent 243 // of 4759491 244 static void testTimeConversions() { 245 // Sample across the entire range 246 long step = Long.MAX_VALUE / 100L; 247 testTimeConversions(Long.MIN_VALUE, Long.MAX_VALUE - step, step); 248 249 // Samples through the near future 250 long currentTime = System.currentTimeMillis(); 251 testTimeConversions(currentTime, currentTime + 1_000_000, 10_000); 252 } 253 254 static void testTimeConversions(long from, long to, long step) { 255 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 256 for (long time = from; time <= to; time += step) { 257 ze.setTime(time); 258 FileTime lastModifiedTime = ze.getLastModifiedTime(); 259 if (lastModifiedTime.toMillis() != time) { 260 throw new RuntimeException("setTime should make getLastModifiedTime " + 261 "return the specified instant: " + time + 262 " got: " + lastModifiedTime.toMillis()); 263 } 264 if (ze.getTime() != time) { 265 throw new RuntimeException("getTime after setTime, expected: " + 266 time + " got: " + ze.getTime()); 267 } 268 } 269 } 270 271 static void check(ZipEntry ze, byte[] extra) { 272 if (extra != null) { 273 byte[] extra1 = ze.getExtra(); 274 if (extra1 == null || extra1.length < extra.length || 275 !Arrays.equals(Arrays.copyOfRange(extra1, 276 extra1.length - extra.length, 277 extra1.length), 278 extra)) { 279 throw new RuntimeException("Timestamp: storing extra field failed!"); 280 } 281 } 282 } 283 284 static void testTagOnlyHandling() throws Throwable { 285 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 286 byte[] extra = new byte[] { 0x0a, 0, 4, 0, 0, 0, 0, 0 }; 287 try (ZipOutputStream zos = new ZipOutputStream(baos)) { 288 ZipEntry ze = new ZipEntry("TestExtraTime.java"); 289 ze.setExtra(extra); 290 zos.putNextEntry(ze); 291 zos.write(new byte[] { 1,2 ,3, 4}); 292 } 293 try (ZipInputStream zis = new ZipInputStream( 294 new ByteArrayInputStream(baos.toByteArray()))) { 295 ZipEntry ze = zis.getNextEntry(); 296 check(ze, extra); 297 } 298 Path zpath = Paths.get(System.getProperty("test.dir", "."), 299 "TestExtraTime.zip"); 300 Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath); 301 try (ZipFile zf = new ZipFile(zpath.toFile())) { 302 ZipEntry ze = zf.getEntry("TestExtraTime.java"); 303 check(ze, extra); 304 } finally { 305 Files.delete(zpath); 306 } 307 } 308 }