1 /*
   2  * Copyright (c) 2008, 2017, 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     // uses statvfs to read the file system information
 107     private UnixFileStoreAttributes readAttributes() throws IOException {
 108         try {
 109             return UnixFileStoreAttributes.get(file);
 110         } catch (UnixException x) {
 111             x.rethrowAsIOException(file);
 112             return null;    // keep compile happy
 113         }
 114     }
 115 
 116     @Override
 117     public long getTotalSpace() throws IOException {
 118         UnixFileStoreAttributes attrs = readAttributes();
 119         return attrs.blockSize() * attrs.totalBlocks();
 120     }
 121 
 122     @Override
 123     public long getUsableSpace() throws IOException {
 124        UnixFileStoreAttributes attrs = readAttributes();
 125        return attrs.blockSize() * attrs.availableBlocks();
 126     }
 127 
 128     @Override
 129     public long getBlockSize() throws IOException {
 130        UnixFileStoreAttributes attrs = readAttributes();
 131        return attrs.blockSize();
 132     }
 133 
 134     @Override
 135     public long getUnallocatedSpace() throws IOException {
 136         UnixFileStoreAttributes attrs = readAttributes();
 137         return attrs.blockSize() * attrs.freeBlocks();
 138     }
 139 
 140     @Override
 141     public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> view)
 142     {
 143         if (view == null)
 144             throw new NullPointerException();
 145         return (V) null;
 146     }
 147 
 148     @Override
 149     public Object getAttribute(String attribute) throws IOException {
 150         if (attribute.equals("totalSpace"))
 151             return getTotalSpace();
 152         if (attribute.equals("usableSpace"))
 153             return getUsableSpace();
 154         if (attribute.equals("unallocatedSpace"))
 155             return getUnallocatedSpace();
 156         throw new UnsupportedOperationException("'" + attribute + "' not recognized");
 157     }
 158 
 159     @Override
 160     public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
 161         if (type == null)
 162             throw new NullPointerException();
 163         if (type == BasicFileAttributeView.class)
 164             return true;
 165         if (type == PosixFileAttributeView.class ||
 166             type == FileOwnerAttributeView.class)
 167         {
 168             // lookup fstypes.properties
 169             FeatureStatus status = checkIfFeaturePresent("posix");
 170             // assume supported if UNKNOWN
 171             return (status != FeatureStatus.NOT_PRESENT);
 172         }
 173         return false;
 174     }
 175 
 176     @Override
 177     public boolean supportsFileAttributeView(String name) {
 178         if (name.equals("basic") || name.equals("unix"))
 179             return true;
 180         if (name.equals("posix"))
 181             return supportsFileAttributeView(PosixFileAttributeView.class);
 182         if (name.equals("owner"))
 183             return supportsFileAttributeView(FileOwnerAttributeView.class);
 184         return false;
 185     }
 186 
 187     @Override
 188     public boolean equals(Object ob) {
 189         if (ob == this)
 190             return true;
 191         if (!(ob instanceof UnixFileStore))
 192             return false;
 193         UnixFileStore other = (UnixFileStore)ob;
 194         return (this.dev == other.dev) &&
 195                Arrays.equals(this.entry.dir(), other.entry.dir()) &&
 196                this.entry.name().equals(other.entry.name());
 197     }
 198 
 199     @Override
 200     public int hashCode() {
 201         return (int)(dev ^ (dev >>> 32)) ^ Arrays.hashCode(entry.dir());
 202     }
 203 
 204     @Override
 205     public String toString() {
 206         StringBuilder sb = new StringBuilder(Util.toString(entry.dir()));
 207         sb.append(" (");
 208         sb.append(entry.name());
 209         sb.append(")");
 210         return sb.toString();
 211     }
 212 
 213     // -- fstypes.properties --
 214 
 215     private static final Object loadLock = new Object();
 216     private static volatile Properties props;
 217 
 218     enum FeatureStatus {
 219         PRESENT,
 220         NOT_PRESENT,
 221         UNKNOWN;
 222     }
 223 
 224     /**
 225      * Returns status to indicate if file system supports a given feature
 226      */
 227     FeatureStatus checkIfFeaturePresent(String feature) {
 228         if (props == null) {
 229             synchronized (loadLock) {
 230                 if (props == null) {
 231                     props = AccessController.doPrivileged(
 232                         new PrivilegedAction<>() {
 233                             @Override
 234                             public Properties run() {
 235                                 return loadProperties();
 236                             }});
 237                 }
 238             }
 239         }
 240 
 241         String value = props.getProperty(type());
 242         if (value != null) {
 243             String[] values = value.split("\\s");
 244             for (String s: values) {
 245                 s = s.trim().toLowerCase();
 246                 if (s.equals(feature)) {
 247                     return FeatureStatus.PRESENT;
 248                 }
 249                 if (s.startsWith("no")) {
 250                     s = s.substring(2);
 251                     if (s.equals(feature)) {
 252                         return FeatureStatus.NOT_PRESENT;
 253                     }
 254                 }
 255             }
 256         }
 257         return FeatureStatus.UNKNOWN;
 258     }
 259 
 260     private static Properties loadProperties() {
 261         Properties result = new Properties();
 262         String fstypes = System.getProperty("java.home") + "/lib/fstypes.properties";
 263         Path file = Path.get(fstypes);
 264         try {
 265             try (ReadableByteChannel rbc = Files.newByteChannel(file)) {
 266                 result.load(Channels.newReader(rbc, "UTF-8"));
 267             }
 268         } catch (IOException x) {
 269         }
 270         return result;
 271     }
 272 }