1 /* 2 * Copyright (c) 2009, 2019, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nio.zipfs; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 import java.nio.file.attribute.PosixFilePermission; 31 import java.time.DateTimeException; 32 import java.time.Instant; 33 import java.time.LocalDateTime; 34 import java.time.ZoneId; 35 import java.util.Arrays; 36 import java.util.Date; 37 import java.util.Set; 38 import java.util.concurrent.TimeUnit; 39 import java.util.regex.PatternSyntaxException; 40 41 /** 42 * @author Xueming Shen 43 */ 44 class ZipUtils { 45 46 /** 47 * The bit flag used to specify read permission by the owner. 48 */ 49 static final int POSIX_USER_READ = 0400; 50 51 /** 52 * The bit flag used to specify write permission by the owner. 53 */ 54 static final int POSIX_USER_WRITE = 0200; 55 56 /** 57 * The bit flag used to specify execute permission by the owner. 58 */ 59 static final int POSIX_USER_EXECUTE = 0100; 60 61 /** 62 * The bit flag used to specify read permission by the group. 63 */ 64 static final int POSIX_GROUP_READ = 040; 65 66 /** 67 * The bit flag used to specify write permission by the group. 68 */ 69 static final int POSIX_GROUP_WRITE = 020; 70 71 /** 72 * The bit flag used to specify execute permission by the group. 73 */ 74 static final int POSIX_GROUP_EXECUTE = 010; 75 76 /** 77 * The bit flag used to specify read permission by others. 78 */ 79 static final int POSIX_OTHER_READ = 04; 80 81 /** 82 * The bit flag used to specify write permission by others. 83 */ 84 static final int POSIX_OTHER_WRITE = 02; 85 86 /** 87 * The bit flag used to specify execute permission by others. 88 */ 89 static final int POSIX_OTHER_EXECUTE = 01; 90 91 /** 92 * Convert a {@link PosixFilePermission} object into the appropriate bit 93 * flag. 94 * 95 * @param perm The {@link PosixFilePermission} object. 96 * @return The bit flag as int. 97 */ 98 static int permToFlag(PosixFilePermission perm) { 99 switch(perm) { 100 case OWNER_READ: 101 return POSIX_USER_READ; 102 case OWNER_WRITE: 103 return POSIX_USER_WRITE; 104 case OWNER_EXECUTE: 105 return POSIX_USER_EXECUTE; 106 case GROUP_READ: 107 return POSIX_GROUP_READ; 108 case GROUP_WRITE: 109 return POSIX_GROUP_WRITE; 110 case GROUP_EXECUTE: 111 return POSIX_GROUP_EXECUTE; 112 case OTHERS_READ: 113 return POSIX_OTHER_READ; 114 case OTHERS_WRITE: 115 return POSIX_OTHER_WRITE; 116 case OTHERS_EXECUTE: 117 return POSIX_OTHER_EXECUTE; 118 default: 119 return 0; 120 } 121 } 122 123 /** 124 * Converts a set of {@link PosixFilePermission}s into an int value where 125 * the according bits are set. 126 * 127 * @param perms A Set of {@link PosixFilePermission} objects. 128 * 129 * @return A bit mask representing the input Set. 130 */ 131 static int permsToFlags(Set<PosixFilePermission> perms) { 132 if (perms == null) { 133 return -1; 134 } 135 int flags = 0; 136 for (PosixFilePermission perm : perms) { 137 flags |= permToFlag(perm); 138 } 139 return flags; 140 } 141 142 /* 143 * Writes a 16-bit short to the output stream in little-endian byte order. 144 */ 145 public static void writeShort(OutputStream os, int v) throws IOException { 146 os.write(v & 0xff); 147 os.write((v >>> 8) & 0xff); 148 } 149 150 /* 151 * Writes a 32-bit int to the output stream in little-endian byte order. 152 */ 153 public static void writeInt(OutputStream os, long v) throws IOException { 154 os.write((int)(v & 0xff)); 155 os.write((int)((v >>> 8) & 0xff)); 156 os.write((int)((v >>> 16) & 0xff)); 157 os.write((int)((v >>> 24) & 0xff)); 158 } 159 160 /* 161 * Writes a 64-bit int to the output stream in little-endian byte order. 162 */ 163 public static void writeLong(OutputStream os, long v) throws IOException { 164 os.write((int)(v & 0xff)); 165 os.write((int)((v >>> 8) & 0xff)); 166 os.write((int)((v >>> 16) & 0xff)); 167 os.write((int)((v >>> 24) & 0xff)); 168 os.write((int)((v >>> 32) & 0xff)); 169 os.write((int)((v >>> 40) & 0xff)); 170 os.write((int)((v >>> 48) & 0xff)); 171 os.write((int)((v >>> 56) & 0xff)); 172 } 173 174 /* 175 * Writes an array of bytes to the output stream. 176 */ 177 public static void writeBytes(OutputStream os, byte[] b) 178 throws IOException 179 { 180 os.write(b, 0, b.length); 181 } 182 183 /* 184 * Writes an array of bytes to the output stream. 185 */ 186 public static void writeBytes(OutputStream os, byte[] b, int off, int len) 187 throws IOException 188 { 189 os.write(b, off, len); 190 } 191 192 /* 193 * Append a slash at the end, if it does not have one yet 194 */ 195 public static byte[] toDirectoryPath(byte[] dir) { 196 if (dir.length != 0 && dir[dir.length - 1] != '/') { 197 dir = Arrays.copyOf(dir, dir.length + 1); 198 dir[dir.length - 1] = '/'; 199 } 200 return dir; 201 } 202 203 /* 204 * Converts DOS time to Java time (number of milliseconds since epoch). 205 */ 206 public static long dosToJavaTime(long dtime) { 207 int year = (int) (((dtime >> 25) & 0x7f) + 1980); 208 int month = (int) ((dtime >> 21) & 0x0f); 209 int day = (int) ((dtime >> 16) & 0x1f); 210 int hour = (int) ((dtime >> 11) & 0x1f); 211 int minute = (int) ((dtime >> 5) & 0x3f); 212 int second = (int) ((dtime << 1) & 0x3e); 213 214 if (month > 0 && month < 13 && day > 0 && hour < 24 && minute < 60 && second < 60) { 215 try { 216 LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second); 217 return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond( 218 ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS); 219 } catch (DateTimeException dte) { 220 // ignore 221 } 222 } 223 return overflowDosToJavaTime(year, month, day, hour, minute, second); 224 } 225 226 /* 227 * Deal with corner cases where an arguably mal-formed DOS time is used 228 */ 229 @SuppressWarnings("deprecation") // Use of Date constructor 230 private static long overflowDosToJavaTime(int year, int month, int day, 231 int hour, int minute, int second) { 232 return new Date(year - 1900, month - 1, day, hour, minute, second).getTime(); 233 } 234 235 /* 236 * Converts Java time to DOS time. 237 */ 238 public static long javaToDosTime(long time) { 239 Instant instant = Instant.ofEpochMilli(time); 240 LocalDateTime ldt = LocalDateTime.ofInstant( 241 instant, ZoneId.systemDefault()); 242 int year = ldt.getYear() - 1980; 243 if (year < 0) { 244 return (1 << 21) | (1 << 16); 245 } 246 return (year << 25 | 247 ldt.getMonthValue() << 21 | 248 ldt.getDayOfMonth() << 16 | 249 ldt.getHour() << 11 | 250 ldt.getMinute() << 5 | 251 ldt.getSecond() >> 1) & 0xffffffffL; 252 } 253 254 // used to adjust values between Windows and java epoch 255 private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L; 256 public static final long winToJavaTime(long wtime) { 257 return TimeUnit.MILLISECONDS.convert( 258 wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS); 259 } 260 261 public static final long javaToWinTime(long time) { 262 return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS) 263 - WINDOWS_EPOCH_IN_MICROSECONDS) * 10; 264 } 265 266 public static final long unixToJavaTime(long utime) { 267 return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS); 268 } 269 270 public static final long javaToUnixTime(long time) { 271 return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS); 272 } 273 274 private static final String regexMetaChars = ".^$+{[]|()"; 275 private static final String globMetaChars = "\\*?[{"; 276 private static boolean isRegexMeta(char c) { 277 return regexMetaChars.indexOf(c) != -1; 278 } 279 private static boolean isGlobMeta(char c) { 280 return globMetaChars.indexOf(c) != -1; 281 } 282 private static char EOL = 0; //TBD 283 private static char next(String glob, int i) { 284 if (i < glob.length()) { 285 return glob.charAt(i); 286 } 287 return EOL; 288 } 289 290 /* 291 * Creates a regex pattern from the given glob expression. 292 * 293 * @throws PatternSyntaxException 294 */ 295 public static String toRegexPattern(String globPattern) { 296 boolean inGroup = false; 297 StringBuilder regex = new StringBuilder("^"); 298 299 int i = 0; 300 while (i < globPattern.length()) { 301 char c = globPattern.charAt(i++); 302 switch (c) { 303 case '\\': 304 // escape special characters 305 if (i == globPattern.length()) { 306 throw new PatternSyntaxException("No character to escape", 307 globPattern, i - 1); 308 } 309 char next = globPattern.charAt(i++); 310 if (isGlobMeta(next) || isRegexMeta(next)) { 311 regex.append('\\'); 312 } 313 regex.append(next); 314 break; 315 case '/': 316 regex.append(c); 317 break; 318 case '[': 319 // don't match name separator in class 320 regex.append("[[^/]&&["); 321 if (next(globPattern, i) == '^') { 322 // escape the regex negation char if it appears 323 regex.append("\\^"); 324 i++; 325 } else { 326 // negation 327 if (next(globPattern, i) == '!') { 328 regex.append('^'); 329 i++; 330 } 331 // hyphen allowed at start 332 if (next(globPattern, i) == '-') { 333 regex.append('-'); 334 i++; 335 } 336 } 337 boolean hasRangeStart = false; 338 char last = 0; 339 while (i < globPattern.length()) { 340 c = globPattern.charAt(i++); 341 if (c == ']') { 342 break; 343 } 344 if (c == '/') { 345 throw new PatternSyntaxException("Explicit 'name separator' in class", 346 globPattern, i - 1); 347 } 348 // TBD: how to specify ']' in a class? 349 if (c == '\\' || c == '[' || 350 c == '&' && next(globPattern, i) == '&') { 351 // escape '\', '[' or "&&" for regex class 352 regex.append('\\'); 353 } 354 regex.append(c); 355 356 if (c == '-') { 357 if (!hasRangeStart) { 358 throw new PatternSyntaxException("Invalid range", 359 globPattern, i - 1); 360 } 361 if ((c = next(globPattern, i++)) == EOL || c == ']') { 362 break; 363 } 364 if (c < last) { 365 throw new PatternSyntaxException("Invalid range", 366 globPattern, i - 3); 367 } 368 regex.append(c); 369 hasRangeStart = false; 370 } else { 371 hasRangeStart = true; 372 last = c; 373 } 374 } 375 if (c != ']') { 376 throw new PatternSyntaxException("Missing ']", globPattern, i - 1); 377 } 378 regex.append("]]"); 379 break; 380 case '{': 381 if (inGroup) { 382 throw new PatternSyntaxException("Cannot nest groups", 383 globPattern, i - 1); 384 } 385 regex.append("(?:(?:"); 386 inGroup = true; 387 break; 388 case '}': 389 if (inGroup) { 390 regex.append("))"); 391 inGroup = false; 392 } else { 393 regex.append('}'); 394 } 395 break; 396 case ',': 397 if (inGroup) { 398 regex.append(")|(?:"); 399 } else { 400 regex.append(','); 401 } 402 break; 403 case '*': 404 if (next(globPattern, i) == '*') { 405 // crosses directory boundaries 406 regex.append(".*"); 407 i++; 408 } else { 409 // within directory boundary 410 regex.append("[^/]*"); 411 } 412 break; 413 case '?': 414 regex.append("[^/]"); 415 break; 416 default: 417 if (isRegexMeta(c)) { 418 regex.append('\\'); 419 } 420 regex.append(c); 421 } 422 } 423 if (inGroup) { 424 throw new PatternSyntaxException("Missing '}", globPattern, i - 1); 425 } 426 return regex.append('$').toString(); 427 } 428 }