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 }