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