Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java
32288 views
/*1* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.fs;2627import java.nio.file.*;28import java.io.IOException;29import java.io.IOError;30import java.security.AccessController;31import java.security.PrivilegedAction;32import sun.misc.Unsafe;3334import static sun.nio.fs.WindowsNativeDispatcher.*;35import static sun.nio.fs.WindowsConstants.*;3637/**38* Utility methods for symbolic link support on Windows Vista and newer.39*/4041class WindowsLinkSupport {42private static final Unsafe unsafe = Unsafe.getUnsafe();4344private WindowsLinkSupport() {45}4647/**48* Returns the target of a symbolic link49*/50static String readLink(WindowsPath path) throws IOException {51long handle = 0L;52try {53handle = path.openForReadAttributeAccess(false); // don't follow links54} catch (WindowsException x) {55x.rethrowAsIOException(path);56}57try {58return readLinkImpl(handle);59} finally {60CloseHandle(handle);61}62}6364/**65* Returns the final path (all symbolic links resolved) or null if this66* operation is not supported.67*/68static String getFinalPath(WindowsPath input) throws IOException {69long h = 0;70try {71h = input.openForReadAttributeAccess(true);72} catch (WindowsException x) {73x.rethrowAsIOException(input);74}75try {76return stripPrefix(GetFinalPathNameByHandle(h));77} catch (WindowsException x) {78// ERROR_INVALID_LEVEL is the error returned when not supported79// (a sym link to file on FAT32 or Samba server for example)80if (x.lastError() != ERROR_INVALID_LEVEL)81x.rethrowAsIOException(input);82} finally {83CloseHandle(h);84}85return null;86}8788/**89* Returns the final path of a given path as a String. This should be used90* prior to calling Win32 system calls that do not follow links.91*/92static String getFinalPath(WindowsPath input, boolean followLinks)93throws IOException94{95WindowsFileSystem fs = input.getFileSystem();96try {97// if not following links then don't need final path98if (!followLinks || !fs.supportsLinks())99return input.getPathForWin32Calls();100101// if file is not a sym link then don't need final path102if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) {103return input.getPathForWin32Calls();104}105} catch (WindowsException x) {106x.rethrowAsIOException(input);107}108109// The file is a symbolic link so attempt to get the final path110String result = getFinalPath(input);111if (result != null)112return result;113114// Fallback: read target of link, resolve against parent, and repeat115// until file is not a link.116WindowsPath target = input;117int linkCount = 0;118do {119try {120WindowsFileAttributes attrs =121WindowsFileAttributes.get(target, false);122// non a link so we are done123if (!attrs.isSymbolicLink()) {124return target.getPathForWin32Calls();125}126} catch (WindowsException x) {127x.rethrowAsIOException(target);128}129WindowsPath link = WindowsPath130.createFromNormalizedPath(fs, readLink(target));131WindowsPath parent = target.getParent();132if (parent == null) {133// no parent so use parent of absolute path134final WindowsPath t = target;135target = AccessController136.doPrivileged(new PrivilegedAction<WindowsPath>() {137@Override138public WindowsPath run() {139return t.toAbsolutePath();140}});141parent = target.getParent();142}143target = parent.resolve(link);144145} while (++linkCount < 32);146147throw new FileSystemException(input.getPathForExceptionMessage(), null,148"Too many links");149}150151/**152* Returns the actual path of a file, optionally resolving all symbolic153* links.154*/155static String getRealPath(WindowsPath input, boolean resolveLinks)156throws IOException157{158WindowsFileSystem fs = input.getFileSystem();159if (resolveLinks && !fs.supportsLinks())160resolveLinks = false;161162// Start with absolute path163String path = null;164try {165path = input.toAbsolutePath().toString();166} catch (IOError x) {167throw (IOException)(x.getCause());168}169170// Collapse "." and ".."171if (path.indexOf('.') >= 0) {172try {173path = GetFullPathName(path);174} catch (WindowsException x) {175x.rethrowAsIOException(input);176}177}178179// string builder to build up components of path180StringBuilder sb = new StringBuilder(path.length());181182// Copy root component183int start;184char c0 = path.charAt(0);185char c1 = path.charAt(1);186if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') &&187c1 == ':' && path.charAt(2) == '\\') {188// Driver specifier189sb.append(Character.toUpperCase(c0));190sb.append(":\\");191start = 3;192} else if (c0 == '\\' && c1 == '\\') {193// UNC pathname, begins with "\\\\host\\share"194int last = path.length() - 1;195int pos = path.indexOf('\\', 2);196// skip both server and share names197if (pos == -1 || (pos == last)) {198// The UNC does not have a share name (collapsed by GetFullPathName)199throw new FileSystemException(input.getPathForExceptionMessage(),200null, "UNC has invalid share");201}202pos = path.indexOf('\\', pos+1);203if (pos < 0) {204pos = last;205sb.append(path).append("\\");206} else {207sb.append(path, 0, pos+1);208}209start = pos + 1;210} else {211throw new AssertionError("path type not recognized");212}213214// if the result is only a root component then we simply check it exists215if (start >= path.length()) {216String result = sb.toString();217try {218GetFileAttributes(result);219} catch (WindowsException x) {220x.rethrowAsIOException(path);221}222return result;223}224225// iterate through each component to get its actual name in the226// directory227int curr = start;228while (curr < path.length()) {229int next = path.indexOf('\\', curr);230int end = (next == -1) ? path.length() : next;231String search = sb.toString() + path.substring(curr, end);232try {233FirstFile fileData = FindFirstFile(WindowsPath.addPrefixIfNeeded(search));234FindClose(fileData.handle());235236// if a reparse point is encountered then we must return the237// final path.238if (resolveLinks &&239WindowsFileAttributes.isReparsePoint(fileData.attributes()))240{241String result = getFinalPath(input);242if (result == null) {243// Fallback to slow path, usually because there is a sym244// link to a file system that doesn't support sym links.245WindowsPath resolved = resolveAllLinks(246WindowsPath.createFromNormalizedPath(fs, path));247result = getRealPath(resolved, false);248}249return result;250}251252// add the name to the result253sb.append(fileData.name());254if (next != -1) {255sb.append('\\');256}257} catch (WindowsException e) {258e.rethrowAsIOException(path);259}260curr = end + 1;261}262263return sb.toString();264}265266/**267* Returns target of a symbolic link given the handle of an open file268* (that should be a link).269*/270private static String readLinkImpl(long handle) throws IOException {271int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;272NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);273try {274try {275DeviceIoControlGetReparsePoint(handle, buffer.address(), size);276} catch (WindowsException x) {277// FIXME: exception doesn't have file name278if (x.lastError() == ERROR_NOT_A_REPARSE_POINT)279throw new NotLinkException(null, null, x.errorString());280x.rethrowAsIOException((String)null);281}282283/*284* typedef struct _REPARSE_DATA_BUFFER {285* ULONG ReparseTag;286* USHORT ReparseDataLength;287* USHORT Reserved;288* union {289* struct {290* USHORT SubstituteNameOffset;291* USHORT SubstituteNameLength;292* USHORT PrintNameOffset;293* USHORT PrintNameLength;294* WCHAR PathBuffer[1];295* } SymbolicLinkReparseBuffer;296* struct {297* USHORT SubstituteNameOffset;298* USHORT SubstituteNameLength;299* USHORT PrintNameOffset;300* USHORT PrintNameLength;301* WCHAR PathBuffer[1];302* } MountPointReparseBuffer;303* struct {304* UCHAR DataBuffer[1];305* } GenericReparseBuffer;306* };307* } REPARSE_DATA_BUFFER308*/309final short OFFSETOF_REPARSETAG = 0;310final short OFFSETOF_PATHOFFSET = 8;311final short OFFSETOF_PATHLENGTH = 10;312final short OFFSETOF_PATHBUFFER = 16 + 4; // check this313314int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG);315if (tag != IO_REPARSE_TAG_SYMLINK) {316// FIXME: exception doesn't have file name317throw new NotLinkException(null, null, "Reparse point is not a symbolic link");318}319320// get offset and length of target321short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET);322short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH);323if ((nameLengthInBytes % 2) != 0)324throw new FileSystemException(null, null, "Symbolic link corrupted");325326// copy into char array327char[] name = new char[nameLengthInBytes/2];328unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset,329name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);330331// remove special prefix332String target = stripPrefix(new String(name));333if (target.length() == 0) {334throw new IOException("Symbolic link target is invalid");335}336return target;337} finally {338buffer.release();339}340}341342/**343* Resolve all symbolic-links in a given absolute and normalized path344*/345private static WindowsPath resolveAllLinks(WindowsPath path)346throws IOException347{348assert path.isAbsolute();349WindowsFileSystem fs = path.getFileSystem();350351// iterate through each name element of the path, resolving links as352// we go.353int linkCount = 0;354int elem = 0;355while (elem < path.getNameCount()) {356WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1));357358WindowsFileAttributes attrs = null;359try {360attrs = WindowsFileAttributes.get(current, false);361} catch (WindowsException x) {362x.rethrowAsIOException(current);363}364365/**366* If a symbolic link then we resolve it against the parent367* of the current name element. We then resolve any remaining368* part of the path against the result. The target of the link369* may have "." and ".." components so re-normalize and restart370* the process from the first element.371*/372if (attrs.isSymbolicLink()) {373linkCount++;374if (linkCount > 32)375throw new IOException("Too many links");376WindowsPath target = WindowsPath377.createFromNormalizedPath(fs, readLink(current));378WindowsPath remainder = null;379int count = path.getNameCount();380if ((elem+1) < count) {381remainder = path.subpath(elem+1, count);382}383path = current.getParent().resolve(target);384try {385String full = GetFullPathName(path.toString());386if (!full.equals(path.toString())) {387path = WindowsPath.createFromNormalizedPath(fs, full);388}389} catch (WindowsException x) {390x.rethrowAsIOException(path);391}392if (remainder != null) {393path = path.resolve(remainder);394}395396// reset397elem = 0;398} else {399// not a link400elem++;401}402}403404return path;405}406407/**408* Strip long path or symbolic link prefix from path409*/410private static String stripPrefix(String path) {411// prefix for resolved/long path412if (path.startsWith("\\\\?\\")) {413if (path.startsWith("\\\\?\\UNC\\")) {414path = "\\" + path.substring(7);415} else {416path = path.substring(4);417}418return path;419}420421// prefix for target of symbolic link422if (path.startsWith("\\??\\")) {423if (path.startsWith("\\??\\UNC\\")) {424path = "\\" + path.substring(7);425} else {426path = path.substring(4);427}428return path;429}430return path;431}432}433434435