rev 11514 : 8073445: (fs) FileSystem.getPathMatcher(...) should check syntax component without regard to case
Summary: Change String equals() to equalsIgnoreCase() where needed.
Reviewed-by: TBD

   1 /*
   2  * Copyright (c) 2014, 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.*;
  33 import java.nio.charset.Charset;
  34 import java.nio.file.AccessMode;
  35 import java.nio.file.ClosedFileSystemException;
  36 import java.nio.file.CopyOption;
  37 import java.nio.file.FileStore;
  38 import java.nio.file.FileSystem;
  39 import java.nio.file.FileSystemException;
  40 import java.nio.file.FileSystemNotFoundException;
  41 import java.nio.file.Files;
  42 import java.nio.file.NoSuchFileException;
  43 import java.nio.file.NotDirectoryException;
  44 import java.nio.file.OpenOption;
  45 import java.nio.file.Path;
  46 import java.nio.file.PathMatcher;
  47 import java.nio.file.ReadOnlyFileSystemException;
  48 import java.nio.file.StandardCopyOption;
  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.security.AccessController;
  56 import java.security.PrivilegedActionException;
  57 import java.security.PrivilegedExceptionAction;
  58 import java.util.ArrayList;
  59 import java.util.Arrays;
  60 import java.util.Collections;
  61 import java.util.HashSet;
  62 import java.util.Iterator;
  63 import java.util.List;
  64 import java.util.Map;
  65 import java.util.Set;
  66 import java.util.regex.Pattern;
  67 import java.util.stream.Collectors;
  68 import jdk.internal.jimage.ImageReader;
  69 import jdk.internal.jimage.ImageReader.Node;
  70 import jdk.internal.jimage.UTF8String;
  71 
  72 /**
  73  * A FileSystem built on System jimage files.
  74  */
  75 class JrtFileSystem extends FileSystem {
  76     private static final Charset UTF_8 = Charset.forName("UTF-8");
  77     private final JrtFileSystemProvider provider;
  78     // System image readers
  79     private ImageReader bootImage;
  80     private ImageReader extImage;
  81     private ImageReader appImage;
  82     // root path
  83     private final JrtPath rootPath;
  84     private volatile boolean isOpen;
  85 
  86     private static void checkExists(Path path) {
  87         if (Files.notExists(path)) {
  88             throw new FileSystemNotFoundException(path.toString());
  89         }
  90     }
  91 
  92     // open a .jimage and build directory structure
  93     private static ImageReader openImage(Path path) throws IOException {
  94         ImageReader image = ImageReader.open(path.toString());
  95         image.getRootDirectory();
  96         return image;
  97     }
  98 
  99     JrtFileSystem(JrtFileSystemProvider provider,
 100             Map<String, ?> env)
 101             throws IOException {
 102         this.provider = provider;
 103         checkExists(SystemImages.bootImagePath);
 104         checkExists(SystemImages.extImagePath);
 105         checkExists(SystemImages.appImagePath);
 106 
 107         // open image files
 108         this.bootImage = openImage(SystemImages.bootImagePath);
 109         this.extImage = openImage(SystemImages.extImagePath);
 110         this.appImage = openImage(SystemImages.appImagePath);
 111 
 112         rootPath = new JrtPath(this, new byte[]{'/'});
 113         isOpen = true;
 114     }
 115 
 116     @Override
 117     public FileSystemProvider provider() {
 118         return provider;
 119     }
 120 
 121     @Override
 122     public String getSeparator() {
 123         return "/";
 124     }
 125 
 126     @Override
 127     public boolean isOpen() {
 128         return isOpen;
 129     }
 130 
 131     @Override
 132     public void close() throws IOException {
 133         cleanup();
 134     }
 135 
 136     @Override
 137     protected void finalize() {
 138         try {
 139             cleanup();
 140         } catch (IOException ignored) {}
 141     }
 142 
 143     // clean up this file system - called from finalize and close
 144     private void cleanup() throws IOException {
 145         if (!isOpen) {
 146             return;
 147         }
 148 
 149         synchronized(this) {
 150             isOpen = false;
 151 
 152             // close all image readers and null out
 153             bootImage.close();
 154             extImage.close();
 155             appImage.close();
 156             bootImage = null;
 157             extImage = null;
 158             appImage = null;
 159         }
 160     }
 161 
 162     private void ensureOpen() throws IOException {
 163         if (!isOpen) {
 164             throw new ClosedFileSystemException();
 165         }
 166     }
 167 
 168     @Override
 169     public boolean isReadOnly() {
 170         return true;
 171     }
 172 
 173     private ReadOnlyFileSystemException readOnly() {
 174         return new ReadOnlyFileSystemException();
 175     }
 176 
 177     @Override
 178     public Iterable<Path> getRootDirectories() {
 179         ArrayList<Path> pathArr = new ArrayList<>();
 180         pathArr.add(rootPath);
 181         return pathArr;
 182     }
 183 
 184     JrtPath getRootPath() {
 185         return rootPath;
 186     }
 187 
 188     @Override
 189     public JrtPath getPath(String first, String... more) {
 190         String path;
 191         if (more.length == 0) {
 192             path = first;
 193         } else {
 194             StringBuilder sb = new StringBuilder();
 195             sb.append(first);
 196             for (String segment : more) {
 197                 if (segment.length() > 0) {
 198                     if (sb.length() > 0) {
 199                         sb.append('/');
 200                     }
 201                     sb.append(segment);
 202                 }
 203             }
 204             path = sb.toString();
 205         }
 206         return new JrtPath(this, getBytes(path));
 207     }
 208 
 209     @Override
 210     public UserPrincipalLookupService getUserPrincipalLookupService() {
 211         throw new UnsupportedOperationException();
 212     }
 213 
 214     @Override
 215     public WatchService newWatchService() {
 216         throw new UnsupportedOperationException();
 217     }
 218 
 219     FileStore getFileStore(JrtPath path) {
 220         return new JrtFileStore(path);
 221     }
 222 
 223     @Override
 224     public Iterable<FileStore> getFileStores() {
 225         ArrayList<FileStore> list = new ArrayList<>(1);
 226         list.add(new JrtFileStore(new JrtPath(this, new byte[]{'/'})));
 227         return list;
 228     }
 229 
 230     private static final Set<String> supportedFileAttributeViews
 231             = Collections.unmodifiableSet(
 232                     new HashSet<String>(Arrays.asList("basic", "jrt")));
 233 
 234     @Override
 235     public Set<String> supportedFileAttributeViews() {
 236         return supportedFileAttributeViews;
 237     }
 238 
 239     @Override
 240     public String toString() {
 241         return "jrt:/";
 242     }
 243 
 244     private static final String GLOB_SYNTAX = "glob";
 245     private static final String REGEX_SYNTAX = "regex";
 246 
 247     @Override
 248     public PathMatcher getPathMatcher(String syntaxAndInput) {
 249         int pos = syntaxAndInput.indexOf(':');
 250         if (pos <= 0 || pos == syntaxAndInput.length()) {
 251             throw new IllegalArgumentException();
 252         }
 253         String syntax = syntaxAndInput.substring(0, pos);
 254         String input = syntaxAndInput.substring(pos + 1);
 255         String expr;
 256         if (syntax.equals(GLOB_SYNTAX)) {
 257             expr = JrtUtils.toRegexPattern(input);
 258         } else {
 259             if (syntax.equals(REGEX_SYNTAX)) {
 260                 expr = input;
 261             } else {
 262                 throw new UnsupportedOperationException("Syntax '" + syntax
 263                         + "' not recognized");
 264             }
 265         }
 266         // return matcher
 267         final Pattern pattern = Pattern.compile(expr);
 268         return (Path path) -> pattern.matcher(path.toString()).matches();
 269     }
 270 
 271     static byte[] getBytes(String name) {
 272         return name.getBytes(UTF_8);
 273     }
 274 
 275     static String getString(byte[] name) {
 276         return new String(name, UTF_8);
 277     }
 278 
 279     private static class NodeAndImage {
 280         final Node node;
 281         final ImageReader image;
 282 
 283         NodeAndImage(Node node, ImageReader image) {
 284             this.node = node; this.image = image;
 285         }
 286 
 287         byte[] getResource() throws IOException {
 288             return image.getResource(node);
 289         }
 290     }
 291 
 292     private NodeAndImage findNode(byte[] path) throws IOException {
 293         ImageReader image = bootImage;
 294         Node node = bootImage.findNode(path);
 295         if (node == null) {
 296             image = extImage;
 297             node = extImage.findNode(path);
 298         }
 299         if (node == null) {
 300             image = appImage;
 301             node = appImage.findNode(path);
 302         }
 303         if (node == null || node.isHidden()) {
 304             throw new NoSuchFileException(getString(path));
 305         }
 306         return new NodeAndImage(node, image);
 307     }
 308 
 309     private NodeAndImage checkNode(byte[] path) throws IOException {
 310         ensureOpen();
 311         return findNode(path);
 312     }
 313 
 314     private NodeAndImage checkResource(byte[] path) throws IOException {
 315         NodeAndImage ni = checkNode(path);
 316         if (ni.node.isDirectory()) {
 317             throw new FileSystemException(getString(path) + " is a directory");
 318         }
 319 
 320         assert ni.node.isResource() : "resource node expected here";
 321         return ni;
 322     }
 323 
 324     // package private helpers
 325     JrtFileAttributes getFileAttributes(byte[] path)
 326             throws IOException {
 327         NodeAndImage ni = checkNode(path);
 328         return new JrtFileAttributes(ni.node);
 329     }
 330 
 331     void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime)
 332             throws IOException {
 333         throw readOnly();
 334     }
 335 
 336     boolean exists(byte[] path) throws IOException {
 337         ensureOpen();
 338         try {
 339             findNode(path);
 340         } catch (NoSuchFileException exp) {
 341             return false;
 342         }
 343         return true;
 344     }
 345 
 346     boolean isDirectory(byte[] path)
 347             throws IOException {
 348         ensureOpen();
 349         NodeAndImage ni = checkNode(path);
 350         return ni.node.isDirectory();
 351     }
 352 
 353     JrtPath toJrtPath(String path) {
 354         return toJrtPath(getBytes(path));
 355     }
 356 
 357     JrtPath toJrtPath(byte[] path) {
 358         return new JrtPath(this, path);
 359     }
 360 
 361     /**
 362      * returns the list of child paths of the given directory "path"
 363      *
 364      * @param path name of the directory whose content is listed
 365      * @param childPrefix prefix added to returned children names - may be null
 366               in which case absolute child paths are returned
 367      * @return iterator for child paths of the given directory path
 368      */
 369     Iterator<Path> iteratorOf(byte[] path, String childPrefix)
 370             throws IOException {
 371         NodeAndImage ni = checkNode(path);
 372         if (!ni.node.isDirectory()) {
 373             throw new NotDirectoryException(getString(path));
 374         }
 375 
 376         if (ni.node.isRootDir()) {
 377             return rootDirIterator(path, childPrefix);
 378         }
 379 
 380         return nodesToIterator(toJrtPath(path), childPrefix, ni.node.getChildren());
 381     }
 382 
 383     private Iterator<Path> nodesToIterator(Path path, String childPrefix, List<Node> childNodes) {
 384         List<Path> childPaths;
 385         if (childPrefix == null) {
 386             childPaths = childNodes.stream()
 387                 .filter(Node::isVisible)
 388                 .map(child -> toJrtPath(child.getNameString()))
 389                 .collect(Collectors.toCollection(ArrayList::new));
 390         } else {
 391             childPaths = childNodes.stream()
 392                 .filter(Node::isVisible)
 393                 .map(child -> toJrtPath(childPrefix + child.getNameString().substring(1)))
 394                 .collect(Collectors.toCollection(ArrayList::new));
 395         }
 396         return childPaths.iterator();
 397     }
 398 
 399     private List<Node> rootChildren;
 400     private static void addRootDirContent(List<Node> dest, List<Node> src) {
 401         for (Node n : src) {
 402             // only module directories at the top level. Filter other stuff!
 403             if (n.isModuleDir()) {
 404                 dest.add(n);
 405             }
 406         }
 407     }
 408 
 409     private synchronized void initRootChildren(byte[] path) {
 410         if (rootChildren == null) {
 411             rootChildren = new ArrayList<>();
 412             addRootDirContent(rootChildren, bootImage.findNode(path).getChildren());
 413             addRootDirContent(rootChildren, extImage.findNode(path).getChildren());
 414             addRootDirContent(rootChildren, appImage.findNode(path).getChildren());
 415         }
 416     }
 417 
 418     private Iterator<Path> rootDirIterator(byte[] path, String childPrefix) throws IOException {
 419         initRootChildren(path);
 420         return nodesToIterator(rootPath, childPrefix, rootChildren);
 421     }
 422 
 423     void createDirectory(byte[] dir, FileAttribute<?>... attrs)
 424             throws IOException {
 425         throw readOnly();
 426     }
 427 
 428     void copyFile(boolean deletesrc, byte[] src, byte[] dst, CopyOption... options)
 429             throws IOException {
 430         throw readOnly();
 431     }
 432 
 433     public void deleteFile(byte[] path, boolean failIfNotExists)
 434             throws IOException {
 435         throw readOnly();
 436     }
 437 
 438     OutputStream newOutputStream(byte[] path, OpenOption... options)
 439             throws IOException {
 440         throw readOnly();
 441     }
 442 
 443     private void checkOptions(Set<? extends OpenOption> options) {
 444         // check for options of null type and option is an intance of StandardOpenOption
 445         for (OpenOption option : options) {
 446             if (option == null) {
 447                 throw new NullPointerException();
 448             }
 449             if (!(option instanceof StandardOpenOption)) {
 450                 throw new IllegalArgumentException();
 451             }
 452         }
 453     }
 454 
 455     // Returns an input stream for reading the contents of the specified
 456     // file entry.
 457     InputStream newInputStream(byte[] path) throws IOException {
 458         final NodeAndImage ni = checkResource(path);
 459         return new ByteArrayInputStream(ni.getResource());
 460     }
 461 
 462     SeekableByteChannel newByteChannel(byte[] path,
 463             Set<? extends OpenOption> options,
 464             FileAttribute<?>... attrs)
 465             throws IOException {
 466         checkOptions(options);
 467         if (options.contains(StandardOpenOption.WRITE)
 468                 || options.contains(StandardOpenOption.APPEND)) {
 469             throw readOnly();
 470         }
 471 
 472         NodeAndImage ni = checkResource(path);
 473         byte[] buf = ni.getResource();
 474         final ReadableByteChannel rbc
 475                 = Channels.newChannel(new ByteArrayInputStream(buf));
 476         final long size = buf.length;
 477         return new SeekableByteChannel() {
 478             long read = 0;
 479 
 480             @Override
 481             public boolean isOpen() {
 482                 return rbc.isOpen();
 483             }
 484 
 485             @Override
 486             public long position() throws IOException {
 487                 return read;
 488             }
 489 
 490             @Override
 491             public SeekableByteChannel position(long pos)
 492                     throws IOException {
 493                 throw new UnsupportedOperationException();
 494             }
 495 
 496             @Override
 497             public int read(ByteBuffer dst) throws IOException {
 498                 int n = rbc.read(dst);
 499                 if (n > 0) {
 500                     read += n;
 501                 }
 502                 return n;
 503             }
 504 
 505             @Override
 506             public SeekableByteChannel truncate(long size)
 507                     throws IOException {
 508                 throw new NonWritableChannelException();
 509             }
 510 
 511             @Override
 512             public int write(ByteBuffer src) throws IOException {
 513                 throw new NonWritableChannelException();
 514             }
 515 
 516             @Override
 517             public long size() throws IOException {
 518                 return size;
 519             }
 520 
 521             @Override
 522             public void close() throws IOException {
 523                 rbc.close();
 524             }
 525         };
 526     }
 527 
 528     // Returns a FileChannel of the specified path.
 529     FileChannel newFileChannel(byte[] path,
 530             Set<? extends OpenOption> options,
 531             FileAttribute<?>... attrs)
 532             throws IOException {
 533         throw new UnsupportedOperationException("newFileChannel");
 534     }
 535 }
--- EOF ---