1 /*
   2  * Copyright (c) 2008, 2012, 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.attribute.*;
  29 import java.util.concurrent.TimeUnit;
  30 import java.security.AccessController;
  31 import sun.misc.Unsafe;
  32 import sun.security.action.GetPropertyAction;
  33 
  34 import static sun.nio.fs.WindowsNativeDispatcher.*;
  35 import static sun.nio.fs.WindowsConstants.*;
  36 
  37 /**
  38  * Windows implementation of DosFileAttributes/BasicFileAttributes
  39  */
  40 
  41 class WindowsFileAttributes
  42     implements DosFileAttributes
  43 {
  44     private static final Unsafe unsafe = Unsafe.getUnsafe();
  45 
  46     /*
  47      * typedef struct _BY_HANDLE_FILE_INFORMATION {
  48      *     DWORD    dwFileAttributes;
  49      *     FILETIME ftCreationTime;
  50      *     FILETIME ftLastAccessTime;
  51      *     FILETIME ftLastWriteTime;
  52      *     DWORD    dwVolumeSerialNumber;
  53      *     DWORD    nFileSizeHigh;
  54      *     DWORD    nFileSizeLow;
  55      *     DWORD    nNumberOfLinks;
  56      *     DWORD    nFileIndexHigh;
  57      *     DWORD    nFileIndexLow;
  58      * } BY_HANDLE_FILE_INFORMATION;
  59      */
  60     private static final short SIZEOF_FILE_INFORMATION  = 52;
  61     private static final short OFFSETOF_FILE_INFORMATION_ATTRIBUTES      = 0;
  62     private static final short OFFSETOF_FILE_INFORMATION_CREATETIME      = 4;
  63     private static final short OFFSETOF_FILE_INFORMATION_LASTACCESSTIME  = 12;
  64     private static final short OFFSETOF_FILE_INFORMATION_LASTWRITETIME   = 20;
  65     private static final short OFFSETOF_FILE_INFORMATION_VOLSERIALNUM    = 28;
  66     private static final short OFFSETOF_FILE_INFORMATION_SIZEHIGH        = 32;
  67     private static final short OFFSETOF_FILE_INFORMATION_SIZELOW         = 36;
  68     private static final short OFFSETOF_FILE_INFORMATION_INDEXHIGH       = 44;
  69     private static final short OFFSETOF_FILE_INFORMATION_INDEXLOW        = 48;
  70 
  71     /*
  72      * typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
  73      *   DWORD dwFileAttributes;
  74      *   FILETIME ftCreationTime;
  75      *   FILETIME ftLastAccessTime;
  76      *   FILETIME ftLastWriteTime;
  77      *   DWORD nFileSizeHigh;
  78      *   DWORD nFileSizeLow;
  79      * } WIN32_FILE_ATTRIBUTE_DATA;
  80      */
  81     private static final short SIZEOF_FILE_ATTRIBUTE_DATA = 36;
  82     private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES      = 0;
  83     private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME      = 4;
  84     private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME  = 12;
  85     private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME   = 20;
  86     private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH        = 28;
  87     private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW         = 32;
  88 
  89     /**
  90      * typedef struct _WIN32_FIND_DATA {
  91      *   DWORD dwFileAttributes;
  92      *   FILETIME ftCreationTime;
  93      *   FILETIME ftLastAccessTime;
  94      *   FILETIME ftLastWriteTime;
  95      *   DWORD nFileSizeHigh;
  96      *   DWORD nFileSizeLow;
  97      *   DWORD dwReserved0;
  98      *   DWORD dwReserved1;
  99      *   TCHAR cFileName[MAX_PATH];
 100      *   TCHAR cAlternateFileName[14];
 101      * } WIN32_FIND_DATA;
 102      */
 103     private static final short SIZEOF_FIND_DATA = 592;
 104     private static final short OFFSETOF_FIND_DATA_ATTRIBUTES = 0;
 105     private static final short OFFSETOF_FIND_DATA_CREATETIME = 4;
 106     private static final short OFFSETOF_FIND_DATA_LASTACCESSTIME = 12;
 107     private static final short OFFSETOF_FIND_DATA_LASTWRITETIME = 20;
 108     private static final short OFFSETOF_FIND_DATA_SIZEHIGH = 28;
 109     private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
 110     private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
 111 
 112     // used to adjust values between Windows and java epoch
 113     private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
 114 
 115     // indicates if accurate metadata is required (interesting on NTFS only)
 116     private static final boolean ensureAccurateMetadata;
 117     static {
 118         String propValue = AccessController.doPrivileged(
 119             new GetPropertyAction("sun.nio.fs.ensureAccurateMetadata", "false"));
 120         ensureAccurateMetadata = (propValue.length() == 0) ?
 121             true : Boolean.valueOf(propValue);
 122     }
 123 
 124     // attributes
 125     private final int fileAttrs;
 126     private final long creationTime;
 127     private final long lastAccessTime;
 128     private final long lastWriteTime;
 129     private final long size;
 130     private final int reparseTag;
 131 
 132     // additional attributes when using GetFileInformationByHandle
 133     private final int volSerialNumber;
 134     private final int fileIndexHigh;
 135     private final int fileIndexLow;
 136 
 137     /**
 138      * Convert 64-bit value representing the number of 100-nanosecond intervals
 139      * since January 1, 1601 to a FileTime.
 140      */
 141     static FileTime toFileTime(long time) {
 142         // 100ns -> us
 143         time /= 10L;
 144         // adjust to java epoch
 145         time += WINDOWS_EPOCH_IN_MICROSECONDS;
 146         return FileTime.from(time, TimeUnit.MICROSECONDS);
 147     }
 148 
 149     /**
 150      * Convert FileTime to 64-bit value representing the number of 100-nanosecond
 151      * intervals since January 1, 1601.
 152      */
 153     static long toWindowsTime(FileTime time) {
 154         long value = time.to(TimeUnit.MICROSECONDS);
 155         // adjust to Windows epoch+= 11644473600000000L;
 156         value -= WINDOWS_EPOCH_IN_MICROSECONDS;
 157         // us -> 100ns
 158         value *= 10L;
 159         return value;
 160     }
 161 
 162     /**
 163      * Initialize a new instance of this class
 164      */
 165     private WindowsFileAttributes(int fileAttrs,
 166                                   long creationTime,
 167                                   long lastAccessTime,
 168                                   long lastWriteTime,
 169                                   long size,
 170                                   int reparseTag,
 171                                   int volSerialNumber,
 172                                   int fileIndexHigh,
 173                                   int fileIndexLow)
 174     {
 175         this.fileAttrs = fileAttrs;
 176         this.creationTime = creationTime;
 177         this.lastAccessTime = lastAccessTime;
 178         this.lastWriteTime = lastWriteTime;
 179         this.size = size;
 180         this.reparseTag = reparseTag;
 181         this.volSerialNumber = volSerialNumber;
 182         this.fileIndexHigh = fileIndexHigh;
 183         this.fileIndexLow = fileIndexLow;
 184     }
 185 
 186     /**
 187      * Create a WindowsFileAttributes from a BY_HANDLE_FILE_INFORMATION structure
 188      */
 189     private static WindowsFileAttributes fromFileInformation(long address, int reparseTag) {
 190         int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
 191         long creationTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_CREATETIME);
 192         long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTACCESSTIME);
 193         long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTWRITETIME);
 194         long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZEHIGH)) << 32)
 195             + (unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZELOW) & 0xFFFFFFFFL);
 196         int volSerialNumber = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_VOLSERIALNUM);
 197         int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXHIGH);
 198         int fileIndexLow = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXLOW);
 199         return new WindowsFileAttributes(fileAttrs,
 200                                          creationTime,
 201                                          lastAccessTime,
 202                                          lastWriteTime,
 203                                          size,
 204                                          reparseTag,
 205                                          volSerialNumber,
 206                                          fileIndexHigh,
 207                                          fileIndexLow);
 208     }
 209 
 210     /**
 211      * Create a WindowsFileAttributes from a WIN32_FILE_ATTRIBUTE_DATA structure
 212      */
 213     private static WindowsFileAttributes fromFileAttributeData(long address, int reparseTag) {
 214         int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
 215         long creationTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME);
 216         long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME);
 217         long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME);
 218         long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH)) << 32)
 219             + (unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW) & 0xFFFFFFFFL);
 220         return new WindowsFileAttributes(fileAttrs,
 221                                          creationTime,
 222                                          lastAccessTime,
 223                                          lastWriteTime,
 224                                          size,
 225                                          reparseTag,
 226                                          0,  // volSerialNumber
 227                                          0,  // fileIndexHigh
 228                                          0); // fileIndexLow
 229     }
 230 
 231 
 232     /**
 233      * Allocates a native buffer for a WIN32_FIND_DATA structure
 234      */
 235     static NativeBuffer getBufferForFindData() {
 236         return NativeBuffers.getNativeBuffer(SIZEOF_FIND_DATA);
 237     }
 238 
 239     /**
 240      * Create a WindowsFileAttributes from a WIN32_FIND_DATA structure
 241      */
 242     static WindowsFileAttributes fromFindData(long address) {
 243         int fileAttrs = unsafe.getInt(address + OFFSETOF_FIND_DATA_ATTRIBUTES);
 244         long creationTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_CREATETIME);
 245         long lastAccessTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTACCESSTIME);
 246         long lastWriteTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME);
 247         long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32)
 248             + (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL);
 249         int reparseTag = isReparsePoint(fileAttrs) ?
 250             unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
 251         return new WindowsFileAttributes(fileAttrs,
 252                                          creationTime,
 253                                          lastAccessTime,
 254                                          lastWriteTime,
 255                                          size,
 256                                          reparseTag,
 257                                          0,  // volSerialNumber
 258                                          0,  // fileIndexHigh
 259                                          0); // fileIndexLow
 260     }
 261 
 262     /**
 263      * Reads the attributes of an open file
 264      */
 265     static WindowsFileAttributes readAttributes(long handle)
 266         throws WindowsException
 267     {
 268         NativeBuffer buffer = NativeBuffers
 269             .getNativeBuffer(SIZEOF_FILE_INFORMATION);
 270         try {
 271             long address = buffer.address();
 272             GetFileInformationByHandle(handle, address);
 273 
 274             // if file is a reparse point then read the tag
 275             int reparseTag = 0;
 276             int fileAttrs = unsafe
 277                 .getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
 278             if (isReparsePoint(fileAttrs)) {
 279                 int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
 280                 NativeBuffer reparseBuffer = NativeBuffers.getNativeBuffer(size);
 281                 try {
 282                     DeviceIoControlGetReparsePoint(handle, reparseBuffer.address(), size);
 283                     reparseTag = (int)unsafe.getLong(reparseBuffer.address());
 284                 } finally {
 285                     reparseBuffer.release();
 286                 }
 287             }
 288 
 289             return fromFileInformation(address, reparseTag);
 290         } finally {
 291             buffer.release();
 292         }
 293     }
 294 
 295     /**
 296      * Returns attributes of given file.
 297      */
 298     static WindowsFileAttributes get(WindowsPath path, boolean followLinks)
 299         throws WindowsException
 300     {
 301         if (!ensureAccurateMetadata) {
 302             WindowsException firstException = null;
 303 
 304             // GetFileAttributesEx is the fastest way to read the attributes
 305             NativeBuffer buffer =
 306                 NativeBuffers.getNativeBuffer(SIZEOF_FILE_ATTRIBUTE_DATA);
 307             try {
 308                 long address = buffer.address();
 309                 GetFileAttributesEx(path.getPathForWin32Calls(), address);
 310                 // if reparse point then file may be a sym link; otherwise
 311                 // just return the attributes
 312                 int fileAttrs = unsafe
 313                     .getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
 314                 if (!isReparsePoint(fileAttrs))
 315                     return fromFileAttributeData(address, 0);
 316             } catch (WindowsException x) {
 317                 if (x.lastError() != ERROR_SHARING_VIOLATION)
 318                     throw x;
 319                 firstException = x;
 320             } finally {
 321                 buffer.release();
 322             }
 323 
 324             // For sharing violations, fallback to FindFirstFile if the file
 325             // is not a root directory.
 326             if (firstException != null) {
 327                 String search = path.getPathForWin32Calls();
 328                 char last = search.charAt(search.length() -1);
 329                 if (last == ':' || last == '\\')
 330                     throw firstException;
 331                 buffer = getBufferForFindData();
 332                 try {
 333                     long handle = FindFirstFile(search, buffer.address());
 334                     FindClose(handle);
 335                     WindowsFileAttributes attrs = fromFindData(buffer.address());
 336                     // FindFirstFile does not follow sym links. Even if
 337                     // followLinks is false, there isn't sufficient information
 338                     // in the WIN32_FIND_DATA structure to know if the reparse
 339                     // point is a sym link.
 340                     if (attrs.isReparsePoint())
 341                         throw firstException;
 342                     return attrs;
 343                 } catch (WindowsException ignore) {
 344                     throw firstException;
 345                 } finally {
 346                     buffer.release();
 347                 }
 348             }
 349         }
 350 
 351         // file is reparse point so need to open file to get attributes
 352         long handle = path.openForReadAttributeAccess(followLinks);
 353         try {
 354             return readAttributes(handle);
 355         } finally {
 356             CloseHandle(handle);
 357         }
 358     }
 359 
 360     /**
 361      * Returns true if the attributes are of the same file - both files must
 362      * be open.
 363      */
 364     static boolean isSameFile(WindowsFileAttributes attrs1,
 365                               WindowsFileAttributes attrs2)
 366     {
 367         // volume serial number and file index must be the same
 368         return (attrs1.volSerialNumber == attrs2.volSerialNumber) &&
 369                (attrs1.fileIndexHigh == attrs2.fileIndexHigh) &&
 370                (attrs1.fileIndexLow == attrs2.fileIndexLow);
 371     }
 372 
 373     /**
 374      * Returns true if the attributes are of a file with a reparse point.
 375      */
 376     static boolean isReparsePoint(int attributes) {
 377         return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
 378     }
 379 
 380     // package-private
 381     int attributes() {
 382         return fileAttrs;
 383     }
 384 
 385     int volSerialNumber() {
 386         return volSerialNumber;
 387     }
 388 
 389     int fileIndexHigh() {
 390         return fileIndexHigh;
 391     }
 392 
 393     int fileIndexLow() {
 394         return fileIndexLow;
 395     }
 396 
 397     @Override
 398     public long size() {
 399         return size;
 400     }
 401 
 402     @Override
 403     public FileTime lastModifiedTime() {
 404         return toFileTime(lastWriteTime);
 405     }
 406 
 407     @Override
 408     public FileTime lastAccessTime() {
 409         return toFileTime(lastAccessTime);
 410     }
 411 
 412     @Override
 413     public FileTime creationTime() {
 414         return toFileTime(creationTime);
 415     }
 416 
 417     @Override
 418     public Object fileKey() {
 419         return null;
 420     }
 421 
 422     // package private
 423     boolean isReparsePoint() {
 424         return isReparsePoint(fileAttrs);
 425     }
 426 
 427     boolean isDirectoryLink() {
 428         return isSymbolicLink() && ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
 429     }
 430 
 431     @Override
 432     public boolean isSymbolicLink() {
 433         return reparseTag == IO_REPARSE_TAG_SYMLINK;
 434     }
 435 
 436     @Override
 437     public boolean isDirectory() {
 438         // ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link
 439         if (isSymbolicLink())
 440             return false;
 441         return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
 442     }
 443 
 444     @Override
 445     public boolean isOther() {
 446         if (isSymbolicLink())
 447             return false;
 448         // return true if device or reparse point
 449         return ((fileAttrs & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) != 0);
 450     }
 451 
 452     @Override
 453     public boolean isRegularFile() {
 454         return !isSymbolicLink() && !isDirectory() && !isOther();
 455     }
 456 
 457     @Override
 458     public boolean isReadOnly() {
 459         return (fileAttrs & FILE_ATTRIBUTE_READONLY) != 0;
 460     }
 461 
 462     @Override
 463     public boolean isHidden() {
 464         return (fileAttrs & FILE_ATTRIBUTE_HIDDEN) != 0;
 465     }
 466 
 467     @Override
 468     public boolean isArchive() {
 469         return (fileAttrs & FILE_ATTRIBUTE_ARCHIVE) != 0;
 470     }
 471 
 472     @Override
 473     public boolean isSystem() {
 474         return (fileAttrs & FILE_ATTRIBUTE_SYSTEM) != 0;
 475     }
 476 }