src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java
changeset 47216 71c04702a3d5
parent 37781 71ed5645f17c
child 53018 8bf9268df0e2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.concurrent.TimeUnit;
+import jdk.internal.misc.Unsafe;
+import sun.security.action.GetPropertyAction;
+
+import static sun.nio.fs.WindowsNativeDispatcher.*;
+import static sun.nio.fs.WindowsConstants.*;
+
+/**
+ * Windows implementation of DosFileAttributes/BasicFileAttributes
+ */
+
+class WindowsFileAttributes
+    implements DosFileAttributes
+{
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    /*
+     * typedef struct _BY_HANDLE_FILE_INFORMATION {
+     *     DWORD    dwFileAttributes;
+     *     FILETIME ftCreationTime;
+     *     FILETIME ftLastAccessTime;
+     *     FILETIME ftLastWriteTime;
+     *     DWORD    dwVolumeSerialNumber;
+     *     DWORD    nFileSizeHigh;
+     *     DWORD    nFileSizeLow;
+     *     DWORD    nNumberOfLinks;
+     *     DWORD    nFileIndexHigh;
+     *     DWORD    nFileIndexLow;
+     * } BY_HANDLE_FILE_INFORMATION;
+     */
+    private static final short SIZEOF_FILE_INFORMATION  = 52;
+    private static final short OFFSETOF_FILE_INFORMATION_ATTRIBUTES      = 0;
+    private static final short OFFSETOF_FILE_INFORMATION_CREATETIME      = 4;
+    private static final short OFFSETOF_FILE_INFORMATION_LASTACCESSTIME  = 12;
+    private static final short OFFSETOF_FILE_INFORMATION_LASTWRITETIME   = 20;
+    private static final short OFFSETOF_FILE_INFORMATION_VOLSERIALNUM    = 28;
+    private static final short OFFSETOF_FILE_INFORMATION_SIZEHIGH        = 32;
+    private static final short OFFSETOF_FILE_INFORMATION_SIZELOW         = 36;
+    private static final short OFFSETOF_FILE_INFORMATION_INDEXHIGH       = 44;
+    private static final short OFFSETOF_FILE_INFORMATION_INDEXLOW        = 48;
+
+    /*
+     * typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
+     *   DWORD dwFileAttributes;
+     *   FILETIME ftCreationTime;
+     *   FILETIME ftLastAccessTime;
+     *   FILETIME ftLastWriteTime;
+     *   DWORD nFileSizeHigh;
+     *   DWORD nFileSizeLow;
+     * } WIN32_FILE_ATTRIBUTE_DATA;
+     */
+    private static final short SIZEOF_FILE_ATTRIBUTE_DATA = 36;
+    private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES      = 0;
+    private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME      = 4;
+    private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME  = 12;
+    private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME   = 20;
+    private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH        = 28;
+    private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW         = 32;
+
+    /**
+     * typedef struct _WIN32_FIND_DATA {
+     *   DWORD dwFileAttributes;
+     *   FILETIME ftCreationTime;
+     *   FILETIME ftLastAccessTime;
+     *   FILETIME ftLastWriteTime;
+     *   DWORD nFileSizeHigh;
+     *   DWORD nFileSizeLow;
+     *   DWORD dwReserved0;
+     *   DWORD dwReserved1;
+     *   TCHAR cFileName[MAX_PATH];
+     *   TCHAR cAlternateFileName[14];
+     * } WIN32_FIND_DATA;
+     */
+    private static final short SIZEOF_FIND_DATA = 592;
+    private static final short OFFSETOF_FIND_DATA_ATTRIBUTES = 0;
+    private static final short OFFSETOF_FIND_DATA_CREATETIME = 4;
+    private static final short OFFSETOF_FIND_DATA_LASTACCESSTIME = 12;
+    private static final short OFFSETOF_FIND_DATA_LASTWRITETIME = 20;
+    private static final short OFFSETOF_FIND_DATA_SIZEHIGH = 28;
+    private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
+    private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
+
+    // used to adjust values between Windows and java epoch
+    private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
+
+    // indicates if accurate metadata is required (interesting on NTFS only)
+    private static final boolean ensureAccurateMetadata;
+    static {
+        String propValue = GetPropertyAction.privilegedGetProperty(
+            "sun.nio.fs.ensureAccurateMetadata", "false");
+        ensureAccurateMetadata = (propValue.length() == 0) ?
+            true : Boolean.valueOf(propValue);
+    }
+
+    // attributes
+    private final int fileAttrs;
+    private final long creationTime;
+    private final long lastAccessTime;
+    private final long lastWriteTime;
+    private final long size;
+    private final int reparseTag;
+
+    // additional attributes when using GetFileInformationByHandle
+    private final int volSerialNumber;
+    private final int fileIndexHigh;
+    private final int fileIndexLow;
+
+    /**
+     * Convert 64-bit value representing the number of 100-nanosecond intervals
+     * since January 1, 1601 to a FileTime.
+     */
+    static FileTime toFileTime(long time) {
+        // 100ns -> us
+        time /= 10L;
+        // adjust to java epoch
+        time += WINDOWS_EPOCH_IN_MICROSECONDS;
+        return FileTime.from(time, TimeUnit.MICROSECONDS);
+    }
+
+    /**
+     * Convert FileTime to 64-bit value representing the number of 100-nanosecond
+     * intervals since January 1, 1601.
+     */
+    static long toWindowsTime(FileTime time) {
+        long value = time.to(TimeUnit.MICROSECONDS);
+        // adjust to Windows epoch+= 11644473600000000L;
+        value -= WINDOWS_EPOCH_IN_MICROSECONDS;
+        // us -> 100ns
+        value *= 10L;
+        return value;
+    }
+
+    /**
+     * Initialize a new instance of this class
+     */
+    private WindowsFileAttributes(int fileAttrs,
+                                  long creationTime,
+                                  long lastAccessTime,
+                                  long lastWriteTime,
+                                  long size,
+                                  int reparseTag,
+                                  int volSerialNumber,
+                                  int fileIndexHigh,
+                                  int fileIndexLow)
+    {
+        this.fileAttrs = fileAttrs;
+        this.creationTime = creationTime;
+        this.lastAccessTime = lastAccessTime;
+        this.lastWriteTime = lastWriteTime;
+        this.size = size;
+        this.reparseTag = reparseTag;
+        this.volSerialNumber = volSerialNumber;
+        this.fileIndexHigh = fileIndexHigh;
+        this.fileIndexLow = fileIndexLow;
+    }
+
+    /**
+     * Create a WindowsFileAttributes from a BY_HANDLE_FILE_INFORMATION structure
+     */
+    private static WindowsFileAttributes fromFileInformation(long address, int reparseTag) {
+        int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
+        long creationTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_CREATETIME);
+        long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTACCESSTIME);
+        long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTWRITETIME);
+        long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZEHIGH)) << 32)
+            + (unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZELOW) & 0xFFFFFFFFL);
+        int volSerialNumber = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_VOLSERIALNUM);
+        int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXHIGH);
+        int fileIndexLow = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXLOW);
+        return new WindowsFileAttributes(fileAttrs,
+                                         creationTime,
+                                         lastAccessTime,
+                                         lastWriteTime,
+                                         size,
+                                         reparseTag,
+                                         volSerialNumber,
+                                         fileIndexHigh,
+                                         fileIndexLow);
+    }
+
+    /**
+     * Create a WindowsFileAttributes from a WIN32_FILE_ATTRIBUTE_DATA structure
+     */
+    private static WindowsFileAttributes fromFileAttributeData(long address, int reparseTag) {
+        int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
+        long creationTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME);
+        long lastAccessTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME);
+        long lastWriteTime = unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME);
+        long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH)) << 32)
+            + (unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW) & 0xFFFFFFFFL);
+        return new WindowsFileAttributes(fileAttrs,
+                                         creationTime,
+                                         lastAccessTime,
+                                         lastWriteTime,
+                                         size,
+                                         reparseTag,
+                                         0,  // volSerialNumber
+                                         0,  // fileIndexHigh
+                                         0); // fileIndexLow
+    }
+
+
+    /**
+     * Allocates a native buffer for a WIN32_FIND_DATA structure
+     */
+    static NativeBuffer getBufferForFindData() {
+        return NativeBuffers.getNativeBuffer(SIZEOF_FIND_DATA);
+    }
+
+    /**
+     * Create a WindowsFileAttributes from a WIN32_FIND_DATA structure
+     */
+    static WindowsFileAttributes fromFindData(long address) {
+        int fileAttrs = unsafe.getInt(address + OFFSETOF_FIND_DATA_ATTRIBUTES);
+        long creationTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_CREATETIME);
+        long lastAccessTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTACCESSTIME);
+        long lastWriteTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME);
+        long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32)
+            + (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL);
+        int reparseTag = isReparsePoint(fileAttrs) ?
+            unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
+        return new WindowsFileAttributes(fileAttrs,
+                                         creationTime,
+                                         lastAccessTime,
+                                         lastWriteTime,
+                                         size,
+                                         reparseTag,
+                                         0,  // volSerialNumber
+                                         0,  // fileIndexHigh
+                                         0); // fileIndexLow
+    }
+
+    /**
+     * Reads the attributes of an open file
+     */
+    static WindowsFileAttributes readAttributes(long handle)
+        throws WindowsException
+    {
+        NativeBuffer buffer = NativeBuffers
+            .getNativeBuffer(SIZEOF_FILE_INFORMATION);
+        try {
+            long address = buffer.address();
+            GetFileInformationByHandle(handle, address);
+
+            // if file is a reparse point then read the tag
+            int reparseTag = 0;
+            int fileAttrs = unsafe
+                .getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
+            if (isReparsePoint(fileAttrs)) {
+                int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
+                NativeBuffer reparseBuffer = NativeBuffers.getNativeBuffer(size);
+                try {
+                    DeviceIoControlGetReparsePoint(handle, reparseBuffer.address(), size);
+                    reparseTag = (int)unsafe.getLong(reparseBuffer.address());
+                } finally {
+                    reparseBuffer.release();
+                }
+            }
+
+            return fromFileInformation(address, reparseTag);
+        } finally {
+            buffer.release();
+        }
+    }
+
+    /**
+     * Returns attributes of given file.
+     */
+    static WindowsFileAttributes get(WindowsPath path, boolean followLinks)
+        throws WindowsException
+    {
+        if (!ensureAccurateMetadata) {
+            WindowsException firstException = null;
+
+            // GetFileAttributesEx is the fastest way to read the attributes
+            NativeBuffer buffer =
+                NativeBuffers.getNativeBuffer(SIZEOF_FILE_ATTRIBUTE_DATA);
+            try {
+                long address = buffer.address();
+                GetFileAttributesEx(path.getPathForWin32Calls(), address);
+                // if reparse point then file may be a sym link; otherwise
+                // just return the attributes
+                int fileAttrs = unsafe
+                    .getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
+                if (!isReparsePoint(fileAttrs))
+                    return fromFileAttributeData(address, 0);
+            } catch (WindowsException x) {
+                if (x.lastError() != ERROR_SHARING_VIOLATION)
+                    throw x;
+                firstException = x;
+            } finally {
+                buffer.release();
+            }
+
+            // For sharing violations, fallback to FindFirstFile if the file
+            // is not a root directory.
+            if (firstException != null) {
+                String search = path.getPathForWin32Calls();
+                char last = search.charAt(search.length() -1);
+                if (last == ':' || last == '\\')
+                    throw firstException;
+                buffer = getBufferForFindData();
+                try {
+                    long handle = FindFirstFile(search, buffer.address());
+                    FindClose(handle);
+                    WindowsFileAttributes attrs = fromFindData(buffer.address());
+                    // FindFirstFile does not follow sym links. Even if
+                    // followLinks is false, there isn't sufficient information
+                    // in the WIN32_FIND_DATA structure to know if the reparse
+                    // point is a sym link.
+                    if (attrs.isReparsePoint())
+                        throw firstException;
+                    return attrs;
+                } catch (WindowsException ignore) {
+                    throw firstException;
+                } finally {
+                    buffer.release();
+                }
+            }
+        }
+
+        // file is reparse point so need to open file to get attributes
+        long handle = path.openForReadAttributeAccess(followLinks);
+        try {
+            return readAttributes(handle);
+        } finally {
+            CloseHandle(handle);
+        }
+    }
+
+    /**
+     * Returns true if the attributes are of the same file - both files must
+     * be open.
+     */
+    static boolean isSameFile(WindowsFileAttributes attrs1,
+                              WindowsFileAttributes attrs2)
+    {
+        // volume serial number and file index must be the same
+        return (attrs1.volSerialNumber == attrs2.volSerialNumber) &&
+               (attrs1.fileIndexHigh == attrs2.fileIndexHigh) &&
+               (attrs1.fileIndexLow == attrs2.fileIndexLow);
+    }
+
+    /**
+     * Returns true if the attributes are of a file with a reparse point.
+     */
+    static boolean isReparsePoint(int attributes) {
+        return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+    }
+
+    // package-private
+    int attributes() {
+        return fileAttrs;
+    }
+
+    int volSerialNumber() {
+        return volSerialNumber;
+    }
+
+    int fileIndexHigh() {
+        return fileIndexHigh;
+    }
+
+    int fileIndexLow() {
+        return fileIndexLow;
+    }
+
+    @Override
+    public long size() {
+        return size;
+    }
+
+    @Override
+    public FileTime lastModifiedTime() {
+        return toFileTime(lastWriteTime);
+    }
+
+    @Override
+    public FileTime lastAccessTime() {
+        return toFileTime(lastAccessTime);
+    }
+
+    @Override
+    public FileTime creationTime() {
+        return toFileTime(creationTime);
+    }
+
+    @Override
+    public Object fileKey() {
+        return null;
+    }
+
+    // package private
+    boolean isReparsePoint() {
+        return isReparsePoint(fileAttrs);
+    }
+
+    boolean isDirectoryLink() {
+        return isSymbolicLink() && ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
+    }
+
+    @Override
+    public boolean isSymbolicLink() {
+        return reparseTag == IO_REPARSE_TAG_SYMLINK;
+    }
+
+    @Override
+    public boolean isDirectory() {
+        // ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link
+        if (isSymbolicLink())
+            return false;
+        return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0);
+    }
+
+    @Override
+    public boolean isOther() {
+        if (isSymbolicLink())
+            return false;
+        // return true if device or reparse point
+        return ((fileAttrs & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) != 0);
+    }
+
+    @Override
+    public boolean isRegularFile() {
+        return !isSymbolicLink() && !isDirectory() && !isOther();
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return (fileAttrs & FILE_ATTRIBUTE_READONLY) != 0;
+    }
+
+    @Override
+    public boolean isHidden() {
+        return (fileAttrs & FILE_ATTRIBUTE_HIDDEN) != 0;
+    }
+
+    @Override
+    public boolean isArchive() {
+        return (fileAttrs & FILE_ATTRIBUTE_ARCHIVE) != 0;
+    }
+
+    @Override
+    public boolean isSystem() {
+        return (fileAttrs & FILE_ATTRIBUTE_SYSTEM) != 0;
+    }
+}