1 /* 2 * Copyright (c) 2013, 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 import java.io.IOException; 25 import java.net.URI; 26 import java.nio.channels.SeekableByteChannel; 27 import java.nio.file.AccessMode; 28 import java.nio.file.CopyOption; 29 import java.nio.file.DirectoryIteratorException; 30 import java.nio.file.DirectoryStream; 31 import java.nio.file.FileStore; 32 import java.nio.file.FileSystem; 33 import java.nio.file.FileSystemAlreadyExistsException; 34 import java.nio.file.FileSystemNotFoundException; 35 import java.nio.file.Files; 36 import java.nio.file.LinkOption; 37 import java.nio.file.NoSuchFileException; 38 import java.nio.file.OpenOption; 39 import java.nio.file.Path; 40 import java.nio.file.PathMatcher; 41 import java.nio.file.WatchService; 42 import java.nio.file.attribute.BasicFileAttributes; 43 import java.nio.file.attribute.FileAttribute; 44 import java.nio.file.attribute.FileAttributeView; 45 import java.nio.file.attribute.UserPrincipalLookupService; 46 import java.nio.file.spi.FileSystemProvider; 47 import java.util.Iterator; 48 import java.util.Map; 49 import java.util.NoSuchElementException; 50 import java.util.Set; 51 import java.util.function.Supplier; 52 53 /** 54 * A {@code FileSystem} that helps testing by trigger exception throwing based on filenames. 55 */ 56 class FaultyFileSystem extends FileSystem { 57 final Path root; 58 final boolean removeRootAfterClose; 59 final FileSystem delegate; 60 boolean isOpen; 61 62 FaultyFileSystem(Path root) throws IOException { 63 if (root == null) { 64 root = Files.createTempDirectory("faultyFS"); 65 removeRootAfterClose = true; 66 } else { 67 if (! Files.isDirectory(root)) { 68 throw new IllegalArgumentException("must be a directory."); 69 } 70 removeRootAfterClose = false; 71 } 72 this.root = root; 73 delegate = root.getFileSystem(); 74 isOpen = true; 75 } 76 77 private static Path unwrap(Path p) { 78 return PassThroughFileSystem.unwrap(p); 79 } 80 81 Path getRoot() { 82 return new PassThroughFileSystem.PassThroughPath(this, root); 83 } 84 85 @Override 86 public void close() throws IOException { 87 if (isOpen) { 88 if (removeRootAfterClose) { 89 TestUtil.removeAll(root); 90 } 91 isOpen = false; 92 } 93 } 94 95 @Override 96 public FileSystemProvider provider() { 97 return FaultyFSProvider.getInstance(); 98 } 99 100 @Override 101 public boolean isOpen() { 102 return isOpen; 103 } 104 105 @Override 106 public boolean isReadOnly() { 107 return delegate.isReadOnly(); 108 } 109 110 @Override 111 public String getSeparator() { 112 return delegate.getSeparator(); 113 } 114 115 private <T> Iterable<T> SoleIterable(final T element) { 116 return new Iterable<T>() { 117 @Override 118 public Iterator<T> iterator() { 119 return new Iterator<T>() { 120 private T soleElement = element; 121 122 @Override 123 public boolean hasNext() { 124 return soleElement != null; 125 } 126 127 @Override 128 public T next() { 129 try { 130 return soleElement; 131 } finally { 132 soleElement = null; 133 } 134 } 135 }; 136 } 137 }; 138 } 139 140 @Override 141 public Iterable<Path> getRootDirectories() { 142 return SoleIterable(getRoot()); 143 } 144 145 @Override 146 public Iterable<FileStore> getFileStores() { 147 FileStore store; 148 try { 149 store = Files.getFileStore(root); 150 } catch (IOException ioe) { 151 store = null; 152 } 153 return SoleIterable(store); 154 } 155 156 @Override 157 public Set<String> supportedFileAttributeViews() { 158 // assume that unwrapped objects aren't exposed 159 return delegate.supportedFileAttributeViews(); 160 } 161 162 @Override 163 public Path getPath(String first, String... more) { 164 return new PassThroughFileSystem.PassThroughPath(this, delegate.getPath(first, more)); 165 } 166 167 @Override 168 public PathMatcher getPathMatcher(String syntaxAndPattern) { 169 final PathMatcher matcher = delegate.getPathMatcher(syntaxAndPattern); 170 return new PathMatcher() { 171 @Override 172 public boolean matches(Path path) { 173 return matcher.matches(unwrap(path)); 174 } 175 }; 176 } 177 178 @Override 179 public UserPrincipalLookupService getUserPrincipalLookupService() { 180 // assume that unwrapped objects aren't exposed 181 return delegate.getUserPrincipalLookupService(); 182 } 183 184 @Override 185 public WatchService newWatchService() throws IOException { 186 // to keep it simple 187 throw new UnsupportedOperationException(); 188 } 189 190 static class FaultyException extends IOException { 191 FaultyException() { 192 super("fault triggered."); 193 } 194 } 195 196 static class FaultyFSProvider extends FileSystemProvider { 197 private static final String SCHEME = "faulty"; 198 private static volatile FaultyFileSystem delegate; 199 private static FaultyFSProvider INSTANCE = new FaultyFSProvider(); 200 private boolean enabled; 201 202 private FaultyFSProvider() {} 203 204 public static FaultyFSProvider getInstance() { 205 return INSTANCE; 206 } 207 208 public void setFaultyMode(boolean enable) { 209 enabled = enable; 210 } 211 212 private void triggerEx(String filename, String... names) throws IOException { 213 if (! enabled) { 214 return; 215 } 216 217 if (filename.equals("SecurityException")) { 218 throw new SecurityException("FaultyFS", new FaultyException()); 219 } 220 221 if (filename.equals("IOException")) { 222 throw new FaultyException(); 223 } 224 225 for (String name: names) { 226 if (name.equals(filename)) { 227 throw new FaultyException(); 228 } 229 } 230 } 231 232 private void triggerEx(Path path, String... names) throws IOException { 233 triggerEx(path.getFileName().toString(), names); 234 } 235 236 @Override 237 public String getScheme() { 238 return SCHEME; 239 } 240 241 private void checkScheme(URI uri) { 242 if (!uri.getScheme().equalsIgnoreCase(SCHEME)) 243 throw new IllegalArgumentException(); 244 } 245 246 private void checkUri(URI uri) { 247 checkScheme(uri); 248 if (!uri.getSchemeSpecificPart().equals("///")) 249 throw new IllegalArgumentException(); 250 } 251 252 @Override 253 public FileSystem newFileSystem(Path fakeRoot, Map<String,?> env) 254 throws IOException 255 { 256 if (env != null && env.keySet().contains("IOException")) { 257 triggerEx("IOException"); 258 } 259 260 synchronized (FaultyFSProvider.class) { 261 if (delegate != null && delegate.isOpen()) 262 throw new FileSystemAlreadyExistsException(); 263 FaultyFileSystem result = new FaultyFileSystem(fakeRoot); 264 delegate = result; 265 return result; 266 } 267 } 268 269 @Override 270 public FileSystem newFileSystem(URI uri, Map<String,?> env) 271 throws IOException 272 { 273 if (env != null && env.keySet().contains("IOException")) { 274 triggerEx("IOException"); 275 } 276 277 checkUri(uri); 278 synchronized (FaultyFSProvider.class) { 279 if (delegate != null && delegate.isOpen()) 280 throw new FileSystemAlreadyExistsException(); 281 FaultyFileSystem result = new FaultyFileSystem(null); 282 delegate = result; 283 return result; 284 } 285 } 286 287 @Override 288 public FileSystem getFileSystem(URI uri) { 289 checkUri(uri); 290 FileSystem result = delegate; 291 if (result == null) 292 throw new FileSystemNotFoundException(); 293 return result; 294 } 295 296 @Override 297 public Path getPath(URI uri) { 298 checkScheme(uri); 299 if (delegate == null) 300 throw new FileSystemNotFoundException(); 301 302 // only allow absolute path 303 String path = uri.getSchemeSpecificPart(); 304 if (! path.startsWith("///")) { 305 throw new IllegalArgumentException(); 306 } 307 return new PassThroughFileSystem.PassThroughPath(delegate, delegate.root.resolve(path.substring(3))); 308 } 309 310 @Override 311 public void setAttribute(Path file, String attribute, Object value, LinkOption... options) 312 throws IOException 313 { 314 triggerEx(file, "setAttribute"); 315 Files.setAttribute(unwrap(file), attribute, value, options); 316 } 317 318 @Override 319 public Map<String,Object> readAttributes(Path file, String attributes, LinkOption... options) 320 throws IOException 321 { 322 triggerEx(file, "readAttributes"); 323 return Files.readAttributes(unwrap(file), attributes, options); 324 } 325 326 @Override 327 public <V extends FileAttributeView> V getFileAttributeView(Path file, 328 Class<V> type, 329 LinkOption... options) 330 { 331 return Files.getFileAttributeView(unwrap(file), type, options); 332 } 333 334 @Override 335 public <A extends BasicFileAttributes> A readAttributes(Path file, 336 Class<A> type, 337 LinkOption... options) 338 throws IOException 339 { 340 triggerEx(file, "readAttributes"); 341 return Files.readAttributes(unwrap(file), type, options); 342 } 343 344 @Override 345 public void delete(Path file) throws IOException { 346 triggerEx(file, "delete"); 347 Files.delete(unwrap(file)); 348 } 349 350 @Override 351 public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) 352 throws IOException 353 { 354 triggerEx(target, "createSymbolicLink"); 355 Files.createSymbolicLink(unwrap(link), unwrap(target), attrs); 356 } 357 358 @Override 359 public void createLink(Path link, Path existing) throws IOException { 360 triggerEx(existing, "createLink"); 361 Files.createLink(unwrap(link), unwrap(existing)); 362 } 363 364 @Override 365 public Path readSymbolicLink(Path link) throws IOException { 366 Path target = Files.readSymbolicLink(unwrap(link)); 367 triggerEx(target, "readSymbolicLink"); 368 return new PassThroughFileSystem.PassThroughPath(delegate, target); 369 } 370 371 372 @Override 373 public void copy(Path source, Path target, CopyOption... options) throws IOException { 374 triggerEx(source, "copy"); 375 Files.copy(unwrap(source), unwrap(target), options); 376 } 377 378 @Override 379 public void move(Path source, Path target, CopyOption... options) throws IOException { 380 triggerEx(source, "move"); 381 Files.move(unwrap(source), unwrap(target), options); 382 } 383 384 private DirectoryStream<Path> wrap(final DirectoryStream<Path> stream) { 385 return new DirectoryStream<Path>() { 386 @Override 387 public Iterator<Path> iterator() { 388 final Iterator<Path> itr = stream.iterator(); 389 return new Iterator<Path>() { 390 private Path next = null; 391 @Override 392 public boolean hasNext() { 393 if (next == null) { 394 if (itr.hasNext()) { 395 next = itr.next(); 396 } else { 397 return false; 398 } 399 } 400 if (next != null) { 401 try { 402 triggerEx(next, "DirectoryIteratorException"); 403 } catch (IOException ioe) { 404 throw new DirectoryIteratorException(ioe); 405 } catch (SecurityException se) { 406 // ??? Does DS throw SecurityException during iteration? 407 next = null; 408 return hasNext(); 409 } 410 } 411 return (next != null); 412 } 413 @Override 414 public Path next() { 415 try { 416 if (next != null || hasNext()) { 417 return new PassThroughFileSystem.PassThroughPath(delegate, next); 418 } else { 419 throw new NoSuchElementException(); 420 } 421 } finally { 422 next = null; 423 } 424 } 425 426 @Override 427 public void remove() { 428 itr.remove(); 429 } 430 }; 431 } 432 @Override 433 public void close() throws IOException { 434 stream.close(); 435 } 436 }; 437 } 438 439 @Override 440 public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) 441 throws IOException 442 { 443 triggerEx(dir, "newDirectoryStream"); 444 return wrap(Files.newDirectoryStream(unwrap(dir), filter)); 445 } 446 447 @Override 448 public void createDirectory(Path dir, FileAttribute<?>... attrs) 449 throws IOException 450 { 451 triggerEx(dir, "createDirectory"); 452 Files.createDirectory(unwrap(dir), attrs); 453 } 454 455 @Override 456 public SeekableByteChannel newByteChannel(Path file, 457 Set<? extends OpenOption> options, 458 FileAttribute<?>... attrs) 459 throws IOException 460 { 461 triggerEx(file, "newByteChannel"); 462 return Files.newByteChannel(unwrap(file), options, attrs); 463 } 464 465 466 @Override 467 public boolean isHidden(Path file) throws IOException { 468 triggerEx(file, "isHidden"); 469 return Files.isHidden(unwrap(file)); 470 } 471 472 @Override 473 public FileStore getFileStore(Path file) throws IOException { 474 triggerEx(file, "getFileStore"); 475 return Files.getFileStore(unwrap(file)); 476 } 477 478 @Override 479 public boolean isSameFile(Path file, Path other) throws IOException { 480 triggerEx(file, "isSameFile"); 481 return Files.isSameFile(unwrap(file), unwrap(other)); 482 } 483 484 @Override 485 public void checkAccess(Path file, AccessMode... modes) 486 throws IOException 487 { 488 triggerEx(file, "checkAccess"); 489 // hack 490 if (modes.length == 0) { 491 if (Files.exists(unwrap(file))) 492 return; 493 else 494 throw new NoSuchFileException(file.toString()); 495 } 496 throw new RuntimeException("not implemented yet"); 497 } 498 } 499 }