1 /* 2 * Copyright (c) 2015, 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 package jdk.internal.jrtfs; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.OutputStream; 31 import java.nio.ByteBuffer; 32 import java.nio.channels.Channels; 33 import java.nio.channels.FileChannel; 34 import java.nio.channels.NonWritableChannelException; 35 import java.nio.channels.ReadableByteChannel; 36 import java.nio.channels.SeekableByteChannel; 37 import java.nio.charset.Charset; 38 import java.nio.file.ClosedFileSystemException; 39 import java.nio.file.CopyOption; 40 import java.nio.file.FileStore; 41 import java.nio.file.FileSystem; 42 import java.nio.file.FileSystemNotFoundException; 43 import java.nio.file.Files; 44 import java.nio.file.LinkOption; 45 import java.nio.file.OpenOption; 46 import java.nio.file.Path; 47 import java.nio.file.PathMatcher; 48 import java.nio.file.ReadOnlyFileSystemException; 49 import java.nio.file.StandardOpenOption; 50 import java.nio.file.WatchService; 51 import java.nio.file.attribute.FileAttribute; 52 import java.nio.file.attribute.FileTime; 53 import java.nio.file.attribute.UserPrincipalLookupService; 54 import java.nio.file.spi.FileSystemProvider; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.Collections; 58 import java.util.HashSet; 59 import java.util.Iterator; 60 import java.util.Map; 61 import java.util.Set; 62 import java.util.regex.Pattern; 63 64 /** 65 * Base class for jrt file systems. jrt filesystem implementations are currently 66 * available on top of .jimage file and on top "exploded" build directories. 67 */ 68 abstract class AbstractJrtFileSystem extends FileSystem { 69 70 private final JrtFileSystemProvider provider; 71 72 AbstractJrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> options) { 73 this.provider = provider; 74 } 75 76 private static final Charset UTF_8 = Charset.forName("UTF-8"); 77 78 // static utility methods 79 static ReadOnlyFileSystemException readOnly() { 80 return new ReadOnlyFileSystemException(); 81 } 82 83 // if a Path does not exist, throw exception 84 static void checkExists(Path path) { 85 if (Files.notExists(path)) { 86 throw new FileSystemNotFoundException(path.toString()); 87 } 88 } 89 90 static byte[] getBytes(String name) { 91 return name.getBytes(UTF_8); 92 } 93 94 static String getString(byte[] name) { 95 return new String(name, UTF_8); 96 } 97 98 // do the supplied options imply that we have to chase symlinks? 99 static boolean followLinks(LinkOption... options) { 100 if (options != null) { 101 for (LinkOption lo : options) { 102 if (lo == LinkOption.NOFOLLOW_LINKS) { 103 return false; 104 } else if (lo == null) { 105 throw new NullPointerException(); 106 } else { 107 throw new AssertionError("should not reach here"); 108 } 109 } 110 } 111 return true; 112 } 113 114 // check that the options passed are supported by (read-only) jrt file system 115 static void checkOptions(Set<? extends OpenOption> options) { 116 // check for options of null type and option is an intance of StandardOpenOption 117 for (OpenOption option : options) { 118 if (option == null) { 119 throw new NullPointerException(); 120 } 121 if (!(option instanceof StandardOpenOption)) { 122 throw new IllegalArgumentException(); 123 } 124 } 125 126 if (options.contains(StandardOpenOption.WRITE) 127 || options.contains(StandardOpenOption.APPEND)) { 128 throw readOnly(); 129 } 130 } 131 132 // FileSystem method implementations 133 @Override 134 public FileSystemProvider provider() { 135 return provider; 136 } 137 138 @Override 139 public Iterable<Path> getRootDirectories() { 140 ArrayList<Path> pathArr = new ArrayList<>(); 141 pathArr.add(getRootPath()); 142 return pathArr; 143 } 144 145 @Override 146 public AbstractJrtPath getPath(String first, String... more) { 147 String path; 148 if (more.length == 0) { 149 path = first; 150 } else { 151 StringBuilder sb = new StringBuilder(); 152 sb.append(first); 153 for (String segment : more) { 154 if (segment.length() > 0) { 155 if (sb.length() > 0) { 156 sb.append('/'); 157 } 158 sb.append(segment); 159 } 160 } 161 path = sb.toString(); 162 } 163 return getRootPath().newJrtPath(getBytes(path)); 164 } 165 166 @Override 167 public final boolean isReadOnly() { 168 return true; 169 } 170 171 @Override 172 public final UserPrincipalLookupService getUserPrincipalLookupService() { 173 throw new UnsupportedOperationException(); 174 } 175 176 @Override 177 public final WatchService newWatchService() { 178 throw new UnsupportedOperationException(); 179 } 180 181 @Override 182 public final Iterable<FileStore> getFileStores() { 183 ArrayList<FileStore> list = new ArrayList<>(1); 184 list.add(getFileStore(getRootPath())); 185 return list; 186 } 187 188 private static final Set<String> supportedFileAttributeViews 189 = Collections.unmodifiableSet( 190 new HashSet<String>(Arrays.asList("basic", "jrt"))); 191 192 @Override 193 public final Set<String> supportedFileAttributeViews() { 194 return supportedFileAttributeViews; 195 } 196 197 @Override 198 public final String toString() { 199 return "jrt:/"; 200 } 201 202 @Override 203 public final String getSeparator() { 204 return "/"; 205 } 206 207 private static final String GLOB_SYNTAX = "glob"; 208 private static final String REGEX_SYNTAX = "regex"; 209 210 @Override 211 public PathMatcher getPathMatcher(String syntaxAndInput) { 212 int pos = syntaxAndInput.indexOf(':'); 213 if (pos <= 0 || pos == syntaxAndInput.length()) { 214 throw new IllegalArgumentException(); 215 } 216 String syntax = syntaxAndInput.substring(0, pos); 217 String input = syntaxAndInput.substring(pos + 1); 218 String expr; 219 if (syntax.equalsIgnoreCase(GLOB_SYNTAX)) { 220 expr = JrtUtils.toRegexPattern(input); 221 } else { 222 if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) { 223 expr = input; 224 } else { 225 throw new UnsupportedOperationException("Syntax '" + syntax 226 + "' not recognized"); 227 } 228 } 229 // return matcher 230 final Pattern pattern = Pattern.compile(expr); 231 return (Path path) -> pattern.matcher(path.toString()).matches(); 232 } 233 234 // These methods throw read only file system exception 235 final void setTimes(AbstractJrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime) 236 throws IOException { 237 throw readOnly(); 238 } 239 240 final void createDirectory(AbstractJrtPath jrtPath, FileAttribute<?>... attrs) throws IOException { 241 throw readOnly(); 242 } 243 244 final void deleteFile(AbstractJrtPath jrtPath, boolean failIfNotExists) 245 throws IOException { 246 throw readOnly(); 247 } 248 249 final OutputStream newOutputStream(AbstractJrtPath jrtPath, OpenOption... options) 250 throws IOException { 251 throw readOnly(); 252 } 253 254 final void copyFile(boolean deletesrc, AbstractJrtPath srcPath, AbstractJrtPath dstPath, CopyOption... options) 255 throws IOException { 256 throw readOnly(); 257 } 258 259 final FileChannel newFileChannel(AbstractJrtPath jrtPath, 260 Set<? extends OpenOption> options, 261 FileAttribute<?>... attrs) 262 throws IOException { 263 throw new UnsupportedOperationException("newFileChannel"); 264 } 265 266 final InputStream newInputStream(AbstractJrtPath jrtPath) throws IOException { 267 return new ByteArrayInputStream(getFileContent(jrtPath)); 268 } 269 270 final SeekableByteChannel newByteChannel(AbstractJrtPath jrtPath, 271 Set<? extends OpenOption> options, 272 FileAttribute<?>... attrs) 273 throws IOException { 274 checkOptions(options); 275 276 byte[] buf = getFileContent(jrtPath); 277 final ReadableByteChannel rbc 278 = Channels.newChannel(new ByteArrayInputStream(buf)); 279 final long size = buf.length; 280 return new SeekableByteChannel() { 281 long read = 0; 282 283 @Override 284 public boolean isOpen() { 285 return rbc.isOpen(); 286 } 287 288 @Override 289 public long position() throws IOException { 290 return read; 291 } 292 293 @Override 294 public SeekableByteChannel position(long pos) 295 throws IOException { 296 throw new UnsupportedOperationException(); 297 } 298 299 @Override 300 public int read(ByteBuffer dst) throws IOException { 301 int n = rbc.read(dst); 302 if (n > 0) { 303 read += n; 304 } 305 return n; 306 } 307 308 @Override 309 public SeekableByteChannel truncate(long size) 310 throws IOException { 311 throw new NonWritableChannelException(); 312 } 313 314 @Override 315 public int write(ByteBuffer src) throws IOException { 316 throw new NonWritableChannelException(); 317 } 318 319 @Override 320 public long size() throws IOException { 321 return size; 322 } 323 324 @Override 325 public void close() throws IOException { 326 rbc.close(); 327 } 328 }; 329 } 330 331 final JrtFileStore getFileStore(AbstractJrtPath jrtPath) { 332 return new JrtFileStore(jrtPath); 333 } 334 335 final void ensureOpen() throws IOException { 336 if (!isOpen()) { 337 throw new ClosedFileSystemException(); 338 } 339 } 340 341 // abstract methods to be implemented by a particular jrt file system 342 abstract AbstractJrtPath getRootPath(); 343 344 abstract boolean isSameFile(AbstractJrtPath jrtPath1, AbstractJrtPath jrtPath2) throws IOException; 345 346 abstract boolean isLink(AbstractJrtPath jrtPath) throws IOException; 347 348 abstract AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException; 349 350 abstract AbstractJrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options) throws IOException; 351 352 abstract boolean exists(AbstractJrtPath jrtPath) throws IOException; 353 354 abstract boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks) throws IOException; 355 356 /** 357 * returns the list of child paths of the given directory "path" 358 * 359 * @param path name of the directory whose content is listed 360 * @return iterator for child paths of the given directory path 361 */ 362 abstract Iterator<Path> iteratorOf(AbstractJrtPath jrtPath) throws IOException; 363 364 // returns the content of the file resource specified by the path 365 abstract byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException; 366 }