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