1 /*
   2  * Copyright (c) 2008, 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 
  26 package sun.nio.fs;
  27 
  28 import java.nio.file.*;
  29 import java.nio.file.attribute.*;
  30 import java.nio.file.spi.*;
  31 import java.io.IOException;
  32 import java.util.*;
  33 import java.util.regex.Pattern;
  34 import sun.security.action.GetPropertyAction;
  35 
  36 /**
  37  * Base implementation of FileSystem for Unix-like implementations.
  38  */
  39 
  40 abstract class UnixFileSystem
  41     extends FileSystem
  42 {
  43     private final UnixFileSystemProvider provider;
  44     private final byte[] defaultDirectory;
  45     private final boolean needToResolveAgainstDefaultDirectory;
  46     private final UnixPath rootDirectory;
  47 
  48     // package-private
  49     UnixFileSystem(UnixFileSystemProvider provider, String dir) {
  50         this.provider = provider;
  51         this.defaultDirectory = Util.toBytes(UnixPath.normalizeAndCheck(dir));
  52         if (this.defaultDirectory[0] != '/') {
  53             throw new RuntimeException("default directory must be absolute");
  54         }
  55 
  56         // if process-wide chdir is allowed or default directory is not the
  57         // process working directory then paths must be resolved against the
  58         // default directory.
  59         String propValue = GetPropertyAction
  60                 .getProperty("sun.nio.fs.chdirAllowed", "false");
  61         boolean chdirAllowed = (propValue.length() == 0) ?
  62             true : Boolean.valueOf(propValue);
  63         if (chdirAllowed) {
  64             this.needToResolveAgainstDefaultDirectory = true;
  65         } else {
  66             byte[] cwd = UnixNativeDispatcher.getcwd();
  67             boolean defaultIsCwd = (cwd.length == defaultDirectory.length);
  68             if (defaultIsCwd) {
  69                 for (int i=0; i<cwd.length; i++) {
  70                     if (cwd[i] != defaultDirectory[i]) {
  71                         defaultIsCwd = false;
  72                         break;
  73                     }
  74                 }
  75             }
  76             this.needToResolveAgainstDefaultDirectory = !defaultIsCwd;
  77         }
  78 
  79         // the root directory
  80         this.rootDirectory = new UnixPath(this, "/");
  81     }
  82 
  83     // package-private
  84     byte[] defaultDirectory() {
  85         return defaultDirectory;
  86     }
  87 
  88     boolean needToResolveAgainstDefaultDirectory() {
  89         return needToResolveAgainstDefaultDirectory;
  90     }
  91 
  92     UnixPath rootDirectory() {
  93         return rootDirectory;
  94     }
  95 
  96     boolean isSolaris() {
  97         return false;
  98     }
  99 
 100     static List<String> standardFileAttributeViews() {
 101         return Arrays.asList("basic", "posix", "unix", "owner");
 102     }
 103 
 104     @Override
 105     public final FileSystemProvider provider() {
 106         return provider;
 107     }
 108 
 109     @Override
 110     public final String getSeparator() {
 111         return "/";
 112     }
 113 
 114     @Override
 115     public final boolean isOpen() {
 116         return true;
 117     }
 118 
 119     @Override
 120     public final boolean isReadOnly() {
 121         return false;
 122     }
 123 
 124     @Override
 125     public final void close() throws IOException {
 126         throw new UnsupportedOperationException();
 127     }
 128 
 129     /**
 130      * Copies non-POSIX attributes from the source to target file.
 131      *
 132      * Copying a file preserving attributes, or moving a file, will preserve
 133      * the file owner/group/permissions/timestamps but it does not preserve
 134      * other non-POSIX attributes. This method is invoked by the
 135      * copy or move operation to preserve these attributes. It should copy
 136      * extended attributes, ACLs, or other attributes.
 137      *
 138      * @param   sfd
 139      *          Open file descriptor to source file
 140      * @param   tfd
 141      *          Open file descriptor to target file
 142      */
 143     void copyNonPosixAttributes(int sfd, int tfd) {
 144         // no-op by default
 145     }
 146 
 147     /**
 148      * Unix systems only have a single root directory (/)
 149      */
 150     @Override
 151     public final Iterable<Path> getRootDirectories() {
 152         final List<Path> allowedList =
 153            Collections.unmodifiableList(Arrays.asList((Path)rootDirectory));
 154         return new Iterable<>() {
 155             public Iterator<Path> iterator() {
 156                 try {
 157                     SecurityManager sm = System.getSecurityManager();
 158                     if (sm != null)
 159                         sm.checkRead(rootDirectory.toString());
 160                     return allowedList.iterator();
 161                 } catch (SecurityException x) {
 162                     List<Path> disallowed = Collections.emptyList();
 163                     return disallowed.iterator();
 164                 }
 165             }
 166         };
 167     }
 168 
 169     /**
 170      * Returns object to iterate over entries in mounttab or equivalent
 171      */
 172     abstract Iterable<UnixMountEntry> getMountEntries();
 173 
 174     /**
 175      * Returns a FileStore to represent the file system for the given mount
 176      * mount.
 177      */
 178     abstract FileStore getFileStore(UnixMountEntry entry) throws IOException;
 179 
 180     /**
 181      * Iterator returned by getFileStores method.
 182      */
 183     private class FileStoreIterator implements Iterator<FileStore> {
 184         private final Iterator<UnixMountEntry> entries;
 185         private FileStore next;
 186 
 187         FileStoreIterator() {
 188             this.entries = getMountEntries().iterator();
 189         }
 190 
 191         private FileStore readNext() {
 192             assert Thread.holdsLock(this);
 193             for (;;) {
 194                 if (!entries.hasNext())
 195                     return null;
 196                 UnixMountEntry entry = entries.next();
 197 
 198                 // skip entries with the "ignore" option
 199                 if (entry.isIgnored())
 200                     continue;
 201 
 202                 // check permission to read mount point
 203                 SecurityManager sm = System.getSecurityManager();
 204                 if (sm != null) {
 205                     try {
 206                         sm.checkRead(Util.toString(entry.dir()));
 207                     } catch (SecurityException x) {
 208                         continue;
 209                     }
 210                 }
 211                 try {
 212                     return getFileStore(entry);
 213                 } catch (IOException ignore) {
 214                     // ignore as per spec
 215                 }
 216             }
 217         }
 218 
 219         @Override
 220         public synchronized boolean hasNext() {
 221             if (next != null)
 222                 return true;
 223             next = readNext();
 224             return next != null;
 225         }
 226 
 227         @Override
 228         public synchronized FileStore next() {
 229             if (next == null)
 230                 next = readNext();
 231             if (next == null) {
 232                 throw new NoSuchElementException();
 233             } else {
 234                 FileStore result = next;
 235                 next = null;
 236                 return result;
 237             }
 238         }
 239 
 240         @Override
 241         public void remove() {
 242             throw new UnsupportedOperationException();
 243         }
 244     }
 245 
 246     @Override
 247     public final Iterable<FileStore> getFileStores() {
 248         SecurityManager sm = System.getSecurityManager();
 249         if (sm != null) {
 250             try {
 251                 sm.checkPermission(new RuntimePermission("getFileStoreAttributes"));
 252             } catch (SecurityException se) {
 253                 return Collections.emptyList();
 254             }
 255         }
 256         return new Iterable<>() {
 257             public Iterator<FileStore> iterator() {
 258                 return new FileStoreIterator();
 259             }
 260         };
 261     }
 262 
 263     @Override
 264     public final Path getPath(String first, String... more) {
 265         String path;
 266         if (more.length == 0) {
 267             path = first;
 268         } else {
 269             StringBuilder sb = new StringBuilder();
 270             sb.append(first);
 271             for (String segment: more) {
 272                 if (segment.length() > 0) {
 273                     if (sb.length() > 0)
 274                         sb.append('/');
 275                     sb.append(segment);
 276                 }
 277             }
 278             path = sb.toString();
 279         }
 280         return new UnixPath(this, path);
 281     }
 282 
 283     @Override
 284     public PathMatcher getPathMatcher(String syntaxAndInput) {
 285         int pos = syntaxAndInput.indexOf(':');
 286         if (pos <= 0 || pos == syntaxAndInput.length())
 287             throw new IllegalArgumentException();
 288         String syntax = syntaxAndInput.substring(0, pos);
 289         String input = syntaxAndInput.substring(pos+1);
 290 
 291         String expr;
 292         if (syntax.equalsIgnoreCase(GLOB_SYNTAX)) {
 293             expr = Globs.toUnixRegexPattern(input);
 294         } else {
 295             if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) {
 296                 expr = input;
 297             } else {
 298                 throw new UnsupportedOperationException("Syntax '" + syntax +
 299                     "' not recognized");
 300             }
 301         }
 302 
 303         // return matcher
 304         final Pattern pattern = compilePathMatchPattern(expr);
 305 
 306         return new PathMatcher() {
 307             @Override
 308             public boolean matches(Path path) {
 309                 return pattern.matcher(path.toString()).matches();
 310             }
 311         };
 312     }
 313 
 314     private static final String GLOB_SYNTAX = "glob";
 315     private static final String REGEX_SYNTAX = "regex";
 316 
 317     @Override
 318     public final UserPrincipalLookupService getUserPrincipalLookupService() {
 319         return LookupService.instance;
 320     }
 321 
 322     private static class LookupService {
 323         static final UserPrincipalLookupService instance =
 324             new UserPrincipalLookupService() {
 325                 @Override
 326                 public UserPrincipal lookupPrincipalByName(String name)
 327                     throws IOException
 328                 {
 329                     return UnixUserPrincipals.lookupUser(name);
 330                 }
 331 
 332                 @Override
 333                 public GroupPrincipal lookupPrincipalByGroupName(String group)
 334                     throws IOException
 335                 {
 336                     return UnixUserPrincipals.lookupGroup(group);
 337                 }
 338             };
 339     }
 340 
 341     // Override if the platform has different path match requirement, such as
 342     // case insensitive or Unicode canonical equal on MacOSX
 343     Pattern compilePathMatchPattern(String expr) {
 344         return Pattern.compile(expr);
 345     }
 346 
 347     // Override if the platform uses different Unicode normalization form
 348     // for native file path. For example on MacOSX, the native path is stored
 349     // in Unicode NFD form.
 350     char[] normalizeNativePath(char[] path) {
 351         return path;
 352     }
 353 
 354     // Override if the native file path use non-NFC form. For example on MacOSX,
 355     // the native path is stored in Unicode NFD form, the path need to be
 356     // normalized back to NFC before passed back to Java level.
 357     String normalizeJavaPath(String path) {
 358         return path;
 359     }
 360 }