1 /*
   2  * Copyright (c) 2008, 2010, 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.channels.*;
  31 import java.util.*;
  32 import java.io.IOException;
  33 import java.security.AccessController;
  34 import java.security.PrivilegedAction;
  35 
  36 /**
  37  * Base implementation of FileStore for Unix/like implementations.
  38  */
  39 
  40 abstract class UnixFileStore
  41     extends FileStore
  42 {
  43     // original path of file that identified file system
  44     private final UnixPath file;
  45 
  46     // device ID
  47     private final long dev;
  48 
  49     // entry in the mount tab
  50     private final UnixMountEntry entry;
  51 
  52     // return the device ID where the given file resides
  53     private static long devFor(UnixPath file) throws IOException {
  54         try {
  55             return UnixFileAttributes.get(file, true).dev();
  56         } catch (UnixException x) {
  57             x.rethrowAsIOException(file);
  58             return 0L;  // keep compiler happy
  59         }
  60     }
  61 
  62     UnixFileStore(UnixPath file) throws IOException {
  63         this.file = file;
  64         this.dev = devFor(file);
  65         this.entry = findMountEntry();
  66     }
  67 
  68     UnixFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
  69         this.file = new UnixPath(fs, entry.dir());
  70         this.dev = (entry.dev() == 0L) ? devFor(this.file) : entry.dev();
  71         this.entry = entry;
  72     }
  73 
  74     /**
  75      * Find the mount entry for the file store
  76      */
  77     abstract UnixMountEntry findMountEntry() throws IOException;
  78 
  79     UnixPath file() {
  80         return file;
  81     }
  82 
  83     long dev() {
  84         return dev;
  85     }
  86 
  87     UnixMountEntry entry() {
  88         return entry;
  89     }
  90 
  91     @Override
  92     public String name() {
  93         return entry.name();
  94     }
  95 
  96     @Override
  97     public String type() {
  98         return entry.fstype();
  99     }
 100 
 101     @Override
 102     public boolean isReadOnly() {
 103         return entry.isReadOnly();
 104     }
 105 
 106     @Override
 107     @SuppressWarnings("unchecked")
 108     public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> view)
 109     {
 110         if (view == null)
 111             throw new NullPointerException();
 112         if (view == FileStoreSpaceAttributeView.class)
 113             return (V) new UnixFileStoreSpaceAttributeView(this);
 114         return (V) null;
 115     }
 116 
 117     @Override
 118     public Object getAttribute(String attribute) throws IOException {
 119         if (attribute.equals("space:totalSpace"))
 120             return new UnixFileStoreSpaceAttributeView(this)
 121                 .readAttributes().totalSpace();
 122         if (attribute.equals("space:usableSpace"))
 123             return new UnixFileStoreSpaceAttributeView(this)
 124                  .readAttributes().usableSpace();
 125         if (attribute.equals("space:unallocatedSpace"))
 126             return new UnixFileStoreSpaceAttributeView(this)
 127                  .readAttributes().unallocatedSpace();
 128         throw new UnsupportedOperationException("'" + attribute + "' not recognized");
 129     }
 130 
 131     @Override
 132     public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
 133         if (type == null)
 134             throw new NullPointerException();
 135         if (type == BasicFileAttributeView.class)
 136             return true;
 137         if (type == PosixFileAttributeView.class ||
 138             type == FileOwnerAttributeView.class)
 139         {
 140             // lookup fstypes.properties
 141             FeatureStatus status = checkIfFeaturePresent("posix");
 142             // assume supported if UNKNOWN
 143             return (status != FeatureStatus.NOT_PRESENT);
 144         }
 145         return false;
 146     }
 147 
 148     @Override
 149     public boolean supportsFileAttributeView(String name) {
 150         if (name.equals("basic") || name.equals("unix"))
 151             return true;
 152         if (name.equals("posix"))
 153             return supportsFileAttributeView(PosixFileAttributeView.class);
 154         if (name.equals("owner"))
 155             return supportsFileAttributeView(FileOwnerAttributeView.class);
 156         return false;
 157     }
 158 
 159     @Override
 160     public boolean equals(Object ob) {
 161         if (ob == this)
 162             return true;
 163         if (!(ob instanceof UnixFileStore))
 164             return false;
 165         UnixFileStore other = (UnixFileStore)ob;
 166         return (this.dev == other.dev) &&
 167                Arrays.equals(this.entry.dir(), other.entry.dir());
 168     }
 169 
 170     @Override
 171     public int hashCode() {
 172         return (int)(dev ^ (dev >>> 32)) ^ Arrays.hashCode(entry.dir());
 173     }
 174 
 175     @Override
 176     public String toString() {
 177         StringBuilder sb = new StringBuilder(new String(entry.dir()));
 178         sb.append(" (");
 179         sb.append(entry.name());
 180         sb.append(")");
 181         return sb.toString();
 182     }
 183 
 184     private static class UnixFileStoreSpaceAttributeView
 185         implements FileStoreSpaceAttributeView
 186     {
 187         private final UnixFileStore fs;
 188 
 189         UnixFileStoreSpaceAttributeView(UnixFileStore fs) {
 190             this.fs = fs;
 191         }
 192 
 193         @Override
 194         public String name() {
 195             return "space";
 196         }
 197 
 198         @Override
 199         public FileStoreSpaceAttributes readAttributes()
 200             throws IOException
 201         {
 202             UnixPath file = fs.file();
 203             final UnixFileStoreAttributes attrs;
 204             try {
 205                 attrs = UnixFileStoreAttributes.get(file);
 206             } catch (UnixException x) {
 207                 x.rethrowAsIOException(file);
 208                 return null;    // keep compile happy
 209             }
 210 
 211             return new FileStoreSpaceAttributes() {
 212                 @Override
 213                 public long totalSpace() {
 214                     return attrs.blockSize() * attrs.totalBlocks();
 215                 }
 216                 @Override
 217                 public long usableSpace() {
 218                     return attrs.blockSize() * attrs.availableBlocks();
 219                 }
 220                 @Override
 221                 public long unallocatedSpace() {
 222                     return attrs.blockSize() * attrs.freeBlocks();
 223                 }
 224             };
 225         }
 226     }
 227 
 228     // -- fstypes.properties --
 229 
 230     private static final Object loadLock = new Object();
 231     private static volatile Properties props;
 232 
 233     enum FeatureStatus {
 234         PRESENT,
 235         NOT_PRESENT,
 236         UNKNOWN;
 237     }
 238 
 239     /**
 240      * Returns status to indicate if file system supports a given feature
 241      */
 242     FeatureStatus checkIfFeaturePresent(String feature) {
 243         if (props == null) {
 244             synchronized (loadLock) {
 245                 if (props == null) {
 246                     props = AccessController.doPrivileged(
 247                         new PrivilegedAction<Properties>() {
 248                             @Override
 249                             public Properties run() {
 250                                 return loadProperties();
 251                             }});
 252                 }
 253             }
 254         }
 255 
 256         String value = props.getProperty(type());
 257         if (value != null) {
 258             String[] values = value.split("\\s");
 259             for (String s: values) {
 260                 s = s.trim().toLowerCase();
 261                 if (s.equals(feature)) {
 262                     return FeatureStatus.PRESENT;
 263                 }
 264                 if (s.startsWith("no")) {
 265                     s = s.substring(2);
 266                     if (s.equals(feature)) {
 267                         return FeatureStatus.NOT_PRESENT;
 268                     }
 269                 }
 270             }
 271         }
 272         return FeatureStatus.UNKNOWN;
 273     }
 274 
 275     private static Properties loadProperties() {
 276         Properties result = new Properties();
 277         String fstypes = System.getProperty("java.home") + "/lib/fstypes.properties";
 278         Path file = Paths.get(fstypes);
 279         try {
 280             ReadableByteChannel rbc = file.newByteChannel();
 281             try {
 282                 result.load(Channels.newReader(rbc, "UTF-8"));
 283             } finally {
 284                 rbc.close();
 285             }
 286         } catch (IOException x) {
 287         }
 288         return result;
 289     }
 290 }