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