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