893N/A/*
2362N/A * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
893N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
893N/A *
893N/A * This code is free software; you can redistribute it and/or modify it
893N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
893N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
893N/A *
893N/A * This code is distributed in the hope that it will be useful, but WITHOUT
893N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
893N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
893N/A * version 2 for more details (a copy is included in the LICENSE file that
893N/A * accompanied this code).
893N/A *
893N/A * You should have received a copy of the GNU General Public License version
893N/A * 2 along with this work; if not, write to the Free Software Foundation,
893N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
893N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
893N/A */
893N/A
893N/Apackage sun.nio.fs;
893N/A
893N/Aimport java.nio.file.*;
893N/Aimport java.io.IOException;
893N/Aimport java.io.IOError;
893N/Aimport java.security.AccessController;
893N/Aimport java.security.PrivilegedAction;
893N/Aimport sun.misc.Unsafe;
893N/A
893N/Aimport static sun.nio.fs.WindowsNativeDispatcher.*;
893N/Aimport static sun.nio.fs.WindowsConstants.*;
893N/A
893N/A/**
893N/A * Utility methods for symbolic link support on Windows Vista and newer.
893N/A */
893N/A
893N/Aclass WindowsLinkSupport {
893N/A private static final Unsafe unsafe = Unsafe.getUnsafe();
893N/A
893N/A private WindowsLinkSupport() {
893N/A }
893N/A
893N/A /**
893N/A * Returns the target of a symbolic link
893N/A */
893N/A static String readLink(WindowsPath path) throws IOException {
893N/A long handle = 0L;
893N/A try {
893N/A handle = path.openForReadAttributeAccess(false); // don't follow links
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(path);
893N/A }
893N/A try {
893N/A return readLinkImpl(handle);
893N/A } finally {
893N/A CloseHandle(handle);
893N/A }
893N/A }
893N/A
893N/A /**
1576N/A * Returns the final path (all symbolic links resolved) or null if this
1576N/A * operation is not supported.
1576N/A */
1576N/A private static String getFinalPath(WindowsPath input) throws IOException {
1576N/A long h = 0;
1576N/A try {
1576N/A h = input.openForReadAttributeAccess(true);
1576N/A } catch (WindowsException x) {
1576N/A x.rethrowAsIOException(input);
1576N/A }
1576N/A try {
1576N/A return stripPrefix(GetFinalPathNameByHandle(h));
1576N/A } catch (WindowsException x) {
1576N/A // ERROR_INVALID_LEVEL is the error returned when not supported
1576N/A // (a sym link to file on FAT32 or Samba server for example)
1576N/A if (x.lastError() != ERROR_INVALID_LEVEL)
1576N/A x.rethrowAsIOException(input);
1576N/A } finally {
1576N/A CloseHandle(h);
1576N/A }
1576N/A return null;
1576N/A }
1576N/A
1576N/A /**
893N/A * Returns the final path of a given path as a String. This should be used
893N/A * prior to calling Win32 system calls that do not follow links.
893N/A */
893N/A static String getFinalPath(WindowsPath input, boolean followLinks)
893N/A throws IOException
893N/A {
893N/A WindowsFileSystem fs = input.getFileSystem();
893N/A try {
893N/A // if not following links then don't need final path
893N/A if (!followLinks || !fs.supportsLinks())
893N/A return input.getPathForWin32Calls();
893N/A
1319N/A // if file is not a sym link then don't need final path
893N/A if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) {
893N/A return input.getPathForWin32Calls();
893N/A }
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(input);
893N/A }
893N/A
1576N/A // The file is a symbolic link so attempt to get the final path
1576N/A String result = getFinalPath(input);
1576N/A if (result != null)
1576N/A return result;
893N/A
893N/A // Fallback: read target of link, resolve against parent, and repeat
893N/A // until file is not a link.
893N/A WindowsPath target = input;
893N/A int linkCount = 0;
893N/A do {
893N/A try {
893N/A WindowsFileAttributes attrs =
893N/A WindowsFileAttributes.get(target, false);
893N/A // non a link so we are done
893N/A if (!attrs.isSymbolicLink()) {
893N/A return target.getPathForWin32Calls();
893N/A }
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(target);
893N/A }
893N/A WindowsPath link = WindowsPath
893N/A .createFromNormalizedPath(fs, readLink(target));
893N/A WindowsPath parent = target.getParent();
893N/A if (parent == null) {
893N/A // no parent so use parent of absolute path
893N/A final WindowsPath t = target;
893N/A target = AccessController
893N/A .doPrivileged(new PrivilegedAction<WindowsPath>() {
893N/A @Override
893N/A public WindowsPath run() {
893N/A return t.toAbsolutePath();
893N/A }});
893N/A parent = target.getParent();
893N/A }
893N/A target = parent.resolve(link);
893N/A
893N/A } while (++linkCount < 32);
893N/A
893N/A throw new FileSystemException(input.getPathForExceptionMessage(), null,
893N/A "Too many links");
893N/A }
893N/A
893N/A /**
893N/A * Returns the actual path of a file, optionally resolving all symbolic
893N/A * links.
893N/A */
893N/A static String getRealPath(WindowsPath input, boolean resolveLinks)
893N/A throws IOException
893N/A {
893N/A WindowsFileSystem fs = input.getFileSystem();
1576N/A if (resolveLinks && !fs.supportsLinks())
893N/A resolveLinks = false;
893N/A
893N/A // Start with absolute path
893N/A String path = null;
893N/A try {
893N/A path = input.toAbsolutePath().toString();
893N/A } catch (IOError x) {
893N/A throw (IOException)(x.getCause());
893N/A }
893N/A
893N/A // Collapse "." and ".."
1576N/A if (path.indexOf('.') >= 0) {
1576N/A try {
1576N/A path = GetFullPathName(path);
1576N/A } catch (WindowsException x) {
1576N/A x.rethrowAsIOException(input);
1576N/A }
893N/A }
893N/A
893N/A // string builder to build up components of path
893N/A StringBuilder sb = new StringBuilder(path.length());
893N/A
893N/A // Copy root component
893N/A int start;
893N/A char c0 = path.charAt(0);
893N/A char c1 = path.charAt(1);
893N/A if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') &&
893N/A c1 == ':' && path.charAt(2) == '\\') {
893N/A // Driver specifier
893N/A sb.append(Character.toUpperCase(c0));
893N/A sb.append(":\\");
893N/A start = 3;
893N/A } else if (c0 == '\\' && c1 == '\\') {
893N/A // UNC pathname, begins with "\\\\host\\share"
893N/A int last = path.length() - 1;
893N/A int pos = path.indexOf('\\', 2);
893N/A // skip both server and share names
893N/A if (pos == -1 || (pos == last)) {
893N/A // The UNC does not have a share name (collapsed by GetFullPathName)
893N/A throw new FileSystemException(input.getPathForExceptionMessage(),
893N/A null, "UNC has invalid share");
893N/A }
893N/A pos = path.indexOf('\\', pos+1);
893N/A if (pos < 0) {
893N/A pos = last;
893N/A sb.append(path).append("\\");
893N/A } else {
893N/A sb.append(path, 0, pos+1);
893N/A }
893N/A start = pos + 1;
893N/A } else {
893N/A throw new AssertionError("path type not recognized");
893N/A }
893N/A
1576N/A // if the result is only a root component then we simply check it exists
1576N/A if (start >= path.length()) {
1576N/A String result = sb.toString();
1576N/A try {
1576N/A GetFileAttributes(result);
1576N/A } catch (WindowsException x) {
1576N/A x.rethrowAsIOException(path);
1576N/A }
1576N/A return result;
893N/A }
893N/A
893N/A // iterate through each component to get its actual name in the
893N/A // directory
893N/A int curr = start;
893N/A while (curr < path.length()) {
893N/A int next = path.indexOf('\\', curr);
893N/A int end = (next == -1) ? path.length() : next;
893N/A String search = sb.toString() + path.substring(curr, end);
893N/A try {
6140N/A FirstFile fileData = FindFirstFile(WindowsPath.addPrefixIfNeeded(search));
1576N/A FindClose(fileData.handle());
1576N/A
1576N/A // if a reparse point is encountered then we must return the
1576N/A // final path.
1576N/A if (resolveLinks &&
1576N/A WindowsFileAttributes.isReparsePoint(fileData.attributes()))
1576N/A {
1576N/A String result = getFinalPath(input);
1576N/A if (result == null) {
1576N/A // Fallback to slow path, usually because there is a sym
1576N/A // link to a file system that doesn't support sym links.
1576N/A WindowsPath resolved = resolveAllLinks(
1576N/A WindowsPath.createFromNormalizedPath(fs, path));
1576N/A result = getRealPath(resolved, false);
893N/A }
1576N/A return result;
1576N/A }
1576N/A
1576N/A // add the name to the result
1576N/A sb.append(fileData.name());
1576N/A if (next != -1) {
1576N/A sb.append('\\');
893N/A }
893N/A } catch (WindowsException e) {
893N/A e.rethrowAsIOException(path);
893N/A }
893N/A curr = end + 1;
893N/A }
893N/A
893N/A return sb.toString();
893N/A }
893N/A
893N/A /**
893N/A * Returns target of a symbolic link given the handle of an open file
893N/A * (that should be a link).
893N/A */
893N/A private static String readLinkImpl(long handle) throws IOException {
893N/A int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
893N/A NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
893N/A try {
893N/A try {
893N/A DeviceIoControlGetReparsePoint(handle, buffer.address(), size);
893N/A } catch (WindowsException x) {
893N/A // FIXME: exception doesn't have file name
893N/A if (x.lastError() == ERROR_NOT_A_REPARSE_POINT)
893N/A throw new NotLinkException(null, null, x.errorString());
893N/A x.rethrowAsIOException((String)null);
893N/A }
893N/A
893N/A /*
893N/A * typedef struct _REPARSE_DATA_BUFFER {
893N/A * ULONG ReparseTag;
893N/A * USHORT ReparseDataLength;
893N/A * USHORT Reserved;
893N/A * union {
893N/A * struct {
893N/A * USHORT SubstituteNameOffset;
893N/A * USHORT SubstituteNameLength;
893N/A * USHORT PrintNameOffset;
893N/A * USHORT PrintNameLength;
893N/A * WCHAR PathBuffer[1];
893N/A * } SymbolicLinkReparseBuffer;
893N/A * struct {
893N/A * USHORT SubstituteNameOffset;
893N/A * USHORT SubstituteNameLength;
893N/A * USHORT PrintNameOffset;
893N/A * USHORT PrintNameLength;
893N/A * WCHAR PathBuffer[1];
893N/A * } MountPointReparseBuffer;
893N/A * struct {
893N/A * UCHAR DataBuffer[1];
893N/A * } GenericReparseBuffer;
893N/A * };
893N/A * } REPARSE_DATA_BUFFER
893N/A */
893N/A final short OFFSETOF_REPARSETAG = 0;
893N/A final short OFFSETOF_PATHOFFSET = 8;
893N/A final short OFFSETOF_PATHLENGTH = 10;
893N/A final short OFFSETOF_PATHBUFFER = 16 + 4; // check this
893N/A
893N/A int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG);
893N/A if (tag != IO_REPARSE_TAG_SYMLINK) {
893N/A // FIXME: exception doesn't have file name
893N/A throw new NotLinkException(null, null, "Reparse point is not a symbolic link");
893N/A }
893N/A
893N/A // get offset and length of target
893N/A short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET);
893N/A short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH);
893N/A if ((nameLengthInBytes % 2) != 0)
893N/A throw new FileSystemException(null, null, "Symbolic link corrupted");
893N/A
893N/A // copy into char array
893N/A char[] name = new char[nameLengthInBytes/2];
893N/A unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset,
893N/A name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
893N/A
893N/A // remove special prefix
893N/A String target = stripPrefix(new String(name));
893N/A if (target.length() == 0) {
893N/A throw new IOException("Symbolic link target is invalid");
893N/A }
893N/A return target;
893N/A } finally {
893N/A buffer.release();
893N/A }
893N/A }
893N/A
893N/A /**
893N/A * Resolve all symbolic-links in a given absolute and normalized path
893N/A */
1576N/A private static WindowsPath resolveAllLinks(WindowsPath path)
893N/A throws IOException
893N/A {
893N/A assert path.isAbsolute();
893N/A WindowsFileSystem fs = path.getFileSystem();
893N/A
893N/A // iterate through each name element of the path, resolving links as
893N/A // we go.
893N/A int linkCount = 0;
893N/A int elem = 0;
893N/A while (elem < path.getNameCount()) {
893N/A WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1));
893N/A
893N/A WindowsFileAttributes attrs = null;
893N/A try {
893N/A attrs = WindowsFileAttributes.get(current, false);
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(current);
893N/A }
893N/A
893N/A /**
893N/A * If a symbolic link then we resolve it against the parent
893N/A * of the current name element. We then resolve any remaining
893N/A * part of the path against the result. The target of the link
893N/A * may have "." and ".." components so re-normalize and restart
893N/A * the process from the first element.
893N/A */
893N/A if (attrs.isSymbolicLink()) {
893N/A linkCount++;
893N/A if (linkCount > 32)
893N/A throw new IOException("Too many links");
893N/A WindowsPath target = WindowsPath
893N/A .createFromNormalizedPath(fs, readLink(current));
893N/A WindowsPath remainder = null;
893N/A int count = path.getNameCount();
893N/A if ((elem+1) < count) {
893N/A remainder = path.subpath(elem+1, count);
893N/A }
893N/A path = current.getParent().resolve(target);
893N/A try {
893N/A String full = GetFullPathName(path.toString());
893N/A if (!full.equals(path.toString())) {
893N/A path = WindowsPath.createFromNormalizedPath(fs, full);
893N/A }
893N/A } catch (WindowsException x) {
893N/A x.rethrowAsIOException(path);
893N/A }
893N/A if (remainder != null) {
893N/A path = path.resolve(remainder);
893N/A }
893N/A
893N/A // reset
893N/A elem = 0;
893N/A } else {
893N/A // not a link
893N/A elem++;
893N/A }
893N/A }
893N/A
1576N/A return path;
893N/A }
893N/A
893N/A /**
893N/A * Strip long path or symbolic link prefix from path
893N/A */
893N/A private static String stripPrefix(String path) {
893N/A // prefix for resolved/long path
893N/A if (path.startsWith("\\\\?\\")) {
893N/A if (path.startsWith("\\\\?\\UNC\\")) {
893N/A path = "\\" + path.substring(7);
893N/A } else {
893N/A path = path.substring(4);
893N/A }
893N/A return path;
893N/A }
893N/A
893N/A // prefix for target of symbolic link
893N/A if (path.startsWith("\\??\\")) {
893N/A if (path.startsWith("\\??\\UNC\\")) {
893N/A path = "\\" + path.substring(7);
893N/A } else {
893N/A path = path.substring(4);
893N/A }
893N/A return path;
893N/A }
893N/A return path;
893N/A }
893N/A}